概要
Flutterでバトルシップパズルを作成する
前回手動で生成していたセルデータを自動生成する
- [Flutterでバトルシップパズルを作成する1]
(https://qiita.com/iharakenji/items/abacc6bc49f7ce11fbbd) - [Flutterでバトルシップパズルを作成する3]
(https://qiita.com/iharakenji/items/ff65749c4b6acd1b57d2)
実行環境
Mac
$ sw_vers
ProductName: macOS
ProductVersion: 11.6
BuildVersion: 20G165
Flutter
$ flutter --version
Flutter 2.5.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 4cc385b4b8 (11 days ago) • 2021-09-07 23:01:49 -0700
Engine • revision f0826da7ef
Tools • Dart 2.14.0
手順
配置する項目を決める
船1(大きさ1, 数4)
船2(大きさ2, 数3)
船3(大きさ3, 数2)
船4(大きさ4, 数1)
波(大きさ1, 数2)
static const ship1 = Ship(1, 4);
static const ship2 = Ship(2, 3);
static const ship3 = Ship(3, 2);
static const ship4 = Ship(4, 1);
static const wave = 2;
セルデータを初期化する
var cellTypes = List.generate(
rows, (_) => List.generate(columns, (_) => CellType.None));
船を配置する
var ships = [ship4, ship3, ship2, ship1];
for (var ship in ships) {
cellTypes = placeShip(cellTypes, ship, rows, columns);
}
波を配置する
cellTypes = placeWave(cellTypes, rows, columns);
アプリの内容
セルデータの作成
static List<List<CellType>> generate(int rows, int columns) {
var cellTypes = List.generate(
rows, (_) => List.generate(columns, (_) => CellType.None));
var ships = [ship4, ship3, ship2, ship1];
for (var ship in ships) {
cellTypes = placeShip(cellTypes, ship, rows, columns);
}
cellTypes = placeWave(cellTypes, rows, columns);
return cellTypes;
}
船を配置する
static List<List<CellType>> placeShip(List<List<CellType>> cellTypes, Ship ship, int rows, int columns) {
for (var numIndex = 0; numIndex < ship.num; numIndex++) {
var positions = buildPositions(cellTypes, ship, rows, columns);
if (positions.isEmpty) {
continue;
}
var position = positions[Random().nextInt(positions.length)];
if (ship.size == 1) {
cellTypes[position.item2][position.item3] = CellType.ShipCircle;
} else if (position.item1 == Direction.Horizontal) {
cellTypes[position.item2][position.item3] = CellType.ShipRoundLeft;
for (var sizeIndex = 1; sizeIndex < ship.size; sizeIndex++) {
cellTypes[position.item2][position.item3 + sizeIndex] =
CellType.ShipSquare;
}
cellTypes[position.item2][position.item3 + ship.size - 1] =
CellType.ShipRoundRight;
} else {
cellTypes[position.item2][position.item3] = CellType.ShipRoundTop;
for (var sizeIndex = 1; sizeIndex < ship.size; sizeIndex++) {
cellTypes[position.item2 + sizeIndex][position.item3] =
CellType.ShipSquare;
}
cellTypes[position.item2 + ship.size - 1][position.item3] =
CellType.ShipRoundBottom;
}
}
return cellTypes;
}
波を配置する
static List<List<CellType>> placeWave(List<List<CellType>> cellTypes, int rows, int columns) {
for (var i = 0; i < wave; i++) {
List<Tuple2<int, int>> positions = [];
for (var row = 0; row < rows; row++) {
for (var column = 0; column < columns; column++) {
if (cellTypes[row][column] == CellType.None) {
positions.add(Tuple2(row, column));
}
}
}
var position = positions[Random().nextInt(positions.length)];
cellTypes[position.item1][position.item2] = CellType.Wave;
}
return cellTypes;
}
波を配置する
static List<Tuple3<Direction, int, int>> buildPositions(List<List<CellType>> cellTypes, Ship ship, int rows, int columns) {
List<Tuple3<Direction, int, int>> positions = [];
for (var row = 0; row < rows; row++) {
for (var column = 0; column < columns; column++) {
if (canExistsHorizontal(
cellTypes, ship, rows, row, columns, column)) {
positions.add(Tuple3(Direction.Horizontal, row, column));
}
if (canExistsVertical(
cellTypes, ship, rows, row, columns, column)) {
positions.add(Tuple3(Direction.Vertical, row, column));
}
}
}
return positions;
}
船が配置できるかチェック(横方向)
static bool canExistsHorizontal(List<List<CellType>> cellTypes, Ship ship,
int rows, int row, int columns, int column) {
if (columns < column + ship.size) {
return false;
}
// 左側チェック
if (0 < column) {
if (0 < row && cellTypes[row - 1][column - 1] != CellType.None) {
return false;
}
if (cellTypes[row][column - 1] != CellType.None) {
return false;
}
if (row + 1 < rows && cellTypes[row + 1][column - 1] != CellType.None) {
return false;
}
}
// 右側チェック
if (column + ship.size < columns) {
if (0 < row && cellTypes[row - 1][column + ship.size] != CellType.None) {
return false;
}
if (cellTypes[row][column + ship.size] != CellType.None) {
return false;
}
if (row + 1 < rows && cellTypes[row + 1][column + ship.size] != CellType.None) {
return false;
}
}
// 真ん中チェック
for (var sizeIndex = 0; sizeIndex < ship.size; sizeIndex++) {
if (0 < row && cellTypes[row - 1][column + sizeIndex] != CellType.None) {
return false;
}
if (cellTypes[row][column + sizeIndex] != CellType.None) {
return false;
}
if (row + 1 < rows &&
cellTypes[row + 1][column + sizeIndex] != CellType.None) {
return false;
}
}
return true;
}
船が配置できるかチェック(縦方向)
static bool canExistsVertical(List<List<CellType>> cellTypes, Ship ship,
int rows, int row, int columns, int column) {
if (rows < row + ship.size) {
return false;
}
// 上側チェック
if (0 < row) {
if (0 < column && cellTypes[row - 1][column - 1] != CellType.None) {
return false;
}
if (cellTypes[row - 1][column] != CellType.None) {
return false;
}
if (column + 1 < columns &&
cellTypes[row - 1][column + 1] != CellType.None) {
return false;
}
}
// 下側チェック
if (row + ship.size < rows) {
if (0 < column && cellTypes[row + ship.size][column - 1] != CellType.None) {
return false;
}
if (cellTypes[row + ship.size][column] != CellType.None) {
return false;
}
if (column + 1 < columns &&
cellTypes[row + ship.size][column + 1] != CellType.None) {
return false;
}
}
// 真ん中チェック
for (var sizeIndex = 0; sizeIndex < ship.size; sizeIndex++) {
if (0 < column &&
cellTypes[row + sizeIndex][column - 1] != CellType.None) {
return false;
}
if (cellTypes[row + sizeIndex][column] != CellType.None) {
return false;
}
if (column + 1 < columns &&
cellTypes[row + sizeIndex][column + 1] != CellType.None) {
return false;
}
}
return true;
}
船(数と大きさ)
class Ship {
final int size, num;
const Ship(this.size, this.num);
}
船の向き
enum Direction {
Vertical,
Horizontal,
}
アプリの実行
アプリを起動
flutter run