Skip to content

Commit

Permalink
[minesweeper] first click
Browse files Browse the repository at this point in the history
  • Loading branch information
liplum committed May 19, 2024
1 parent 8793839 commit 16aece4
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 24 deletions.
12 changes: 5 additions & 7 deletions lib/game/minesweeper/entity/board.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,10 @@ class CellBoard extends ICellBoard<Cell> {
required int rows,
required int columns,
required int mines,
required int rowExclude,
required int columnExclude,
required ({int row, int column}) firstClick,
}) {
final builder = CellBoardBuilder.generate(rows: rows, columns: columns);
builder.randomMines(mines: mines, rowFirstClick: rowExclude, columnFirstClick: columnExclude);
builder.randomMines(mines: mines, firstClick: firstClick);
return builder.build();
}

Expand Down Expand Up @@ -286,15 +285,14 @@ class CellBoardBuilder extends ICellBoard<CellBuilder> {

void randomMines({
required int mines,
required int rowFirstClick,
required int columnFirstClick,
required ({int row, int column}) firstClick,
}) {
final rand = Random();
final candidates = List.generate(rows * columns, (index) => (row: index ~/ columns, column: index % columns));
// Clicked cell and one-cell nearby cells can't be mines.
for (final (dx, dy) in CellBoard.nearbyDeltaAndThis) {
final row = rowFirstClick + dx;
final column = columnFirstClick + dy;
final row = firstClick.row + dx;
final column = firstClick.column + dy;
candidates.remove((row: row, column: column));
}
final maxMines = candidates.length - 1;
Expand Down
2 changes: 2 additions & 0 deletions lib/game/minesweeper/entity/save.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ class SaveMinesweeper {
final List2D<Cell4Save> cells;
final Duration playtime;
final GameModeMinesweeper mode;
final ({int row, int column}) firstClick;

const SaveMinesweeper({
required this.cells,
this.playtime = Duration.zero,
this.mode = GameModeMinesweeper.easy,
required this.firstClick,
});

Map<String, dynamic> toJson() => _$SaveMinesweeperToJson(this);
Expand Down
17 changes: 17 additions & 0 deletions lib/game/minesweeper/entity/save.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions lib/game/minesweeper/entity/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,17 @@ class GameStateMinesweeper {
board: board,
playtime: save.playtime,
status: GameStatus.running,
firstClick: save.firstClick,
);
}

SaveMinesweeper toSave() {
assert(firstClick != null, "save before first click");
return SaveMinesweeper(
cells: board.toSave(),
playtime: playtime,
mode: mode,
firstClick: firstClick!,
);
}
}
28 changes: 12 additions & 16 deletions lib/game/minesweeper/manager/logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {

void fromSave(SaveMinesweeper save) {
state = GameStateMinesweeper.fromSave(save);
final firstClick = state.firstClick;
if (state.status == GameStatus.idle && firstClick != null) {
dig(cell: state.board.getCell(row: firstClick.row, column: firstClick.column));
}
final firstClick = save.firstClick;
dig(cell: state.board.getCell(row: firstClick.row, column: firstClick.column));
}

Duration get playtime => state.playtime;
Expand All @@ -59,17 +57,17 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
void dig({required Cell cell}) {
assert(state.status != GameStatus.gameOver && state.status != GameStatus.victory, "Game is already over");
// Generating mines on first dig
if (state.status == GameStatus.idle) {
if (state.status == GameStatus.idle && state.firstClick == null) {
final mode = state.mode;
final firstClick = (row: cell.row, column: cell.column);
state = state.copyWith(
firstClick: (row: cell.row, column: cell.column),
firstClick: firstClick,
status: GameStatus.running,
board: CellBoard.withMines(
rows: mode.gameRows,
columns: mode.gameColumns,
mines: mode.gameMines,
rowExclude: cell.row,
columnExclude: cell.column,
firstClick: firstClick,
),
);
}
Expand All @@ -84,8 +82,6 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
onVictory();
}
}
} else {
assert(false, "$cell");
}
}

Expand Down Expand Up @@ -136,7 +132,7 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
}

bool digAroundBesidesFlagged({required Cell cell}) {
if(!state.status.canPlay) return false;
if (!state.status.canPlay) return false;
bool digAny = false;
if (state.board.countAroundByState(cell: cell, state: CellState.flag) >= cell.minesAround) {
for (final neighbor in state.board.iterateAround(row: cell.row, column: cell.column)) {
Expand All @@ -150,7 +146,7 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
}

bool flagRestCovered({required Cell cell}) {
if(!state.status.canPlay) return false;
if (!state.status.canPlay) return false;
bool flagAny = false;
final coveredCount = state.board.countAroundByState(cell: cell, state: CellState.covered);
if (coveredCount == 0) return false;
Expand Down Expand Up @@ -183,7 +179,7 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
}

void toggleFlag({required Cell cell}) {
if(!state.status.canPlay) return;
if (!state.status.canPlay) return;
if (cell.state == CellState.flag) {
_changeCell(cell: cell, state: CellState.covered);
} else if (cell.state == CellState.covered) {
Expand All @@ -194,7 +190,7 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
}

void flag({required Cell cell}) {
if(!state.status.canPlay) return;
if (!state.status.canPlay) return;
if (cell.state == CellState.covered) {
_changeCell(cell: cell, state: CellState.flag);
} else {
Expand All @@ -203,7 +199,7 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
}

void removeFlag({required Cell cell}) {
if(!state.status.canPlay) return;
if (!state.status.canPlay) return;
if (cell.state == CellState.flag) {
_changeCell(cell: cell, state: CellState.covered);
} else {
Expand All @@ -212,7 +208,7 @@ class GameLogic extends StateNotifier<GameStateMinesweeper> {
}

Future<void> save() async {
if (state.status.shouldSave) {
if (state.status.shouldSave && state.firstClick != null) {
await StorageMinesweeper.save.save(state.toSave());
} else {
await StorageMinesweeper.save.delete();
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,4 @@ msix_config:
protocol_activation: sit-life, life.mysit
execution_alias: sitlife
output_name: SIT-Life
enable_at_startup: true
enable_at_startup: false

0 comments on commit 16aece4

Please sign in to comment.