本文为C#控制台应用程序,使用VS2019,.net framework 4.0,本测试程序只考虑一副52张的扑克牌。
黑杰克(21点)游戏规则如下:
点数计算:每张牌都有点数,2到10的牌的点数就是其牌面的数字;J、Q、K的点数是10分;A有两种算法,1或者11,如果A算为11时总和大于21,则A算为1。例如(A,8)是19点,(A,7,J)则为18点。
21点一般用到1-8副牌。庄家给每个玩家发两张明牌,牌面朝上面;给自己发两张牌,一张牌面朝上(叫明牌),一张牌面朝下(叫暗牌)。大家手中扑克点数的计算是:K、Q、J 和 10 牌都算作 10 点。A 牌既可算作1 点也可算作11 点。如果 A 算为 11 时总和大于 21 ,则 A 算为 1 。其余所有2 至9 牌均按其原面值计算。首先玩家开始要牌,如果玩家拿到的前两张牌是一张 A 和一张10点牌,就拥有黑杰克(Blackjack);没有黑杰克的玩家可以继续拿牌,可以随意要多少张。目的是尽量往21点靠,靠得越近越好,最好就是21点了。在要牌的过程中,如果所有的牌加起来超过21点,玩家就输了——叫爆掉(Bust),游戏也就结束了。假如玩家没爆掉,又决定不再要牌了,这时庄家就把他的那张暗牌打开来。一般到17点或17点以上不再拿牌,但也有可能15到16点甚至12到13点就不再拿牌或者18到19点继续拿牌。假如庄家爆掉了,那他就输了。假如他没爆掉,那么你就与他比点数大小,大为赢。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DemoBlackJack
{
///
/// 公共资源类,主要有黑杰克点数常量,52张扑克牌集合,以及计算当前玩家的点数
///
public class CommonUtil
{
///
/// 最大点【极值】,21点代表黑杰克
///
public const int BlackJack = 21;
///
/// 定义52张牌,暂定A代表的值为11,如果玩家手上存在A,则要对A进行11或1进行处理,尽量小于等于21点
/// 键:牌的花色点数 值:点数
///
public static Dictionary
{
//庄家dealer 玩家player
//四种花色:♠黑桃(Spade)、♥红心(Heart)、♦方块(Diamond)、♣梅花(Club),每种花色有13种点数
{"♠A黑桃",11},{"♠2黑桃",2},{"♠3黑桃",3},{"♠4黑桃",4},{"♠5黑桃",5},{"♠6黑桃",6},{"♠7黑桃",7},{"♠8黑桃",8},{"♠9黑桃",9},{"♠10黑桃",10},{"♠J黑桃",10},{"♠Q黑桃",10},{"♠K黑桃",10},
{"♥A红心",11},{"♥2红心",2},{"♥3红心",3},{"♥4红心",4},{"♥5红心",5},{"♥6红心",6},{"♥7红心",7},{"♥8红心",8},{"♥9红心",9},{"♥10红心",10},{"♥J红心",10},{"♥Q红心",10},{"♥K红心",10},
{"♣A梅花",11},{"♣2梅花",2},{"♣3梅花",3},{"♣4梅花",4},{"♣5梅花",5},{"♣6梅花",6},{"♣7梅花",7},{"♣8梅花",8},{"♣9梅花",9},{"♣10梅花",10},{"♣J梅花",10},{"♣Q梅花",10},{"♣K梅花",10},
{"♦A方块",11},{"♦2方块",2},{"♦3方块",3},{"♦4方块",4},{"♦5方块",5},{"♦6方块",6},{"♦7方块",7},{"♦8方块",8},{"♦9方块",9},{"♦10方块",10},{"♦J方块",10},{"♦Q方块",10},{"♦K方块",10}
};
///
/// 获取玩家 或者 庄家 手上所有牌的点数
/// 当手上的牌存在A时,需要对A进行11 或 1特殊处理【如果存在两个或以上的A,则最多只有一个A可以当做11来计算】
///
///
///
public static int GetCurrentPoints(params string[] poker)
{
if (poker == null || poker.Length < 2)
{
Console.WriteLine("参与玩家至少需要有两张牌...");
return 0;
}
//从集合中筛选出含有A的牌
List
for (int i = 0; i < poker.Length; i++)
{
if (poker[i].Length < 2)
{
Console.WriteLine("传入的扑克牌参数非法,请检查:{0}", poker[i]);
return -1;
}
if ('A' == poker[i][1])
{
//将A所在的数组索引放入集合中
listAIndex.Add(i);
}
}
int sum = 0;
//如果存在A,则 按照 11 和 1 分别特殊处理,获取最接近 21点的
if (listAIndex.Count > 0)
{
//如果存在两个或以上的A,则最多只有一个A可以当做11来计算【因:两个A按11计算为22点,爆点】
//先将所有非A的进行求和
for (int i = 0; i < poker.Length; i++)
{
if (!listAIndex.Contains(i))
{
sum += Dict[poker[i]];
}
}
//此时 sum值为 所有非A的牌之和
//sum+listAIndex.Count,21,sum+listAIndex.Count+10 三个数 进行比较,即 oneSum,elevenSum=oneSum+10,21 这三个数进行比较
//最多只有一个A作为11进行计算,其它A按照1处理
int elevenSum = sum + 11 + (listAIndex.Count - 1);//按照11进行计算
int oneSum = sum + 1 + (listAIndex.Count - 1);//按照1进行计算
if (oneSum >= BlackJack)
{
//21 小于或等于 oneSum
return oneSum;
}
else if (oneSum < BlackJack && BlackJack < elevenSum)
{
//21 在oneSum和 elevenSum之间
return oneSum;
}
else
{
//21 大于或等于 elevenSum
return elevenSum;
}
}
else
{
//如果不存在A,直接累加器求和
for (int i = 0; i < poker.Length; i++)
{
sum += Dict[poker[i]];
}
return sum;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DemoBlackJack
{
///
/// 玩家,庄家
///
public class Player
{
///
/// 使用姓名和是否庄家来初始化一个玩家
///
///
///
public Player(string playerName, bool isDealer = false)
{
PlayerName = playerName;
IsDealer = isDealer;
ListCard = new List
}
///
/// 玩家姓名或ID
///
public string PlayerName { get; set; }
///
/// 是否是庄家 true为庄家,false为玩家
///
public bool IsDealer { get; set; }
///
/// 手上已有的牌
///
public List
///
/// 当前点数【由手上的扑克牌组合决定】
///
public int CurrentPoint
{
get
{
if (ListCard == null)
{
return 0;
}
return CommonUtil.GetCurrentPoints(ListCard.ToArray());
}
}
///
/// 重写类的ToString方法,用于直接打印对象,用于Console.WriteLine(对象)
///
///
public override string ToString()
{
return string.Format("{0}【{1}】--手上的牌:【{2}】,当前点数:【{3}】",
IsDealer ? "庄家" : "玩家", PlayerName, string.Join(",", ListCard), CurrentPoint);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DemoBlackJack
{
///
/// 黑杰克,或叫21点【扑克牌】,无大小王。 游戏者的目标是使手中的牌的点数之和不超过21点且尽量大。【小于等于21】
/// 算法如下:2,3,4,5,6,7,8,9,10分别代表各自原来的点数
/// A(Ace 一流)代表11或者1,J(Jack 贵族,骑士,侍卫),Q(Queen 王后),K(King 国王)代表10。A牌(Ace)根据需要选择数字11或者数字1,确保点数之和 【小于等于21】 即可
/// 玩家手上的所有牌之和为21为黑杰克【无敌】,超过21(大于等于22)则为爆点【输掉】
/// 如A+4+8:如果A为11,则总和为23点,爆掉,如果A为1,则为13点,因此 A+4+8=13
/// 如A+K+10,则为21点,黑杰克。 A+J 也是黑杰克=21
/// 如A+5+A=17点,A+A=12点
///
public class BlackjackUtil
{
///
/// 已选中的扑克牌所在的索引集合,已随机到的扑克牌,下次不能随机到
///
private static List
///
/// 庄家
///
private static Player dealer = null;
///
/// 玩家
///
private static Player player = null;
///
/// 开始21点游戏【黑杰克】
///
public static void StartPlay()
{
Init();
//开始时 庄家、玩家都发两张牌
Console.WriteLine("庄家随机发两张扑克牌...");
DealCard(dealer);
DealCard(dealer);
Console.WriteLine(dealer);
Console.WriteLine();
Console.WriteLine("玩家随机获取两张扑克牌...");
DealCard(player);
DealCard(player);
Console.WriteLine(player);
while (true)
{
Console.WriteLine();
Console.WriteLine("庄家选择:继续要牌(Y),不要牌(N),开牌(R或其他字符)...");
string input = Console.ReadLine();
if ("Y".Equals(input, StringComparison.CurrentCultureIgnoreCase))
{
bool isContinue = DealCard(dealer);
if (!isContinue)
{
Console.WriteLine(dealer);
Console.WriteLine(player);
JudgeResult();
break;
}
}
else if ("N".Equals(input, StringComparison.CurrentCultureIgnoreCase))
{
//不要牌
}
else
{
//直接开牌,结束
Console.WriteLine(dealer);
Console.WriteLine(player);
JudgeResult();
break;
}
Console.WriteLine();
Console.WriteLine("玩家选择:继续要牌(Y),不要牌(N),开牌(R或其他字符)...");
input = Console.ReadLine();
if ("Y".Equals(input, StringComparison.CurrentCultureIgnoreCase))
{
bool isContinue = DealCard(player);
if (!isContinue)
{
Console.WriteLine(dealer);
Console.WriteLine(player);
JudgeResult();
break;
}
}
else if ("N".Equals(input, StringComparison.CurrentCultureIgnoreCase))
{
//不要牌
}
else
{
//直接开牌,结束
Console.WriteLine(dealer);
Console.WriteLine(player);
JudgeResult();
break;
}
Console.WriteLine();
Console.WriteLine("此时,系统双方点数情况...");
Console.WriteLine(dealer);
Console.WriteLine(player);
}
}
///
/// 判定结果:根据双方当前点数进行判定,如果 点数一致 或者 同时爆点(大于21) 则和局,否则进行输赢判定
/// 庄家有【小于21、等于21、大于21】三种可能,玩家也有【小于21、等于21、大于21】三种可能。因此共用九种判定
/// 该逻辑也可简化判定为【点数一致、庄家黑杰克、玩家黑杰克、庄家玩家都小于黑杰克、庄家玩家都大于黑杰克、庄家小于黑杰克 且 玩家大于黑杰克、庄家大于黑杰克 且 玩家小于黑杰克】
///
public static void JudgeResult()
{
if (dealer.CurrentPoint == player.CurrentPoint)
{
Console.WriteLine("和局!庄家和玩家点数一样:{0}{1}", dealer.CurrentPoint, dealer.CurrentPoint == CommonUtil.BlackJack ? "【黑杰克】" : "");
return;
}
if (dealer.CurrentPoint < CommonUtil.BlackJack && player.CurrentPoint < CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", dealer.CurrentPoint > player.CurrentPoint ? "庄家胜利" : "玩家胜利", dealer.CurrentPoint, player.CurrentPoint);
}
else if (dealer.CurrentPoint < CommonUtil.BlackJack && player.CurrentPoint == CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "玩家胜利【黑杰克】", dealer.CurrentPoint, player.CurrentPoint);
}
else if (dealer.CurrentPoint < CommonUtil.BlackJack && player.CurrentPoint > CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "庄家胜利【玩家爆点】", dealer.CurrentPoint, player.CurrentPoint);
}
else if (dealer.CurrentPoint == CommonUtil.BlackJack && player.CurrentPoint < CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "庄家胜利【黑杰克】", dealer.CurrentPoint, player.CurrentPoint);
}
//else if (dealer.CurrentPoint == CommonUtil.BlackJack && player.CurrentPoint == CommonUtil.BlackJack)
//{
//}
else if (dealer.CurrentPoint == CommonUtil.BlackJack && player.CurrentPoint > CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "庄家胜利【黑杰克】", dealer.CurrentPoint, player.CurrentPoint);
}
else if (dealer.CurrentPoint > CommonUtil.BlackJack && player.CurrentPoint < CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "玩家胜利【庄家爆点】", dealer.CurrentPoint, player.CurrentPoint);
}
else if (dealer.CurrentPoint > CommonUtil.BlackJack && player.CurrentPoint == CommonUtil.BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "玩家胜利【黑杰克】", dealer.CurrentPoint, player.CurrentPoint);
}
else //if (dealer.CurrentPoint > BlackJack && player.CurrentPoint > BlackJack)
{
Console.WriteLine("{0}!庄家点数:{1},玩家点数:{2}", "和局【庄家和玩家都已爆点】", dealer.CurrentPoint, player.CurrentPoint);
}
}
///
/// 发一张牌,当超过11张牌时,强行结束
/// 假设一个人最多拥有11张牌:2+2+2+2+3+3+3+A+A+A+A=21
///
///
///
public static bool DealCard(Player p)
{
int selectedIndex = GetRandomPokerIndex();
p.ListCard.Add(CommonUtil.Dict.ElementAt(selectedIndex).Key);
if (p.ListCard.Count >= 11)
{
Console.WriteLine("已有牌已超过11张,强行结束,开牌计算...");
return false;
}
return true;
}
///
/// 获得一张扑克牌索引,发牌一张
///
///
public static int GetRandomPokerIndex()
{
//注意:共52张牌,因此随机数索引为[0,51]
int index = new Random(Guid.NewGuid().GetHashCode()).Next(0, 52);
//如果在集合中存在该索引,则重新随机一张牌
while (listSelect.Contains(index))
{
index = new Random(Guid.NewGuid().GetHashCode()).Next(0, 52);
}
listSelect.Add(index);
return index;
}
///
/// 初始化重新开始
///
public static void Init()
{
dealer = new Player("张三", true);
player = new Player("李四", false);
listSelect.Clear();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DemoBlackJack
{
class Program
{
static void Main(string[] args)
{
string input;
do
{
BlackjackUtil.StartPlay();
Console.WriteLine("游戏已结束,是否继续游戏(继续:Y,结束:N)...");
input = Console.ReadLine();
} while ("Y".Equals(input, StringComparison.CurrentCultureIgnoreCase));
Console.ReadLine();
}
}
}