这表示德州持有它们的表(服务器)。
这不是一个完全保持他们的游戏,但它是工作的代码,它所做的。它洗牌,处理孔牌,交易棋盘,移动百叶窗,收集百叶窗。它还不支持真正的赌博,也不奖励大麻。它只是装了个罐子。真正的赌博变得很复杂。在某一时刻,我希望能进行一次审查。
在扑克,按钮移动到经销商的右边,即玩家左边(+1) (顺时针方向)。在第一轮,有一个随机抽签按钮。有4轮投注/交易。那个小瞎子的按钮(+1)的左边有一个强迫的某人下注。某人左边的大瞎子被迫押注BB (通常是SB的2倍)。之后的BB是第一个行动在第一轮投注。在第一次押注某人之后,首先采取行动。在第一条街(预),每个玩家得到两张洞牌。在第二条街(失败),董事会得到三张卡片。转一圈,冲浪板再得到一张牌。我知道这听起来很复杂,但这就是它的工作原理。
在提个醒(两名球员),它变得混乱,因为经销商也是SB。这还不涉及提防问题。
桌子上有座位。此代码不使用座椅。按顺序排列的球员名单是座位。此代码将按钮直接移动到播放机,并将投注顺序直接分配给播放机。
这个代码里没有毒贩。表执行经销商的操作。
这使用了一个标准的52牌扑克牌,没有任何意图支持另一套。
这不会烧掉对游戏没有影响的牌。
不打算成为游戏级别的服务器,在那里钱是交换的。只是举办一些简单的家庭游戏。
public static void PokerPlayerTest()
{
PokerTable pt = new PokerTable("Hook", new List { new PokerPlayer("Peter0"), new PokerPlayer("Tinker1"), new PokerPlayer("Wendy2"), new PokerPlayer("Rianna3"), new PokerPlayer("Lady4") });
for(int i = 0; i<10; i++)
{
pt.Shuffle();
Debug.WriteLine(pt.ToString());
pt.Flop();
Debug.WriteLine(pt.ToString());
pt.Trun();
Debug.WriteLine(pt.ToString());
pt.River();
Debug.WriteLine(pt.ToString());
Debug.WriteLine("");
}
}public class PokerCard
{
byte id;
public enum Suits { heart, spade, diamond, club };
public enum Ranks { two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, ace };
public Suits Suit { get { return (Suits)(id/13); } }
public Ranks Rank { get { return (Ranks)(id%13); } }
public override string ToString()
{
return $"{Rank} {Suit}";
}
public PokerCard(byte ID)
{
id = ID;
}
}public class PokerPlayer
{
public enum Poss { sb, bb, btn, mid}
//current PokerPlayer does not bet
public int ChipCount { get; set; } = 200;
public PokerCard[] HoleCards { get; } = new PokerCard[2];
public int ID { get; }
public int BettingOrder { get; set; } = 0;
public Poss Pos { get; set; }
public string Name { get; }
public override string ToString()
{
return $"{Name} {HoleCards[0].ToString()}_{HoleCards[1].ToString()} pos {Pos} chips {ChipCount.ToString("N0")}";
}
public PokerPlayer (byte id, string name)
{
ID = id;
Name = name;
}
public PokerPlayer(byte id, string name, int chipCount)
{
ID = id;
Name = name;
ChipCount = chipCount;
}
public PokerPlayer(string name)
{
ID = 0;
Name = name;
}
public PokerPlayer(string name, int chipCount)
{
ID = 0;
Name = name;
ChipCount = chipCount;
}
}public class PokerTable
{
//pot and betting not yet implemented
//current PokerTable just deals
Random rand = new Random();
List deck = new List();
public enum Streets { pre, flop, turn, rivr }
byte street = 0;
byte cardNum = 0;
int round = 0;
int btn = 0;
int sb = 1;
int bb = 2;
public PokerPlayer Button { get { return PokerPlayers[btn]; } }
public int Pot { get; set; } = 0;
public Streets Street { get { return (Streets)street; } }
public List Board { get; } = new List();
public string BoardStr { get { return string.Join(", ", Board.Select(c => c.ToString())); } }
public string PlayersStr { get { return string.Join(", ", PokerPlayers.OrderBy(p => p.BettingOrder).Select(p => p.ToString())); } }
void SetBettingOrder()
{
int cnt = PokerPlayers.Count;
int offset = street == 0 ? 3 : 1;
for(int i = 0; i < PokerPlayers.Count; i++)
{
int bettingOrder = (i + 2*cnt - btn - offset) % cnt;
PokerPlayers[i].BettingOrder = bettingOrder;
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
if(street == 0)
{
sb.AppendLine($"dealer {DealerName} round {round} button {Button.Name}\n {Street} "); //{ PlayersStr}
}
else
{
sb.AppendLine($" {Street} {BoardStr}");
}
foreach (PokerPlayer pp in PokerPlayers.OrderBy(p => p.BettingOrder))
{
sb.AppendLine($" {pp.ToString()}");
}
sb.Append($" pot {Pot}");
return sb.ToString();
}
public string DealerName { get; }
public List PokerPlayers { get; }
private void MoveButton()
{
if (round == 1)
{
btn = rand.Next(PokerPlayers.Count - 1);
}
else
{
btn++;
if(btn == PokerPlayers.Count)
{
btn = 0;
}
}
foreach (PokerPlayer pp in PokerPlayers)
{
pp.Pos = PokerPlayer.Poss.mid;
}
int tpos = btn + 1;
if(tpos == PokerPlayers.Count)
{
tpos = 0;
}
PokerPlayers[tpos].Pos = PokerPlayer.Poss.sb;
PokerPlayers[tpos].ChipCount -= sb;
tpos ++;
if (tpos == PokerPlayers.Count)
{
tpos = 0;
}
PokerPlayers[tpos].Pos = PokerPlayer.Poss.bb;
PokerPlayers[tpos].ChipCount -= bb;
PokerPlayers[btn].Pos = PokerPlayer.Poss.btn;
}
public void Shuffle()
{
//fisher yates shuffle
street = 0;
round++;
Pot = sb + bb;
Board.Clear();
MoveButton();
SetBettingOrder();
for (int b = deck.Count - 1; b > 0; b--)
{
int r = rand.Next(b);
if(r != b)
{
PokerCard temp = deck[r];
deck[r] = deck[b];
deck[b] = temp;
}
}
//no need for cut or burn cards cards
cardNum = 0;
foreach(PokerPlayer pp in PokerPlayers)
{
pp.HoleCards[0] = deck[cardNum];
cardNum++;
pp.HoleCards[1] = deck[cardNum];
cardNum++;
}
}
public void Flop()
{
if(street != 0)
{
throw new ArgumentOutOfRangeException("wrong street");
}
street++;
SetBettingOrder();
Board.Add(deck[cardNum]);
cardNum++;
Board.Add(deck[cardNum]);
cardNum++;
Board.Add(deck[cardNum]);
cardNum++;
Pot += Pot;
}
public void Trun()
{
if (street != 1)
{
throw new ArgumentOutOfRangeException("wrong street");
}
street++;
Board.Add(deck[cardNum]);
cardNum++;
Pot += Pot;
}
public void River()
{
if (street != 2)
{
throw new ArgumentOutOfRangeException("wrong street");
}
street++;
Board.Add(deck[cardNum]);
Pot += Pot;
}
public PokerTable(string dealerName, List pokerPlayers)
{
DealerName = dealerName;
PokerPlayers = pokerPlayers;
for(byte b = 0; b < 52; b++)
{
deck.Add(new PokerCard(b));
}
}
}得到一些关于短枚举的(贬损)评论。当你为屏幕空间而战时,它们是很好的。扑克有一些标准的缩写。客户端可以自由地使用他们想要的任何文本或图像(并且应该使用)。

Can忽略这个,这是用于面向服务的体系结构(SAO)的。如果加上这一点,可能会使现有的答案失效,这是不公平的。
发布于 2018-03-19 09:41:46
我将对downrep_nation的第二个评论:sb、bb、rivr等是枚举元素(和成员)的糟糕名称。我更喜欢smallBlind bigBlind和button:btn看起来像过去的控件。我一直为类型使用首字母,但从未用于域概念,从river中删除“e”有什么好处呢?
这也是一个糟糕的标识符:Poss。PokerTable.Trun有拼写错误。
说到Trun,你为什么不使用你的Streets枚举?
if (street != 1)
{
throw new ArgumentOutOfRangeException("wrong street");
}
street++可能是
if (street != Street.Flop)
{
throw new ArgumentOutOfRangeException("Wrong street: expected Flop, but received " + street.ToString());
}
street = Street.Turn;这比依赖于枚举命令的细节要清晰得多(这一点根本没有文档记录)。当然,这取决于street是一个Streets,而不是byte,这一点也要清楚得多。
在SetBettingOrder中:
int offset = street == Steets.Pre ? 3 : 1;在您的代码中有大量的“添加一个可能的重置”,这严重破坏了逻辑,只是在maintaince期间需要担心更多的事情:
int tpos = btn + 1;
if(tpos == PokerPlayers.Count)
{
tpos = 0;
}tpos = (tpos + 1) % PokerPlayers.Count是一种更短的方法,但考虑到您经常需要它,我肯定会使它成为自己的功能:
private int NextPlayerIndex(int playerIndex)
{
return (playerIndex + 1) % PokerPlayers.Count;
}你也有很多建筑工人..。这句话毫无意义:
public PokerPlayer(string name){
ID = 0;
Name = name;
}在哪个世界里,不需要指定ID是一件好事?这只是乞求被滥用。
考虑到您的构造函数中没有逻辑,我还希望看到一个'base‘构造函数,其他构造函数(老实说,我只会删除一个)调用它,而不是每个构造函数都维护自己的默认列表:
public PokerPlayer(byte id, string name, int chipCount)
{
ID = id;
Name = name;
ChipCount = chipCount;
}
public PokerPlayer (byte id, string name) : this(id, name, 200)
{
// nix // I usually leave a short, semi-esoteric but recognisable comment to indicate there is meant to be nothing here when I have a pair of empty braces
}这意味着,如果您添加了一个成员,您只需要更新基本构造函数并修复所产生的所有错误,而不是担心不会丢失任何错误。在您的特殊情况下,chipCount有点奇怪,因为它有一个常见且不寻常的缺省值,但我假设这是调试的遗物,而不是在生产代码中出现的东西:如果出现了这样一个神奇的数字,那么它应该是一个常数,您可以在构造函数中引用它。
`const int DefaultChipCount = 200`或者,您可以使用一个可选的参数,并在失去一些灵活性的情况下放弃所有可怕的东西。
public PokerPlayer(byte id, string name, int chipCount = DefaultChipCount)这意味着您不能在当前位置设置id的默认设置,但是如果它非常不重要,可能会有一个默认值,那么它就不是第一个参数。
同样,正如downrep_nation所说,对于某些方法所做的事情有很多困惑。尤其是洗牌数组(如在Shuffle中执行的)与Poker无关,我将实现这个完全独立的类,因为它本质上是一个通用的通用操作。事实上,我从//fisher yates shuffle评论的立场可以猜到,Shuffle最初只是想洗牌,但后来又被其他职责夸大了。
我将在一个稍微不同的方向上听取downrep_nation关于此代码的建议:
pp.HoleCards[0] = deck[cardNum];
cardNum++;这应该被推到TakeCard() => cardNum++;方法中。然后,组装社区卡将给您提供以下内容,我认为它比downrep_nation的LINQ更具表现力:
for (int i = 0; i < magicNumber; i++)
Board.Add(TakeCard);这里的pokerPlayers参数有点吓人,因为您不知道它是从哪里来的,然后继续将它用于状态:
PokerTable(string dealerName, List pokerPlayers)我会让它(和属性,特别是因为它是公共的)一个IReadOnlyList,并克隆它,只是为了确定。您不希望创建PokerTable的代码单方面更改此列表。
还有一些无聊的事情:
suit和rank都是公共的,尽管在PokerCard之外是无用的,并且有一些看起来很私密的名字,也没有提供任何保护来防止“越界”查找。PokerCard看起来是一个拥有一些Debug.Assert语句的好地方,因此如果它提供了一个无效的id,它就可以立即抛出,给您一个有价值的调用堆栈,并提供一些文档。PokerCard.id):但显然这不是一个大问题(除非您正在与来自C++的人合作-可能/任何其他语言具有不同的默认值)Poss Pos中删除PokerPlayer,并在需要时只计算它,除非PokerPlayer类有充分的理由需要知道它的位置。ToString()方法没有多大意义。PokerPlayer可以在调用ToString()时返回一些未初始化的状态(Pos将是默认值)。PokerTable中,DealerName和PokerPlayers定义在下面一些方法中,这会使寻找它们的人感到困惑。发布于 2018-03-19 06:24:35
总的来说,代码似乎没问题。
但是有一些事情需要考虑,这与更干净的代码和坚实的原则有关。
首先,您的变量名称是混乱的,而不是指示性的。
sb, bb, btn, cardNum对我来说没有任何意义,也许它们是扑克术语,但我不是扑克玩家,我是一个开发人员,我正在阅读您的代码。
与id存在一次是不一致的
ID and id选择一种套管风格,并与之保持一致。
public Suits Suit { get { return (Suits)(id/13); } }这可以写成
public Suits Suit => (Suits)(id/13);这只是语法上的糖,只适用于成员,但不是必须的,而必须的是命名您的神奇数字。
我不知道13应该代表什么,考虑创建一个常量来存储这个值以便更好地理解,这会在代码中重复出现。
在PokerPlayer类中,考虑使用函数参数的默认参数来删除某些代码重复。
另一点是坚实的原则,特别是SRP (单一责任)。
通过将图形逻辑和业务逻辑结合起来,您的函数破坏了SRP,这意味着函数应该或聚合不同的抽象来定义逻辑,或者负责使用代码及其底层数据结构进行低级工作。
此外,SRP通过实现低级别数组改组和处理抽象(如Shuffle() ),使函数更具有指示性和可测试性,是一个很好的例子。
尝试将这些函数划分为逻辑组件,并为它们编写测试。
在Flop()中,您可以编写Pot *= 2来指示锅的加倍(吹毛求疵)
另外,可以用更容易阅读的Linq来代替它。
Board.Add(deck[cardNum]);
cardNum++;
Board.Add(deck[cardNum]);
cardNum++;
Board.Add(deck[cardNum]);
cardNum++;代写
Board.AddRange(deck.Skip(cardNum).Take(magicNumber));
cardNum+=magicNumber;发布于 2018-03-19 13:07:28
https://codereview.stackexchange.com/questions/189899
复制相似问题