Reversi をつくろう！ - 1

# 趣旨
今まで学んだことを簡単なゲームを作りながら整理していく。

- コードは [github](https://github.com/t-sone/reversi) に公開する。
- githubのissue単位でTDD方式で開発を進める。
- 開発した内容を整理して記事に投稿していく。

## 開発する前に
可読性を意識してコーディングすること。

なぜなら開発には多くの人が関わるため、他人に理解しやすいものを提供する必要がある。
理想は英語でWikipediaを書く感覚でコーディングする。


# 初期構築
ひとまずはSpring BootでjUnitを使って開発する。

1. ローカル環境に [git clone](https://github.com/t-sone/reversi.git) でリポジトリを取得する。
1. Springの[サイト](https://start.spring.io/)で必要な情報をダウンロードする。
    - Project: Gradle Project
    - Language: Java(11)
    - Spring Boot: 2.5.0(M3)
1. `./gradlew build` コマンドでSpring Bootの環境を構築する。

以上。


# リバーシの初期状態の盤面を取得する
まずはここから。単純に初期状態の盤面を取得するところまで開発する。

## モデルを抽出する
必要なモデルを手っ取り早く「[オセロ（ボードゲーム） - Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%82%BB%E3%83%AD_(%E3%83%9C%E3%83%BC%E3%83%89%E3%82%B2%E3%83%BC%E3%83%A0))」から抽出する。

必要なモデルを抽出したら、それを英語に翻訳する。
（英語だとどう表現しているのか[Reversi - Wikipedia](https://en.wikipedia.org/wiki/Reversi) から抽出する）

- 盤: board
    - 位置 : pieces
        - 横: horizontal (a - h)
        - 縦: vertical (1 - 8)
- 石: disk
    - 色: color
        - 白: light
        - 黒: dark
- 初期状態の盤面: starting position
    - クロス: normal version
    - パラレル: （英語版に説明なし。なのでこのローカルルールは削除）
    - オリジナル: historical version

どうやら、初期リバーシは空の状態から中央４マスに黒から配置するバージョンがあるらしい。

### 対応ブランチを作成する
対応する内容を把握したら Issue を起票し、対応ブランチを作成する。

https://github.com/t-sone/reversi/issues/1

- 対応ブランチ: [get_start_position](https://github.com/t-sone/reversi/tree/get_start_position)

## TDD方式で開発する
以下の手順で開発を進めていく。

- テストケースを作成する。
- テストケースが正しく動作するように実装する。
- リファクタリングを行う。

### テストケースを作成する
テストケースは以下を想定。

- クロスの場合は中央4マスに石を配置（白黒をクロス）
- オリジナルの場合は配置なし。

今回のテストは初期状態がクロスかオリジナルかの２パターンなので`@ParameterizedTest`で作成。
初期状態の指定で盤の情報が期待値となるかどうかをチェックする。

```java:StartPositionService.java

    @Autowired
    StartPositionService service;

    Stream<Arguments> 初期状態の盤面一覧() {
        return Stream.of(arguments(StartPosition.historical,
                                   "[]"),
                         arguments(StartPosition.normal,
                                   "[{light:(d, 4)}, {dark:(e, 4)}, {dark: (d, 5)}, {light: (e, 5)}]")
        );
    }

    @ParameterizedTest
    @MethodSource("初期状態の盤面一覧")
    public void 初期状態の盤面を取得する(
            StartPosition startPosition,
            String 期待する盤面) {
        Board board = service.get(startPosition);
        assertThat(board.toString(), is(期待する盤面));
    }

```

コミットした状態は[こちら](https://github.com/t-sone/reversi/commit/a4dc2077cc6e4c9ea82673bbb8f2552134730621)

### テストケースが正しく動作するように実装する

#### Boardモデルの構成を作成する
必要なモデルはすでに抽出済みなので、それを元にBoardの構成を作成する。
今回は以下のようになった。

![class1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113636/3d58aa8c-5622-c3bc-c88e-938e6815605a.png)

#### サービス層には業務ロジックのみ
サービス層は業務ロジックのみコーディングするように徹底する。
不要な情報を削ぎ落とすことで可読性が上がり、調査や機能改修のスピードが向上する。

ここでは `クロス形式の盤かオリジナル形式の盤を取得する` 以外不要。
削ぎ落とした情報はモデル層の `BoardFactory` に任せてしまう。

```java:StartPositionService.java

@Service
public class StartPositionService {

    final BoardFactory factory;

    public Board get(StartPosition startPosition) {
        return factory.create(startPosition);
    }

```


コミットした状態は[こちら](https://github.com/t-sone/reversi/pull/2/commits/884e69025244d4dd8ca341462de6354db783ddf1)

### リファクタリング
テストが正常に動くようになったタイミングで、リファクタリングを行う。
今回の場合、`StartPositionService.get`が初期状態サービスとよくわからないネーミングだったので、`BoardService.start`にリネーム。

```java:BoardService.java
@Service
public class BoardService {

    final BoardFactory factory;

    public Board start(StartPosition startPosition) {
        return factory.create(startPosition);
    }
```

コミットした状態は[こちら](https://github.com/t-sone/reversi/commit/b39b79392bbca18079d9708bdbcfdbfda27a98db)

# 最後に
今回はここまで。

最終的にこういうクラス構成になりました。
![refactored_class.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113636/f1cc2f7d-5735-896b-3770-ee2d82d8f973.png)


とりあえず、github ＋ TDD で開発する際の雰囲気だけでも伝わっていると幸いです。
最終的に普通にオンライン対戦でリバーシで遊べる。。といいなぁ。。

[次の記事＞](https://qiita.com/toranekos/items/4efae75c0c81432848a1)