public void HandEvaluator() { Assert.AreEqual("High Card (A)", HoldemGame.HandClass(hand("2C 3C 4C TD AC"))); Assert.AreEqual("Pair (2)", HoldemGame.HandClass(hand("2C 2D 4C TD AC"))); Assert.AreEqual("Two Pairs (3/2)", HoldemGame.HandClass(hand("2C 2D 3C 3D AC"))); Assert.AreEqual("Three of a Kind (2)", HoldemGame.HandClass(hand("2C 2D 2S 3D AC"))); Assert.AreEqual("Straight (Wheel)", HoldemGame.HandClass(hand("2C 3S 4S 5D AC"))); Assert.AreEqual("Straight", HoldemGame.HandClass(hand("3C 4S 5S 6D 7C"))); Assert.AreEqual("Flush", HoldemGame.HandClass(hand("3S TS 5S 6S 7S"))); Assert.AreEqual("Full House (10/3)", HoldemGame.HandClass(hand("TS TC TC 3S 3S"))); Assert.AreEqual("Four of a Kind (10)", HoldemGame.HandClass(hand("TS TC TC TS 4S"))); Assert.AreEqual("Straight Flush", HoldemGame.HandClass(hand("9S TS JS QS KS"))); Assert.AreEqual("Royal Flush", HoldemGame.HandClass(hand("TS JS QS KS AS"))); // high card Assert.Greater( hand("2C 3C 4C TD AC"), hand("2H 3H 4H TH KD")); // straight flushes Assert.Greater( HoldemGame.EvaluateHand(1, 2, 3, 4, 5), HoldemGame.EvaluateHand(0, 1, 2, 3, 4)); }
private void Update() { // XXX apparently udon will run some Update()s before Starts() so // HoldemGame's start might not initialize this if (game == null) { return; } // don't bother if game isn't initialized if (game.tableState == TABLE_UNINITIALIZED) { return; } var locallySeated = isLocallySeated(); var seated = IsSeated(); uiReady.interactable = locallySeated; goReadyToggle.SetActive(locallySeated || !seated); if (locallySeated) { // one ui read playerReady = uiReady.isOn; uiJoinLeaveButton.interactable = !playerReady; if (playerReady) { uiJoinLeaveText.text = "Ready"; } else { uiJoinLeaveText.text = "Leave"; } } else { if (seated) { uiJoinLeaveButton.interactable = false; uiJoinLeaveText.text = $"{OwnerName()}"; } else { uiJoinLeaveButton.interactable = true; uiJoinLeaveText.text = "Join"; } } var playerState = game.playerState[seatIdx]; var inPlay = playerState != PLAYER_DEAD; uiDealer.enabled = game.tableState == TABLE_PLAYING && game.dealerSeat == seatIdx; var winner = game.tableState == TABLE_WINNER; bool showdown = false; if (winner) { int challengers = 0; for (int i = 0; i < 10; ++i) { if (game.playerState[i] == PLAYER_COMMITED) { challengers++; } } if (challengers > 1) { showdown = true; } } // display at least blank cards if in play goHoleCards.SetActive(inPlay); var holes = inPlay && (locallySeated || game.headsUp || showdown); uiHole0.enabled = holes; uiHole1.enabled = holes; uiBestHand.enabled = holes && game.bettingRound >= FLOP; var acting = playerState == PLAYER_ACTING; var valid = game.IsValidBet(bet, seatIdx); uiStatusColor.enabled = seated; uiStatusColor.color = playerState == PLAYER_DEAD ? Color.black : playerState == PLAYER_PENDING ? Color.white : playerState == PLAYER_ACTING ? Color.green : playerState == PLAYER_COMMITED ? Color.blue : Color.red; goBetUi.SetActive(acting); uiBet.text = GetBet(); goChipDisplay.SetActive(seated); var stack = game.stacks[seatIdx]; if (acting && bet > 0) { var left = stack - bet; uiChips.text = $"{left} (total {stack})"; } else { uiChips.text = $"{stack}"; } uiCallCheck.interactable = locallySeated && acting && valid; uiFold.interactable = locallySeated && acting; uiConfirm.interactable = uiPending && (valid || bet == -1); var uiConfirmColors = uiConfirm.colors; uiConfirmColors.normalColor = committedEpoch == game.epoch ? Color.yellow : Color.white; uiConfirmColors.disabledColor = playerState == PLAYER_COMMITED ? Color.blue : Color.grey; uiConfirm.colors = uiConfirmColors; var uiFoldColors = uiFold.colors; uiFoldColors.normalColor = (bet == -1 && uiPending) ? Color.red : Color.white; uiFold.colors = uiFoldColors; var uiCallCheckColors = uiCallCheck.colors; uiCallCheckColors.normalColor = bet >= 0 && uiPending ? Color.green : Color.white; uiCallCheck.colors = uiCallCheckColors; uiCallCheckText.text = bet == 0 ? "Check" : bet == game.stacks[seatIdx] ? "All-in" : bet == -1 ? "(fold)" : !valid ? "(invalid bet)" : bet > game.currentBet ? (game.currentBet > 0 ? "Re-Raise" : "Raise") : game.currentBet > 0 ? "Call" : "Check"; uiActTimer.enabled = acting; if (acting) { var now = Networking.LocalPlayer == null ? (int)(Time.time * 1000) : Networking.GetServerTimeInMilliseconds(); var timeout = game.lastTransitionMillis; var remaining = game.actionTimeoutSecs - (now - timeout) / 1000; uiActTimer.text = $"{remaining / 60}:{remaining % 60} to act"; } // transition check if (updateSeenEpoch != game.epoch) { updateSeenEpoch = game.epoch; // edge-triggered if (locallySeated) { if (acting) { // start off with a call bet = Mathf.Min(stack, game.currentBet - game.roundContribution[seatIdx]); // require the confirm again uiPending = false; } else { // once it comes around to us again, make sure we aren't // accidentally already commited from the past bet = 0; committedEpoch = -1; } } if (game.tableState == TABLE_PLAYING) { uiHole0.text = game.unicard(game.holeCards0[seatIdx]); uiHole1.text = game.unicard(game.holeCards1[seatIdx]); var bestHand = game.bettingRound == RIVER?game.BestPlayerHandSeat(seatIdx) : game.bettingRound == TURN?game.BestPlayerHandTurnSeat(seatIdx) : game.bettingRound == FLOP?game.BestPlayerHandFlop(seatIdx) : 0; if (bestHand > 0UL) { #if !COMPILER_UDONSHARP uiBestHand.text = $"{HoldemGame.HandClass(bestHand)}"; #else uiBestHand.text = $"{game.HandClass(bestHand)}"; #endif } else { uiBestHand.text = ""; } } } }