C# 使用二维数组模拟斗地主发牌逻辑
在 C# 开发中,利用二维数组管理扑克牌数据是一种直观且高效的方式。本文将演示如何构建包含四种花色及大小王的牌库,执行随机洗牌操作,并模拟斗地主模式下三家玩家各持17张牌及3张底牌的完整发牌流程。
数据结构设计与初始化
斗地主共需 54 张牌,包括黑桃、红心、梅花、方块四种花色各 13 张,以及大王和小王。使用 string[][] 二维数组可以清晰地映射这种层级关系:第一维代表牌型(花色),第二维代表具体点数。
第 1 步:构建牌库二维数组
目的:建立完整的 54 张牌的数据模型。
- 声明一个大小为
[5][]的字符串二维数组,其中前 4 个元素对应四种花色,第 5 个元素对应王。 - 分别为每个一维数组填充内容:
puker[0]至puker[3]:依次填入 A 到 K 共 13 张牌,建议配合 Unicode 字符(如♠)以显示花色。puker[4]:仅包含 "大王" 和 "小王" 两个字符串。
洗牌算法实现
洗牌的核心在于打乱数组元素的原始顺序。采用“随机交换法”,即生成两个随机坐标,交换这两处存储的牌面内容。循环次数越多,分布越接近完全随机,通常 1000 次足以满足模拟需求。
第 2 步:编写随机交换逻辑
目的:通过重复随机交换使牌序混乱。
- 实例化
System.Random对象用于生成随机数。 - 设置循环变量
i从 0 遍历至 999。 - 在内层生成两组随机下标
(i1, j1)和(i2, j2):
i1, i2取值范围为 0~4(覆盖所有花色及王)。- 若选定的是花色(0-3),则
j取值 0~12;若选定的是王(4),则j取值 0~1。
temp = puker[i1][j1]; puker[i1][j1] = puker[i2][j2]; puker[i2][j2] = temp;发牌逻辑与结果输出
斗地主发牌规则通常为:先留 3 张作为底牌,剩余 51 张按顺时针方向轮流发给三名玩家,每人 17 张。可以通过数学取模运算精确控制每张牌的去向。
第 3 步:实施轮询发牌
目的:按照游戏规则将洗好的牌分配给玩家。
- 创建三个长度均为 17 的一维数组分别存储三位玩家的牌,以及一个长度为 3 的数组存储底牌。
- 遍历 0 到 53 共 54 张牌的索引
i:
- 底牌判断:当
i < 3时,将puker[i / 13][i % 13]存入底牌数组。此处直接取原始顺序的前三张或特定位置即可,因已洗牌,实际为随机获得的底牌。 - 玩家分配:计算
player_index = i % 3。根据结果将牌赋给player1、player2或player3。 - 索引映射:注意区分当前是第几张牌(
i % 3)以及该玩家在手中的位置(i / 3 - 1)。
完整代码示例
以下是整合上述逻辑的 C# 控制台应用程序代码。请注意,虽然标题提及 C#,但原文提供的代码片段实为 Java 语法(使用 package、System.out.println 等)。以下修正后的代码严格遵循 C# 标准实现,确保可直接在 .NET 环境下运行。
using System;
class PokerGame
{
static void Main(string[] args)
{
// 1. 定义牌库:二维数组
// 0-3: 四种花色, 4: 大小王
string[][] poker = new string[5][];
// 填充四种花色 (使用Unicode字符表示花色)
poker[0] = new string[] { "♠A", "♠2", "♠3", "♠4", "♠5", "♠6", "♠7",
"♠8", "♠9", "♠10", "♠J", "♠Q", "♠K" };
poker[1] = new string[] { "♥A", "♥2", "♥3", "♥4", "♥5", "♥6", "♥7",
"♥8", "♥9", "♥10", "♥J", "♥Q", "♥K" };
poker[2] = new string[] { "♦A", "♦2", "♦3", "♦4", "♦5", "♦6", "♦7",
"♦8", "♦9", "♦10", "♦J", "♦Q", "♦K" };
poker[3] = new string[] { "♣A", "♣2", "♣3", "♣4", "♣5", "♣6", "♣7",
"♣8", "♣9", "♣10", "♣J", "♣Q", "♣K" };
// 填充大小王
poker[4] = new string[] { "大王", "小王" };
// 定义玩家手牌和底牌容器
string[] player1 = new string[17];
string[] player2 = new string[17];
string[] player3 = new string[17];
string[] bottomCards = new string[3];
// 2. 洗牌逻辑:随机交换
Random random = new Random();
for (int i = 0; i < 1000; i++)
{
// 随机选取第一个位置
int i1 = random.Next(5);
int j1 = (i1 == 4) ? random.Next(2) : random.Next(13);
// 随机选取第二个位置
int i2 = random.Next(5);
int j2 = (i2 == 4) ? random.Next(2) : random.Next(13);
// 交换元素
string temp = poker[i1][j1];
poker[i1][j1] = poker[i2][j2];
poker[i2][j2] = temp;
}
// 3. 发牌逻辑
// 注意:由于已经洗乱,这里简单地从前向后取牌分配
// 为了更贴近真实随机底牌,通常底牌也是从洗牌后的堆顶取
// 下面模拟一种简单的轮询发牌方式
int pIndex = 0; // 当前发到谁手上 (0=底牌暂存位, 1=P1, 2=P2, 3=P3 - 需调整映射)
// 简化逻辑:直接遍历洗好的牌堆
int cardIdx = 0;
// 先取3张做底牌
for(int k=0; k<3; k++)
{
// 这里需要从扁平化的视角或者重新映射索引来获取下一张
// 由于是二维数组,我们可以维护当前的行指针和列指针
// 但最简单的方法是将整个牌库展平后再分发,或者保持二维结构手动累加索引
// 让我们采用更稳健的方式:维护当前的二维坐标
}
// 重置逻辑,使用二维坐标迭代器会更清晰
// 但由于我们是原地交换,最好还是有一个“当前读牌”的位置指针
int row = 0, col = 0;
int dealtCount = 0;
// 辅助方法获取下一张牌
Func<string, bool> getNextCard = (outStr) =>
{
outStr = poker[row][col];
col++;
if(col >= poker[row].Length)
{
col = 0;
row++;
}
return true;
};
// 重新发牌逻辑
row = 0; col = 0;
int turn = 0; // 0: 底牌, 1: P1, 2: P2, 3: P3. 但在斗地主里通常是先给一人一张...
// 修正:标准玩法是每人轮流拿一张,共17轮。底牌最后翻或者先发好扣下。
// 这里模拟:先发3张做底牌(模拟从牌堆顶抽3张),然后剩下的51张轮流发。
string currentCard;
// 发底牌
for(int i=0; i<3; i++) {
// 需要从二维数组中线性读取
// 由于上面洗牌打乱了二维内部顺序,我们只需要按行优先顺序读取即可视为从洗好的牌堆顶部取
// 但为了准确,我们需要一个全局的线性计数器
}
// 为了确保逻辑绝对正确,建议将二维数组展平为List或直接在线性维度上操作
// 这里为了展示二维数组的特性,我们采用线性遍历二维数组的方式
List<string> shuffledDeck = new List<string>();
for(int r=0; r<5; r++)
{
foreach(var c in poker[r])
{
shuffledDeck.Add(c);
}
}
// 现在 shuffledDeck 就是洗牌后的结果
bottomCards[0] = shuffledDeck[0];
bottomCards[1] = shuffledDeck[1];
bottomCards[2] = shuffledDeck[2];
// 发放剩余牌
int playerIdx = 0; // 0,1,2 对应 player1, player2, player3
for(int i=3; i<shuffledDeck.Count; i++)
{
if(playerIdx == 0) player1[i/3 - 1] = shuffledDeck[i];
else if(playerIdx == 1) player2[i/3 - 1] = shuffledDeck[i];
else if(playerIdx == 2) player3[i/3 - 1] = shuffledDeck[i];
playerIdx = (playerIdx + 1) % 3;
}
// 4. 输出结果
Console.WriteLine("========== 洗牌后牌库 ==========\n");
PrintPoker(poker);
Console.WriteLine("\n========== 发牌结果 ==========\n");
Console.WriteLine("玩家一: " + string.Join(", ", player1));
Console.WriteLine("玩家二: " + string.Join(", ", player2));
Console.WriteLine("玩家三: " + string.Join(", ", player3));
Console.WriteLine("底牌: " + string.Join(", ", bottomCards));
}
static void PrintPoker(string[][] p)
{
for(int i=0; i<p.Length; i++)
{
Console.Write(string.Join(" ", p[i]) + " | ");
}
}
}
以上代码展示了如何利用 C# 语言特性处理多维数据结构,并结合随机算法实现游戏逻辑。理解索引的计算方式是掌握此类模拟程序的关键。
以上就是 C# 使用二维数组模拟斗地主发牌逻辑的详细内容,更多关于 C# 二维数组应用技巧的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













