扑克牌
本案例以10点半的游戏为例讲解,也可以扩展到其他扑克牌类游戏,总体的思路是类似的。
10点半游戏介绍
- 每个玩家可以从牌堆中起任意数量的牌。
- 起到A~10的牌,按牌面数计分。
- 起到JQK大小王,记0.5分。
- 超过了10.5分,就爆炸了。
- 比较每个玩家的得分,没爆炸并且得分最高的就赢了。
定义扑克牌的数据结构
- 扑克的数值用1~13表示。其中1代表A,11代表J,12代表Q,13代表K。
- 14代表小王,15代表大王,大小王的花色就不重要了。
- 四种花色用枚举定义。
enum CardFlower { RedHeart, RedSquare, BlackHeart, BlackSquare } struct Card { public int Number; public CardFlower Flower; }
初始化整套扑克牌
- 从1到13初始化。
- 每一数字的牌有4种花色。
大小王单独处理。
const int CARD_COUNT = 54; static Card[] cards = new Card[CARD_COUNT]; // 初始化整套扑克牌 static void Init() { int index = 0; // 从1到13初始化卡牌 for (int i = 1; i <= 13; i++) { // (每个数字)有4种花色 for (int j = 0; j < 4; j++) { cards[index] = new Card(); cards[index].Number = i; cards[index].Flower = (CardFlower)j; // 初始化了一张 index++; } } // 大小王特殊处理 cards[52].Number = 14; // 用14表示小王 cards[53].Number = 15; // 用15表示大王 }
显示扑克牌
- 遍历扑克牌数组。
对J,Q,K,A,大小王特殊处理。
// 显示一张卡牌 static void Show(Card card) { // 不显示没有初始化的卡牌 if (card.Number == 0) return; string cn; switch(card.Number) { case 1: cn = "A"; break; case 11: cn = "J"; break; case 12: cn = "Q"; break; case 13: cn = "K"; break; case 14: cn = "小王"; break; case 15: cn = "大王"; break; default: cn = card.Number.ToString(); break; } cn = (card.Number < 14 ? card.Flower + " " : "") + cn; Console.WriteLine(cn); }
- 为了使用方便还提供两个重载。
// 显示一组卡牌 static void Show(Card[] cs) { // 这是典型的遍历数组的方法 for (int i = 0; i < cs.Length; i++) { Show(cs[i]); } } // 显示所有卡牌 static void Show() { Show(cards); }
洗牌
- 随机选择0~53中的两个位置。
- 交换这两个位置的牌,完成一轮洗牌。
进行足够多次数的洗牌。
// 用于产生随机数的对象 static Random r = new Random(); // 洗牌,默认洗1000次。 static void ExchangeCards(int count=1000) { // 交换count次 for (int i = 0; i < count; i++ ) { // 随机找两张牌 int index1 = r.Next(CARD_COUNT); int index2 = r.Next(CARD_COUNT); // 交换 Card temp = cards[index1]; cards[index1] = cards[index2]; cards[index2] = temp; } }
计算得分
- 起到A~10的牌,按牌面数计分。
- 起到JQK大小王,记0.5分。
private static double Score(Card card) { if (card.Number >= 11) return 0.5; else return card.Number; }
起牌
- 玩家可以用数组保存起到的牌。后续还可以使用集合来保存。
static Card[] playerA = new Card[CARD_COUNT]; static Card[] playerB = new Card[CARD_COUNT];
- 起牌时,从0开始,顺序的从洗好的扑克牌数组中拿出牌。
- 需要用到一个索引变量,记录当前应该拿哪张牌。
// 当前起牌的索引 static int cardIndex = 0;
如果是玩家A起牌,就从牌堆中拿出一起牌,放到自己的扑克牌数组里。
static void PlayA() { // 以下代码实现玩家A起到一张牌 int playerCardIndex = 0; // 起到的牌是cards[cardIndex] // 要放到玩家A自己的数组playerA中 playerA[playerCardIndex] = cards[cardIndex]; // 起到一张牌后将起牌的索引加1,下次再起牌,起到的就是下一张牌了。 cardIndex++; // 每次放到playerA中的位置不能重复,以免覆盖之前的数据。 playerCardIndex++; }
玩家分别起牌并算分
每个玩家可以从牌堆中起任意数量的牌。
static double PlayA() { int playerCardIndex = 0; double score = 0; while (true) { Console.WriteLine("轮到PlayerA起牌[a:起牌, p:过]:"); string input = Console.ReadLine(); if (input == "a") { playerA[playerCardIndex] = cards[cardIndex]; // 提示起到的牌 Show(cards[cardIndex]); // 计算得分 score += Score(cards[cardIndex]); // 提示总分 Console.WriteLine("当前得分:" + score); cardIndex++; playerCardIndex++; } else if (input == "p") { // 过 break; } else { Console.WriteLine("输入有误."); } } return score; }
玩家B的代码类似,其实可以将玩家A和玩家B的代码合成一个函数,但为了便于理解和学习,我们还是分成两个函数。
private static double PlayB() { int myCardIndex = 0; double score = 0; while (true) { Console.WriteLine("轮到PlayerB起牌[b:起牌, p:过]:"); string input = Console.ReadLine(); if (input == "b") { playerB[myCardIndex] = cards[cardIndex]; // 提示起到的牌 Show(cards[cardIndex]); // 计算得分 score += Score(cards[cardIndex]); // 提示总分 Console.WriteLine("当前得分:"+ score); cardIndex++; myCardIndex++; } else if (input == "p") { // 过 break; } else { Console.WriteLine("输入有误."); } } return score; }
判断胜负
比较每个玩家的得分,没爆炸并且得分最高的就赢了。
// 返回0表示平局、1表示玩家A获胜、2表示玩家B获胜。 static int Compare(double scoreA, double scoreB) { // 玩家A和玩家B都爆了。 if (scoreA > 10.5 && scoreB > 10.5) return 0; // 玩家A爆了,玩家B没爆。 if (scoreA > 10.5) return 2; // 玩家B爆了,玩家A没爆。 if (scoreB > 10.5) return 1; // 都没爆,比较分数大小。 if (scoreA > scoreB) return 1; else if (scoreB > scoreA) return 2; else return 0; }
游戏业务逻辑的实现
static void Main(string[] args)
{
// 初始化一套新牌
Init();
// 洗牌
ExchangeCards();
// 轮到A了
double scoreA = PlayA();
// 轮到B了
double scoreB = PlayB();
// 比较大小
int result = Comparer(scoreA, scoreB);
switch (result)
{
case 0:
Console.WriteLine("平局");
break;
case 1:
Console.WriteLine("A获胜");
break;
case 2:
Console.WriteLine("B获胜");
break;
}
}