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) |
实现思路#
- 定义花色数组:
string[] suits = { "♠", "♥", "♦", "♣" } - 定义点数数组:
string[] ranks = { "A", "2", "3", ..., "10", "J", "Q", "K" } - 双重循环:外层遍历 4 种花色,内层遍历 13 个点数,拼接成
"花色+点数"存入数组 - 最后两位:数组索引 52、53 分别存入
"大王"、"小王" - 洗牌:可使用
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 |
关键知识点#
- C# 7.0 元组交换:
(card54[i], card54[j]) = (card54[j], card54[i]);无需临时变量即可交换两个元素 Random.Next(int, int):生成[0, 54)范围内的随机整数- 索引计算:发牌时通过
i - 17、i - 34、i - 51映射到各玩家数组的 0 起始索引 - 斗地主牌数: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 | 统计非均匀 |
相关笔记#
- 数组基础 — 数组声明与遍历
- 继承 — Farmer/Landlord 继承 Player
- 图书管理系统综合练习 — 同阶段的类设计练习
- 循环嵌套 — 双重循环取牌