diff --git a/PioHoldem/Source/AI/FishAI.cs b/PioHoldem/Source/AI/FishAI.cs index 1e03cdc..d5c5a85 100644 --- a/PioHoldem/Source/AI/FishAI.cs +++ b/PioHoldem/Source/AI/FishAI.cs @@ -15,21 +15,15 @@ public override int GetAction(Game game) // Fold[1] Check[2] Bet[4] action = rng.Next(3); if (action == 0) - { // Check return 0; - } else { // Bet 1/2 pot if (game.pot / 2 < game.players[game.actingIndex].stack) - { return game.pot / 2; - } else - { return game.players[game.actingIndex].stack; - } } } else if (game.betAmt == game.players[game.actingIndex].inFor) @@ -41,32 +35,22 @@ public override int GetAction(Game game) { // Raise 3x if ((3 * game.betAmt) - game.players[game.actingIndex].inFor < game.players[game.actingIndex].stack) - { return (3 * game.betAmt) - game.players[game.actingIndex].inFor; - } else - { return game.players[game.actingIndex].stack; - } } else - { // Check return 0; - } } else if (game.players[game.GetPreviousPosition(game.actingIndex)].stack == 0) { // Facing an all-in bet // Call if (game.betAmt - game.players[game.actingIndex].inFor >= game.players[game.actingIndex].stack) - { return game.players[game.actingIndex].stack; - } else - { return game.betAmt - game.players[game.actingIndex].inFor; - } } else { @@ -74,33 +58,23 @@ public override int GetAction(Game game) // Fold[1] Call[3] Raise[5] action = rng.Next(10); if (action < 0) - { // Fold return -1; - } else if (action < 5) { // Call if (game.betAmt - game.players[game.actingIndex].inFor >= game.players[game.actingIndex].stack) - { return game.players[game.actingIndex].stack; - } else - { return game.betAmt - game.players[game.actingIndex].inFor; - } } else { // Raise 3x if ((3 * game.betAmt) - game.players[game.actingIndex].inFor >= game.players[game.actingIndex].stack) - { return game.players[game.actingIndex].stack; - } else - { return (3 * game.betAmt) - game.players[game.actingIndex].inFor; - } } } } diff --git a/PioHoldem/Source/Game/Client.cs b/PioHoldem/Source/Game/Client.cs index 8600026..d3af8d8 100644 --- a/PioHoldem/Source/Game/Client.cs +++ b/PioHoldem/Source/Game/Client.cs @@ -15,17 +15,11 @@ static void Main(string[] args) Console.WriteLine("[1] Human vs Bot\n[2] Bot vs Bot\n[3] Human vs Human"); int gameMode = SelectGameMode(); if (gameMode == 1) - { players = new Player[] { new HumanPlayer("Chris", startingStack), new BotPlayer("SharkBot", startingStack, shark) }; - } else if (gameMode == 2) - { players = new Player[] { new BotPlayer("GreatWhite", startingStack, shark), new BotPlayer("Hammerhead", startingStack, shark) }; - } else - { players = new Player[] { new HumanPlayer("Chris", startingStack), new HumanPlayer("Aaron", startingStack) }; - } //UnitTests(); @@ -76,14 +70,10 @@ private static void UnitTests() Card[] hand = new Card[5] { c1, c2, c3, c4, c5 }; Card[] hand2 = new Card[5] { c8, c9, c10, c11, c12 }; foreach (Card card in hand) - { Console.Write("|" + card); - } Console.WriteLine("|"); foreach (Card card in hand2) - { Console.Write("|" + card); - } Console.WriteLine("|"); HandEvaluator eval = new HandEvaluator(); diff --git a/PioHoldem/Source/Game/Deck.cs b/PioHoldem/Source/Game/Deck.cs index 1249539..881c416 100644 --- a/PioHoldem/Source/Game/Deck.cs +++ b/PioHoldem/Source/Game/Deck.cs @@ -15,9 +15,7 @@ public Deck() for (int suit = 0; suit < 4; suit++) { for (int value = 0; value < 13; value++) - { cards[13 * suit + value] = new Card(suit, value); - } } topIndex = 0; rng = new Random(); @@ -52,9 +50,7 @@ public Card[] Deal(int count) { Card[] toReturn = new Card[count]; for (int i = 0; i < count; i++) - { toReturn[i] = Deal(); - } return toReturn; } @@ -68,9 +64,7 @@ public override string ToString() { string toReturn = ""; foreach (Card card in cards) - { toReturn += "|" + card; - } return toReturn + "|"; } } diff --git a/PioHoldem/Source/Game/Game.cs b/PioHoldem/Source/Game/Game.cs index 446f286..5ca5600 100644 --- a/PioHoldem/Source/Game/Game.cs +++ b/PioHoldem/Source/Game/Game.cs @@ -54,53 +54,35 @@ private void GameLoop() { Flop(); if (!AllInSkipToShowdown()) - { allButOneFolded = BettingRound(GetNextPosition(btnIndex)); - } if (!allButOneFolded) { Turn(); if (!AllInSkipToShowdown()) - { allButOneFolded = BettingRound(GetNextPosition(btnIndex)); - } if (!allButOneFolded) { River(); if (!AllInSkipToShowdown()) - { allButOneFolded = BettingRound(GetNextPosition(btnIndex)); - } if (!allButOneFolded) - { Showdown(); - } else - { EndHand(); - } } else - { EndHand(); - } } else - { EndHand(); - } } else - { EndHand(); - } // If there is only one remaining player with a chip stack, end the game if (CountNotBusted() == 1) - { gameOver = true; - } // Otherwise, begin the next hand else { @@ -188,14 +170,12 @@ private void PostBlinds() private void DealHoleCards() { foreach (Player player in players) - { if (!player.busted) { player.holeCards = deck.Deal(2); player.folded = false; player.isAggressor = false; } - } } // Get an action from each player in order until all players have either folded or @@ -217,13 +197,9 @@ private bool BettingRound(int toActFirstIndex) PrintPlayers(); Console.Write("Pot:" + pot + " Bet:" + betAmt + " InFor:" + players[actingIndex].inFor + " ToCall:"); if (betAmt - players[actingIndex].inFor >= players[actingIndex].stack) - { Console.WriteLine("ALL-IN"); - } else - { Console.WriteLine(betAmt - players[actingIndex].inFor); - } // Get an action from the acting player int playerAction = players[actingIndex].GetAction(this); @@ -234,12 +210,10 @@ private bool BettingRound(int toActFirstIndex) actionCount++; } - + // If all but one player have folded, end the hand if (CountNotFolded() == 1) - { return true; - } // Get the next player to act actingIndex = GetNextPosition(actingIndex); @@ -280,14 +254,10 @@ private bool ProcessPlayerAction(int playerAction, int toActLastIndex) // Close the action if the big blind checks and there has been no aggressive action if (actingIndex == bbIndex && betAmt == bbAmt && street == 0) - { return true; - } // Leave the action open if there is no bet and not all players have acted yet else if (betAmt == 0 && actingIndex != toActLastIndex) - { return false; - } } // Positive value: the player put chips in the pot (call/bet/raise) else if (playerAction > 0) @@ -343,18 +313,12 @@ private bool ProcessPlayerAction(int playerAction, int toActLastIndex) // ...at least one non-folded player has not matched the betAmt foreach (Player player in players) - { if (!player.folded && player.inFor != betAmt) - { isActionClosed = false; - } - } // ...the player in the big blind is next to act and no player has raised if (bbIndex == GetNextPosition(actingIndex) && betAmt == bbAmt && street == 0 && players[bbIndex].stack > 0) - { isActionClosed = false; - } return isActionClosed; } @@ -407,14 +371,12 @@ private void River() private void EndHand() { for (int i = 0; i < players.Length; i++) - { if (players[i].folded == false) { Console.WriteLine(players[i].name + " wins pot of " + (pot - players[i].inFor)); players[i].stack += pot; pot = 0; } - } // Move the button for the start of the next hand btnIndex = GetNextPosition(btnIndex); @@ -433,13 +395,11 @@ private void Showdown() foreach (Player player in players) - { if (!player.folded) { showdownPlayers[trackerIndex] = player; trackerIndex++; } - } int winnerIndex = eval.EvaluateHands(showdownPlayers, board); Console.WriteLine(); @@ -450,14 +410,12 @@ private void Showdown() int remainder = pot % showdownCount; int remainderIndex = GetToActLastIndex(sbIndex); for (int i = 0; i < players.Length; i++) - { if (!players[i].folded) { int take = chopAmt + (i == remainderIndex ? remainder : 0); Console.WriteLine(players[i].name + " wins " + take); players[i].stack += take; } - } } else { @@ -473,33 +431,27 @@ private void Showdown() btnIndex = GetNextPosition(btnIndex); } - // Reset the committed number of chips to 0 for each player + // Reset the current bet amounts and committed number of chips to 0 for each player private void ClearBetAmts() { betAmt = 0; prevBetAmount = 0; foreach (Player player in players) - { player.inFor = 0; - } } // Reset the community cards private void ClearBoard() { for (int i = 0; i < board.Length; i++) - { board[i] = null; - } } // Print the community cards private void PrintBoard() { foreach (Card card in board) - { Console.Write(card != null ? "|" + card : "| "); - } Console.WriteLine("|"); } @@ -507,9 +459,7 @@ private void PrintBoard() private void PrintPlayers() { foreach (Player player in players) - { Console.Write(player + " "); - } Console.WriteLine(); } @@ -517,9 +467,7 @@ private void PrintPlayers() private void PrintScore() { foreach (Player player in players) - { Console.Write(player.name + ":" + player.winCount + " "); - } Console.WriteLine(); } @@ -540,15 +488,11 @@ private int GetToActLastIndex(int toActFirstIndex) { // The player in the BB always acts last preflop if (street == 0) - { return bbIndex; - } // Postflop, find the non-folded player in latest position else - { return players[GetPreviousPosition(toActFirstIndex)].folded ? GetToActLastIndex(GetPreviousPosition(toActFirstIndex)) : GetPreviousPosition(toActFirstIndex); - } } // Divide the shortest stack by the BB amount @@ -556,12 +500,8 @@ private double CalculateEffectiveStack() { int shortestStack = players[0].stack; foreach (Player player in players) - { if (player.stack < shortestStack) - { shortestStack = player.stack; - } - } return (double)shortestStack / bbAmt; } @@ -573,18 +513,12 @@ private bool AllInSkipToShowdown() foreach (Player player in players) { if (!player.folded) - { notFoldedCount++; if (player.stack == 0) - { allInCount++; - } - } } if (notFoldedCount - allInCount <= 1) - { return true; - } return false; } @@ -593,12 +527,8 @@ private int CountNotFolded() { int playersRemaining = 0; foreach (Player player in players) - { if (!player.folded) - { playersRemaining++; - } - } return playersRemaining; } @@ -607,12 +537,8 @@ private int CountNotBusted() { int count = 0; foreach (Player player in players) - { if (!player.busted) - { count++; - } - } return count; } @@ -620,14 +546,12 @@ private int CountNotBusted() private void ReportBustedPlayers(Player[] players) { for (int i = 0; i < players.Length; i++) - { if (players[i].stack == 0 && !players[i].busted) { Console.WriteLine(); Console.WriteLine(players[i].name + " busted"); players[i].busted = true; } - } } // End the game and announce the winner @@ -635,13 +559,11 @@ private void GameOver() { string winner = ""; foreach (Player player in players) - { if (!player.busted) { winner = player.name; player.winCount++; } - } Console.WriteLine(winner + " wins!"); Console.WriteLine(); diff --git a/PioHoldem/Source/Players/HumanPlayer.cs b/PioHoldem/Source/Players/HumanPlayer.cs index b90870d..42c20a9 100644 --- a/PioHoldem/Source/Players/HumanPlayer.cs +++ b/PioHoldem/Source/Players/HumanPlayer.cs @@ -52,57 +52,37 @@ private int GetInput(Game game, int[] validActions, string options) if (validActions.Contains(input)) { if (input == 1) - { return -1; - } else if (input == 2) - { return 0; - } else if (input == 3) { if (game.betAmt - inFor >= stack) - { return stack; - } else - { return game.betAmt - inFor; - } } else if (input == 4) { int amtInput = GetAmtInput(game.betAmt, game.prevBetAmount, game.bbAmt, "Bet amount:"); if (amtInput >= stack) - { return stack; - } else - { return amtInput; - } } else if (input == 5) { int amtInput = GetAmtInput(game.betAmt, game.prevBetAmount, game.bbAmt, "Raise to amount:"); if (amtInput - inFor >= stack) - { return stack; - } else - { return amtInput - inFor; - } } else - { throw new Exception(); - } } else - { throw new Exception(); - } } catch (Exception) { @@ -128,17 +108,13 @@ private int GetAmtInput(int betAmt, int prevBetAmt, int minBet, string prompt) return GetAmtInput(betAmt, prevBetAmt, minBet, prompt); } else - { return amtInput; - } } catch (Exception ex) { if (ex.Message == "Value was either too large or too small for an Int32.") - { return 2 * stack; - } else { Console.WriteLine("Invalid input!"); diff --git a/PioHoldem/Source/Utilities/HandEvaluator.cs b/PioHoldem/Source/Utilities/HandEvaluator.cs index ca8ecac..a99c161 100644 --- a/PioHoldem/Source/Utilities/HandEvaluator.cs +++ b/PioHoldem/Source/Utilities/HandEvaluator.cs @@ -14,10 +14,8 @@ public int EvaluateHands(Player[] players, Card[] board) int[] handValues = new int[players.Length]; foreach (Card card in board) - { if (card != null) boardLength++; - } Card[] hand = new Card[2 + boardLength]; @@ -27,19 +25,18 @@ public int EvaluateHands(Player[] players, Card[] board) // Populate the first five cards with the board for (int j = 0; j < boardLength; j++) - { hand[j] = board[j]; - } // Populate the last two cards with the current player's hole cards hand[boardLength] = player.holeCards[0]; hand[boardLength + 1] = player.holeCards[1]; - Console.Write(player.name + " shows |" + player.holeCards[0] + "|" + player.holeCards[1] + "| "); - // Calculate the relative value of the current player's hand handValue = GetHandValue(hand); + // Show the player's hole cards and hand rank + Console.WriteLine(player.name + " shows |" + player.holeCards[0] + "|" + player.holeCards[1] + "| *" + HandRankToString(handValue) + "*"); + // Write the value to the list of hand values for each player handValues[i] = handValue; } @@ -51,24 +48,18 @@ public int EvaluateHands(Player[] players, Card[] board) // Check if there is a tie for the highest value int count = 0; for (int i = 0; i < handValues.Length; i++) - { if (handValues[i] == highestHandValue) { count++; winnerIndex = i; } - } // If there is a tie, return -1 to indicate a chop pot if (count > 1) - { return -1; - } // Otherwise, return the index of the player with the highest ranking hand else - { return winnerIndex; - } } // Calculate the relative value of the given hand using the helper methods below @@ -90,14 +81,9 @@ private int HasRoyalFlush(Card[] hand) // Check for TJQKA of same suit if (value == 8) - { if (HandContains(hand, suit, 9) && HandContains(hand, suit, 10) && HandContains(hand, suit, 11) && HandContains(hand, suit, 12)) - { - Console.WriteLine("*Royal Flush*"); - return 800000012; - } - } + return 800000012; } return HasStraightFlush(hand); } @@ -112,19 +98,13 @@ private int HasStraightFlush(Card[] hand) // Check for 5 cards of same suit and consecutive values if (HandContains(hand, suit, value + 1) && HandContains(hand, suit, value + 2) && HandContains(hand, suit, value + 3) && HandContains(hand, suit, value + 4)) - { - Console.WriteLine("*Straight Flush*"); - return 800000000 + (value + 4); - } + return 800000000 + (value + 4); // Check for A2345 of same suit else if (value == 12) { if (HandContains(hand, suit, 0) && HandContains(hand, suit, 1) && HandContains(hand, suit, 2) && HandContains(hand, suit, 3)) - { - Console.WriteLine("*Straight Flush*"); return 800000003; - } } } return HasFourOfAKind(hand); @@ -136,19 +116,13 @@ private int HasFourOfAKind(Card[] hand) { int count = 0; foreach (Card card2 in hand) - { // Excluding the current card itself, count the cards in the hand that have the same value if (card.suit != card2.suit && card.value == card2.value) { count++; if (count == 3) - { - Console.WriteLine("*Four Of A Kind*"); - // Give the quads card more weight than the kicker card return 700000000 + (500000 * card.value) + GetKickerValue(hand, 1); - } } - } } return HasFullHouse(hand); } @@ -173,33 +147,24 @@ private int HasFullHouse(Card[] hand) } // If 3 cards of the same value are found, do not continue searching if (value1 >= 0) - { break; - } } } foreach (Card card in hand) { foreach (Card card2 in hand) - { // Check for 2 cards of the same value (different value than above) if (card.suit != card2.suit && card.value == card2.value && card.value != value1) { value2 = card.value; break; } - } // If a pair is found, do not continue searching if (value2 >= 0) - { break; - } } if (value1 >= 0 && value2 >= 0) - { - Console.WriteLine("*Full House*"); return 600000000 + (500000 * value1) + value2; - } return HasFlush(hand); } @@ -209,18 +174,13 @@ private int HasFlush(Card[] hand) { int count = 0; foreach (Card card2 in hand) - { // Excluding the current card itself, count the cards in the hand that have the same suit if (card.value != card2.value && card.suit == card2.suit) { count++; if (count == 4) - { - Console.WriteLine("*Flush*"); return 500000000 + GetKickerValue(hand, 5); - } } - } } return HasStraight(hand); } @@ -234,19 +194,13 @@ private int HasStraight(Card[] hand) // Check for 5 cards of consecutive values if (HandContainsValueOnly(hand, value + 1) && HandContainsValueOnly(hand, value + 2) && HandContainsValueOnly(hand, value + 3) && HandContainsValueOnly(hand, value + 4)) - { - Console.WriteLine("*Straight*"); - return 400000000 + (value + 4); - } + return 400000000 + (value + 4); // Check for A2345 else if (value == 12) { if (HandContainsValueOnly(hand, 0) && HandContainsValueOnly(hand, 1) && HandContainsValueOnly(hand, 2) && HandContainsValueOnly(hand, 3)) - { - Console.WriteLine("*Straight*"); return 400000003; - } } } return HasThreeOfAKind(hand); @@ -258,18 +212,13 @@ private int HasThreeOfAKind(Card[] hand) { int count = 0; foreach (Card card2 in hand) - { // Excluding the current card itself, count the cards in the hand that have the same value if (card.suit != card2.suit && card.value == card2.value) { count++; if (count == 2) - { - Console.WriteLine("*Three Of A Kind*"); return 300000000 + (500000 * card.value) + GetKickerValue(hand, 2); - } } - } } return HasTwoPair(hand); } @@ -281,60 +230,41 @@ private int HasTwoPair(Card[] hand) foreach (Card card in hand) { foreach (Card card2 in hand) - { // Check for 2 cards of the same value if (card.suit != card2.suit && card.value == card2.value) { value1 = card.value; break; } - } // If a pair is found, do not continue searching if (value1 >= 0) - { break; - } } foreach (Card card in hand) { foreach (Card card2 in hand) - { // Check for 2 cards of the same value (different value than above) if (card.suit != card2.suit && card.value == card2.value && card.value != value1) { value2 = card.value; break; } - } // If a pair is found, do not continue searching if (value2 >= 0) - { break; - } } if (value1 >= 0 && value2 >= 0) - { - Console.WriteLine("*Two Pair*"); return 200000000 + (7000000 * value1) + (500000 * value2) + GetKickerValue(hand, 1); - } return HasPair(hand); } private int HasPair(Card[] hand) { foreach (Card card in hand) - { foreach (Card card2 in hand) - { // Check for 2 cards of the same value if (card.suit != card2.suit && card.value == card2.value) - { - Console.WriteLine("*Pair*"); return 100000000 + (500000 * card.value) + GetKickerValue(hand, 3); - } - } - } - Console.WriteLine("*High Card*"); return GetKickerValue(hand, 5); } @@ -342,12 +272,8 @@ private int HasPair(Card[] hand) private bool HandContains(Card[] hand, int suit, int value) { foreach (Card card in hand) - { if (card.suit == suit && card.value == value) - { return true; - } - } return false; } @@ -355,49 +281,36 @@ private bool HandContains(Card[] hand, int suit, int value) private bool HandContainsValueOnly(Card[] hand, int value) { foreach (Card card in hand) - { if (card.value == value) - { return true; - } - } return false; } // Get the weighted sum of the n kicker cards of greatest value private int GetKickerValue(Card[] hand, int n) { - // Find card values that appear more than once within the hand - // (not included in the kicker value). Only remove 2 maximum. + // Find "paired values" that appear more than once within the hand. + // These are not included in the calculated kicker value. int[] pairedValues = new int[hand.Length]; int removedCount = 0; for (int i = 0; i < hand.Length; i++) - { foreach (Card card in hand) - { - if (hand[i].suit != card.suit && hand[i].value == card.value) + if (hand[i].suit != card.suit && hand[i].value == card.value && !pairedValues.Contains(card.value) && removedCount < 2) { - if (!pairedValues.Contains(card.value) && removedCount < 2) - { - pairedValues[i] = hand[i].value; - removedCount++; - } + pairedValues[i] = hand[i].value; + removedCount++; } - } - } // Get only the values that are not duplicates int[] values = new int[hand.Length - removedCount]; int trackerIndex = 0; foreach (Card card in hand) - { if (!pairedValues.Contains(card.value)) { - // Add 1 to the value so that 2s are not value of 0 + // Add 1 to the value so that 2s (value 0) are not weightless values[trackerIndex] = card.value + 1; trackerIndex++; } - } // Sort the values and return the weighted sum of the n greatest values int[] sortedValues = values.OrderBy(v => v).ToList().ToArray(); @@ -406,25 +319,15 @@ private int GetKickerValue(Card[] hand, int n) { // Give the kicker card of greatest value the most weight if (i == sortedValues.Length - 1) - { weight = 36000; - } else if (i == sortedValues.Length - 2) - { weight = 2700; - } else if (i == sortedValues.Length - 3) - { weight = 200; - } else if ((i == sortedValues.Length - 4)) - { weight = 15; - } else - { weight = 1; - } kickerValueSum += weight * sortedValues[i]; } @@ -450,16 +353,12 @@ private int GetMaxValueCardIndex(Card[] cards) int maxValueCardIndex = 0; int maxValue = 0; for (int i = 0; i < cards.Length; i++) - { if (cards[i] != null) - { if (cards[i].value >= maxValue) { maxValue = cards[i].value; maxValueCardIndex = i; } - } - } return maxValueCardIndex; } @@ -474,17 +373,33 @@ public string ClassifyHoleCards(Card[] holeCards) string holeCardString = sortedHoleCards[0].ToStringValueOnly() + sortedHoleCards[1].ToStringValueOnly(); if (sortedHoleCards[0].value != sortedHoleCards[1].value) - { - if (sortedHoleCards[0].suit != sortedHoleCards[1].suit) - { - holeCardString += "o"; - } - else - { - holeCardString += "s"; - } - } + holeCardString += sortedHoleCards[0].suit == sortedHoleCards[1].suit ? "s" : "o"; return holeCardString; } + + // Get the string representation of the player's hand rank from the given hand value + private string HandRankToString(int handValue) + { + if (handValue == 800000012) + return "Royal Flush"; + else if (handValue >= 800000000) + return "Straight Flush"; + else if (handValue >= 700000000) + return "Four of a Kind"; + else if (handValue >= 600000000) + return "Full House"; + else if (handValue >= 500000000) + return "Flush"; + else if (handValue >= 400000000) + return "Straight"; + else if (handValue >= 300000000) + return "Three of a Kind"; + else if (handValue >= 200000000) + return "Two Pair"; + else if (handValue >= 100000000) + return "Pair"; + else + return "High Card"; + } } }