import 'package:flutter/material.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:minehunter/models/activity/cell.dart'; import 'package:minehunter/models/activity/activity.dart'; import 'package:minehunter/models/settings/settings_activity.dart'; import 'package:minehunter/models/settings/settings_global.dart'; import 'package:minehunter/models/types.dart'; part 'activity_state.dart'; class ActivityCubit extends HydratedCubit<ActivityState> { ActivityCubit() : super(ActivityState( currentActivity: Activity.createEmpty(), )); void updateState(Activity activity) { emit(ActivityState( currentActivity: activity, )); } void refresh() { final Activity activity = Activity( // Settings activitySettings: state.currentActivity.activitySettings, globalSettings: state.currentActivity.globalSettings, // State isRunning: state.currentActivity.isRunning, isStarted: state.currentActivity.isStarted, isFinished: state.currentActivity.isFinished, animationInProgress: state.currentActivity.animationInProgress, // Base data board: state.currentActivity.board, sizeHorizontal: state.currentActivity.sizeHorizontal, sizeVertical: state.currentActivity.sizeVertical, isBoardMined: state.currentActivity.isBoardMined, // Game data minesCount: state.currentActivity.minesCount, reportMode: state.currentActivity.reportMode, gameWin: state.currentActivity.gameWin, gameFail: state.currentActivity.gameFail, ); // game.dump(); updateState(activity); } void startNewActivity({ required ActivitySettings activitySettings, required GlobalSettings globalSettings, }) { final Activity newActivity = Activity.createNew( // Settings activitySettings: activitySettings, globalSettings: globalSettings, ); newActivity.dump(); updateState(newActivity); refresh(); } void quitActivity() { state.currentActivity.isRunning = false; refresh(); } void resumeSavedActivity() { state.currentActivity.isRunning = true; refresh(); } void deleteSavedActivity() { state.currentActivity.isRunning = false; state.currentActivity.isFinished = true; refresh(); } void updateBoard(Board board) { state.currentActivity.board = board; refresh(); } void addMines({ required int forbiddenRow, required int forbiddenCol, }) { state.currentActivity.isStarted = true; state.currentActivity.addMines( forbiddenRow: forbiddenRow, forbiddenCol: forbiddenCol, ); state.currentActivity.isBoardMined = true; refresh(); } void reportCell({ required int row, required int col, }) { if (!state.currentActivity.board[row][col].isExplored) { toggleCellMark(row, col); } } void walkOnCell({ required int row, required int col, }) { setCellAsExplored(row, col); if (state.currentActivity.board[row][col].minesCountAround == 0) { final List<List<int>> safeCells = getAllSafeCellsAround(row, col); for (int safeCellIndex = 0; safeCellIndex < safeCells.length; safeCellIndex++) { final int safeCellRow = safeCells[safeCellIndex][0]; final int safeCellCol = safeCells[safeCellIndex][1]; if (!state.currentActivity.board[safeCellRow][safeCellCol].isExplored) { walkOnCell( row: safeCellRow, col: safeCellCol, ); } } } } List<List<int>> getAllSafeCellsAround(int row, int col) { final List<List<Cell>> board = state.currentActivity.board; final int sizeHorizontal = board.length; final int sizeVertical = board[0].length; final List<List<int>> safeCellsCoordinates = []; if (board[row][col].minesCountAround == 0) { for (int deltaRow = -1; deltaRow <= 1; deltaRow++) { for (int deltaCol = -1; deltaCol <= 1; deltaCol++) { final int candidateRow = row + deltaRow; final int candidateCol = col + deltaCol; if ((candidateRow >= 0 && candidateRow < sizeVertical) && (candidateCol >= 0 && candidateCol < sizeHorizontal) && !board[candidateRow][candidateCol].isExplored) { safeCellsCoordinates.add([candidateRow, candidateCol]); } } } } return safeCellsCoordinates; } void setCellAsExplored(int row, int col) { state.currentActivity.board[row][col].isExplored = true; state.currentActivity.board[row][col].isMarked = false; // Boom? if (state.currentActivity.board[row][col].isMined) { // Boom! state.currentActivity.board[row][col].isExploded = true; } refresh(); } void toggleCellMark(int row, int col) { state.currentActivity.board[row][col].isMarked = !state.currentActivity.board[row][col].isMarked; refresh(); } void updateReportMode(bool reportMode) { state.currentActivity.reportMode = reportMode; refresh(); } bool checkGameIsFinished() { final Activity currentActivity = state.currentActivity; final Board board = currentActivity.board; final int sizeHorizontal = board.length; final int sizeVertical = board[0].length; currentActivity.printGrid(); updateGameWin(false); updateGameFail(false); for (int row = 0; row < sizeVertical; row++) { for (int col = 0; col < sizeHorizontal; col++) { // Walked on a mine if (board[row][col].isExploded == true) { updateGameFail(true); return true; } } } for (int row = 0; row < sizeVertical; row++) { for (int col = 0; col < sizeHorizontal; col++) { if ( // Mine not already found (board[row][col].isMined == true && board[row][col].isMarked == false) || // Safe cell marked as mined (board[row][col].isMined == false && board[row][col].isMarked == true)) { return false; } } } printlog('-> ok all mines found!'); updateGameWin(true); return true; } void updateGameWin(bool value) { state.currentActivity.gameWin = value; if (true == value) { state.currentActivity.isFinished = true; } refresh(); } void updateGameFail(bool value) { state.currentActivity.gameFail = value; if (true == value) { state.currentActivity.isFinished = true; } refresh(); } void updateAnimationInProgress(bool animationInProgress) { state.currentActivity.animationInProgress = animationInProgress; refresh(); } void setAnimatedBackground(List animatedCellsPattern) { for (int row = 0; row < state.currentActivity.sizeVertical; row++) { for (int col = 0; col < state.currentActivity.sizeHorizontal; col++) { state.currentActivity.board[row][col].isAnimated = animatedCellsPattern[row][col]; } } refresh(); } void resetAnimatedBackground() { for (int row = 0; row < state.currentActivity.sizeVertical; row++) { for (int col = 0; col < state.currentActivity.sizeHorizontal; col++) { state.currentActivity.board[row][col].isAnimated = false; } } } @override ActivityState? fromJson(Map<String, dynamic> json) { final Activity currentActivity = json['currentActivity'] as Activity; return ActivityState( currentActivity: currentActivity, ); } @override Map<String, dynamic>? toJson(ActivityState state) { return <String, dynamic>{ 'currentActivity': state.currentActivity.toJson(), }; } }