Skip to content

Instantly share code, notes, and snippets.

@DavidIAm
Created February 18, 2016 19:22
Show Gist options
  • Save DavidIAm/e535b2795e0cf7400cf1 to your computer and use it in GitHub Desktop.
Save DavidIAm/e535b2795e0cf7400cf1 to your computer and use it in GitHub Desktop.
Yatzy Kata for your review
package testpackage;
// This enum represents the sides on a six sided die.
// A die face can only be one of these six values.
// Not five.
// Not seven.
// Eight is right out.
public enum Face {
ONE, TWO, THREE, FOUR, FIVE, SIX;
public Integer value() {
return this.ordinal() + 1;
}
}
package testpackage;
// This is a list of faces. It is very simple
// It is probably overkill but I was exploring how to create a type that looks externally less parameterized.
// This looks nicer from outside the class with less parameterizations
import java.util.ArrayList;
import java.util.List;
public class FaceList {
private List<Face> faceList = new ArrayList<>();
public Face get(Integer index) {
return faceList.get(index);
}
public void add(Face index) {
faceList.add(index);
}
public Integer size() {
return faceList.size();
}
}
package testpackage;
// This enum is the limit of the types of scoring we are doing in this game.
// If its not on this list, you can't score it that way.
public enum Scoring {
YATZY, PAIR, TWOPAIR, THREEOFAKIND, FOUROFAKIND, SMALLSTRAIGHT, LARGESTRAIGHT, FULLHOUSE;
}
package testpackage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// This represents a throw of five dice
// You must construct it with five <Face> parameters.
// Their order doesn't matter.
public class Throw {
public List<Face> faces = new ArrayList<>();
Throw(Face a, Face b, Face c, Face d, Face e) {
this.faces.add(a);
this.faces.add(b);
this.faces.add(c);
this.faces.add(d);
this.faces.add(e);
}
// This provides a map of Face => count
// for the throw represented by this object
// as it is rather important to figure out the groupings and how large they are
public Map<Face, Integer> mapCounts() {
Map<Face, Integer> faceCounts = new HashMap<>();// <Face, Integer>();
for (Face thisFace : this.faces) {
int count;
count = faceCounts.getOrDefault(thisFace, 0) + 1;
faceCounts.put(thisFace, count);
}
return faceCounts;
}
}
package testpackage;
import java.util.Map;
// The Yatzy class provides the answer method which scores the result.
public class Yatzy {
// This uses a case swi]tch to select the various elements of the throw
public static int answer(Throw dice, Scoring scoreType) {
Map<Face, Integer> faceCounts = dice.mapCounts();
switch (scoreType) {
case YATZY:
if (faceCounts.keySet().size() == 1) {
return 50;
}
break;
case PAIR:
Face maxDie;
maxDie = Face.ONE;
boolean paired;
paired = false;
// I think the line below looks as bad as perl with all the < > and such.
for (Map.Entry<Face, Integer> countEntry : faceCounts.entrySet()) {
if (countEntry.getValue() == 2) {
paired = true;
if (countEntry.getKey().value() >= maxDie.value()) {
maxDie = countEntry.getKey();
}
}
}
if (paired) {
return (maxDie.value()) * 2; // twice the plus one of the
// value? bleh.
}
break;
case TWOPAIR:
FaceList faceList = new FaceList();
for (Map.Entry<Face, Integer> countEntry : faceCounts.entrySet()) {
if (countEntry.getValue() == 2) {
faceList.add(countEntry.getKey());
}
}
if (faceList.size() == 2) {
return ((faceList.get(0).value()) * 2 + (faceList.get(1).value()) * 2);
}
break;
case THREEOFAKIND:
for (Map.Entry<Face, Integer> countEntry : faceCounts.entrySet()) {
if (countEntry.getValue() == 3) {
return ((countEntry.getKey().value()) * 3);
}
}
break;
case FOUROFAKIND:
for (Map.Entry<Face, Integer> countEntry : faceCounts.entrySet()) {
if (countEntry.getValue() == 4) {
return ((countEntry.getKey().value()) * 4);
}
}
break;
case SMALLSTRAIGHT:
if (faceCounts.containsKey(Face.TWO) && faceCounts.containsKey(Face.THREE) && faceCounts.containsKey(
Face.FOUR)) {
if (faceCounts.containsKey(Face.ONE)) {
return 15;
} else {
return 0;
}
}
break;
case LARGESTRAIGHT:
if (faceCounts.containsKey(Face.TWO) && faceCounts.containsKey(Face.THREE) && faceCounts.containsKey(
Face.FOUR)) {
if (faceCounts.containsKey(Face.SIX)) {
return 20;
} else {
return 0;
}
}
break;
case FULLHOUSE:
if (faceCounts.entrySet().size() == 2) {
for (Integer oneofthem : faceCounts.values()) {
if (oneofthem == 1 || oneofthem == 4) {
return 0; // shortcut! Must have been a 4/1 split.
}
}
// Streams are fun. It lets me map like perl.
// map-reduce is fun. sum is short for reduce(a+b)
return dice.faces.stream().mapToInt(b -> b.value()).sum();
}
break;
}
return 0;
}
}
package testpackage;
import org.junit.*;
import static org.junit.Assert.*;
public class YatzyTest {
@Test
public void yatzy_scoring_ones() {
assertEquals(50, Yatzy.answer(new Throw(Face.ONE, Face.ONE, Face.ONE, Face.ONE, Face.ONE), Scoring.YATZY));
// 1,1,1,1,1 scores 50 as yatzy
}
@Test
public void yatzy_scoring_fives() {
assertEquals(50, Yatzy.answer(new Throw(Face.FIVE, Face.FIVE, Face.FIVE, Face.FIVE, Face.FIVE), Scoring.YATZY));
// 5,5,5,5,5 scores 50 as yatzy
}
@Test
public void yatzy_scoring_four() {
assertEquals(0, Yatzy.answer(new Throw(Face.FIVE, Face.FIVE, Face.FIVE, Face.FIVE, Face.ONE), Scoring.YATZY));
// 5,5,5,5,1 scores 0 as yatzy
}
@Test
public void yatzy_scoring_full_house() {
assertEquals(0, Yatzy.answer(new Throw(Face.THREE, Face.THREE, Face.THREE, Face.FOUR, Face.FOUR),
Scoring.YATZY));
// 3,3,3,4,4 scores 8 (4+4) as yatzy
}
@Test
public void pair_scoring_two_pair() {
assertEquals(12, Yatzy.answer(new Throw(Face.ONE, Face.ONE, Face.SIX, Face.TWO, Face.SIX), Scoring.PAIR));
// 1,1,6,2,6 scores 12 (6+6) as pair
}
@Test
public void pair_scoring_three() {
assertEquals(0, Yatzy.answer(new Throw(Face.THREE, Face.THREE, Face.THREE, Face.FOUR, Face.ONE), Scoring.PAIR));
// 3,3,3,4,1 scores 0 as pair
}
@Test
public void pair_scoring_four() {
assertEquals(0, Yatzy.answer(new Throw(Face.THREE, Face.THREE, Face.THREE, Face.THREE, Face.ONE),
Scoring.PAIR));
// 3,3,3,3,1 scores 0 as pair
}
@Test
public void two_pair_scoring_two_pair() {
assertEquals(8, Yatzy.answer(new Throw(Face.ONE, Face.ONE, Face.TWO, Face.THREE, Face.THREE), Scoring.TWOPAIR));
// 1,1,2,3,3 scores 8 (1+1+3+3) as two pair
}
@Test
public void two_pair_scoring_one_pair() {
assertEquals(0, Yatzy.answer(new Throw(Face.ONE, Face.ONE, Face.TWO, Face.THREE, Face.FOUR), Scoring.TWOPAIR));
// 1,1,2,3,4 scores 0 as two pair
}
@Test
public void two_pair_scoring_full_hosue() {
assertEquals(0, Yatzy.answer(new Throw(Face.ONE, Face.ONE, Face.TWO, Face.TWO, Face.TWO), Scoring.TWOPAIR));
// 1,1,2,2,2 scores 0 as two pair
}
@Test
public void three_of_kind_scoring_three() {
assertEquals(9, Yatzy.answer(new Throw(Face.THREE, Face.THREE, Face.THREE, Face.FOUR, Face.FIVE),
Scoring.THREEOFAKIND));
// 3,3,3,4,5 scores 9 (3+3+3) as three of a kind
}
@Test
public void three_of_kind_scoring_pair() {
assertEquals(0, Yatzy.answer(new Throw(Face.THREE, Face.THREE, Face.FOUR, Face.FIVE, Face.SIX),
Scoring.THREEOFAKIND));
// 3,3,4,5,6 scores 0 as three of a kind
}
@Test
public void three_of_kind_scoring_four() {
assertEquals(0, Yatzy.answer(new Throw(Face.THREE, Face.THREE, Face.THREE, Face.THREE, Face.ONE),
Scoring.THREEOFAKIND));
// 3,3,3,3,1 scores 0 as three of a kind
}
@Test
public void four_of_kind_scoring_four_twos() {
assertEquals(8, Yatzy.answer(new Throw(Face.TWO, Face.TWO, Face.TWO, Face.TWO, Face.FIVE),
Scoring.FOUROFAKIND));
// 2,2,2,2,5 scores 8 (2+2+2+2) as four of a kind
}
@Test
public void four_of_kind_scoring_full_house() {
assertEquals(0, Yatzy.answer(new Throw(Face.TWO, Face.TWO, Face.TWO, Face.FIVE, Face.FIVE),
Scoring.FOUROFAKIND));
// 2,2,2,5,5 scores 0 as four of a kind
}
@Test
public void four_of_kind_scoring_five_twos() {
assertEquals(0, Yatzy.answer(new Throw(Face.TWO, Face.TWO, Face.TWO, Face.TWO, Face.TWO), Scoring.FOUROFAKIND));
// 2,2,2,2,2 scores 0 as four of a kind
}
@Test
public void small_straight_scoring_small() {
assertEquals(15, Yatzy.answer(new Throw(Face.ONE, Face.TWO, Face.THREE, Face.FOUR, Face.FIVE),
Scoring.SMALLSTRAIGHT));
// 1,2,3,4,5 scores 15 as small straight
}
@Test
public void large_straight_scoring_large() {
assertEquals(20, Yatzy.answer(new Throw(Face.TWO, Face.THREE, Face.FOUR, Face.FIVE, Face.SIX),
Scoring.LARGESTRAIGHT));
// 2,3,4,5,6 scores 20 as a large straight
}
@Test
public void full_house_scoring_full_house() {
assertEquals(8, Yatzy.answer(new Throw(Face.ONE, Face.ONE, Face.TWO, Face.TWO, Face.TWO), Scoring.FULLHOUSE));
// 1,1,2,2,2 scores 8 (1+1+2+2+2) as a full house
}
@Test
public void full_house_scoring_two_pair() {
assertEquals(0, Yatzy.answer(new Throw(Face.TWO, Face.TWO, Face.THREE, Face.THREE, Face.FOUR),
Scoring.FULLHOUSE));
// 2,2,3,3,4 scores 0 as a full house
}
@Test
public void full_house_scoring_yatzy() {
assertEquals(0, Yatzy.answer(new Throw(Face.FOUR, Face.FOUR, Face.FOUR, Face.FOUR, Face.FOUR),
Scoring.FULLHOUSE));
// 4,4,4,4,4 scores 0 as a full house
}
}
@btforsythe
Copy link

To add on to Chris's polymorphism comments, you could have something like a ThrowFactory, that looks something like this (I'm leaving out visibility modifiers, etc):

class ThrowFactory {
  Throw create(Face.... faces) {
    Throw created; 
    // = ... (your implementation -- figure out type to return based on faces)
    return created;
  }
}
interface Throw {
  Score score();
}

Then implementations of Throw for each of your combinations: Pair, TwoPair, ThreeOfAKind, etc. Guessing Pair would look something like this:

class Pair implements Throw {
  private final Face of; 
  Pair(Face of) {
    this.of = of; // 'this' necessary here
  }
  Score score() {
    return new Score(of.value * 2);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment