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.