On se retrouve aujourd'hui pour la solution du précédent #KataOfTheWeek proposé par Damien en début de semaine !

La solution complète et testée de Ron Jeffries en C# ici. Il décrit tout son processus d'analyse et de TDD, c'est hyper intéressant.

Ma solution perso en Java, avec le parsing de la ligne de score:

import java.util.ArrayList;
import java.util.List;

interface Frame {

    int getScore();

}

enum Roll {

    STRIKE('X', 10),
    SPARE('/', 10),
    NINE('9', 9),
    EIGHT('8', 8),
    SEVEN('7', 7),
    SIX('6', 6),
    FIVE('5', 5),
    FOUR('4', 4),
    THREE('3', 3),
    TWO('2', 2),
    ONE('1', 1),
    MISS('-', 0);

    private final char output;
    private final int points;

    Roll(char output, int points) {
        this.output = output;
        this.points = points;
    }

    public int getPoints() {
        return points;
    }

    public static Roll of(char c) {
        return Arrays.stream(values())
                .filter(it -> it.output == c)
                .findAny()
                .orElse(null);
    }

}

final class SimpleFrame implements Frame {

    private final Roll first;
    private final Roll second;

    public SimpleFrame(Roll first, Roll second) {
        this.first = first;
        this.second = second;
    }

    @Override
    public int getScore() {
        return second.getPoints();
    }

}

final class Spare implements Frame {

    private final Roll first;
    private final Roll third;

    public Spare(Roll first, Roll third) {
        this.first = first;
        this.third = third;
    }

    @Override
    public int getScore() {
        return Roll.SPARE.getPoints() + third.getPoints();
    }

}

final class Strike implements Frame {

    private final Roll second;
    private final Roll third;

    public Strike(Roll second, Roll third) {
        this.second = second;
        this.third = third;
    }

    private int pointsFromNextFrame() {
        if (second == Roll.STRIKE) return second.getPoints() + third.getPoints();
        return third.getPoints();
    }

    @Override
    public int getScore() {
        return Roll.STRIKE.getPoints() + pointsFromNextFrame();
    }

}

final class Strings {

    static String skip(String s, int skip) {
        return s.substring(skip);
    }

    private Strings() {

    }

}

public final class Line {

    private static final int NUMBER_OF_FRAMES = 10;

    public static Line parse(String line) {
        line = line.replace(" ", "");
        List<Frame> frames = new ArrayList<>();

        int remainingFrames = NUMBER_OF_FRAMES;

        while (remainingFrames > 0) {
            Roll first = Roll.of(line.charAt(0));
            Roll second = Roll.of(line.charAt(1));

            if (first == Roll.STRIKE) {
                frames.add(new Strike(second, Roll.of(line.charAt(2))));
                line = Strings.skip(line, 1);
            } else if (second == Roll.SPARE) {
                frames.add(new Spare(first, Roll.of(line.charAt(2))));
                line = Strings.skip(line, 2);
            } else {
                frames.add(new SimpleFrame(first, second));
                line = Strings.skip(line, 2);
            }

            remainingFrames--;
        }

        return new Line(frames);
    }

    private final List<Frame> frames;

    public Line(List<Frame> frames) {
        this.frames = frames;
    }

    public int getScore() {
        return frames.stream()
                .mapToInt(Frame::getScore)
                .sum();
    }

}

Et les tests:

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class LineTest {

    @Test
    public void getPoints_of_full_miss_should_return_0() {
        assertThat(Line.parse("-- -- -- -- -- -- -- -- -- --").getScore()).isEqualTo(0);
    }

    @Test
    public void getPoints_of_full_miss_spare_should_return_0() {
        assertThat(Line.parse("-/ -/ -/ -/ -/ -/ -/ -/ -/ -/-").getScore()).isEqualTo(100);
    }

    @Test
    public void getPoints_spare_five_should_return_20() {
        assertThat(Line.parse("-/ 55 -- -- -- -- -- -- -- --").getScore()).isEqualTo(20);
    }

    @Test
    public void getPoints_spare_strike_should_return_30() {
        assertThat(Line.parse("-/ X -- -- -- -- -- -- -- --").getScore()).isEqualTo(30);
    }

    @Test
    public void getPoints_spare_five_at_end_should_return_15() {
        assertThat(Line.parse("-- -- -- -- -- -- -- -- -- -/5").getScore()).isEqualTo(15);
    }

    @Test
    public void getPoints_spare_strike_at_end_should_return_20() {
        assertThat(Line.parse("-- -- -- -- -- -- -- -- -- -/X").getScore()).isEqualTo(20);
    }

    @Test
    public void getPoints_strike_five_five_should_return_20() {
        assertThat(Line.parse("X 55 -- -- -- -- -- -- -- --").getScore()).isEqualTo(20);
    }

    @Test
    public void getPoints_strike_five_eight_should_return_26() {
        assertThat(Line.parse("X 58 -- -- -- -- -- -- -- --").getScore()).isEqualTo(26);
    }

    @Test
    public void getPoints_strike_strike_miss_should_return_30() {
        assertThat(Line.parse("X X -- -- -- -- -- -- -- --").getScore()).isEqualTo(30);
    }

    @Test
    public void getPoints_strike_strike_five_should_return_45() {
        assertThat(Line.parse("X X 55 -- -- -- -- -- -- --").getScore()).isEqualTo(45);
    }

    @Test
    public void getPoints_strike_five_eight_at_end_should_return_18() {
        assertThat(Line.parse("-- -- -- -- -- -- -- -- -- X58").getScore()).isEqualTo(18);
    }

    @Test
    public void getPoints_strike_strike_miss_at_end_should_return_20() {
        assertThat(Line.parse("-- -- -- -- -- -- -- -- -- XX-").getScore()).isEqualTo(20);
    }

    @Test
    public void getPoints_strike_strike_five_at_end_should_return_25() {
        assertThat(Line.parse("-- -- -- -- -- -- -- -- -- XX5").getScore()).isEqualTo(25);
    }

    @Test
    public void getPoints_of_full_strike_should_return_300() {
        assertThat(Line.parse("X X X X X X X X X XXX").getScore()).isEqualTo(300);
    }

}

A bientôt pour un nouveau #KataOfTheWeek !