package com.jsjdesigns.poker import com.jsjdesigns.poker.game.DecisionOffer import com.jsjdesigns.poker.game.Street import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class ActionButtonsTest { private fun offer( toCall: Int = 10, minRaiseTo: Int = 20, maxRaiseTo: Int = 500, canCheck: Boolean = false, canRaise: Boolean = true, stack: Int = 500, ) = DecisionOffer( token = 1L, handNumber = 1, street = Street.FLOP, seat = 0, hole = listOf(0, 1), board = emptyList(), pot = 40, toCall = toCall, minRaiseTo = minRaiseTo, maxRaiseTo = maxRaiseTo, stack = stack, canCheck = canCheck, canRaise = canRaise, activeOpponents = 1, seatsActingAfter = 0, bigBlind = 2, eligiblePot = 40, currentBet = if (canCheck) 0 else toCall, ) // ---------- the raise omission ---------- /** * The UI used to require maxRaiseTo > minRaiseTo, which hid an exact-minimum * raise even though the engine accepts it. */ @Test fun `an exact minimum raise is still offered`() { val b = buttonsFor(offer(minRaiseTo = 100, maxRaiseTo = 100)) assertTrue("a raise with exactly one legal size must still be offered", b.showRaise) assertFalse("no slider is useful for a single size", b.showSlider) assertEquals(100, b.fixedRaiseTo) } /** * A stack too short for a full min-raise may still shove; the engine clamps * such a raise to maxRaiseTo. That was hidden too. */ @Test fun `a short all-in is still offered`() { val b = buttonsFor(offer(minRaiseTo = 200, maxRaiseTo = 120, stack = 120)) assertTrue("a legal short all-in must be offered", b.showRaise) assertFalse(b.showSlider) assertEquals(120, b.fixedRaiseTo) assertEquals("All in", raiseLabel(b, b.fixedRaiseTo)) } @Test fun `a genuine range gets a slider`() { val b = buttonsFor(offer(minRaiseTo = 20, maxRaiseTo = 500)) assertTrue(b.showRaise) assertTrue(b.showSlider) assertEquals(20, b.sliderMin) assertEquals(500, b.sliderMax) } @Test fun `no raise button when the engine would not accept one`() { assertFalse(buttonsFor(offer(canRaise = false)).showRaise) } @Test fun `raise presets use the pot after calling and legal raise-to amounts`() { val decision = offer( toCall = 10, minRaiseTo = 20, maxRaiseTo = 500, ) assertEquals( listOf( RaisePreset("Min", 20), RaisePreset("½ pot", 35), RaisePreset("Pot", 60), RaisePreset("All in", 500), ), raisePresets(decision), ) } @Test fun `raise presets drop clamped duplicates instead of mislabelling them`() { val decision = offer( toCall = 10, minRaiseTo = 45, maxRaiseTo = 50, stack = 50, ) assertEquals( listOf(RaisePreset("Min", 45), RaisePreset("All in", 50)), raisePresets(decision), ) } @Test fun `a single legal shove has one all-in preset`() { val decision = offer(minRaiseTo = 200, maxRaiseTo = 120, stack = 120) assertEquals(listOf(RaisePreset("All in", 120)), raisePresets(decision)) } // ---------- fold and call ---------- @Test fun `fold is hidden when checking is free`() { assertFalse( "folding a free hand is never correct; offering it invites an accidental muck", buttonsFor(offer(toCall = 0, canCheck = true)).showFold, ) assertTrue(buttonsFor(offer(toCall = 10, canCheck = false)).showFold) } @Test fun `the call button names the price`() { assertEquals("Call 35", buttonsFor(offer(toCall = 35)).checkOrCallLabel) assertEquals("Check", buttonsFor(offer(toCall = 0, canCheck = true)).checkOrCallLabel) } @Test fun `the raise label switches to all in at the top of the range`() { val b = buttonsFor(offer(minRaiseTo = 20, maxRaiseTo = 500)) assertEquals("Raise 60", raiseLabel(b, 60)) assertEquals("All in", raiseLabel(b, 500)) } } class CallCostTest { private fun offer(toCall: Int, stack: Int) = DecisionOffer( token = 1L, handNumber = 1, street = Street.RIVER, seat = 0, hole = listOf(0, 1), board = emptyList(), pot = 100, toCall = toCall, minRaiseTo = 999, maxRaiseTo = stack, stack = stack, canCheck = false, canRaise = false, activeOpponents = 1, seatsActingAfter = 0, bigBlind = 2, eligiblePot = 100, currentBet = toCall, ) /** * Facing more than you have left is a call for your whole stack; the engine * clamps the commitment. Quoting the full price promised a payment the player * could not make and did not owe. */ @Test fun `facing more than the stack is labelled as an all in for the stack`() { val b = buttonsFor(offer(toCall = 35, stack = 20)) assertEquals("All in 20", b.checkOrCallLabel) assertEquals(20, b.effectiveCall) } @Test fun `calling exactly the stack is also an all in`() { val b = buttonsFor(offer(toCall = 20, stack = 20)) assertEquals("All in 20", b.checkOrCallLabel) assertEquals(20, b.effectiveCall) } @Test fun `an affordable call quotes the real price`() { val b = buttonsFor(offer(toCall = 35, stack = 500)) assertEquals("Call 35", b.checkOrCallLabel) assertEquals(35, b.effectiveCall) } }