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
18 changes: 15 additions & 3 deletions src/main/java/net/tfminecraft/games/game/BlackjackGame.java
Original file line number Diff line number Diff line change
Expand Up @@ -1206,8 +1206,10 @@ private void settle(Table table) {
table.setPhase(SETTLE);
checkBoxBets(table, "settle");
TableManager manager = TableManager.get();
int dealerTotal = bestTotal(table.tablePile(DEALER));
List<HandCard> dealerPile = table.tablePile(DEALER);
int dealerTotal = bestTotal(dealerPile);
boolean dealerBust = dealerTotal > 21;
boolean dealerNatural = isDealerNatural(dealerPile);
Player dealer = dealerAtShoe(table);
Map<UUID, Integer> collect = new HashMap<>();
Map<UUID, Integer> pay = new HashMap<>();
Expand All @@ -1220,12 +1222,14 @@ private void settle(Table table) {
boolean bust = playerTotal > 21 || cards.isEmpty();
boolean natural = isNatural(hand, cards);
Player player = Bukkit.getPlayer(hand.owner);
if (bust || (!dealerBust && playerTotal < dealerTotal)) {
// A natural is settled before any totals: it beats any other 21, and only another
// natural draws with it. Every other hand is decided on its total alone.
if (bust || (dealerNatural && !natural) || (!dealerBust && playerTotal < dealerTotal)) {
collect.merge(hand.owner, hand.bet, Integer::sum);
tell(player, Messages.get("bet.lose"));
continue;
}
if (!dealerBust && playerTotal == dealerTotal) {
if (natural == dealerNatural && !dealerBust && playerTotal == dealerTotal) {
tell(player, Messages.get("bet.push"));
continue;
}
Expand Down Expand Up @@ -1376,6 +1380,14 @@ private static boolean isNatural(BjHand hand, List<HandCard> cards) {
return !hand.fromSplit && cards.size() == 2 && bestTotal(cards) == 21;
}

/**
* The dealer's two dealt cards making twenty-one. The dealer never draws on twenty-one, so a
* pile that still holds exactly two cards at the settle holds the two that were dealt.
*/
private static boolean isDealerNatural(List<HandCard> pile) {
return pile.size() == 2 && bestTotal(pile) == 21;
}

/** Two cards of one rank, or two ten-value cards. Only ever asked about two cards. */
private static boolean isPair(List<HandCard> cards) {
Card a = cards.get(0).card();
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/help.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ blackjack:
- "#6b3f1d&lSplitting aces\n\n#232323Aces are the exception. Each one gets a single card and that hand is done, whatever it comes to.\n\nAnother ace can be split again, if the table allows it and you have hands to spare.\n\nAn ace and a ten after a split is 21, but it is not blackjack and pays even money."
- "#6b3f1d&lThe dealer's turn\n\n#232323Once everybody is done the dealer turns over the hidden card and keeps drawing until reaching 17, then stops there.\n\nNothing you can do at this point but watch."
- "#6b3f1d&lGetting paid\n\n#232323Beat the dealer and your bet comes back doubled.\n\n21 on your first two cards pays 3 to 2.\n\nDraw with the dealer and your money comes back."
- "#6b3f1d&lBlackjack beats 21\n\n#232323Blackjack is 21 on your first two cards. It beats any other 21, yours or the dealer's.\n\nA dealer blackjack also takes any doubles and splits.\n\nTwo blackjacks draw."
- "#6b3f1d&lNo insurance\n\n#232323This house does not offer insurance and it does not offer surrender. If the dealer shows an ace, that is simply your problem.\n\nCoins land back on the felt when you win."
- "#6b3f1d&lDealing it yourself\n\n#232323Stand at the dealer's place and right click the shoe. The table is yours.\n\nRight click it again when you have had enough and somebody else can take over."
- "#6b3f1d&lRunning a round\n\n#232323/games bet min 10\n/games bet max 500\n/games bet open\n\n#232323Then let people put their money down."
Expand Down
61 changes: 61 additions & 0 deletions src/test/java/net/tfminecraft/games/game/BlackjackGameTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,37 @@ void naturalTwentyOneSkipsActionAndPaysThreeToTwo() {
assertMessage(player, "bet.natural");
}

@Test
void naturalBeatsADealerWhoDrawsToTwentyOne() {
start(1, 10, 13, 5, 6);
assertEquals(3, table.tablePile("dealer").size());
assertEquals(21, BlackjackGame.bestTotal(table.tablePile("dealer")));
verifyWin(30);
assertMessage(player, "bet.natural");
}

@Test
void dealerNaturalBeatsAPlayerWhoDrawsToTwentyOne() {
start(5, 1, 6, 13, 10);
game.onBetHit(table, player);
finishAnimations();
assertEquals(21, BlackjackGame.bestTotal(table.handOf(player.getUniqueId())));
assertEquals(BlackjackGame.SETTLE, table.phase());
assertEquals(2, table.tablePile("dealer").size());
verify(wagers).refund(eq(table), eq(player.getUniqueId()), eq(dealer), eq(20), anyList(), eq("loss to dealer"));
verify(wagers, never()).payWin(any(), any(), any(), anyInt(), any(), anyBoolean(), any(), anyList());
assertMessage(player, "bet.lose");
}

@Test
void twoNaturalsPush() {
start(1, 1, 10, 13);
assertEquals(BlackjackGame.SETTLE, table.phase());
verify(wagers, never()).payWin(any(), any(), any(), anyInt(), any(), anyBoolean(), any(), anyList());
verify(wagers).refund(eq(table), eq(player.getUniqueId()), eq(player), eq(0), anyList(), eq("bet returned"));
assertMessage(player, "bet.push");
}

@Test
void equalTotalsReturnStakeWithoutWinPayment() {
start(10, 10, 8, 8);
Expand Down Expand Up @@ -443,6 +474,36 @@ void splitAcesReceiveOneCardEachAndTwentyOnePaysEvenMoney() {
verifyWin(40);
}

@Test
void splitAceAndTenIsTwentyOneButNotANatural() {
start(1, 10, 1, 8, 10, 13);
game.onBetSplit(table, player);
finishAnimations();
assertEquals(List.of(1, 10), ranks(0));
assertEquals(List.of(1, 13), ranks(1));
assertEquals(BlackjackGame.SETTLE, table.phase());
verifyWin(40);
List<String> messages = new ArrayList<>();
String message;
while ((message = player.nextMessage()) != null) {
messages.add(message);
}
assertFalse(messages.contains(Messages.get("bet.natural")));
assertEquals(2, messages.stream().filter(Messages.get("bet.win")::equals).count());
}

@Test
void splitAceAndTenLosesBothStakesToADealerNatural() {
start(1, 1, 1, 13, 10, 10);
game.onBetSplit(table, player);
finishAnimations();
assertEquals(List.of(1, 10), ranks(0));
assertEquals(List.of(1, 10), ranks(1));
assertEquals(BlackjackGame.SETTLE, table.phase());
verify(wagers).refund(eq(table), eq(player.getUniqueId()), eq(dealer), eq(40), anyList(), eq("loss to dealer"));
verify(wagers, never()).payWin(any(), any(), any(), anyInt(), any(), anyBoolean(), any(), anyList());
}

@Test
void enabledResplitAcesOffersNewAcePairWithoutAllowingHitOrDouble() {
configure(false, 3, true, 0);
Expand Down
Loading