跳转至

0717 笔记#

📁 项目结构#

stu0717/
├── stu0717.slnx              # 解决方案文件
├── stu0717/
│   ├── Program.cs            # 程序入口
│   ├── 54Card.cs             # 54张扑克牌工具类
│   ├── App.config
│   ├── Properties/
│   └── stu0717.csproj

📄 Program.cs — 程序入口#

当前为空白模板,仅包含标准的 Main 方法框架,尚未编写实际逻辑。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace stu0717
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // TODO: 在此处编写主程序逻辑
        }
    }
}

📄 54Card.cs — 扑克牌工具类(未完成)#

目标:生成一副标准的 54 张扑克牌(52 张普通牌 + 2 张王牌)。

当前代码#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace stu0717
{
    internal class _54Card
    {
        public static string[] Get54()
        {
            string[] result = new string[54];
            for()  // ⚠️ for 循环未完成
            {
                // TODO: 填充 54 张牌的数组
            }
            return result;
        }
    }
}

扑克牌 54 张构成#

分类 数量 内容
普通牌 52 张 4 种花色(♠♥♦♣)× 13 个点数(A, 2~10, J, Q, K)
王牌 2 张 大王(Big Joker)、小王(Small Joker)

实现思路#

  1. 定义花色数组string[] suits = { "♠", "♥", "♦", "♣" }
  2. 定义点数数组string[] ranks = { "A", "2", "3", ..., "10", "J", "Q", "K" }
  3. 双重循环:外层遍历 4 种花色,内层遍历 13 个点数,拼接成 "花色+点数" 存入数组
  4. 最后两位:数组索引 52、53 分别存入 "大王""小王"
  5. 洗牌:可使用 Random 类对数组进行随机打乱

📝 待办#

  • 补全 Get54() 方法中的 for 循环逻辑
  • Program.Main 中调用 Get54() 并输出结果
  • 可选:增加洗牌(Shuffle)功能
  • 可选:增加发牌(Deal)功能

📁 补充项目:斗地主发牌程序#

来源:D:\study\C#1\褚浩鹏\test\test\Program.cs

功能概述#

完整的斗地主发牌流程,包含 取牌 → 洗牌 → 发牌 → 显示 四个步骤。

完整代码#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // ── 取牌 ──
            string[] card = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2" };
            string[] sym = { "♠ ", "❤ ", "♣ ", "♦ " };
            string[] card54 = new string[54];
            int index = 0;
            for (int i = 0; i < card.Length; i++)
            {
                for (int j = 0; j < sym.Length; j++)
                {
                    card54[index++] = sym[j] + card[i];
                }
            }
            card54[52] = "大王";
            card54[53] = "小王";

            // ── 洗牌 ──
            Random r = new Random();
            for (int i = 0; i < card54.Length; i++)
            {
                int j = r.Next(0, card54.Length);
                (card54[i], card54[j]) = (card54[j], card54[i]);
            }

            // ── 发牌 ──
            string[] player1 = new string[17];
            string[] player2 = new string[17];
            string[] player3 = new string[17];
            string[] down = new string[3];
            for (int i = 0; i < card54.Length; i++)
            {
                if (i < 17)
                {
                    player1[i] = card54[i];
                }
                else if (i >= 17 && i < 34)
                {
                    player2[i - 17] = card54[i];
                }
                else if (i >= 34 && i < 51)
                {
                    player3[i - 34] = card54[i];
                }
                else
                {
                    down[i - 51] = card54[i];
                }
            }

            // ── 显示牌 ──
            Console.WriteLine("第1的牌");
            foreach (string v in player1)
            {
                Console.Write(v + " ");
            }
            Console.WriteLine();
            Console.WriteLine("第2的牌");
            foreach (string v in player2)
            {
                Console.Write(v + " ");
            }
            Console.WriteLine();
            Console.WriteLine("第3的牌");
            foreach (string v in player3)
            {
                Console.Write(v + " ");
            }
            Console.WriteLine();
            Console.WriteLine("底牌");
            foreach (string v in down)
            {
                Console.Write(v + " ");
            }
            Console.WriteLine();
        }
    }
}

代码流程解析#

步骤 逻辑 关键代码
取牌 13 个点数 × 4 种花色 = 52 张,加上大小王 双重 for 循环 + 手动赋值 card54[52/53]
洗牌 遍历数组,每次随机与另一张牌交换 Random.Next() + 元组交换 (a, b) = (b, a)
发牌 3 个玩家各 17 张,剩余 3 张为底牌 按索引范围分配到 4 个数组
显示 遍历输出每个玩家的手牌和底牌 foreach + Console.Write

关键知识点#

  1. C# 7.0 元组交换(card54[i], card54[j]) = (card54[j], card54[i]); 无需临时变量即可交换两个元素
  2. Random.Next(int, int):生成 [0, 54) 范围内的随机整数
  3. 索引计算:发牌时通过 i - 17i - 34i - 51 映射到各玩家数组的 0 起始索引
  4. 斗地主牌数:54 张牌,3 人各 17 张 = 51 张,剩余 3 张底牌

可优化方向#

  • 大小王可设置更大的花色标识(如 "🃏大王"
  • 牌面大小排序可添加排序逻辑,方便看牌
  • 洗牌算法可改用 Fisher-Yates 算法(更均匀)
  • 可将发牌、洗牌逻辑封装为独立方法,提高复用性

补充项目:斗地主发牌程序(OOP 版)#

来源:D:\study\C#1\stu0725\7斗地主 与上面的过程式版本对比,此版本采用面向对象设计,将牌、玩家、游戏逻辑分离到不同类中。

📁 项目结构#

7斗地主/
├── Program.cs      # 程序入口
├── Card.cs         # 牌的数据模型
├── Player.cs       # 玩家/角色模型(含 Farmer、Landlord 子类)
├── Game.cs         # 游戏核心逻辑(取牌、洗牌、发牌)
└── 详细讲解.md     # 代码问题分析文档

类关系图#

Game
├── List<Card>    cards       — 牌堆
├── List<Player>  players     — 3 个玩家
├── Landlord      Landlord    — 地主
└── List<Farmer>  Farmers     — 农民们

Player
├── string Name
└── List<Card> Handcards

Card
├── string Flower  (花色)
└── string Point   (点数)

Farmer : Player       — 农民角色
Landlord : Player     — 地主角色

📄 Card.cs — 牌的数据模型#

internal class Card
{
    public string Flower { get; }
    public string Point { get; }

    public Card(string flower, string point)
    {
        Flower = flower;
        Point = point;
    }
}

📄 Player.cs — 玩家/角色模型#

internal class Player
{
    List<Card> cards;                      // 内部字段(已废弃,冗余)
    public string Name { get; set; }
    public List<Card> Handcards { get; set; }

    public Player(string name)
    {
        Name = name;
        Handcards = new List<Card>();      // 初始化手牌列表
    }
}

// 农民角色
class Farmer : Player
{
    public Farmer(Player player) : base(player.Name)
    {
        Handcards = player.Handcards;       // 共享同一副手牌
        Console.WriteLine($"农民是:{player.Name}");
    }
}

// 地主角色
class Landlord : Player
{
    public Landlord(Player player) : base(player.Name)
    {
        Handcards = player.Handcards;
        Console.WriteLine($"地主是:{player.Name}");
    }
}

📄 Game.cs — 游戏核心逻辑#

internal class Game
{
    List<Player> players;
    List<Card> cards;
    Landlord Landlord;
    List<Farmer> Farmers;
    Random rnd = new Random();

    // 构造函数 — 创建 3 个默认玩家
    public Game() : this("p1", "p2", "p3") { }

    // 带参构造函数 — 创建指定名称的玩家
    public Game(string p1, string p2, string p3)
    {
        players = new List<Player>()
        {
            new Player(p1),
            new Player(p2),
            new Player(p3)
        };
    }

    /// <summary>生成 54 张牌</summary>
    public List<Card> GenerateDeck()
    {
        cards = new List<Card>();
        string[] points = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2" };
        string[] flowers = { "❤️", "♠ ", "♦ ", "♣ " };

        foreach (var f in flowers)
            foreach (var c in points)
                cards.Add(new Card(f, c));

        cards.Add(new Card("🃏", "大王"));
        cards.Add(new Card("🃏", "小王"));
        return cards;
    }

    /// <summary>洗牌</summary>
    public List<Card> ShuffleDeck()
    {
        for (var i = 0; i < cards.Count; i++)
        {
            int r = rnd.Next(cards.Count);
            (cards[i], cards[r]) = (cards[r], cards[i]);
        }
        return cards;
    }

    /// <summary>发牌</summary>
    public void DealCards()
    {
        Console.WriteLine("发牌:");
        int landlordIndex = rnd.Next(0, 3);          // 随机选地主

        // 51 张牌分配给 3 个玩家(每人 17 张)
        for (int i = 0; i < cards.Count - 3; i++)
            players[i % 3].Handcards.Add(cards[i]);

        // 3 张底牌给地主
        for (int i = cards.Count - 3; i < cards.Count; i++)
            players[landlordIndex].Handcards.Add(cards[i]);

        // 设置角色
        Landlord = new Landlord(players[landlordIndex]);
        Farmers = new List<Farmer>();
        foreach (var player in players)
            if (player.Handcards.Count == 17)
                Farmers.Add(new Farmer(player));

        Console.WriteLine("发牌完成");
    }

    /// <summary>显示所有玩家的手牌</summary>
    public void PlayerCard()
    {
        Console.WriteLine($"地主:{Landlord.Name}\n 手牌:");
        foreach (var card in Landlord.Handcards)
            Console.Write($"{card.Flower}{card.Point} ");
        Console.WriteLine();

        foreach (var farmer in Farmers)
        {
            Console.WriteLine($"农民:{farmer.Name}\n 手牌:");
            foreach (var card in farmer.Handcards)
                Console.Write($"{card.Flower}{card.Point} ");
            Console.WriteLine();
        }
    }
}

📄 Program.cs — 程序入口#

static void Main(string[] args)
{
    Console.OutputEncoding = Encoding.UTF8;
    Console.BackgroundColor = ConsoleColor.White;
    Console.ForegroundColor = ConsoleColor.Black;

    Game game = new Game();
    game.GenerateDeck();
    game.DealCards();
    game.PlayerCard();

    Console.ResetColor();
}

知识点对比#

知识点 过程式版本(0717) OOP 版本(0725)
数据结构 字符串数组 string[] Card 类、Player
玩家管理 3 个独立数组 List<Player> + Farmer/Landlord 继承
角色区分 通过继承实现地主/农民
洗牌 字符串数组交换 List<Card> 元组交换
代码复用 高(类继承、方法复用)

⚠️ 已知问题分析#

详细分析文档见 7斗地主/详细讲解.md,核心问题汇总:

优先级 问题 文件 后果
P0 players 列表初始化后未添加玩家对象 Game.cs IndexOutOfRangeException
P0 Handcards 未初始化 Player.cs NullReferenceException
P0 构造函数参数 p1, p2, p3 未使用 Game.cs 玩家从未被创建
P1 Card 缺少牌面权重字段 Card.cs 无法比较牌大小
P1 Farmer/Landlord 空洞继承 Player.cs 无实质差异
P2 花色字符串尾部有空格 Card.cs/Game.cs 输出双空格
P2 洗牌非标准 Fisher-Yates Game.cs 统计非均匀

相关笔记#