196 lines
5.8 KiB
Kotlin
196 lines
5.8 KiB
Kotlin
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)
|
|
}
|
|
}
|