
本案例要求编写一个**斗地主的洗牌发牌**程序，要求按照斗地主的规则完成洗牌发牌的过程。

## 任务描述

一副扑克总共有54张牌，牌面由花色和数字组成（包括J、Q、K、A字母）组成，花色有♠、♥、♦、♣ 四种，分别表示黑桃、红桃、方块、梅花，小☺、大☻分别表示小王和大王。

斗地主游戏共有3位玩家参与，首先将这54张牌的顺序打乱每人轮流摸一次牌，剩余3张留作底牌，然后在控制台打印3位玩家的牌和3张底牌。

## 运行结果

![](https://static.seahi.me/2024/12/202412030938880.png)

## 实现思路

### 1. 准备牌面元素

- 创建扑克牌的基本元素：花色和点数
- 花色：♠(黑桃)、♥(红桃)、♣(梅花)、♦(方块)
- 点数：2-10、J、Q、K、A
- 使用`ArrayList`分别存储花色和点数

### 2. 生成完整的牌组

- 使用HashMap建立牌的编号和具体牌面的对应关系
- 通过两层循环组合花色和点数，生成52张普通牌
- 额外加入大小王，总共54张牌
- 每张牌都有唯一的编号(0-53)

### 3. 洗牌准备

- 创建一个包含0-53数字的ArrayList，代表54张牌
- 使用`Collections.shuffle()`方法随机打乱这些数字的顺序
- 打乱后的顺序就是发牌的顺序

### 4. 发牌过程

- 创建4个ArrayList用于存储发牌结果：
    - 3个玩家的牌（iPlayer1、iPlayer2、iPlayer3）
    - 1个底牌（iSecretCards）
- 发牌规则：
    - 前51张牌轮流发给3个玩家（i%3来分配）
    - 最后3张作为底牌

### 5. 整理手牌

- 使用`Collections.sort()`方法对每个玩家手中的牌进行排序
- 排序是对牌的编号进行排序，方便后续显示

### 6. 转换和显示

- 创建新的ArrayList存储实际的牌面字符串
- 遍历每个玩家手中的牌号
- 通过HashMap查找对应的牌面
- 最后打印每个玩家的牌和底牌

## 关键技术点

1. 集合的使用：
    - ArrayList：存储花色、点数和牌组
    - HashMap：建立编号和牌面的对应关系
2. 工具类的应用：
    - Collections.shuffle()：用于洗牌
    - Collections.sort()：用于整理手牌
3. 循环的应用：
    - 两层循环生成牌组
    - 取余操作(%)实现轮流发牌


## 示例代码

```java
import java.util.ArrayList;  
import java.util.Collections;  
import java.util.HashMap;  
  
public class Main {  
    public static void main(String[] args) {  
        /* 第一步：准备扑克牌的基本元素 */        
        // 准备4种花色  
        ArrayList<String> colors = new ArrayList<String>();  
        colors.add("♠");  
        colors.add("♥");  
        colors.add("♣");  
        colors.add("♦");  
  
        // TODO 准备13种点数（2-10，J，Q，K，A）  
        ArrayList<String> point = new ArrayList<String>(); 
         
  
        /* 第二步：组装54张扑克牌 */        
        // 用HashMap将每张牌的编号(0-53)与具体牌面对应起来  
        HashMap<Integer, String> map = new HashMap<Integer, String>();  
        int index = 0;      // 牌的编号，从0开始  
  
        // TODO 两层循环，生成52张普通牌（13点数 × 4花色）  
        
  
        // 加入大小王，编号为52和53  
        map.put(index++, "小☺");  
        map.put(index++, "大☻");  
  
        /* 第三步：洗牌准备 */        
        // TOOD 准备一个数字序列，代表54张牌  
        ArrayList<Integer> cards = new ArrayList<Integer>();  
        
  
        // 使用shuffle方法打乱牌的顺序（洗牌）  
        Collections.shuffle(cards);  
  
        /* 第四步：发牌 */        
        // 创建4个集合，分别存储三个玩家的牌和底牌  
        ArrayList<Integer> iPlayer = new ArrayList<Integer>();  // 玩家1的牌 
        ArrayList<Integer> iPlayer2 = new ArrayList<Integer>(); // 玩家2的牌  
        ArrayList<Integer> iPlayer3 = new ArrayList<Integer>(); // 玩家3的牌  
        ArrayList<Integer> iSecretCards = new ArrayList<Integer>(); // 底牌  
  
        // TODO 发牌规则：留3张底牌，其余轮流发给3个玩家  
        for (int i = 0; i < cards.size(); i++) {  
            
        }  
  
        /* 第五步：整理手牌 */        
        // 对每个玩家手中的牌排序  
        Collections.sort(iPlayer);  
        Collections.sort(iPlayer2);  
        Collections.sort(iPlayer3);  
  
        /* 第六步：转换牌面并显示 */        
        // iPlayer 中的存储的是每个玩家拥有的牌的编号（0-53）  
        // 将玩家手中的牌号转换为具体的牌面  
        ArrayList<String> sPlayer = new ArrayList<String>();  
        ArrayList<String> sPlayer2 = new ArrayList<String>();  
        ArrayList<String> sPlayer3 = new ArrayList<String>();  
        ArrayList<String> sSecretCards = new ArrayList<String>();  
  
        // TODO 根据牌号，从map中找出对应的牌面  
        for (Integer key : iPlayer) {  
            sPlayer.add(map.get(key));  
        }  
       
  
        // 展示每个玩家的牌  
        System.out.println("玩家1：" + sPlayer);  
        System.out.println("玩家2：" + sPlayer2);  
        System.out.println("玩家3：" + sPlayer3);  
        System.out.println("底牌：" + sSecretCards);  
    }  
}
```


## 进阶任务

利用面向对象思想改造程序

### 类的设计级要求
#### 1. Card类（扑克牌）

**职责**：表示一张扑克牌

- 属性：
    - suit（花色）：字符串类型
    - rank（点数）：字符串类型
    - index（编号）：整型
- 功能：
    - 构造方法：初始化一张牌
    - toString方法：返回牌面字符串
    - 实现Comparable接口：用于排序

#### 2. Deck类（牌组）

**职责**：管理一副完整的扑克牌

- 属性：
    - cards：Card类型的ArrayList
    - 花色和点数的常量数组
- 功能：
    - 初始化54张牌
    - 洗牌方法
    - 发牌方法

#### 3. Player类（玩家）

**职责**：管理玩家信息和手牌

- 属性：
    - name：玩家名称
    - hand：存储手牌的ArrayList
- 功能：
    - 接收牌
    - 整理手牌
    - 显示手牌

#### 4. Game类（游戏管理）

**职责**：控制游戏流程

- 属性：
    - deck：牌组对象
    - 三个玩家对象
    - bottomCards：底牌列表
- 功能：
    - 初始化游戏
    - 执行游戏流程
    - 显示游戏结果

### 改造要点

#### 1. 封装性要求

- 所有类的属性都应该是私有的
- 必要的属性提供getter/setter方法
- 对外只暴露必要的方法

#### 2. 类之间的关系

- Game类包含Deck类和Player类的对象
- Player类包含Card类的对象
- Deck类负责Card对象的创建和管理

#### 3. 功能性要求

- 保持原有程序的所有功能
- 洗牌和发牌的逻辑不变
- 结果显示格式保持一致
