From 8aab5f6c8c095b955521c60ea861b39d90bb93ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr> Date: Thu, 21 Jul 2022 14:54:46 +0200 Subject: [PATCH] Auto save current game and allow resume saved game --- android/gradle.properties | 4 +- assets/icons/button_delete_saved_game.png | Bin 0 -> 5813 bytes assets/icons/button_resume_game.png | Bin 0 -> 3659 bytes assets/icons/button_start.png | Bin 3999 -> 3666 bytes .../metadata/android/en-US/changelogs/31.txt | 1 + .../metadata/android/fr-FR/changelogs/31.txt | 1 + icons/build_game_icons.sh | 2 + icons/button_delete_saved_game.svg | 2 + icons/button_resume_game.svg | 2 + icons/button_start.svg | 2 +- lib/layout/game.dart | 2 +- lib/layout/parameters.dart | 40 +++++++++- lib/provider/data.dart | 69 ++++++++++++++++++ lib/screens/home.dart | 2 +- lib/utils/board_utils.dart | 37 +++++++++- lib/utils/game_utils.dart | 33 ++++++++- 16 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 assets/icons/button_delete_saved_game.png create mode 100644 assets/icons/button_resume_game.png create mode 100644 fastlane/metadata/android/en-US/changelogs/31.txt create mode 100644 fastlane/metadata/android/fr-FR/changelogs/31.txt create mode 100644 icons/button_delete_saved_game.svg create mode 100644 icons/button_resume_game.svg diff --git a/android/gradle.properties b/android/gradle.properties index 803b8f0..32d7d3c 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.1.9 -app.versionCode=30 +app.versionName=0.1.10 +app.versionCode=31 diff --git a/assets/icons/button_delete_saved_game.png b/assets/icons/button_delete_saved_game.png new file mode 100644 index 0000000000000000000000000000000000000000..5e4f217689b11e444b7163557d7e5d68f3bbfe7d GIT binary patch literal 5813 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 literal 0 HcmV?d00001 diff --git a/assets/icons/button_resume_game.png b/assets/icons/button_resume_game.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ea0a02d05e42377eb551a4b51428b511a32f5d GIT binary patch literal 3659 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 literal 0 HcmV?d00001 diff --git a/assets/icons/button_start.png b/assets/icons/button_start.png index 6845e2f5c21598ab61f1684d2075aeec0334bf23..f0ead9744e59874fa15d70d7e5e49336a15009dd 100644 GIT binary patch delta 3551 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 GLK6UlU0Oi^ delta 3898 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 X^R>?7);`6+z`)??>gTe~DWM4f`qcPy diff --git a/fastlane/metadata/android/en-US/changelogs/31.txt b/fastlane/metadata/android/en-US/changelogs/31.txt new file mode 100644 index 0000000..36b3521 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/31.txt @@ -0,0 +1 @@ +Autosave current game, allow to continue current game on home screen diff --git a/fastlane/metadata/android/fr-FR/changelogs/31.txt b/fastlane/metadata/android/fr-FR/changelogs/31.txt new file mode 100644 index 0000000..f655421 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/31.txt @@ -0,0 +1 @@ +Sauvegarde automatique de la partie en cours, proposition de continuer sur l'écran d'accueil diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh index 9e88ceb..ea87901 100755 --- a/icons/build_game_icons.sh +++ b/icons/build_game_icons.sh @@ -18,6 +18,8 @@ ICON_SIZE=192 AVAILABLE_GAME_IMAGES=" button_back button_start + button_resume_game + button_delete_saved_game game_fail game_win placeholder diff --git a/icons/button_delete_saved_game.svg b/icons/button_delete_saved_game.svg new file mode 100644 index 0000000..ac7eefe --- /dev/null +++ b/icons/button_delete_saved_game.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#ee7d49" stroke="#fff" stroke-width=".238"/><path d="m61.07 35.601-1.7399 27.837c-0.13442 2.1535-1.9205 3.8312-4.0781 3.8312h-16.84c-2.1576 0-3.9437-1.6777-4.0781-3.8312l-1.7399-27.837h-2.6176c-0.84621 0-1.5323-0.68613-1.5323-1.5323 0-0.84655 0.68613-1.5323 1.5323-1.5323h33.711c0.84621 0 1.5323 0.68578 1.5323 1.5323 0 0.84621-0.68613 1.5323-1.5323 1.5323zm-3.2617 0h-21.953l1.4715 26.674c0.05985 1.0829 0.95531 1.9305 2.0403 1.9305h14.929c1.085 0 1.9804-0.84757 2.0403-1.9305zm-10.977 3.0647c0.78977 0 1.4301 0.6403 1.4301 1.4301v19.614c0 0.78977-0.6403 1.4301-1.4301 1.4301s-1.4301-0.6403-1.4301-1.4301v-19.614c0-0.78977 0.6403-1.4301 1.4301-1.4301zm-6.1293 0c0.80004 0 1.4588 0.62935 1.495 1.4286l0.89647 19.719c0.03182 0.70016-0.50998 1.2933-1.2101 1.3255-0.01915 7.02e-4 -0.03831 1e-3 -0.05781 1e-3 -0.74462 0-1.3596-0.58215-1.4003-1.3261l-1.0757-19.719c-0.0407-0.74701 0.53188-1.3852 1.2786-1.4259 0.02462-0.0014 0.04926-2e-3 0.07388-2e-3zm12.259 0c0.74804 0 1.3541 0.60609 1.3541 1.3541 0 0.02462-3.28e-4 0.04926-0.0017 0.07388l-1.0703 19.618c-0.04379 0.80106-0.70597 1.4281-1.5081 1.4281-0.74804 0-1.3541-0.60609-1.3541-1.3541 0-0.02462 3.49e-4 -0.04925 0.0017-0.07388l1.0703-19.618c0.04379-0.80106 0.70597-1.4281 1.5081-1.4281zm-10.216-12.259h8.1728c2.2567 0 4.086 1.8293 4.086 4.086v2.0433h-16.344v-2.0433c0-2.2567 1.8293-4.086 4.086-4.086zm0.20453 3.0647c-0.67725 0-1.2259 0.54863-1.2259 1.2259v1.8388h10.215v-1.8388c0-0.67725-0.54863-1.2259-1.2259-1.2259z" fill="#fff" fill-rule="evenodd" stroke="#bd4812" stroke-width=".75383"/></svg> diff --git a/icons/button_resume_game.svg b/icons/button_resume_game.svg new file mode 100644 index 0000000..6ad8b64 --- /dev/null +++ b/icons/button_resume_game.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m39.211 31.236c-0.84086-0.84489-2.9911-0.84489-2.9911 0v34.329c0 0.84594 2.1554 0.84594 2.9993 0l28.178-15.637c0.84392-0.84086 0.85812-2.2091 0.01623-3.053z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="6.1726"/><path d="m40.355 33.714c-0.71948-0.72294-2.5594-0.72294-2.5594 0v29.373c0 0.72383 1.8442 0.72383 2.5663 0l24.11-13.38c0.7221-0.71948 0.73426-1.8902 0.01389-2.6124z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.225"/><path d="m28.369 66.919v-37.591" fill="#105ca2" stroke="#105ca2" stroke-linecap="round" stroke-width="4.0337"/></svg> diff --git a/icons/button_start.svg b/icons/button_start.svg index e9d49d2..633a634 100644 --- a/icons/button_start.svg +++ b/icons/button_start.svg @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="UTF-8"?> -<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><path d="m34.852 25.44c-1.1248-1.1302-4.0012-1.1302-4.0012 0v45.921c0 1.1316 2.8832 1.1316 4.0121 0l37.693-20.918c1.1289-1.1248 1.1479-2.9551 0.02171-4.084z" fill="#fefeff" stroke="#105ca1" stroke-linecap="round" stroke-linejoin="round" stroke-width="8.257"/><path d="m36.382 28.754c-0.96243-0.96706-3.4236-0.96706-3.4236 0v39.292c0 0.96825 2.467 0.96825 3.4329 0l32.252-17.898c0.96594-0.96243 0.9822-2.5285 0.01858-3.4945z" fill="#fefeff" stroke="#feffff" stroke-linecap="round" stroke-linejoin="round" stroke-width="4.314"/></svg> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect x=".44662" y=".89101" width="92.772" height="91.894" ry="11.689" fill="#49a1ee" stroke="#fff" stroke-width=".238"/><g transform="matrix(.8268 0 0 .8268 9.0269 8.3829)" fill="#fefeff" stroke-linecap="round" stroke-linejoin="round"><path d="m34.852 25.44c-1.1248-1.1302-4.0012-1.1302-4.0012 0v45.921c0 1.1316 2.8832 1.1316 4.0121 0l37.693-20.918c1.1289-1.1248 1.1479-2.9551 0.02171-4.084z" stroke="#105ca1" stroke-width="8.257"/><path d="m36.382 28.754c-0.96243-0.96706-3.4236-0.96706-3.4236 0v39.292c0 0.96825 2.467 0.96825 3.4329 0l32.252-17.898c0.96594-0.96243 0.9822-2.5285 0.01858-3.4945z" stroke="#feffff" stroke-width="4.314"/></g></svg> diff --git a/lib/layout/game.dart b/lib/layout/game.dart index 49573e1..7ed0df0 100644 --- a/lib/layout/game.dart +++ b/lib/layout/game.dart @@ -92,7 +92,7 @@ class Game { fit: BoxFit.fill, ), ), - onPressed: () => GameUtils.resetGame(myProvider), + onPressed: () => GameUtils.quitGame(myProvider), ); } diff --git a/lib/layout/parameters.dart b/lib/layout/parameters.dart index f7a868c..2f329d4 100644 --- a/lib/layout/parameters.dart +++ b/lib/layout/parameters.dart @@ -23,6 +23,11 @@ class Parameters { lines.add(SizedBox(height: separatorHeight)); } + myProvider.loadCurrentSavedState(); + Widget buttonsBlock = myProvider.hasCurrentSavedState() + ? buildResumeGameButton(myProvider) + : buildStartNewGameButton(myProvider); + return Container( child: Column( mainAxisAlignment: MainAxisAlignment.start, @@ -38,7 +43,7 @@ class Parameters { ), SizedBox(height: separatorHeight), Container( - child: buildStartNewGameButton(myProvider), + child: buttonsBlock, ), ], ), @@ -95,6 +100,39 @@ class Parameters { ); } + static Container buildResumeGameButton(Data myProvider) { + return Container( + margin: EdgeInsets.all(blockMargin), + padding: EdgeInsets.all(blockPadding), + child: Table( + defaultColumnWidth: IntrinsicColumnWidth(), + children: [ + TableRow( + children: [ + Column( + children: [ + TextButton( + child: buildImageContainerWidget('button_delete_saved_game'), + onPressed: () => GameUtils.deleteSavedGame(myProvider), + ), + ], + ), + Column( + children: [ + TextButton( + child: buildImageContainerWidget('button_resume_game'), + onPressed: () => GameUtils.resumeSavedGame(myProvider), + ), + ], + ), + buildDecorationImageWidget(), + ], + ), + ], + ), + ); + } + static Widget buildParameterSelector(Data myProvider, String parameterCode) { List availableValues = myProvider.getParameterAvailableValues(parameterCode); diff --git a/lib/provider/data.dart b/lib/provider/data.dart index 96d5dbc..2fd520e 100644 --- a/lib/provider/data.dart +++ b/lib/provider/data.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -40,6 +42,7 @@ class Data extends ChangeNotifier { bool _reportMode = false; bool _gameWin = false; bool _gameFail = false; + String _currentState = ''; void updateParameterLevel(String parameterLevel) { _parameterLevel = parameterLevel; @@ -128,6 +131,69 @@ class Data extends ChangeNotifier { setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault); } + String get currentState => _currentState; + + String computeCurrentGameState() { + String cellsValues = ''; + for (var rowIndex = 0; rowIndex < _cells.length; rowIndex++) { + for (var colIndex = 0; colIndex < _cells[rowIndex].length; colIndex++) { + cellsValues += _cells[rowIndex][colIndex].isMined ? 'X' : ' '; + cellsValues += _cells[rowIndex][colIndex].isExplored ? 'E' : ' '; + cellsValues += _cells[rowIndex][colIndex].isMarked ? 'P' : ' '; + cellsValues += _cells[rowIndex][colIndex].isExploded ? '*' : ' '; + cellsValues += _cells[rowIndex][colIndex].minesCountAround.toString(); + cellsValues += ';'; + } + } + + var currentState = { + 'level': _parameterLevel, + 'size': _parameterSize, + 'skin': _parameterSkin, + 'board': cellsValues, + }; + + return json.encode(currentState); + } + + void saveCurrentGameState() async { + if (_gameIsRunning) { + _currentState = computeCurrentGameState(); + + final prefs = await SharedPreferences.getInstance(); + prefs.setString('savedState', _currentState); + } else { + resetCurrentSavedState(); + } + } + + void resetCurrentSavedState() async { + _currentState = ''; + + final prefs = await SharedPreferences.getInstance(); + prefs.setString('savedState', _currentState); + notifyListeners(); + } + + void loadCurrentSavedState() async { + final prefs = await SharedPreferences.getInstance(); + _currentState = prefs.getString('savedState') ?? ''; + } + + bool hasCurrentSavedState() { + return (_currentState != ''); + } + + Map<String, dynamic> getCurrentSavedState() { + if (_currentState != '') { + Map<String, dynamic> savedState = json.decode(_currentState); + if (savedState.isNotEmpty) { + return savedState; + } + } + return {}; + } + bool get gameIsRunning => _gameIsRunning; void updateGameIsRunning(bool gameIsRunning) { _gameIsRunning = gameIsRunning; @@ -162,11 +228,14 @@ class Data extends ChangeNotifier { _cells[row][col].isExploded = true; } + saveCurrentGameState(); notifyListeners(); } void toggleCellMark(int row, int col) { _cells[row][col].isMarked = !_cells[row][col].isMarked; + + saveCurrentGameState(); notifyListeners(); } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 18986be..39bb1f0 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -79,7 +79,7 @@ class _HomeState extends State<Home> { ), ), onPressed: () => toast('Long press to quit game...'), - onLongPress: () => GameUtils.resetGame(myProvider), + onLongPress: () => GameUtils.quitGame(myProvider), ), ]; } diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart index a82c965..42437b1 100644 --- a/lib/utils/board_utils.dart +++ b/lib/utils/board_utils.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:minehunter/entities/cell.dart'; import 'package:minehunter/provider/data.dart'; @@ -98,7 +100,7 @@ class BoardUtils { int sizeHorizontal = myProvider.sizeHorizontal; int sizeVertical = myProvider.sizeVertical; - // Shuffle cells to put random mines, expect on currently selected one + // Shuffle cells to put random mines, except on currently selected one List allowedCells = []; for (var row = 0; row < sizeVertical; row++) { for (var col = 0; col < sizeHorizontal; col++) { @@ -126,6 +128,39 @@ class BoardUtils { return cells; } + static List createBoardFromSavedState(Data myProvider, String savedBoard) { + List<List<Cell?>> board = []; + int boardSize = pow((savedBoard.length / 6), 1 / 2).round(); + String boardSizeAsString = boardSize.toString() + 'x' + boardSize.toString(); + myProvider.updateParameterSize(boardSizeAsString); + + int index = 0; + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + List<Cell?> row = []; + for (var colIndex = 0; colIndex < boardSize; colIndex++) { + bool isMined = (savedBoard[index++] == 'X'); + bool isExplored = (savedBoard[index++] == 'E'); + bool isMarked = (savedBoard[index++] == 'P'); + bool isExploded = (savedBoard[index++] == '*'); + int minesCountAround = int.parse(savedBoard[index++]); + index++; // ";" + + Cell cell = Cell(isMined); + cell.isExplored = isExplored; + cell.isMarked = isMarked; + cell.isExploded = isExploded; + cell.minesCountAround = minesCountAround; + + row.add(cell); + } + board.add(row); + } + + printGrid(board); + + return board; + } + static void reportCell(Data myProvider, int row, int col) { if (!myProvider.cells[row][col].isExplored) { myProvider.toggleCellMark(row, col); diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart index 02de51a..107cff4 100644 --- a/lib/utils/game_utils.dart +++ b/lib/utils/game_utils.dart @@ -3,8 +3,11 @@ import 'package:minehunter/utils/board_animate.dart'; import 'package:minehunter/utils/board_utils.dart'; class GameUtils { - static void resetGame(Data myProvider) { + static Future<void> quitGame(Data myProvider) async { myProvider.updateGameIsRunning(false); + if (BoardUtils.checkGameIsFinished(myProvider)) { + myProvider.resetCurrentSavedState(); + } } static void startNewGame(Data myProvider) { @@ -16,4 +19,32 @@ class GameUtils { BoardUtils.createInitialEmptyBoard(myProvider); BoardAnimate.startAnimation(myProvider, 'start'); } + + static void deleteSavedGame(Data myProvider) { + myProvider.resetCurrentSavedState(); + } + + static void resumeSavedGame(Data myProvider) { + Map<String, dynamic> savedState = myProvider.getCurrentSavedState(); + if (savedState.isNotEmpty) { + try { + myProvider.setParameterValue('level', savedState['level']); + myProvider.setParameterValue('size', savedState['size']); + myProvider.setParameterValue('skin', savedState['skin']); + + myProvider.updateCells( + BoardUtils.createBoardFromSavedState(myProvider, savedState['board'])); + myProvider.updateGameIsRunning(true); + } catch (e) { + print('Failed to resume game. Will start new one instead.'); + myProvider.resetCurrentSavedState(); + myProvider.initParametersValues(); + startNewGame(myProvider); + } + } else { + myProvider.resetCurrentSavedState(); + myProvider.initParametersValues(); + startNewGame(myProvider); + } + } } -- GitLab