Flutterでバトルシップパズルを作成する3

## 概要
Flutterでバトルシップパズルを作成する
縦横サイズ、船の種類と数、波の数を引数で渡せるようにする
表示を改善(クリアメッセージを表示する、下に船の種類と数を表示する)
セルデータを自動生成ロジックの改善

- [Flutterでバトルシップパズルを作成する2]
(https://qiita.com/iharakenji/items/137d02f3d045af3d68ff)
- [Flutterでバトルシップパズルを作成する4]
(https://qiita.com/iharakenji/items/6e0e199cf4ab608c5dc0)

###  実行環境
#### Mac
```sh
$ sw_vers
ProductName:    macOS
ProductVersion: 11.6
BuildVersion:   20G165
```

#### Flutter
```sh
$ flutter --version
Flutter 2.5.1 • channel stable • https://github.com/flutter/flutter.git
Framework • revision ffb2ecea52 (5 days ago) • 2021-09-17 15:26:33 -0400
Engine • revision b3af521a05
Tools • Dart 2.14.2
```

## 修正内容
### 縦横サイズ、船の種類と数、波の数を引数で渡せるようにする
```
class BattleshipApp extends StatelessWidget {
  final int rows, columns, waves;
  late final List<Ship> ships;

  BattleshipApp({this.columns = 10, this.rows = 10, this.waves = 2}) {
    ships = [
      const Ship(4, 1),
      const Ship(3, 2),
      const Ship(2, 3),
      const Ship(1, 4)
    ];
  }

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: const Text('BattleshipPuzzle'),
          ),
          body: _BattleshipHome(rows, columns, ships, waves),
        ),
      );
}
```

### 表示改善
#### 共通部分
```
Widget _buildContainer() => Center(
      child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
        const SizedBox(height: 20),
        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          ElevatedButton(
            onPressed: () => _pushReset(),
            child: const Text('Reset'),
          ),
          const SizedBox(width: 20),
          Text('TapCount: $_tapCount'),
        ]),
        const SizedBox(height: 20),
        SizedBox(
          width: 440,
          height: 440,
          child: Stack(
            children: [
              Center(
                child: Container(
                  child: _buildClearText(),
              ),
              ),
              _buildColumn(),
            ],
          ),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(0, 0, 0, 0),
          child: _buildBottom(),
        ),
      ]),
    );
```

### クリアメッセージを表示する
```
Widget? _buildClearText() {
  if (_remainCount == 0) {
    return Text(
      "Clear!!",
      style: TextStyle(
        fontWeight: FontWeight.bold,
        fontSize: 50.0,
        color: Colors.pink[500],
      ),
    );
  } else {
    return null;
  }
}
```

### 下に船の種類と数を表示する
```
Widget _buildBottom() {
  final column = Column(
      children:
          List.generate(_ships.length, (index) => _buildShips(_ships[index])));
  return SizedBox(
    width: 440,
    height: 160,
    child: column,
  );
}

Widget _buildShips(Ship ship) {
  return Row(
    children: List.generate(ship.num, (index) => _buildShip(ship.size)),
  );
}

Widget _buildShip(int size) {
  if (size == 1) {
    return SizedBox(
    width: 40,
      height: 40,
      child: _buildShipCircle(),
    );
  } else {
    List<Widget> row = [];
    row.add(
      SizedBox(
        width: 40,
        height: 40,
        child: _buildShipRoundLeft(),
      ),
    );
    for (var sizeIndex = 2; sizeIndex < size; sizeIndex++) {
      row.add(
        SizedBox(
          width: 40,
          height: 40,
          child: _buildShipSquare(),
        ),
      );
    }
    row.add(
      SizedBox(
        width: 40,
        height: 40,
        child: _buildShipRoundRight(),
      ),
    );
    return SizedBox(
      width: size * 40,
      height: 40,
      child: Row(children: row),
    );
  }
}
```

### セルデータを自動生成ロジックの改善
#### 船が配置できるかチェック(横方向)
```
bool canExistsHorizontal(
    List<List<CellType>> cellTypes, Ship ship, int row, int column) {
  if (_columns < column + ship.size) {
    return false;
  }
  // 上下チェック
  for (var i = column - 1; i <= column + ship.size; i++) {
    if (i < 0 || _columns <= i) {
      continue;
    }
    if (0 < row && cellTypes[row - 1][i] != CellType.None) {
      return false;
    }
    if (cellTypes[row][i] != CellType.None) {
      return false;
    }
    if (row + 1 < _rows && cellTypes[row + 1][i] != CellType.None) {
      return false;
    }
  }
  return true;
}
```

#### 船が配置できるかチェック(縦方向)
```
bool canExistsVertical(
    List<List<CellType>> cellTypes, Ship ship, int row, int column) {
  if (_rows < row + ship.size) {
    return false;
  }
  // 左右チェック
  for (var i = row - 1; i <= row + ship.size; i++) {
    if (i < 0 || _rows <= i) {
      continue;
    }
    if (0 < column && cellTypes[i][column - 1] != CellType.None) {
      return false;
    }
    if (cellTypes[i][column] != CellType.None) {
      return false;
    }
    if (column + 1 < _columns && cellTypes[i][column + 1] != CellType.None) {
      return false;
    }
  }
  return true;
}
```

## アプリの実行
### アプリを起動
```sh
flutter run
```

### 実行結果
![battleship3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109450/845bafb8-19c9-35b2-9eeb-a51e3b300035.gif)

## GitHub
- https://github.com/iharakenji/battleship

### Webアプリ
- https://battleship-74d4f.web.app/