This represents a table (server) for Texas Hold'em.
This is not a full Hold'em game but it is working code for what it does. It shuffles, deals the hole cards, deals the board, moves the blinds, and collects the blinds. It does not yet support real betting nor award the pot. It just fakes a pot. Real betting gets complex. It is at a point I would appreciate a review.
In Poker the button moves to the dealers right which is the players left (+1) (clockwise). For the first round there is a random draw for the button. There are 4 rounds of betting/dealing. The small blind is left of the button (+1) has a forced bet of SB. The big blind to the left of SB has a forced bet of BB (typically 2x the SB). After the BB is first to act in the first betting round. After the first betting round the SB is first to act. In the first street (pre) each player gets two hole cards. On the second street (flop) the board gets three cards. Turn and river the board gets one more card each. I know it sounds complex but that is how it works.
In heads up (two players) it gets messy as the dealer is also the SB. This does not yet deal with heads up.
A table has seats. This code does not use seat. The ordered List of players is the seats. This code moves the button directly to a player and assigns betting order directly to a player.
There is no dealer in this code. The table performs the actions of the dealer.
This uses a standard 52 card poker deck and no intention of ever supporting another deck.
This does not burn cards which has no effect on the game.
Not intended to be gaming level server where money is exchanged. Just host some simple home games.
public static void PokerPlayerTest()
{
PokerTable pt = new PokerTable("Hook", new List<PokerPlayer> { 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<PokerCard> deck = new List<PokerCard>();
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<PokerCard> Board { get; } = new List<PokerCard>();
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<PokerPlayer> 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<PokerPlayer> pokerPlayers)
{
DealerName = dealerName;
PokerPlayers = pokerPlayers;
for(byte b = 0; b < 52; b++)
{
deck.Add(new PokerCard(b));
}
}
}
Getting a few (disparaging) comments about short enum. When you are fighting for screen space they are nice. Poker has some standard abbreviations. Client is free to use any text or image they want (and should).
Can ignore this
This is for for a service oriented architecture (SAO). To add that would possibly invalidate existing answers which is not fair.
public static void PokerPlayerTest()
scope just... floating in the namespace? \$\endgroup\$In Poker the button moves to the right (+1).
From a player's perspective, it moves to the left, not the right. Are you thinking in a top-down view, i.e. where the button moves from 12 o'clock to 1 o'clock? Because that would be a confusing definition: 12 to 1 move to the right, but 6 to 7 moves to the left. \$\endgroup\$