本案例要求编写一个**斗地主的洗牌发牌**程序,要求按照斗地主的规则完成洗牌发牌的过程。 ## 任务描述 一副扑克总共有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 colors = new ArrayList(); colors.add("♠"); colors.add("♥"); colors.add("♣"); colors.add("♦"); // TODO 准备13种点数(2-10,J,Q,K,A) ArrayList point = new ArrayList(); /* 第二步:组装54张扑克牌 */ // 用HashMap将每张牌的编号(0-53)与具体牌面对应起来 HashMap map = new HashMap(); int index = 0; // 牌的编号,从0开始 // TODO 两层循环,生成52张普通牌(13点数 × 4花色) // 加入大小王,编号为52和53 map.put(index++, "小☺"); map.put(index++, "大☻"); /* 第三步:洗牌准备 */ // TOOD 准备一个数字序列,代表54张牌 ArrayList cards = new ArrayList(); // 使用shuffle方法打乱牌的顺序(洗牌) Collections.shuffle(cards); /* 第四步:发牌 */ // 创建4个集合,分别存储三个玩家的牌和底牌 ArrayList iPlayer = new ArrayList(); // 玩家1的牌 ArrayList iPlayer2 = new ArrayList(); // 玩家2的牌 ArrayList iPlayer3 = new ArrayList(); // 玩家3的牌 ArrayList iSecretCards = new ArrayList(); // 底牌 // TODO 发牌规则:留3张底牌,其余轮流发给3个玩家 for (int i = 0; i < cards.size(); i++) { } /* 第五步:整理手牌 */ // 对每个玩家手中的牌排序 Collections.sort(iPlayer); Collections.sort(iPlayer2); Collections.sort(iPlayer3); /* 第六步:转换牌面并显示 */ // iPlayer 中的存储的是每个玩家拥有的牌的编号(0-53) // 将玩家手中的牌号转换为具体的牌面 ArrayList sPlayer = new ArrayList(); ArrayList sPlayer2 = new ArrayList(); ArrayList sPlayer3 = new ArrayList(); ArrayList sSecretCards = new ArrayList(); // 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. 功能性要求 - 保持原有程序的所有功能 - 洗牌和发牌的逻辑不变 - 结果显示格式保持一致