Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions src/main/java/net/tfminecraft/games/game/PokerGame.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ public void onPlayWord(Table table, Player player, String word) {
street.acted.clear();
}
street.capped.add(id);
announceAction(table, player, "allin");
}
case "fold" -> {
street.folded.add(id);
Expand All @@ -221,6 +220,9 @@ public void onPlayWord(Table table, Player player, String word) {
return;
}
}
if (street.capped.contains(id)) {
announceAllIn(table, player);
}
street.acted.add(id);
finishOrAdvance(table, new ArrayList<>(table.actives()), true);
}
Expand Down Expand Up @@ -284,10 +286,17 @@ public String extraLabel(Table table) {
}
if (table.live()) {
lines.add(streetLabel(table.phase()));
Street street = streets.get(table.getId());
if (street != null) {
for (UUID id : table.actives()) {
if (street.capped.contains(id) && !street.folded.contains(id)) {
lines.add(Messages.get("label.holdem_allin", "name", RpNames.of(id)));
}
}
}
UUID actor = table.actor();
if (actor != null) {
lines.add(Messages.get("label.turn", "name", RpNames.of(actor)));
Street street = streets.get(table.getId());
int toCall = street == null ? 0 : Math.max(0, street.currentBet - streetContrib(table, actor));
lines.add(Messages.get("label.holdem_tocall", "n", String.valueOf(toCall)));
}
Expand Down Expand Up @@ -801,7 +810,10 @@ private boolean postForcedBets(Table table, Street street) {
if (small > big) street.currentBet = small;
if (small > 0 || big > 0 || tournament.enabled()) {
for (Player player : seatedOnline(table)) {
if (isAllIn(table, player)) street.capped.add(player.getUniqueId());
if (isAllIn(table, player)) {
street.capped.add(player.getUniqueId());
announceAllIn(table, player);
}
}
}
return true;
Expand Down Expand Up @@ -833,6 +845,14 @@ private void leaveTournament(Table table, Player player) {
finishOrAdvance(table, before, false);
}

private static void announceAllIn(Table table, Player player) {
announceAction(table, player, "allin");
player.sendMessage(Messages.get("poker.allin_notice"));
if (!table.poker().enabled()) {
player.sendMessage(Messages.get("poker.cash_allin_hint"));
}
}

private static void announceAction(Table table, Player player, String action) {
tellSeated(table, Messages.get("poker.action_" + action, "name", RpNames.of(player.getUniqueId())));
}
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ poker:
action_raised: "#55ff55{name} raises to {n}."
action_folded: "#aaaaaa{name} folds."
action_allin: "#ffaa00{name} goes all in."
allin_notice: "#ffaa00You are all in. You stay in the hand and have no more betting turns."
cash_allin_hint: "#aaaaaaNo spendable coins remain in your inventory. Use /deco toitem <amount> before the next hand to take coins from your pouch."
stake_closed: "#ff5555Stake only on your turn in this hand. Tournament chips use /games poker bet <chips>."
blind_failed: "#ff5555Cannot collect blinds exactly. Bring suitable coins before dealing."
prize_pending: "#ff5555Some prize coins remain on the table. Retry finishing before starting another tournament."
Expand Down Expand Up @@ -260,6 +262,7 @@ label:
holdem_river: "#aaaaaaRiver"
holdem_showdown: "#aaaaaaShowdown"
holdem_tocall: "#aaaaaaTo call {n}"
holdem_allin: "#ffaa00{name}: All in"
draw_bet: "#aaaaaaBetting"
draw_draw: "#aaaaaaDraw"
draw_showdown: "#aaaaaaShowdown"
Expand Down
3 changes: 3 additions & 0 deletions src/test/java/net/tfminecraft/games/MessagesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ void bundledNewMessagesFillMissingKeysWithoutReplacingCustomText() throws Except
assertEquals("Custom Alice", Messages.get("poker.action_called", "name", "Alice"));
assertTrue(Messages.getRaw("poker.action_allin").contains("{name}"));
assertFalse(Messages.getRaw("poker.action_allin").equals("poker.action_allin"));
assertTrue(Messages.getRaw("poker.allin_notice").contains("betting turns"));
assertTrue(Messages.getRaw("poker.cash_allin_hint").contains("/deco toitem"));
assertTrue(Messages.getRaw("label.holdem_allin").contains("{name}"));
}

@Test
Expand Down
54 changes: 54 additions & 0 deletions src/test/java/net/tfminecraft/games/game/PokerGameTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,60 @@ void chipsPastTheBetAreARaiseEvenWhenCalledACheckOrCall() {
assertEquals(bob.getUniqueId(), table.actor(), "the first bettor has to answer the raise");
}

@Test
void cashAllInExplainsSkippedTurnsAndStaysLabelledForTheRestOfTheHand() {
start(alice, bob);
contribute(bob, 5);
broke.add(bob.getUniqueId());
act("raise");
assertTrue(game.extraLabel(table).lines().anyMatch(line ->
line.startsWith("label.holdem_allin") && line.contains(bob.getName())));
assertEquals(alice.getUniqueId(), table.actor());
contribute(alice, 5);
act("call");
drain();
for (String phase : List.of(PokerGame.FLOP, PokerGame.TURN, PokerGame.RIVER)) {
assertEquals(phase, table.phase());
assertEquals(alice.getUniqueId(), table.actor());
assertTrue(game.extraLabel(table).contains("label.holdem_allin"));
act("check");
drain();
}
assertFalse(table.live());
assertFalse(game.extraLabel(table).contains("label.holdem_allin"));
List<String> heard = new ArrayList<>();
for (String line = ((PlayerMock) bob).nextMessage(); line != null; line = ((PlayerMock) bob).nextMessage()) {
heard.add(line);
}
assertEquals(1, heard.stream().filter(line -> line.startsWith("poker.action_allin")).count());
assertEquals(1, heard.stream().filter(line -> line.equals("poker.allin_notice")).count());
assertEquals(1, heard.stream().filter(line -> line.equals("poker.cash_allin_hint")).count());
for (String line = ((PlayerMock) alice).nextMessage(); line != null; line = ((PlayerMock) alice).nextMessage()) {
assertNotEquals("poker.allin_notice", line, "only the all-in player receives the personal notice");
assertNotEquals("poker.cash_allin_hint", line);
}
}

@Test
void explicitTournamentAllInIsAnnouncedOnceWithoutCashAdvice() {
table.poker().configure(10, 10, 0, 0, 0);
table.poker().buy(alice.getUniqueId());
table.poker().buy(bob.getUniqueId());
start(alice, bob);
act("allin");
assertEquals(0, table.poker().stack(bob.getUniqueId()));
assertEquals(alice.getUniqueId(), table.actor());
assertTrue(game.extraLabel(table).lines().anyMatch(line ->
line.startsWith("label.holdem_allin") && line.contains(bob.getName())));
List<String> heard = new ArrayList<>();
for (String line = ((PlayerMock) bob).nextMessage(); line != null; line = ((PlayerMock) bob).nextMessage()) {
heard.add(line);
}
assertEquals(1, heard.stream().filter(line -> line.startsWith("poker.action_allin")).count());
assertEquals(1, heard.stream().filter(line -> line.equals("poker.allin_notice")).count());
assertFalse(heard.contains("poker.cash_allin_hint"));
}

@Test
void allInSeatIsSkippedWhileTheOthersKeepRaising() {
start(alice, bob, carol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
import static org.mockito.Mockito.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
Expand All @@ -26,8 +31,9 @@
/** A complete poker or Five-Draw round across the real game, table, deck and money implementations. */
class TableManagerGameRoundTest extends TableManagerFixture {
@ParameterizedTest
@CsvSource({"poker, 2", "draw, 5"})
void shoeToShowdownConservesCardsAndMoneyAcrossRealComponents(String gameId, int cardsPerPlayer) {
@CsvSource({"poker, 2, 0, 4, 4", "draw, 5, 0, 0, 0", "poker, 2, 1, 4, 4", "poker, 2, 1, 4, 0"})
void shoeToShowdownConservesCardsAndMoneyAcrossRealComponents(String gameId, int cardsPerPlayer,
int blind, int pocketCoins, int otherPocketCoins) {
List<Card> fullDeck = new ArrayList<>();
for (String suit : List.of("oseni", "cerrith", "clubs", "hearts")) {
for (int rank = 1; rank <= 13; rank++) {
Expand All @@ -49,6 +55,10 @@ void shoeToShowdownConservesCardsAndMoneyAcrossRealComponents(String gameId, int
assertEquals(player.getUniqueId(), table.dealerId());
stakeCoin(other, table);
assertEquals(2, table.ledger().total());
table.setSmallBlind(blind);
table.setBigBlind(blind);
if (pocketCoins > 0) player.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, pocketCoins));
if (otherPocketCoins > 0) other.getInventory().setItemInMainHand(new ItemStack(Material.GOLD_NUGGET, otherPocketCoins));
assertFalse(table.live());
Entity shoe = mock(Entity.class);
anchors.when(() -> WorldAnchors.tableId(shoe)).thenReturn(table.getId().toString());
Expand All @@ -60,14 +70,23 @@ void shoeToShowdownConservesCardsAndMoneyAcrossRealComponents(String gameId, int
assertEquals(cardsPerPlayer, table.handOf(player.getUniqueId()).size());
assertEquals(cardsPerPlayer, table.handOf(other.getUniqueId()).size());
Set<String> phases = new HashSet<>();
Map<String, Set<UUID>> actors = new HashMap<>();
for (int action = 0; table.live() && action < 12; action++) {
phases.add(table.phase());
assertNotNull(table.actor(), "A finished deal must nominate the next actor");
var actor = org.bukkit.Bukkit.getPlayer(table.actor());
assertNotNull(actor);
actors.computeIfAbsent(table.phase(), ignored -> new HashSet<>()).add(actor.getUniqueId());
assertTrue(rules.allowPlayChat(table, actor));
assertTrue(rules.extraLabel(table).contains("label.turn"));
manager.applyPlayCall(actor, "draw".equals(table.phase()) ? "draw" : "check");
if (blind > 0 && otherPocketCoins == 0) {
assertTrue(rules.extraLabel(table).contains("label.holdem_allin"),
"the player skipped after posting their last coin must stay visibly all in");
}
AsyncPlayerChatEvent chat = new AsyncPlayerChatEvent(true, actor,
"draw".equals(table.phase()) ? "draw" : "check", Set.of(player, other));
manager.onPlayChat(chat);
assertTrue(chat.isCancelled(), "The acting seat's play word must reach the game");
tick(40);
}
assertFalse(table.live(), "Checking/standing pat through the whole round must settle");
Expand All @@ -77,10 +96,23 @@ void shoeToShowdownConservesCardsAndMoneyAcrossRealComponents(String gameId, int
assertEquals(52, table.getDeck().remaining() + table.getDeck().discarded());
assertEquals(52, java.util.stream.Stream.concat(table.getDeck().remainingIds().stream(),
table.getDeck().discardedIds().stream()).distinct().count());
assertEquals(2, java.util.stream.Stream.of(player, other)
assertEquals(2 + pocketCoins + otherPocketCoins, java.util.stream.Stream.of(player, other)
.flatMap(p -> p.getInventory().all(Material.GOLD_NUGGET).values().stream())
.mapToInt(item -> item.getAmount()).sum());
assertEquals(other.getUniqueId(), table.dealerId(), "The button advances after settlement");
assertEquals(gameId.equals("poker") ? Set.of("preflop", "flop", "turn", "river") : Set.of("bet", "draw"), phases);
Set<UUID> expectedActors = blind > 0 && otherPocketCoins == 0
? Set.of(player.getUniqueId()) : Set.of(player.getUniqueId(), other.getUniqueId());
for (String phase : phases) {
assertEquals(expectedActors, actors.get(phase), "Only all-in seats skip betting in " + phase);
}
List<String> heard = new ArrayList<>();
for (String line = other.nextMessage(); line != null; line = other.nextMessage()) {
heard.add(line);
}
int notices = blind > 0 && otherPocketCoins == 0 ? 1 : 0;
assertEquals(notices, heard.stream().filter("poker.action_allin"::equals).count());
assertEquals(notices, heard.stream().filter("poker.allin_notice"::equals).count());
assertEquals(notices, heard.stream().filter("poker.cash_allin_hint"::equals).count());
}
}
Loading