From f7a257e6aeb3500e3cc6537fbbd51532e3acf6b6 Mon Sep 17 00:00:00 2001 From: Dave Burke Date: Thu, 3 Mar 2022 21:55:30 -0600 Subject: [PATCH] Refactor scoreHand and compareHands into ScoringUtils --- 10_Blackjack/java/src/Game.java | 53 +---- 10_Blackjack/java/src/ScoringUtils.java | 52 +++++ 10_Blackjack/java/test/GameTest.java | 201 ------------------- 10_Blackjack/java/test/ScoringUtilsTest.java | 143 +++++++++++++ 4 files changed, 198 insertions(+), 251 deletions(-) create mode 100644 10_Blackjack/java/src/ScoringUtils.java create mode 100644 10_Blackjack/java/test/ScoringUtilsTest.java diff --git a/10_Blackjack/java/src/Game.java b/10_Blackjack/java/src/Game.java index a254aa4a..5a1736e3 100644 --- a/10_Blackjack/java/src/Game.java +++ b/10_Blackjack/java/src/Game.java @@ -152,7 +152,7 @@ public class Game { if(action.equalsIgnoreCase("H")){ // HIT Card c = deck.deal(); player.dealCard(c, handNumber); - if(scoreHand(player.getHand(handNumber)) > 21){ + if(ScoringUtils.scoreHand(player.getHand(handNumber)) > 21){ userIo.println("...BUSTED"); break; } @@ -162,7 +162,7 @@ public class Game { } else if(action.equalsIgnoreCase("D") && player.canDoubleDown(handNumber)) { // DOUBLE DOWN Card c = deck.deal(); player.doubleDown(c, handNumber); - if(scoreHand(player.getHand(handNumber)) > 21){ + if(ScoringUtils.scoreHand(player.getHand(handNumber)) > 21){ userIo.println("...BUSTED"); break; } @@ -206,54 +206,7 @@ public class Game { } } } - userIo.println("TOTAL IS " + scoreHand(player.getHand(handNumber))); - } - - /** - * Calculates the value of a hand. When the hand contains aces, it will - * count one of them as 11 if that does not result in a bust. - * - * @param hand the hand to evaluate - * @return The numeric value of a hand. A value over 21 indicates a bust. - */ - protected int scoreHand(List hand){ - int nAces = (int) hand.stream().filter(c -> c.getValue() == 1).count(); - int value = hand.stream() - .mapToInt(Card::getValue) - .filter(v -> v != 1) // start without aces - .map(v -> v > 10 ? 10 : v) // all face cards are worth 10. The 'expr ? a : b' syntax is called the 'ternary operator' - .sum(); - value += nAces; // start by treating all aces as 1 - if(nAces > 0 && value <= 11) { - value += 10; // We can use one of the aces to an 11 - // You can never use more than one ace as 11, since that would be 22 and a bust. - } - return value; - } - - /** - * Compares two hands accounting for natural blackjacks using the - * java.lang.Comparable convention of returning positive or negative integers - * - * @param handA hand to compare - * @param handB other hand to compare - * @return a negative integer, zero, or a positive integer as handA is less than, equal to, or greater than handB. - */ - protected int compareHands(List handA, List handB) { - int scoreA = this.scoreHand(handA); - int scoreB = this.scoreHand(handB); - - if(scoreA == 21 && scoreB == 21){ - if(handA.size() == 2 && handB.size() != 2){ - return 1; //Hand A wins with a natural blackjack - } else if (handA.size() != 2 && handB.size() == 2) { - return -1; //Hand B wins with a natural blackjack - } else { - return 0; //Tie - } - } else { - return Integer.compare(scoreA, scoreB); - } + userIo.println("TOTAL IS " + ScoringUtils.scoreHand(player.getHand(handNumber))); } /** diff --git a/10_Blackjack/java/src/ScoringUtils.java b/10_Blackjack/java/src/ScoringUtils.java new file mode 100644 index 00000000..df149e27 --- /dev/null +++ b/10_Blackjack/java/src/ScoringUtils.java @@ -0,0 +1,52 @@ +import java.util.List; + +public final class ScoringUtils { + + /** + * Calculates the value of a hand. When the hand contains aces, it will + * count one of them as 11 if that does not result in a bust. + * + * @param hand the hand to evaluate + * @return The numeric value of a hand. A value over 21 indicates a bust. + */ + public static final int scoreHand(List hand){ + int nAces = (int) hand.stream().filter(c -> c.getValue() == 1).count(); + int value = hand.stream() + .mapToInt(Card::getValue) + .filter(v -> v != 1) // start without aces + .map(v -> v > 10 ? 10 : v) // all face cards are worth 10. The 'expr ? a : b' syntax is called the 'ternary operator' + .sum(); + value += nAces; // start by treating all aces as 1 + if(nAces > 0 && value <= 11) { + value += 10; // We can use one of the aces to an 11 + // You can never use more than one ace as 11, since that would be 22 and a bust. + } + return value; + } + + /** + * Compares two hands accounting for natural blackjacks using the + * java.lang.Comparable convention of returning positive or negative integers + * + * @param handA hand to compare + * @param handB other hand to compare + * @return a negative integer, zero, or a positive integer as handA is less than, equal to, or greater than handB. + */ + public static final int compareHands(List handA, List handB) { + int scoreA = scoreHand(handA); + int scoreB = scoreHand(handB); + + if(scoreA == 21 && scoreB == 21){ + if(handA.size() == 2 && handB.size() != 2){ + return 1; //Hand A wins with a natural blackjack + } else if (handA.size() != 2 && handB.size() == 2) { + return -1; //Hand B wins with a natural blackjack + } else { + return 0; //Tie + } + } else { + return Integer.compare(scoreA, scoreB); + } + } + +} diff --git a/10_Blackjack/java/test/GameTest.java b/10_Blackjack/java/test/GameTest.java index 84cd7d58..e6807219 100644 --- a/10_Blackjack/java/test/GameTest.java +++ b/10_Blackjack/java/test/GameTest.java @@ -154,207 +154,6 @@ public class GameTest { assertTrue(out.toString().contains("TYPE H, OR S, PLEASE")); } - @Test - @DisplayName("scoreHand should sum non-ace values normally") - public void scoreHandNormally() { - // Given - LinkedList hand = new LinkedList<>(); - hand.add(new Card(4, Card.Suit.SPADES)); - hand.add(new Card(6, Card.Suit.SPADES)); - hand.add(new Card(10, Card.Suit.SPADES)); - - initGame(); - - // When - int result = game.scoreHand(hand); - - // Then - assertEquals(20, result); - } - - @Test - @DisplayName("scoreHand should treat face cards as 10") - public void scoreHandFaceCards() { - // Given - LinkedList hand = new LinkedList<>(); - hand.add(new Card(11, Card.Suit.SPADES)); - hand.add(new Card(12, Card.Suit.SPADES)); - hand.add(new Card(13, Card.Suit.SPADES)); - - initGame(); - - // When - int result = game.scoreHand(hand); - - // Then - assertEquals(30, result); - } - - @Test - @DisplayName("scoreHand should score aces as 11 when possible") - public void scoreHandSoftAce() { - // Given - LinkedList hand = new LinkedList<>(); - hand.add(new Card(10, Card.Suit.SPADES)); - hand.add(new Card(1, Card.Suit.SPADES)); - - initGame(); - - // When - int result = game.scoreHand(hand); - - // Then - assertEquals(21, result); - } - - @Test - @DisplayName("scoreHand should score aces as 1 when using 11 would bust") - public void scoreHandHardAce() { - // Given - LinkedList hand = new LinkedList<>(); - hand.add(new Card(10, Card.Suit.SPADES)); - hand.add(new Card(9, Card.Suit.SPADES)); - hand.add(new Card(1, Card.Suit.SPADES)); - - initGame(); - - // When - int result = game.scoreHand(hand); - - // Then - assertEquals(20, result); - } - - @Test - @DisplayName("scoreHand should score 3 aces as 13") - public void scoreHandMultipleAces() { - // Given - LinkedList hand = new LinkedList<>(); - hand.add(new Card(1, Card.Suit.SPADES)); - hand.add(new Card(1, Card.Suit.CLUBS)); - hand.add(new Card(1, Card.Suit.HEARTS)); - - initGame(); - - // When - int result = game.scoreHand(hand); - - // Then - assertEquals(13, result); - } - - @Test - @DisplayName("compareHands should return 1 meaning A beat B, 20 to 12") - public void compareHandsAWins() { - LinkedList handA = new LinkedList<>(); - handA.add(new Card(10, Card.Suit.SPADES)); - handA.add(new Card(10, Card.Suit.CLUBS)); - - LinkedList handB = new LinkedList<>(); - handB.add(new Card(1, Card.Suit.SPADES)); - handB.add(new Card(1, Card.Suit.CLUBS)); - - initGame(); - - int result = game.compareHands(handA,handB); - - assertEquals(1, result); - } - - @Test - @DisplayName("compareHands should return -1 meaning B beat A, 18 to 4") - public void compareHandsBwins() { - LinkedList handA = new LinkedList<>(); - handA.add(new Card(2, Card.Suit.SPADES)); - handA.add(new Card(2, Card.Suit.CLUBS)); - - LinkedList handB = new LinkedList<>(); - handB.add(new Card(5, Card.Suit.SPADES)); - handB.add(new Card(6, Card.Suit.HEARTS)); - handB.add(new Card(7, Card.Suit.CLUBS)); - - initGame(); - - int result = game.compareHands(handA,handB); - - assertEquals(-1, result); - } - - @Test - @DisplayName("compareHands should return 1 meaning A beat B, natural Blackjack to Blackjack") - public void compareHandsAWinsWithNaturalBlackJack() { - //Hand A wins with natural BlackJack, B with Blackjack - LinkedList handA = new LinkedList<>(); - handA.add(new Card(10, Card.Suit.SPADES)); - handA.add(new Card(1, Card.Suit.CLUBS)); - - LinkedList handB = new LinkedList<>(); - handB.add(new Card(6, Card.Suit.SPADES)); - handB.add(new Card(7, Card.Suit.HEARTS)); - handB.add(new Card(8, Card.Suit.CLUBS)); - - initGame(); - - int result = game.compareHands(handA,handB); - - assertEquals(1, result); - } - - @Test - @DisplayName("compareHands should return -1 meaning B beat A, natural Blackjack to Blackjack") - public void compareHandsBWinsWithNaturalBlackJack() { - LinkedList handA = new LinkedList<>(); - handA.add(new Card(6, Card.Suit.SPADES)); - handA.add(new Card(7, Card.Suit.HEARTS)); - handA.add(new Card(8, Card.Suit.CLUBS)); - - LinkedList handB = new LinkedList<>(); - handB.add(new Card(10, Card.Suit.SPADES)); - handB.add(new Card(1, Card.Suit.CLUBS)); - - initGame(); - - int result = game.compareHands(handA,handB); - - assertEquals(-1, result); - } - - @Test - @DisplayName("compareHands should return 0, hand A and B tied with a Blackjack") - public void compareHandsTieBothBlackJack() { - LinkedList handA = new LinkedList<>(); - handA.add(new Card(11, Card.Suit.SPADES)); - handA.add(new Card(10, Card.Suit.CLUBS)); - - LinkedList handB = new LinkedList<>(); - handB.add(new Card(10, Card.Suit.SPADES)); - handB.add(new Card(11, Card.Suit.CLUBS)); - - initGame(); - - int result = game.compareHands(handA,handB); - - assertEquals(0, result); - } - - @Test - @DisplayName("compareHands should return 0, hand A and B tie without a Blackjack") - public void compareHandsTieNoBlackJack() { - LinkedList handA = new LinkedList<>(); - handA.add(new Card(10, Card.Suit.DIAMONDS)); - handA.add(new Card(10, Card.Suit.HEARTS)); - - LinkedList handB = new LinkedList<>(); - handB.add(new Card(10, Card.Suit.SPADES)); - handB.add(new Card(10, Card.Suit.CLUBS)); - - initGame(); - - int result = game.compareHands(handA,handB); - - assertEquals(0, result); - } - @Test @DisplayName("play() should end on STAY after split") public void playSplitEndOnStay(){ diff --git a/10_Blackjack/java/test/ScoringUtilsTest.java b/10_Blackjack/java/test/ScoringUtilsTest.java new file mode 100644 index 00000000..a6c3877f --- /dev/null +++ b/10_Blackjack/java/test/ScoringUtilsTest.java @@ -0,0 +1,143 @@ +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.api.DisplayName; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.LinkedList; + +public class ScoringUtilsTest { + + @Test + @DisplayName("scoreHand should score aces as 1 when using 11 would bust") + public void scoreHandHardAce() { + // Given + LinkedList hand = new LinkedList<>(); + hand.add(new Card(10, Card.Suit.SPADES)); + hand.add(new Card(9, Card.Suit.SPADES)); + hand.add(new Card(1, Card.Suit.SPADES)); + + // When + int result = ScoringUtils.scoreHand(hand); + + // Then + assertEquals(20, result); + } + + @Test + @DisplayName("scoreHand should score 3 aces as 13") + public void scoreHandMultipleAces() { + // Given + LinkedList hand = new LinkedList<>(); + hand.add(new Card(1, Card.Suit.SPADES)); + hand.add(new Card(1, Card.Suit.CLUBS)); + hand.add(new Card(1, Card.Suit.HEARTS)); + + // When + int result = ScoringUtils.scoreHand(hand); + + // Then + assertEquals(13, result); + } + + @Test + @DisplayName("compareHands should return 1 meaning A beat B, 20 to 12") + public void compareHandsAWins() { + LinkedList handA = new LinkedList<>(); + handA.add(new Card(10, Card.Suit.SPADES)); + handA.add(new Card(10, Card.Suit.CLUBS)); + + LinkedList handB = new LinkedList<>(); + handB.add(new Card(1, Card.Suit.SPADES)); + handB.add(new Card(1, Card.Suit.CLUBS)); + + int result = ScoringUtils.compareHands(handA,handB); + + assertEquals(1, result); + } + + @Test + @DisplayName("compareHands should return -1 meaning B beat A, 18 to 4") + public void compareHandsBwins() { + LinkedList handA = new LinkedList<>(); + handA.add(new Card(2, Card.Suit.SPADES)); + handA.add(new Card(2, Card.Suit.CLUBS)); + + LinkedList handB = new LinkedList<>(); + handB.add(new Card(5, Card.Suit.SPADES)); + handB.add(new Card(6, Card.Suit.HEARTS)); + handB.add(new Card(7, Card.Suit.CLUBS)); + + int result = ScoringUtils.compareHands(handA,handB); + + assertEquals(-1, result); + } + + @Test + @DisplayName("compareHands should return 1 meaning A beat B, natural Blackjack to Blackjack") + public void compareHandsAWinsWithNaturalBlackJack() { + //Hand A wins with natural BlackJack, B with Blackjack + LinkedList handA = new LinkedList<>(); + handA.add(new Card(10, Card.Suit.SPADES)); + handA.add(new Card(1, Card.Suit.CLUBS)); + + LinkedList handB = new LinkedList<>(); + handB.add(new Card(6, Card.Suit.SPADES)); + handB.add(new Card(7, Card.Suit.HEARTS)); + handB.add(new Card(8, Card.Suit.CLUBS)); + + int result = ScoringUtils.compareHands(handA,handB); + + assertEquals(1, result); + } + + @Test + @DisplayName("compareHands should return -1 meaning B beat A, natural Blackjack to Blackjack") + public void compareHandsBWinsWithNaturalBlackJack() { + LinkedList handA = new LinkedList<>(); + handA.add(new Card(6, Card.Suit.SPADES)); + handA.add(new Card(7, Card.Suit.HEARTS)); + handA.add(new Card(8, Card.Suit.CLUBS)); + + LinkedList handB = new LinkedList<>(); + handB.add(new Card(10, Card.Suit.SPADES)); + handB.add(new Card(1, Card.Suit.CLUBS)); + + int result = ScoringUtils.compareHands(handA,handB); + + assertEquals(-1, result); + } + + @Test + @DisplayName("compareHands should return 0, hand A and B tied with a Blackjack") + public void compareHandsTieBothBlackJack() { + LinkedList handA = new LinkedList<>(); + handA.add(new Card(11, Card.Suit.SPADES)); + handA.add(new Card(10, Card.Suit.CLUBS)); + + LinkedList handB = new LinkedList<>(); + handB.add(new Card(10, Card.Suit.SPADES)); + handB.add(new Card(11, Card.Suit.CLUBS)); + + int result = ScoringUtils.compareHands(handA,handB); + + assertEquals(0, result); + } + + @Test + @DisplayName("compareHands should return 0, hand A and B tie without a Blackjack") + public void compareHandsTieNoBlackJack() { + LinkedList handA = new LinkedList<>(); + handA.add(new Card(10, Card.Suit.DIAMONDS)); + handA.add(new Card(10, Card.Suit.HEARTS)); + + LinkedList handB = new LinkedList<>(); + handB.add(new Card(10, Card.Suit.SPADES)); + handB.add(new Card(10, Card.Suit.CLUBS)); + + int result = ScoringUtils.compareHands(handA,handB); + + assertEquals(0, result); + } + +}