From 6a2058e9b89611d5d9386cd3abc381b234c70a4d Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 17 Mar 2021 11:00:43 +0900 Subject: [PATCH 01/20] add hand class: -receives card -calculates score and best score -test scoring --- .../java/com/coderanch/blackjack/Card.java | 12 +++- .../java/com/coderanch/blackjack/Hand.java | 72 +++++++++++++++++++ .../com/coderanch/blackjack/HandTest.java | 43 +++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/coderanch/blackjack/Hand.java create mode 100644 core/src/test/java/com/coderanch/blackjack/HandTest.java diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index 98db633..83ea163 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -25,7 +25,15 @@ final class Card implements Comparable { * The rank of a card. */ enum Rank { - ACE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING + ACE(1,11), TWO(2), THREE(3), FOUR(4), FIVE(5), + SIX(6), SEVEN(7), EIGHT(8), NINE(9), + TEN(10), JACK(10), QUEEN(10), KING(10); + + public int[] values; + + private Rank(int...values){ + this.values = values; + } } /** @@ -69,6 +77,8 @@ Suit suit() { return this.suit; } + int[] values() { return this.rank.values; } + /** * Compares this card to another card. * diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java new file mode 100644 index 0000000..ebf4018 --- /dev/null +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -0,0 +1,72 @@ +package com.coderanch.blackjack; + +import java.util.*; + +/** + * A hand in a game of Blackjack. + */ +public class Hand { + private List cards = new ArrayList<>(); + private boolean isStopped; + private Set possibleScores; + private int bestScore; + + /** + * Adds a card to the hand. + * + * @param card the card being added to the hand. + */ + public void addCard(Card card) { + if(isStopped){ + throw new IllegalArgumentException("Hand is already stopped"); + } + cards.add(card); + this.possibleScores = calculateScores(); + this.bestScore = calculateBestScore(); + } + + private Set calculateScores() { + var startingScore = cards.stream() + .filter(c -> c.values().length == 1) + .flatMapToInt(c -> Arrays.stream(c.values())) + .sum(); + var scores = new ArrayDeque(2^4); + scores.add(startingScore); + + cards.stream() + .filter(c -> c.values().length > 1) + .map(c -> c.values()) + .forEach(nums ->{ + var initialSize = scores.size(); + for (int i = 0; i < initialSize; i++) { + var currentNum = scores.pop(); + for (int j = 0; j < nums.length; j++) { + scores.addLast(currentNum + nums[j]); + } + } + }); + return new LinkedHashSet<>(scores); + } + + private int calculateBestScore() { + return this.possibleScores.stream() + .sorted((o1, o2) -> Integer.compare(o2, o1)) + .filter(i -> i <= 21) + .findFirst() + .orElse(0); + } + + /** + * Gets the current best score. + * + * @return the current best score. + */ + public int bestScore() { return this.bestScore; } + + /** + * Stops the hand. Cannot receive more cards. + */ + public void stop() { + this.isStopped = true; + } +} diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java new file mode 100644 index 0000000..c6996a4 --- /dev/null +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -0,0 +1,43 @@ +package com.coderanch.blackjack; + +import com.coderanch.blackjack.Card.Rank; +import com.coderanch.blackjack.Card.Suit; + +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + + +/** + * Tests the {@link Hand} class. + */ +@RunWith(Theories.class) +public final class HandTest { + + /** + * Tests that {@link Hand#addCard(Card)} ()} maintains the correct score + */ + @Theory + public void addCard_maintainsCorrectScore() { + var hand = new Hand(); + hand.addCard(new Card(Rank.ACE, Suit.CLUBS)); + assertThat(hand.bestScore(), is(equalTo(11))); + + hand.addCard(new Card(Rank.ACE, Suit.CLUBS)); + assertThat(hand.bestScore(), is(equalTo(12))); + + hand.addCard(new Card(Rank.KING, Suit.CLUBS)); + assertThat(hand.bestScore(), is(equalTo(12))); + + hand.addCard(new Card(Rank.EIGHT, Suit.CLUBS)); + assertThat(hand.bestScore(), is(equalTo(20))); + + hand.addCard(new Card(Rank.ACE, Suit.CLUBS)); + assertThat(hand.bestScore(), is(equalTo(21))); + } + +} \ No newline at end of file From 331d71d41b5da5fea0f436ebc52ed30809a95905 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 17 Mar 2021 11:52:51 +0900 Subject: [PATCH 02/20] resolve sonar problems --- core/src/main/java/com/coderanch/blackjack/Card.java | 8 ++++---- core/src/main/java/com/coderanch/blackjack/Hand.java | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index 83ea163..be590a0 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -29,10 +29,10 @@ enum Rank { SIX(6), SEVEN(7), EIGHT(8), NINE(9), TEN(10), JACK(10), QUEEN(10), KING(10); - public int[] values; + private int[] points; - private Rank(int...values){ - this.values = values; + Rank(int... points){ + this.points = points; } } @@ -77,7 +77,7 @@ Suit suit() { return this.suit; } - int[] values() { return this.rank.values; } + int[] values() { return this.rank.points; } /** * Compares this card to another card. diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index ebf4018..30b5434 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -6,6 +6,10 @@ * A hand in a game of Blackjack. */ public class Hand { + + private static final int HIGHEST_SCORE = 21; + private static final int MAX_SCORE_COUNT = 2^4; + private List cards = new ArrayList<>(); private boolean isStopped; private Set possibleScores; @@ -30,13 +34,13 @@ private Set calculateScores() { .filter(c -> c.values().length == 1) .flatMapToInt(c -> Arrays.stream(c.values())) .sum(); - var scores = new ArrayDeque(2^4); + var scores = new ArrayDeque(MAX_SCORE_COUNT); scores.add(startingScore); cards.stream() .filter(c -> c.values().length > 1) - .map(c -> c.values()) - .forEach(nums ->{ + .map(Card::values) + .forEach((int[] nums) ->{ var initialSize = scores.size(); for (int i = 0; i < initialSize; i++) { var currentNum = scores.pop(); @@ -51,7 +55,7 @@ private Set calculateScores() { private int calculateBestScore() { return this.possibleScores.stream() .sorted((o1, o2) -> Integer.compare(o2, o1)) - .filter(i -> i <= 21) + .filter(i -> i <= HIGHEST_SCORE) .findFirst() .orElse(0); } From 5b82e678f873148fdfef0f7247df0834ffc30df5 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 17 Mar 2021 13:24:25 +0900 Subject: [PATCH 03/20] resolve sonar smells --- core/src/main/java/com/coderanch/blackjack/Card.java | 4 +++- core/src/main/java/com/coderanch/blackjack/Hand.java | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index be590a0..c6fa984 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -77,7 +77,9 @@ Suit suit() { return this.suit; } - int[] values() { return this.rank.points; } + int[] values() { + return this.rank.points; + } /** * Compares this card to another card. diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index 30b5434..f9c66c4 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2018 Coderanch. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ package com.coderanch.blackjack; import java.util.*; @@ -65,7 +72,9 @@ private int calculateBestScore() { * * @return the current best score. */ - public int bestScore() { return this.bestScore; } + public int bestScore() { + return this.bestScore; + } /** * Stops the hand. Cannot receive more cards. From 3e14930005f8f4de9e161e28ddfc1e49ac7c6d93 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Mar 2021 10:16:55 +0900 Subject: [PATCH 04/20] -make hand immutable -update hand tests -update calculate method with ace as special case with 11 points --- .../java/com/coderanch/blackjack/Card.java | 8 +- .../java/com/coderanch/blackjack/Hand.java | 83 +++++++++++-------- .../com/coderanch/blackjack/HandTest.java | 22 ++--- 3 files changed, 65 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index c6fa984..eaf9018 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -25,13 +25,13 @@ final class Card implements Comparable { * The rank of a card. */ enum Rank { - ACE(1,11), TWO(2), THREE(3), FOUR(4), FIVE(5), + ACE(11), TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9), TEN(10), JACK(10), QUEEN(10), KING(10); - private int[] points; + private int points; - Rank(int... points){ + Rank(int points){ this.points = points; } } @@ -77,7 +77,7 @@ Suit suit() { return this.suit; } - int[] values() { + int points() { return this.rank.points; } diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index f9c66c4..c1287dc 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -12,74 +12,89 @@ /** * A hand in a game of Blackjack. */ -public class Hand { +final class Hand { - private static final int HIGHEST_SCORE = 21; - private static final int MAX_SCORE_COUNT = 2^4; + /** + * Highest score in Blackjack. + */ + private static final int MAX_LEGAL_SCORE = 21; + + /** + * Max amount of aces + */ + private static final int MAX_SCORE_COUNT = 4; - private List cards = new ArrayList<>(); - private boolean isStopped; - private Set possibleScores; + + private List cards; + private List possibleScores; private int bestScore; /** - * Adds a card to the hand. + * Creates a new hand. * - * @param card the card being added to the hand. + * @param card first card dealt. + * @param card2 second card dealt */ - public void addCard(Card card) { - if(isStopped){ - throw new IllegalArgumentException("Hand is already stopped"); - } - cards.add(card); + public Hand(Card card, Card card2) { + this(List.of(card, card2)); + } + + private Hand(List cards) { + this.cards = cards; this.possibleScores = calculateScores(); this.bestScore = calculateBestScore(); } - private Set calculateScores() { + /** + * Gets a new hand with the extra card. + * + * @param card the card being added to the hand. + * @return a new hand with the extra card. + */ + public Hand withAdditionalCard(Card card) { + var newCards = new ArrayList(this.cards); + newCards.add(card); + return new Hand(newCards); + } + + private List calculateScores() { var startingScore = cards.stream() - .filter(c -> c.values().length == 1) - .flatMapToInt(c -> Arrays.stream(c.values())) + .filter(c -> c.rank() != Card.Rank.ACE) + .mapToInt(c -> c.points()) .sum(); - var scores = new ArrayDeque(MAX_SCORE_COUNT); + + var scores = new ArrayList(MAX_SCORE_COUNT); scores.add(startingScore); cards.stream() - .filter(c -> c.values().length > 1) - .map(Card::values) - .forEach((int[] nums) ->{ + .filter(c -> c.rank() == Card.Rank.ACE) + .forEach(c ->{ var initialSize = scores.size(); for (int i = 0; i < initialSize; i++) { - var currentNum = scores.pop(); - for (int j = 0; j < nums.length; j++) { - scores.addLast(currentNum + nums[j]); + var score = scores.get(i); + scores.set(i, score + 1); + if(i + 1 == initialSize){ + scores.add(score + c.points()); } } }); - return new LinkedHashSet<>(scores); + return scores; } private int calculateBestScore() { return this.possibleScores.stream() .sorted((o1, o2) -> Integer.compare(o2, o1)) - .filter(i -> i <= HIGHEST_SCORE) + .filter(i -> i <= MAX_LEGAL_SCORE) .findFirst() .orElse(0); } /** - * Gets the current best score. + * Gets the best score. * - * @return the current best score. + * @return the best score. */ public int bestScore() { return this.bestScore; } - - /** - * Stops the hand. Cannot receive more cards. - */ - public void stop() { - this.isStopped = true; - } } diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index c6996a4..b36f8df 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -1,3 +1,10 @@ +/* + * Copyright (C) 2018 Coderanch. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ package com.coderanch.blackjack; import com.coderanch.blackjack.Card.Rank; @@ -19,25 +26,20 @@ public final class HandTest { /** - * Tests that {@link Hand#addCard(Card)} ()} maintains the correct score + * Tests that {@link Hand#withAdditionalCard(Card)} maintains the correct score */ @Theory public void addCard_maintainsCorrectScore() { - var hand = new Hand(); - hand.addCard(new Card(Rank.ACE, Suit.CLUBS)); - assertThat(hand.bestScore(), is(equalTo(11))); - - hand.addCard(new Card(Rank.ACE, Suit.CLUBS)); + var hand = new Hand(new Card(Rank.ACE, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS)); assertThat(hand.bestScore(), is(equalTo(12))); - hand.addCard(new Card(Rank.KING, Suit.CLUBS)); + hand = hand.withAdditionalCard(new Card(Rank.KING, Suit.CLUBS)); assertThat(hand.bestScore(), is(equalTo(12))); - hand.addCard(new Card(Rank.EIGHT, Suit.CLUBS)); + hand = hand.withAdditionalCard(new Card(Rank.EIGHT, Suit.CLUBS)); assertThat(hand.bestScore(), is(equalTo(20))); - hand.addCard(new Card(Rank.ACE, Suit.CLUBS)); + hand = hand.withAdditionalCard(new Card(Rank.ACE, Suit.CLUBS)); assertThat(hand.bestScore(), is(equalTo(21))); } - } \ No newline at end of file From d846c777a26e6ad3b6928abcf591f547fa0b4627 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Mar 2021 11:46:00 +0900 Subject: [PATCH 05/20] Hand.java -remove sonar smells --- core/src/main/java/com/coderanch/blackjack/Hand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index c1287dc..da1415e 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -60,7 +60,7 @@ public Hand withAdditionalCard(Card card) { private List calculateScores() { var startingScore = cards.stream() .filter(c -> c.rank() != Card.Rank.ACE) - .mapToInt(c -> c.points()) + .mapToInt(Card::points) .sum(); var scores = new ArrayList(MAX_SCORE_COUNT); @@ -68,7 +68,7 @@ private List calculateScores() { cards.stream() .filter(c -> c.rank() == Card.Rank.ACE) - .forEach(c ->{ + .forEach((Card c) ->{ var initialSize = scores.size(); for (int i = 0; i < initialSize; i++) { var score = scores.get(i); From 8ce4e1f6a04aa830484e407315c516ea4d62162f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Mar 2021 15:05:40 +0900 Subject: [PATCH 06/20] HandTest.java -use datapoints in hand test --- .../com/coderanch/blackjack/HandTest.java | 68 ++++++++++++++++--- 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index b36f8df..3987279 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -10,10 +10,15 @@ import com.coderanch.blackjack.Card.Rank; import com.coderanch.blackjack.Card.Suit; +import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; +import java.util.Collections; +import java.util.List; +import java.util.Set; + import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -26,20 +31,61 @@ public final class HandTest { /** - * Tests that {@link Hand#withAdditionalCard(Card)} maintains the correct score + * Hands to test scoring. */ - @Theory - public void addCard_maintainsCorrectScore() { - var hand = new Hand(new Card(Rank.ACE, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS)); - assertThat(hand.bestScore(), is(equalTo(12))); + @DataPoints + public static final List HANDS = + List.of( + new HandTestArgument( + List.of(new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.EIGHT, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS)), + 21 + ), + + new HandTestArgument( + List.of(new Card(Rank.QUEEN, Suit.CLUBS), + new Card(Rank.EIGHT, Suit.CLUBS)), + 18 + ), + + new HandTestArgument( + List.of(new Card(Rank.QUEEN, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS)), + 21 + ), + + new HandTestArgument( + List.of(new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS)), + 0 + ) + ); - hand = hand.withAdditionalCard(new Card(Rank.KING, Suit.CLUBS)); - assertThat(hand.bestScore(), is(equalTo(12))); + /** + * Tests that {@link Hand#withAdditionalCard(Card)} maintains the correct score + */ + @Theory + public void addCard_maintainsCorrectScore(HandTestArgument argument) { + var hand = new Hand(argument.Cards.get(0), argument.Cards.get(1)); + for (int i = 2; i < argument.Cards.size(); i++) { + hand = hand.withAdditionalCard(argument.Cards.get(i)); + } + assertThat("The best score must match the target", hand.bestScore(), is(argument.targetScore)); + } - hand = hand.withAdditionalCard(new Card(Rank.EIGHT, Suit.CLUBS)); - assertThat(hand.bestScore(), is(equalTo(20))); + private static final class HandTestArgument { + public final List Cards; + public final int targetScore; - hand = hand.withAdditionalCard(new Card(Rank.ACE, Suit.CLUBS)); - assertThat(hand.bestScore(), is(equalTo(21))); + public HandTestArgument(List cards, int targetScore) { + Cards = Collections.unmodifiableList(cards); + this.targetScore = targetScore; + } } } \ No newline at end of file From 72eb16a4431b7df5aebf42142aab769a7e079cac Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Mar 2021 16:32:42 +0900 Subject: [PATCH 07/20] HandTest.java, Hand.java -add exception documentation -add tests for null cards --- .../java/com/coderanch/blackjack/Hand.java | 14 ++- .../com/coderanch/blackjack/HandTest.java | 109 +++++++++++++----- 2 files changed, 89 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index da1415e..ef4e5e9 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -9,6 +9,10 @@ import java.util.*; +import static com.coderanch.util.require.Require.requireThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + /** * A hand in a game of Blackjack. */ @@ -32,8 +36,9 @@ final class Hand { /** * Creates a new hand. * - * @param card first card dealt. + * @param card first card dealt. * @param card2 second card dealt + * @throws NullPointerException if either {@code card} or {@code card2} is {@code null}. */ public Hand(Card card, Card card2) { this(List.of(card, card2)); @@ -50,8 +55,11 @@ private Hand(List cards) { * * @param card the card being added to the hand. * @return a new hand with the extra card. + * @throws IllegalArgumentException if {@code card} is {@code null}. */ public Hand withAdditionalCard(Card card) { + requireThat("card", card, is(notNullValue())); + var newCards = new ArrayList(this.cards); newCards.add(card); return new Hand(newCards); @@ -68,12 +76,12 @@ private List calculateScores() { cards.stream() .filter(c -> c.rank() == Card.Rank.ACE) - .forEach((Card c) ->{ + .forEach((Card c) -> { var initialSize = scores.size(); for (int i = 0; i < initialSize; i++) { var score = scores.get(i); scores.set(i, score + 1); - if(i + 1 == initialSize){ + if (i + 1 == initialSize) { scores.add(score + c.points()); } } diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index 3987279..ecf4d4d 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -10,9 +10,12 @@ import com.coderanch.blackjack.Card.Rank; import com.coderanch.blackjack.Card.Suit; +import com.coderanch.test.ConsistentComparableTest; +import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import java.util.Collections; @@ -22,6 +25,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertThrows; /** @@ -35,37 +39,43 @@ public final class HandTest { */ @DataPoints public static final List HANDS = - List.of( - new HandTestArgument( - List.of(new Card(Rank.ACE, Suit.CLUBS), - new Card(Rank.ACE, Suit.CLUBS), - new Card(Rank.KING, Suit.CLUBS), - new Card(Rank.EIGHT, Suit.CLUBS), - new Card(Rank.ACE, Suit.CLUBS)), - 21 - ), - - new HandTestArgument( - List.of(new Card(Rank.QUEEN, Suit.CLUBS), - new Card(Rank.EIGHT, Suit.CLUBS)), - 18 - ), - - new HandTestArgument( - List.of(new Card(Rank.QUEEN, Suit.CLUBS), - new Card(Rank.ACE, Suit.CLUBS)), - 21 - ), - - new HandTestArgument( - List.of(new Card(Rank.KING, Suit.CLUBS), - new Card(Rank.KING, Suit.CLUBS), - new Card(Rank.KING, Suit.CLUBS), - new Card(Rank.KING, Suit.CLUBS), - new Card(Rank.KING, Suit.CLUBS)), - 0 - ) - ); + List.of( + new HandTestArgument( + List.of(new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.EIGHT, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS)), + 21 + ), + + new HandTestArgument( + List.of(new Card(Rank.QUEEN, Suit.CLUBS), + new Card(Rank.EIGHT, Suit.CLUBS)), + 18 + ), + + new HandTestArgument( + List.of(new Card(Rank.QUEEN, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS)), + 21 + ), + + new HandTestArgument( + List.of(new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS)), + 0 + ) + ); + + /** + * Cards to test. + */ + @DataPoints("objects") + public static final Set CARDS = Cards.getStandardDeck(); /** * Tests that {@link Hand#withAdditionalCard(Card)} maintains the correct score @@ -88,4 +98,41 @@ public HandTestArgument(List cards, int targetScore) { this.targetScore = targetScore; } } + + /** + * Tests that passing {@code null} for {@code card} when constructing a new hand causes an exception to be thrown. + * + * @param card the card to construct the hand with. + */ + @Theory(nullsAccepted = false) + public void newHand_withNullCard1_throwsException(Card card) { + assertThrows(NullPointerException.class, () -> { + new Hand(null, card); + }); + } + + /** + * Tests that passing {@code null} for {@code card} when constructing a new hand causes an exception to be thrown. + * + * @param card the card to construct the hand with. + */ + @Theory(nullsAccepted = false) + public void newHand_withNullCard2_throwsException(Card card) { + assertThrows(NullPointerException.class, () -> { + new Hand(card, null); + }); + } + + /** + * Tests that passing {@code null} for {@code card} when adding a new card causes an exception to be thrown. + */ + @Theory + public void addCard_withNullCard_throwsException() { + var hand = new Hand(new Card(Rank.ACE, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS)); + assertThrows(IllegalArgumentException.class, () -> { + hand.withAdditionalCard(null); + }); + } + + } \ No newline at end of file From cf4265cf3e574a0eac9871157a88768bebc39dc5 Mon Sep 17 00:00:00 2001 From: aquino-a Date: Fri, 19 Mar 2021 08:35:54 +0900 Subject: [PATCH 08/20] Update core/src/main/java/com/coderanch/blackjack/Card.java -rank display style Co-authored-by: Stephan van Hulst <2323961+nibsi@users.noreply.github.com> --- .../main/java/com/coderanch/blackjack/Card.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index eaf9018..01d1cc5 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -25,9 +25,19 @@ final class Card implements Comparable { * The rank of a card. */ enum Rank { - ACE(11), TWO(2), THREE(3), FOUR(4), FIVE(5), - SIX(6), SEVEN(7), EIGHT(8), NINE(9), - TEN(10), JACK(10), QUEEN(10), KING(10); + ACE (11), + TWO ( 2), + THREE ( 3), + FOUR ( 4), + FIVE ( 5), + SIX ( 6), + SEVEN ( 7), + EIGHT ( 8), + NINE ( 9), + TEN (10), + JACK (10), + QUEEN (10), + KING (10); private int points; From 695f1c36eeae293ecd0c322f92afe65203e661b9 Mon Sep 17 00:00:00 2001 From: aquino-a Date: Fri, 19 Mar 2021 08:36:54 +0900 Subject: [PATCH 09/20] Update core/src/main/java/com/coderanch/blackjack/Card.java make rank points variable final Co-authored-by: Stephan van Hulst <2323961+nibsi@users.noreply.github.com> --- core/src/main/java/com/coderanch/blackjack/Card.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index 01d1cc5..74bb5a1 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -39,7 +39,7 @@ enum Rank { QUEEN (10), KING (10); - private int points; + private final int points; Rank(int points){ this.points = points; From 6fe891b4e07253de42d2731e5fc6c7440849c4a2 Mon Sep 17 00:00:00 2001 From: aquino-a Date: Fri, 19 Mar 2021 08:37:27 +0900 Subject: [PATCH 10/20] Update core/src/main/java/com/coderanch/blackjack/Hand.java -make the cards variable final Co-authored-by: Stephan van Hulst <2323961+nibsi@users.noreply.github.com> --- core/src/main/java/com/coderanch/blackjack/Hand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index ef4e5e9..54dc21d 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -29,7 +29,7 @@ final class Hand { private static final int MAX_SCORE_COUNT = 4; - private List cards; + private final List cards; private List possibleScores; private int bestScore; From a0e67d11bfbf64a59d21aab4d7a3f210d8af77a8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 19 Mar 2021 08:51:25 +0900 Subject: [PATCH 11/20] Hand.java -remove MAX_SCORE_COUNT --- core/src/main/java/com/coderanch/blackjack/Hand.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index 54dc21d..3860604 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -23,12 +23,6 @@ final class Hand { */ private static final int MAX_LEGAL_SCORE = 21; - /** - * Max amount of aces - */ - private static final int MAX_SCORE_COUNT = 4; - - private final List cards; private List possibleScores; private int bestScore; @@ -71,7 +65,7 @@ private List calculateScores() { .mapToInt(Card::points) .sum(); - var scores = new ArrayList(MAX_SCORE_COUNT); + var scores = new ArrayList(Card.Suit.values().length); scores.add(startingScore); cards.stream() From 68607db2ebe90b3f7f0a22ec13e3823ec9322146 Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Fri, 19 Mar 2021 09:36:45 +0900 Subject: [PATCH 12/20] Hand.java -remove cached scores and best score --- core/src/main/java/com/coderanch/blackjack/Hand.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index 3860604..31fff5c 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -24,8 +24,6 @@ final class Hand { private static final int MAX_LEGAL_SCORE = 21; private final List cards; - private List possibleScores; - private int bestScore; /** * Creates a new hand. @@ -40,8 +38,6 @@ public Hand(Card card, Card card2) { private Hand(List cards) { this.cards = cards; - this.possibleScores = calculateScores(); - this.bestScore = calculateBestScore(); } /** @@ -83,8 +79,8 @@ private List calculateScores() { return scores; } - private int calculateBestScore() { - return this.possibleScores.stream() + private int calculateBestScore(List possibleScores) { + return possibleScores.stream() .sorted((o1, o2) -> Integer.compare(o2, o1)) .filter(i -> i <= MAX_LEGAL_SCORE) .findFirst() @@ -97,6 +93,7 @@ private int calculateBestScore() { * @return the best score. */ public int bestScore() { - return this.bestScore; + var scores = calculateScores(); + return calculateBestScore(scores); } } From 6b5aa43b7d4f7c72bbdd85015a51b3a44b392e9d Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Fri, 19 Mar 2021 09:50:14 +0900 Subject: [PATCH 13/20] Hand.java, Card.java, HandTest.java -fix checkstyle violations --- .../java/com/coderanch/blackjack/Card.java | 40 +++++++-------- .../java/com/coderanch/blackjack/Hand.java | 5 +- .../com/coderanch/blackjack/HandTest.java | 49 +++++++++++++------ 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index dcdf10e..b7db09d 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -25,23 +25,26 @@ final class Card implements Comparable { * The rank of a card. */ enum Rank { - ACE (11), - TWO ( 2), - THREE ( 3), - FOUR ( 4), - FIVE ( 5), - SIX ( 6), - SEVEN ( 7), - EIGHT ( 8), - NINE ( 9), - TEN (10), - JACK (10), - QUEEN (10), - KING (10); - + ACE(11), + TWO(2), + THREE(3), + FOUR(4), + FIVE(5), + SIX(6), + SEVEN(7), + EIGHT(8), + NINE(9), + TEN(10), + JACK(10), + QUEEN(10), + KING(10); + + /** + * The points value of the rank. + */ private final int points; - Rank(int points){ + Rank(int points) { this.points = points; } } @@ -68,7 +71,6 @@ enum Suit { * * @param rank the rank of the new card. * @param suit the suit of the new card. - * * @throws IllegalArgumentException if either {@code rank} or {@code suit} is {@code null}. */ Card(Rank rank, Suit suit) { @@ -100,11 +102,10 @@ int points() { /** * Compares this card to another card. - * + *

* Cards are first compared by rank, then by suit. * * @param other {@inheritDoc} - * * @return {@inheritDoc} */ @Override @@ -114,11 +115,10 @@ public int compareTo(Card other) { /** * Compares this card to another object for equality. - * + *

* Cards are considered equal if they have the same rank and suit. * * @param object {@inheritDoc} - * * @return {@inheritDoc} */ @Override diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index 31fff5c..4b5c108 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -23,6 +23,9 @@ final class Hand { */ private static final int MAX_LEGAL_SCORE = 21; + /** + * The cards in the hand. + */ private final List cards; /** @@ -32,7 +35,7 @@ final class Hand { * @param card2 second card dealt * @throws NullPointerException if either {@code card} or {@code card2} is {@code null}. */ - public Hand(Card card, Card card2) { + Hand(Card card, Card card2) { this(List.of(card, card2)); } diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index ecf4d4d..fcb1de3 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -10,19 +10,15 @@ import com.coderanch.blackjack.Card.Rank; import com.coderanch.blackjack.Card.Suit; -import com.coderanch.test.ConsistentComparableTest; -import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; -import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; import java.util.Set; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertThrows; @@ -77,26 +73,46 @@ public final class HandTest { @DataPoints("objects") public static final Set CARDS = Cards.getStandardDeck(); + /** - * Tests that {@link Hand#withAdditionalCard(Card)} maintains the correct score + * Tests that {@link Hand#withAdditionalCard(Card)} maintains the correct score. + * + * @param handTest */ @Theory - public void addCard_maintainsCorrectScore(HandTestArgument argument) { - var hand = new Hand(argument.Cards.get(0), argument.Cards.get(1)); - for (int i = 2; i < argument.Cards.size(); i++) { - hand = hand.withAdditionalCard(argument.Cards.get(i)); + @SuppressWarnings("checkstyle:methodname") + public void addCard_maintainsCorrectScore(HandTestArgument handTest) { + var hand = new Hand(handTest.getCards().get(0), handTest.getCards().get(1)); + for (int i = 2; i < handTest.getCards().size(); i++) { + hand = hand.withAdditionalCard(handTest.getCards().get(i)); } - assertThat("The best score must match the target", hand.bestScore(), is(argument.targetScore)); + assertThat("The best score must match the target", hand.bestScore(), is(handTest.getTargetScore())); } private static final class HandTestArgument { - public final List Cards; - public final int targetScore; - public HandTestArgument(List cards, int targetScore) { - Cards = Collections.unmodifiableList(cards); + /** + * Cards included in the test. + */ + private final List cards; + + /** + * The target score the hand should have. + */ + private final int targetScore; + + HandTestArgument(List cards, int targetScore) { + this.cards = Collections.unmodifiableList(cards); this.targetScore = targetScore; } + + List getCards() { + return cards; + } + + int getTargetScore() { + return targetScore; + } } /** @@ -105,6 +121,7 @@ public HandTestArgument(List cards, int targetScore) { * @param card the card to construct the hand with. */ @Theory(nullsAccepted = false) + @SuppressWarnings("checkstyle:methodname") public void newHand_withNullCard1_throwsException(Card card) { assertThrows(NullPointerException.class, () -> { new Hand(null, card); @@ -117,6 +134,7 @@ public void newHand_withNullCard1_throwsException(Card card) { * @param card the card to construct the hand with. */ @Theory(nullsAccepted = false) + @SuppressWarnings("checkstyle:methodname") public void newHand_withNullCard2_throwsException(Card card) { assertThrows(NullPointerException.class, () -> { new Hand(card, null); @@ -127,6 +145,7 @@ public void newHand_withNullCard2_throwsException(Card card) { * Tests that passing {@code null} for {@code card} when adding a new card causes an exception to be thrown. */ @Theory + @SuppressWarnings("checkstyle:methodname") public void addCard_withNullCard_throwsException() { var hand = new Hand(new Card(Rank.ACE, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS)); assertThrows(IllegalArgumentException.class, () -> { @@ -135,4 +154,4 @@ public void addCard_withNullCard_throwsException() { } -} \ No newline at end of file +} From 0ad63ff4fb9b3a0211805e66a3bc8bcca2d6bd31 Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Fri, 19 Mar 2021 10:03:09 +0900 Subject: [PATCH 14/20] make calculation methods static --- core/src/main/java/com/coderanch/blackjack/Hand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index 4b5c108..f8bd13d 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -58,7 +58,7 @@ public Hand withAdditionalCard(Card card) { return new Hand(newCards); } - private List calculateScores() { + private static List calculateScores(List cards) { var startingScore = cards.stream() .filter(c -> c.rank() != Card.Rank.ACE) .mapToInt(Card::points) @@ -82,7 +82,7 @@ private List calculateScores() { return scores; } - private int calculateBestScore(List possibleScores) { + private static int calculateBestScore(List possibleScores) { return possibleScores.stream() .sorted((o1, o2) -> Integer.compare(o2, o1)) .filter(i -> i <= MAX_LEGAL_SCORE) @@ -96,7 +96,7 @@ private int calculateBestScore(List possibleScores) { * @return the best score. */ public int bestScore() { - var scores = calculateScores(); + var scores = calculateScores(this.cards); return calculateBestScore(scores); } } From b575690f4fe65d2cbc7fab955a39601376c781c8 Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Fri, 19 Mar 2021 11:18:45 +0900 Subject: [PATCH 15/20] add message to assert --- core/src/test/java/com/coderanch/blackjack/HandTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index fcb1de3..2fd40f3 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -123,7 +123,7 @@ int getTargetScore() { @Theory(nullsAccepted = false) @SuppressWarnings("checkstyle:methodname") public void newHand_withNullCard1_throwsException(Card card) { - assertThrows(NullPointerException.class, () -> { + assertThrows("Hand must throw exception", NullPointerException.class, () -> { new Hand(null, card); }); } @@ -136,7 +136,7 @@ public void newHand_withNullCard1_throwsException(Card card) { @Theory(nullsAccepted = false) @SuppressWarnings("checkstyle:methodname") public void newHand_withNullCard2_throwsException(Card card) { - assertThrows(NullPointerException.class, () -> { + assertThrows("Hand must throw exception", NullPointerException.class, () -> { new Hand(card, null); }); } @@ -148,7 +148,7 @@ public void newHand_withNullCard2_throwsException(Card card) { @SuppressWarnings("checkstyle:methodname") public void addCard_withNullCard_throwsException() { var hand = new Hand(new Card(Rank.ACE, Suit.CLUBS), new Card(Rank.ACE, Suit.CLUBS)); - assertThrows(IllegalArgumentException.class, () -> { + assertThrows("Hand must throw exception", IllegalArgumentException.class, () -> { hand.withAdditionalCard(null); }); } From f0b28eede091a3baba2f645c81639aeed5d7decd Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Mon, 22 Mar 2021 09:43:59 +0900 Subject: [PATCH 16/20] Card.java, HandTest.java, Hand.java -remove html -add parameter validation -update thrown exception in test --- .../src/main/java/com/coderanch/blackjack/Card.java | 2 -- .../src/main/java/com/coderanch/blackjack/Hand.java | 13 ++++++++----- .../test/java/com/coderanch/blackjack/HandTest.java | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index b7db09d..b209b5e 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -102,7 +102,6 @@ int points() { /** * Compares this card to another card. - *

* Cards are first compared by rank, then by suit. * * @param other {@inheritDoc} @@ -115,7 +114,6 @@ public int compareTo(Card other) { /** * Compares this card to another object for equality. - *

* Cards are considered equal if they have the same rank and suit. * * @param object {@inheritDoc} diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index f8bd13d..6238353 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -31,12 +31,15 @@ final class Hand { /** * Creates a new hand. * - * @param card first card dealt. - * @param card2 second card dealt - * @throws NullPointerException if either {@code card} or {@code card2} is {@code null}. + * @param firstCard first card dealt. + * @param secondCard second card dealt + * @throws IllegalArgumentException if either {@code firstCard} or {@code secondCard} is {@code null}. */ - Hand(Card card, Card card2) { - this(List.of(card, card2)); + Hand(Card firstCard, Card secondCard) { + this(List.of( + requireThat("firstCard", firstCard, is(notNullValue())), + requireThat("secondCard", secondCard, is(notNullValue())) + )); } private Hand(List cards) { diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index 2fd40f3..9471db3 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -123,7 +123,7 @@ int getTargetScore() { @Theory(nullsAccepted = false) @SuppressWarnings("checkstyle:methodname") public void newHand_withNullCard1_throwsException(Card card) { - assertThrows("Hand must throw exception", NullPointerException.class, () -> { + assertThrows("Hand must throw exception", IllegalArgumentException.class, () -> { new Hand(null, card); }); } @@ -136,7 +136,7 @@ public void newHand_withNullCard1_throwsException(Card card) { @Theory(nullsAccepted = false) @SuppressWarnings("checkstyle:methodname") public void newHand_withNullCard2_throwsException(Card card) { - assertThrows("Hand must throw exception", NullPointerException.class, () -> { + assertThrows("Hand must throw exception", IllegalArgumentException.class, () -> { new Hand(card, null); }); } From 13ea61c7af10e7904dd3d26663c96778ab7c5e03 Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Mon, 22 Mar 2021 10:10:48 +0900 Subject: [PATCH 17/20] Card.java, HandTest.java, Hand.java -refactor bestScore() -add more hand test cases --- .../java/com/coderanch/blackjack/Card.java | 4 ++ .../java/com/coderanch/blackjack/Hand.java | 70 +++++++++---------- .../com/coderanch/blackjack/HandTest.java | 16 +++++ 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/Card.java b/core/src/main/java/com/coderanch/blackjack/Card.java index b209b5e..cff1ba0 100644 --- a/core/src/main/java/com/coderanch/blackjack/Card.java +++ b/core/src/main/java/com/coderanch/blackjack/Card.java @@ -47,6 +47,10 @@ enum Rank { Rank(int points) { this.points = points; } + + public int getPoints() { + return points; + } } /** diff --git a/core/src/main/java/com/coderanch/blackjack/Hand.java b/core/src/main/java/com/coderanch/blackjack/Hand.java index 6238353..a87a6c6 100644 --- a/core/src/main/java/com/coderanch/blackjack/Hand.java +++ b/core/src/main/java/com/coderanch/blackjack/Hand.java @@ -9,7 +9,9 @@ import java.util.*; +import static com.coderanch.blackjack.Card.Rank.ACE; import static com.coderanch.util.require.Require.requireThat; +import static java.util.function.Predicate.not; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -37,7 +39,7 @@ final class Hand { */ Hand(Card firstCard, Card secondCard) { this(List.of( - requireThat("firstCard", firstCard, is(notNullValue())), + requireThat("firstCard", firstCard, is(notNullValue())), requireThat("secondCard", secondCard, is(notNullValue())) )); } @@ -61,45 +63,43 @@ public Hand withAdditionalCard(Card card) { return new Hand(newCards); } - private static List calculateScores(List cards) { - var startingScore = cards.stream() - .filter(c -> c.rank() != Card.Rank.ACE) - .mapToInt(Card::points) - .sum(); - - var scores = new ArrayList(Card.Suit.values().length); - scores.add(startingScore); - - cards.stream() - .filter(c -> c.rank() == Card.Rank.ACE) - .forEach((Card c) -> { - var initialSize = scores.size(); - for (int i = 0; i < initialSize; i++) { - var score = scores.get(i); - scores.set(i, score + 1); - if (i + 1 == initialSize) { - scores.add(score + c.points()); - } - } - }); - return scores; - } - - private static int calculateBestScore(List possibleScores) { - return possibleScores.stream() - .sorted((o1, o2) -> Integer.compare(o2, o1)) - .filter(i -> i <= MAX_LEGAL_SCORE) - .findFirst() - .orElse(0); - } - /** * Gets the best score. * * @return the best score. */ public int bestScore() { - var scores = calculateScores(this.cards); - return calculateBestScore(scores); + var minimumScore = cards.stream() + .map(Card::rank) + .filter(not(ACE::equals)) + .mapToInt(Card.Rank::getPoints) + .sum(); + + var numberOfFreeAces = (int) cards.stream() + .map(Card::rank) + .filter(ACE::equals) + .count(); + + var bestScore = calculateBestScore(minimumScore, numberOfFreeAces); + if (bestScore > MAX_LEGAL_SCORE) { + return 0; + } + else { + return bestScore; + } + } + + private static int calculateBestScore(int minimumScore, int numberOfFreeAces) { + if (numberOfFreeAces <= 0) { + return minimumScore; + } + var bestScoreWithBigAce = calculateBestScore(minimumScore + ACE.getPoints(), numberOfFreeAces - 1); + + if (bestScoreWithBigAce > MAX_LEGAL_SCORE) { + return calculateBestScore(minimumScore + 1, numberOfFreeAces - 1); + } + else { + return bestScoreWithBigAce; + } } } diff --git a/core/src/test/java/com/coderanch/blackjack/HandTest.java b/core/src/test/java/com/coderanch/blackjack/HandTest.java index 9471db3..247fca3 100644 --- a/core/src/test/java/com/coderanch/blackjack/HandTest.java +++ b/core/src/test/java/com/coderanch/blackjack/HandTest.java @@ -64,6 +64,22 @@ public final class HandTest { new Card(Rank.KING, Suit.CLUBS), new Card(Rank.KING, Suit.CLUBS)), 0 + ), + + new HandTestArgument( + List.of(new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS), + new Card(Rank.KING, Suit.CLUBS)), + 0 + ), + + new HandTestArgument( + List.of(new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.ACE, Suit.CLUBS), + new Card(Rank.NINE, Suit.CLUBS)), + 21 ) ); From 08a28f67b481462f306411b70db1e6909ea6810c Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Mon, 22 Mar 2021 17:01:24 +0900 Subject: [PATCH 18/20] first commit --- .../java/com/coderanch/blackjack/Game.java | 82 +++++++++++++++++++ .../com/coderanch/blackjack/HumanPlayer.java | 28 +++++++ .../java/com/coderanch/blackjack/Player.java | 35 ++++++++ .../com/coderanch/blackjack/GameTest.java | 52 ++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 core/src/main/java/com/coderanch/blackjack/Game.java create mode 100644 core/src/main/java/com/coderanch/blackjack/HumanPlayer.java create mode 100644 core/src/main/java/com/coderanch/blackjack/Player.java create mode 100644 core/src/test/java/com/coderanch/blackjack/GameTest.java diff --git a/core/src/main/java/com/coderanch/blackjack/Game.java b/core/src/main/java/com/coderanch/blackjack/Game.java new file mode 100644 index 0000000..3c2210d --- /dev/null +++ b/core/src/main/java/com/coderanch/blackjack/Game.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 Coderanch. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.coderanch.blackjack; + +import java.util.List; + +/** + * A game of Blackjack. + */ +public final class Game { + + + /** + * Possible Blackjack game choices. + */ + public enum CHOICE { + HIT, PASS + } + + /** + * Represents whether the game has finished or not. + */ + private boolean isFinished; + + /** + * Players in the game. + */ + private final List players; + + /** + * Constructs a new game of Blackjack. + * + * @param players players in the game. + */ + public Game(List players) { + throw new UnsupportedOperationException(); + } + + /** + * Run the game. + * Deal cards to the players + */ + public void run() { + throw new UnsupportedOperationException(); + } + + /** + * Start the next turn in the game. + * Ask each player their next move. + */ + public void nextTurn() { + throw new UnsupportedOperationException(); + } + + private CHOICE getChoice(Player player) { + throw new UnsupportedOperationException(); + } + + /** + * Getter for players. + * + * @return players + */ + public List players() { + throw new UnsupportedOperationException(); + } + + /** + * Getter for isFinished. + * + * @return if the game is finished. + */ + public boolean isFinished() { + return isFinished; + } + +} diff --git a/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java b/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java new file mode 100644 index 0000000..5e5e626 --- /dev/null +++ b/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2018 Coderanch. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.coderanch.blackjack; + +/** + * A player that is controlled by human input. + */ +public final class HumanPlayer implements Player { + @Override + public Hand hand() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isFixed() { + throw new UnsupportedOperationException(); + } + + @Override + public Game.CHOICE askChoice() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/com/coderanch/blackjack/Player.java b/core/src/main/java/com/coderanch/blackjack/Player.java new file mode 100644 index 0000000..034d5a7 --- /dev/null +++ b/core/src/main/java/com/coderanch/blackjack/Player.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2018 Coderanch. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.coderanch.blackjack; + +/** + * Represents a player in the game of Blackjack. + * Has a hand and can give choices for the game. + */ +public interface Player { + /** + * Get the hand of the player. + * + * @return the hand of the player. + */ + Hand hand(); + + /** + * Gets whether the player has passed or gone bust. + * + * @return whether the player can play or not. + */ + boolean isFixed(); + + /** + * Asks the player for their response and returns it. + * + * @return the players choice. + */ + Game.CHOICE askChoice(); +} diff --git a/core/src/test/java/com/coderanch/blackjack/GameTest.java b/core/src/test/java/com/coderanch/blackjack/GameTest.java new file mode 100644 index 0000000..3e05cb4 --- /dev/null +++ b/core/src/test/java/com/coderanch/blackjack/GameTest.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Coderanch. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.coderanch.blackjack; + +import org.junit.Test; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + + +public class GameTest { + + /** + * Tests whether the players get a hand of cards. + */ + @Test + public void run() { + var game = new Game(List.of(new HumanPlayer())); + game.run(); + game.players().forEach(p -> { + assertThat("Has players with hands", p.hand(), is(notNullValue())); + }); + } + + /** + * Tests whether the game get's new choices from the players. + */ + @Test + public void nextTurn() { + var game = new Game(List.of(new HumanPlayer())); + game.run(); + game.nextTurn(); + + } + + /** + * Tests if the game will return the players in the game. + */ + @Test + public void players() { + var game = new Game(List.of(new HumanPlayer())); + assertThat("The player list isn't null", game.players(), is(notNullValue())); + } +} From 57364c18a9a33dfcccd5a8e7b46bd912da4748d5 Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Tue, 23 Mar 2021 08:58:02 +0900 Subject: [PATCH 19/20] add isFinished test --- .../com/coderanch/blackjack/GameTest.java | 64 +++++++++++++++++-- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/core/src/test/java/com/coderanch/blackjack/GameTest.java b/core/src/test/java/com/coderanch/blackjack/GameTest.java index 3e05cb4..657a706 100644 --- a/core/src/test/java/com/coderanch/blackjack/GameTest.java +++ b/core/src/test/java/com/coderanch/blackjack/GameTest.java @@ -12,11 +12,12 @@ import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.*; -public class GameTest { +public final class GameTest { + + private static final int LOWEST_POSSIBLE_START_SCORE = 3; /** * Tests whether the players get a hand of cards. @@ -26,19 +27,43 @@ public void run() { var game = new Game(List.of(new HumanPlayer())); game.run(); game.players().forEach(p -> { - assertThat("Has players with hands", p.hand(), is(notNullValue())); + assertThat("The player must have a hand.", p.hand(), is(notNullValue())); + assertThat( + "The player must have cards in the hand.", + p.hand().bestScore(), + is(greaterThan(LOWEST_POSSIBLE_START_SCORE))); }); } /** - * Tests whether the game get's new choices from the players. + * Tests whether the game gets new choices from the players. */ @Test public void nextTurn() { - var game = new Game(List.of(new HumanPlayer())); + var testPlayer = new TestPlayer(); + var game = new Game(List.of(testPlayer)); game.run(); game.nextTurn(); + assertThat( + "The player must have chosen more than once.", + ((TestPlayer) game.players().get(0)).choiceCount, + is(greaterThan(0)) + ); + } + /** + * Tests whether the game finishes. + */ + @Test + public void isFinished() { + var testPlayer = new TestPlayer(); + var game = new Game(List.of(testPlayer)); + assertThat("The game must not be finished.", game.isFinished(), is(equalTo(false))); + game.run(); + while (!game.players().stream().allMatch(Player::isFixed)) { + game.nextTurn(); + } + assertThat("The game must be finished.", game.isFinished(), is(equalTo(true))); } /** @@ -47,6 +72,31 @@ public void nextTurn() { @Test public void players() { var game = new Game(List.of(new HumanPlayer())); - assertThat("The player list isn't null", game.players(), is(notNullValue())); + assertThat("The player list must not be null", game.players(), is(notNullValue())); + assertThat("The player list must not be empty", game.players().size(), is(greaterThan(0))); + } + + private static final class TestPlayer implements Player { + + /** + * How many times the player chose. + */ + private int choiceCount; + + @Override + public Hand hand() { + return null; + } + + @Override + public boolean isFixed() { + return false; + } + + @Override + public Game.CHOICE askChoice() { + choiceCount++; + return Game.CHOICE.HIT; + } } } From 778d36ea0ef670ad958488cf3075ca21a6b03190 Mon Sep 17 00:00:00 2001 From: Alex <29205589+aquino-a@users.noreply.github.com> Date: Tue, 23 Mar 2021 10:22:18 +0900 Subject: [PATCH 20/20] add setters for Player --- .../com/coderanch/blackjack/HumanPlayer.java | 10 +++++++ .../java/com/coderanch/blackjack/Player.java | 16 ++++++++++ .../com/coderanch/blackjack/GameTest.java | 29 +++++++++++++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java b/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java index 5e5e626..3f088a4 100644 --- a/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java +++ b/core/src/main/java/com/coderanch/blackjack/HumanPlayer.java @@ -16,11 +16,21 @@ public Hand hand() { throw new UnsupportedOperationException(); } + @Override + public void hand(Hand hand) { + throw new UnsupportedOperationException(); + } + @Override public boolean isFixed() { throw new UnsupportedOperationException(); } + @Override + public void isFixed(boolean isFixed) { + throw new UnsupportedOperationException(); + } + @Override public Game.CHOICE askChoice() { throw new UnsupportedOperationException(); diff --git a/core/src/main/java/com/coderanch/blackjack/Player.java b/core/src/main/java/com/coderanch/blackjack/Player.java index 034d5a7..c3949f7 100644 --- a/core/src/main/java/com/coderanch/blackjack/Player.java +++ b/core/src/main/java/com/coderanch/blackjack/Player.java @@ -12,6 +12,7 @@ * Has a hand and can give choices for the game. */ public interface Player { + /** * Get the hand of the player. * @@ -19,6 +20,13 @@ public interface Player { */ Hand hand(); + /** + * Set the hand of the player. + * + * @param hand the new hand of the player. + */ + void hand(Hand hand); + /** * Gets whether the player has passed or gone bust. * @@ -26,6 +34,14 @@ public interface Player { */ boolean isFixed(); + /** + * Fix the player. + * The player cannot hit or pass anymore. + * + * @param isFixed whether the player is fixed or not. + */ + void isFixed(boolean isFixed); + /** * Asks the player for their response and returns it. * diff --git a/core/src/test/java/com/coderanch/blackjack/GameTest.java b/core/src/test/java/com/coderanch/blackjack/GameTest.java index 657a706..7826fe6 100644 --- a/core/src/test/java/com/coderanch/blackjack/GameTest.java +++ b/core/src/test/java/com/coderanch/blackjack/GameTest.java @@ -17,7 +17,10 @@ public final class GameTest { - private static final int LOWEST_POSSIBLE_START_SCORE = 3; + /** + * The minimum value for a Blackjack hand. (2 {@link com.coderanch.blackjack.Card.Rank#TWO}) + */ + private static final int LOWEST_POSSIBLE_START_SCORE = 4; /** * Tests whether the players get a hand of cards. @@ -31,7 +34,7 @@ public void run() { assertThat( "The player must have cards in the hand.", p.hand().bestScore(), - is(greaterThan(LOWEST_POSSIBLE_START_SCORE))); + is(greaterThanOrEqualTo(LOWEST_POSSIBLE_START_SCORE))); }); } @@ -83,11 +86,27 @@ private static final class TestPlayer implements Player { */ private int choiceCount; + /** + * The hand of the player. + */ + private Hand hand; + + /** + * Represents whether the player is fixed or not. + */ + private boolean isFixed; + @Override public Hand hand() { return null; } + @Override + @SuppressWarnings("checkstyle:hiddenfield") + public void hand(Hand hand) { + this.hand = hand; + } + @Override public boolean isFixed() { return false; @@ -98,5 +117,11 @@ public Game.CHOICE askChoice() { choiceCount++; return Game.CHOICE.HIT; } + + @Override + @SuppressWarnings("checkstyle:hiddenfield") + public void isFixed(boolean isFixed) { + this.isFixed = isFixed; + } } }