#Kata – The bowling game

The bowling game jest kolejną propozycją ćwiczenia, która zostało mocno spopularyzowana przez Wujka Boba. Związana jest ona z grą w kręgle. W tym ćwiczeniu bardzo ciekawym elementem jest system naliczania punktów. W bardzo dobry sposób sprawdza tok algorytmicznego myślenia.

Jak napisałem we wstępie, ćwiczenie to dotyczy gry w kręgle (ang. bowling game). Od klienta dostajemy kilka wymagań:

  • Jeśli wszystkie twoje rzuty (ang. roll/throw) były chybione to wynik końcowy wynosi zero
  • W każdym rzucie został strącony tylko jeden kręgiel (ang. pin). Wynik końcowy wynosi 20.
  • W pierwszej turze (ang. frame) został trafiony spare, następnie zostały strącone trzy kręgle. Wynik końcowy wynosi 16.
  • W pierwszej turze został trafiony strike, następnie zostały strącone trzy i cztery kręgle. Reszta rzutów była chybiona. Wynik końcowy wynosi 24.
  • Wszystkie rzuty to strikes. Wynik końcowy wynosi 300.

Podczas gry w kręgle obowiązują pewne zasady:

  • Jest dziesięć tur
  • Jest dziesięć kręgli
  • W każdej turze możemy wykonać dwa rzuty
  • Jeśli w jednej turze w wyników dwóch rzutów osiągniemy wynik 10 to do tej tury zostanie doliczony wynik kolejnego rzutu – spare
  • Jeśli w jednej turze w jednym rzucie osiągniemy wynik 10 to do tej tury zostanie doliczony wynik kolejnych dwóch rzutów – strike

Moja propozycja Javowa:

public class BowlingGameTest {

    BowlingGame game = new BowlingGame();

    //Should return zero as a score when all rolls are missed

    @Test
    public void shouldReturnZeroWhenAllRollsAreMissed() throws Exception {
        // When
        IntStream.range(0,20).forEach(roll->game.roll(0));
        int score = game.getScore();
        // Then
        assertThat(score).isEqualTo(0);
    }

    //Should return 20 as a score when you knock down one pin per roll

    @Test
    public void shouldReturnTwentyWhenKnockDownOnePinPerRoll() throws Exception {
        // When
        IntStream.range(0,20).forEach(roll->game.roll(1));
        int score = game.getScore();
        // Then
        assertThat(score).isEqualTo(20);

    }

    //Should return 16 as a score when you knock down spare in first frame followed by three

    @Test
    public void shouldReturnSixTeenWhenSpareIsKnockDown() throws Exception {
        // When
        game.roll(5);
        game.roll(5);
        game.roll(3);
        IntStream.range(0,17).forEach(roll->game.roll(0));
        int score = game.getScore();
        // Then
        assertThat(score).isEqualTo(16);
    }

    //Should return 24 as a score when you knock down strike in first frame followed by three and four

    @Test
    public void shouldReturnTwentyFourWhenStrikeIsKnockDown() throws Exception {
        // When
        game.roll(10);
        game.roll(3);
        game.roll(4);
        IntStream.range(0,17).forEach(roll->game.roll(0));
        int score = game.getScore();
        // Then
        assertThat(score).isEqualTo(24);
    }

    //Should return 300 as a score when you knock down all strikes

    @Test
    public void shouldReturnThreeHundredsWhenAllStrikesAreKnockDown() throws Exception {
        // When
        IntStream.range(0,20).forEach(roll->game.roll(10));
        int score = game.getScore();
        // Then
        assertThat(score).isEqualTo(300);
    }

    private class BowlingGame {

        int[] rolls = new int[21];
        int roll = 0;

        int index = 0;

        int getScore() {
            int score = 0;
            for(int frame = 0; frame<10; frame++){
                if(isStrike()) { 
                    score += 10 + rolls[index+1] + rolls[index+2];
                    index++;
                }
                else if(isSpare()){ 
                    score += 10 + rolls[index+2];
                    index+=2;
                } else{
                    score+=rolls[index] + rolls[index+1];
                    index+=2;
                }
            }
            return score;
        }

        private boolean isSpare() {
            return rolls[index] + rolls[index+1] == 10;
        }

        private boolean isStrike() {
            return rolls[index] == 10;
        }

        void roll(int pins) {
            rolls[roll++] = pins;
        }
    }

Kod jak zawsze w całości na GitHubie.