diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index 7b2f9115a9bd412af67c699117004fdd370b0dfc..e06408205830beb6b8e5c86f52a01b8e20644f53 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/launch_image.png b/android/app/src/main/res/mipmap-hdpi/launch_image.png index 7b2f9115a9bd412af67c699117004fdd370b0dfc..e06408205830beb6b8e5c86f52a01b8e20644f53 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/launch_image.png and b/android/app/src/main/res/mipmap-hdpi/launch_image.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index a8cd7531476f32d181bb840246e414425e86edd0..b54ec718c3d74369a3b9863d698f9348cff22fcb 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/launch_image.png b/android/app/src/main/res/mipmap-mdpi/launch_image.png index a8cd7531476f32d181bb840246e414425e86edd0..b54ec718c3d74369a3b9863d698f9348cff22fcb 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/launch_image.png and b/android/app/src/main/res/mipmap-mdpi/launch_image.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 0f30a0494c2d105d5d8dafa39425c800e8e00f01..2129702d44bd65a022ec7a59ff5e187453d59c8a 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/launch_image.png b/android/app/src/main/res/mipmap-xhdpi/launch_image.png index 0f30a0494c2d105d5d8dafa39425c800e8e00f01..2129702d44bd65a022ec7a59ff5e187453d59c8a 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/launch_image.png and b/android/app/src/main/res/mipmap-xhdpi/launch_image.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 3773eeeef8f29cf1f1303448894b6a473c42116d..12a2adfad532918958782914cc20f2cc24e6909e 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/launch_image.png b/android/app/src/main/res/mipmap-xxhdpi/launch_image.png index 3773eeeef8f29cf1f1303448894b6a473c42116d..12a2adfad532918958782914cc20f2cc24e6909e 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/launch_image.png and b/android/app/src/main/res/mipmap-xxhdpi/launch_image.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 2227e093b294ce65a443702c16c5b20e5b833e24..0ce12a293f8bf4c6eaf3550de76c4f6758d78e03 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/launch_image.png b/android/app/src/main/res/mipmap-xxxhdpi/launch_image.png index 2227e093b294ce65a443702c16c5b20e5b833e24..0ce12a293f8bf4c6eaf3550de76c4f6758d78e03 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/launch_image.png and b/android/app/src/main/res/mipmap-xxxhdpi/launch_image.png differ diff --git a/android/gradle.properties b/android/gradle.properties index bc2d95e8567abcfd41c26ebeb95fced48f43e773..818e87b23b224ced309ae5c147e5ed827826e237 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.0.1 -app.versionCode=1 +app.versionName=0.0.2 +app.versionCode=2 diff --git a/assets/skins/default_empty.png b/assets/skins/default_board.png similarity index 100% rename from assets/skins/default_empty.png rename to assets/skins/default_board.png diff --git a/assets/skins/default_hole.png b/assets/skins/default_hole.png new file mode 100644 index 0000000000000000000000000000000000000000..d6c839077a02b3d3e14593e6d986ce6a844f9190 Binary files /dev/null and b/assets/skins/default_hole.png differ diff --git a/assets/skins/default_peg.png b/assets/skins/default_peg.png new file mode 100644 index 0000000000000000000000000000000000000000..5ba2cc9f0887a67bd4643ef7df647eb809482115 Binary files /dev/null and b/assets/skins/default_peg.png differ diff --git a/fastlane/metadata/android/en-US/changelogs/2.txt b/fastlane/metadata/android/en-US/changelogs/2.txt new file mode 100644 index 0000000000000000000000000000000000000000..57e563eca79a526fa6986226ae4e802a84e72a7e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/2.txt @@ -0,0 +1 @@ +Create minimal playable game \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/images/icon.png b/fastlane/metadata/android/en-US/images/icon.png index bc10e1eae9d63710ab0f26b439b493557e938c00..5ee834dd7c8c76e334fecf70b10fece0f9d0d185 100644 Binary files a/fastlane/metadata/android/en-US/images/icon.png and b/fastlane/metadata/android/en-US/images/icon.png differ diff --git a/fastlane/metadata/android/fr-FR/changelogs/2.txt b/fastlane/metadata/android/fr-FR/changelogs/2.txt new file mode 100644 index 0000000000000000000000000000000000000000..35f530831b09bef219422e8ab9a11edb20600b62 --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/2.txt @@ -0,0 +1 @@ +Création du jeu minimal jouable \ No newline at end of file diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh index e0233acb66be46c189858591517645f46ca30d4c..aef865033c1d8e95a8b2bc654752c85ea9826e04 100755 --- a/icons/build_game_icons.sh +++ b/icons/build_game_icons.sh @@ -36,7 +36,9 @@ AVAILABLE_SKINS=" # Images per skin SKIN_IMAGES=" - empty + board + hole + peg " ####################################################### diff --git a/icons/icon.svg b/icons/icon.svg index a4609114d8f74aff540032078873cc124c7b6633..f215ae5833909c330705d8c78cdadef35a4ca975 100644 --- a/icons/icon.svg +++ b/icons/icon.svg @@ -75,50 +75,38 @@ </g> <path d="m24.549 1119.5c0.66325 0 1.1979-0.5346 1.1979-1.1979v-0.3333c0 0.6632-0.53461 1.1978-1.1979 1.1978h-20.354c-0.66325 0-1.1979-0.5346-1.1979-1.1978v0.3333c0 0.6633 0.53461 1.1979 1.1979 1.1979z" fill="#1a237e" opacity=".2"/> </g> - <g transform="translate(.059003 -.23356)" fill="#fffffe" stroke="#fffffd" stroke-linecap="round" stroke-linejoin="round" stroke-width=".66394"> - <ellipse cx="12.739" cy="8.1361" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="8.1361" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="8.1361" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="9.7538" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="9.7538" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="9.7538" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="11.372" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="11.372" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="11.372" rx=".32" ry=".32"/> - <ellipse cx="9.5883" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="8.0129" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="19.041" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="20.616" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="11.164" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="17.465" cy="12.989" rx=".32" ry=".32"/> - <ellipse cx="9.5883" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="8.0129" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="19.041" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="20.616" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="11.164" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="17.465" cy="14.607" rx=".32" ry=".32"/> - <ellipse cx="9.5883" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="8.0129" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="19.041" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="20.616" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="11.164" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="17.465" cy="16.225" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="17.843" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="17.843" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="17.843" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="21.078" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="21.078" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="21.078" rx=".32" ry=".32"/> - <ellipse cx="12.739" cy="19.46" rx=".32" ry=".32"/> - <ellipse cx="14.314" cy="19.46" rx=".32" ry=".32"/> - <ellipse cx="15.89" cy="19.46" rx=".32" ry=".32"/> + <g transform="matrix(1.3242 0 0 1.3242 -4.6596 -4.6595)" fill="#fffffe" stroke="#fffffd" stroke-linecap="round" stroke-linejoin="round" stroke-width=".66394"> + <circle cx="12.798" cy="9.5202" r=".32"/> + <circle cx="14.373" cy="9.5202" r=".32"/> + <circle cx="15.949" cy="9.5202" r=".32"/> + <circle cx="12.798" cy="11.138" r=".32"/> + <circle cx="14.373" cy="11.138" r=".32"/> + <circle cx="15.949" cy="11.138" r=".32"/> + <circle cx="9.6473" cy="12.755" r=".32"/> + <circle cx="19.1" cy="12.755" r=".32"/> + <circle cx="11.223" cy="12.755" r=".32"/> + <circle cx="12.798" cy="12.755" r=".32"/> + <circle cx="14.373" cy="12.755" r=".32"/> + <circle cx="15.949" cy="12.755" r=".32"/> + <circle cx="17.524" cy="12.755" r=".32"/> + <circle cx="9.6473" cy="14.373" r=".32"/> + <circle cx="19.1" cy="14.373" r=".32"/> + <circle cx="11.223" cy="14.373" r=".32"/> + <circle cx="12.798" cy="14.373" r=".32"/> + <circle cx="15.949" cy="14.373" r=".32"/> + <circle cx="17.524" cy="14.373" r=".32"/> + <circle cx="9.6473" cy="15.991" r=".32"/> + <circle cx="19.1" cy="15.991" r=".32"/> + <circle cx="11.223" cy="15.991" r=".32"/> + <circle cx="12.798" cy="15.991" r=".32"/> + <circle cx="14.373" cy="15.991" r=".32"/> + <circle cx="15.949" cy="15.991" r=".32"/> + <circle cx="17.524" cy="15.991" r=".32"/> + <circle cx="12.798" cy="17.609" r=".32"/> + <circle cx="14.373" cy="17.609" r=".32"/> + <circle cx="15.949" cy="17.609" r=".32"/> + <circle cx="12.798" cy="19.226" r=".32"/> + <circle cx="14.373" cy="19.226" r=".32"/> + <circle cx="15.949" cy="19.226" r=".32"/> </g> </svg> diff --git a/icons/skins/default/empty.svg b/icons/skins/default/board.svg similarity index 100% rename from icons/skins/default/empty.svg rename to icons/skins/default/board.svg diff --git a/icons/skins/default/hole.svg b/icons/skins/default/hole.svg new file mode 100644 index 0000000000000000000000000000000000000000..0a7a1add764c2db361052df50d08f22ce97fa7c3 --- /dev/null +++ b/icons/skins/default/hole.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><rect width="100" height="100" fill="#c5c5c5" stroke="#505050" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.6"/><circle cx="50" cy="50" r="12.853" fill="#333" stroke="#797979" stroke-linecap="round" stroke-linejoin="round" stroke-width=".914"/></svg> diff --git a/icons/skins/default/peg.svg b/icons/skins/default/peg.svg new file mode 100644 index 0000000000000000000000000000000000000000..d7e72c5f2fee9b4d91424ad13c4b7d363f728e5c --- /dev/null +++ b/icons/skins/default/peg.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 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" ry="2" fill="none"/><circle cx="50" cy="50" r="28.385" fill="#b98212" stroke="#745517" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.0228"/></svg> diff --git a/lib/entities/tile.dart b/lib/entities/tile.dart new file mode 100644 index 0000000000000000000000000000000000000000..75b694bc51bd2576ccd31109610315323ee00d09 --- /dev/null +++ b/lib/entities/tile.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:solitaire_game/provider/data.dart'; +import 'package:solitaire_game/utils/game_utils.dart'; + +class Tile { + int currentRow; + int currentCol; + bool hasPeg; + + Tile( + this.currentRow, + this.currentCol, + this.hasPeg, + ); + + Widget render(Data myProvider) { + List<Widget> stack = [ + this.hole(myProvider), + ]; + + if (this.hasPeg) { + stack.add(this.draggable(myProvider)); + } + + return Stack( + alignment: Alignment.center, + children: stack, + ); + } + + Widget hole(Data myProvider) { + String image = 'assets/skins/' + myProvider.parameterSkin + '_hole.png'; + + return DragTarget<List<int>>( + builder: ( + BuildContext context, + List<dynamic> accepted, + List<dynamic> rejected, + ) { + return Container( + child: Image( + image: AssetImage(image), + width: myProvider.tileSize, + height: myProvider.tileSize, + fit: BoxFit.fill, + ), + ); + }, + onAccept: (List<int> source) { + List<int> target = [this.currentCol, this.currentRow]; + // print('(drag) Pick from ' + source.toString() + ' and drop on ' + target.toString()); + if (GameUtils.isMoveAllowed(myProvider, source, target)) { + GameUtils.move(myProvider, source, target); + } + }, + ); + } + + Widget draggable(Data myProvider) { + return Draggable<List<int>>( + data: [this.currentCol, this.currentRow], + + // Widget when draggable is stationary + child: this.peg(myProvider), + + // Widget when draggable is being dragged + feedback: this.peg(myProvider), + + // Widget to display on original place when being dragged + childWhenDragging: Container(), + ); + } + + Widget peg(Data myProvider) { + String image = 'assets/skins/' + myProvider.parameterSkin + '_peg.png'; + + return Image( + image: AssetImage(image), + width: myProvider.tileSize, + height: myProvider.tileSize, + fit: BoxFit.fill, + ); + } +} diff --git a/lib/layout/board.dart b/lib/layout/board.dart index e10ed6100fd859b72ce72d1ca8ecd01157d3fb42..1843cc9906f48768a5fb67b5d868d4b061201595 100644 --- a/lib/layout/board.dart +++ b/lib/layout/board.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:solitaire_game/entities/tile.dart'; import 'package:solitaire_game/provider/data.dart'; class Board { static Container buildGameBoard(Data myProvider) { return Container( - margin: EdgeInsets.all(4), - padding: EdgeInsets.all(4), child: Column( children: [ buildGameTileset(myProvider), @@ -15,19 +14,26 @@ class Board { } static Table buildGameTileset(Data myProvider) { - int boardSize = 9; + List<List<Tile?>> board = myProvider.board; + + Widget boardTileWithoutHole = Image( + image: AssetImage('assets/skins/' + myProvider.parameterSkin + '_board.png'), + width: myProvider.tileSize, + height: myProvider.tileSize, + fit: BoxFit.fill, + ); return Table( defaultColumnWidth: IntrinsicColumnWidth(), children: [ - for (var row = 0; row < boardSize; row++) + for (var row = 0; row < board.length; row++) TableRow( children: [ - for (var col = 0; col < boardSize; col++) - Column( - children: [ - Text('[' + col.toString() + ',' + row.toString() + "]"), - ], + for (var col = 0; col < board[row].length; col++) + TableCell( + child: board[row][col] != null + ? (board[row][col]?.render(myProvider) ?? Container()) + : boardTileWithoutHole, ), ], ), diff --git a/lib/provider/data.dart b/lib/provider/data.dart index a9512a9c3148cc529d35a19d9d154bc44e7630ce..61e1e394c07c8598db1e2dddddbe2ba07c03dc64 100644 --- a/lib/provider/data.dart +++ b/lib/provider/data.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:solitaire_game/entities/tile.dart'; class Data extends ChangeNotifier { // Configuration available values @@ -22,6 +23,9 @@ class Data extends ChangeNotifier { // Game data bool _assetsPreloaded = false; bool _gameIsRunning = false; + List<List<Tile?>> _board = []; + int _boardSize = 0; + double _tileSize = 0; String _currentState = ''; void updateParameterSkin(String parameterSkin) { @@ -63,8 +67,24 @@ class Data extends ChangeNotifier { String get currentState => _currentState; String computeCurrentGameState() { + String boardValues = ''; + + String textBoard = ' '; + String textHole = '·'; + String textPeg = 'o'; + for (var rowIndex = 0; rowIndex < _board.length; rowIndex++) { + for (var colIndex = 0; colIndex < _board[rowIndex].length; colIndex++) { + String cellValue = textBoard; + if (_board[rowIndex][colIndex] != null) { + cellValue = (_board[rowIndex][colIndex]?.hasPeg ?? false) ? textPeg : textHole; + } + boardValues += cellValue; + } + } + var currentState = { 'skin': _parameterSkin, + 'boardValues': boardValues, }; return json.encode(currentState); @@ -113,6 +133,32 @@ class Data extends ChangeNotifier { _assetsPreloaded = assetsPreloaded; } + double get tileSize => _tileSize; + void updateTileSize(double tileSize) { + _tileSize = tileSize; + } + + int get boardSize => _boardSize; + void updateBoardSize(int boardSize) { + _boardSize = boardSize; + } + + List<List<Tile?>> get board => _board; + void updateBoard(List<List<Tile?>> board) { + _board = board; + updateBoardSize(board.length); + notifyListeners(); + } + + updatePegValue(int row, int col, bool hasPeg) { + if (_board[row][col] != null) { + _board[row][col]?.hasPeg = hasPeg; + + saveCurrentGameState(); + notifyListeners(); + } + } + bool get gameIsRunning => _gameIsRunning; bool get isGameFinished => !_gameIsRunning; void updateGameIsRunning(bool gameIsRunning) { diff --git a/lib/screens/home.dart b/lib/screens/home.dart index be742886eccb32a42338920ba1f288ad6cc5c8d5..32c3b5e10f8ae5c406fa5d8d2d96115d44358ce8 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -40,7 +40,9 @@ class _HomeState extends State<Home> { gameImages.forEach((image) => assets.add('assets/icons/' + image + '.png')); List skinImages = [ - 'empty', + 'board', + 'hole', + 'peg', ]; myProvider.availableSkinValues.forEach((skin) => skinImages @@ -59,6 +61,8 @@ class _HomeState extends State<Home> { myProvider.updateAssetsPreloaded(true); } + myProvider.updateTileSize((MediaQuery.of(context).size.width - 20) / myProvider.boardSize); + List<Widget> menuActions = []; if (myProvider.gameIsRunning) { diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart new file mode 100644 index 0000000000000000000000000000000000000000..5f60126993ddea1db46d2e9f72e7d4d4e4b60b17 --- /dev/null +++ b/lib/utils/board_utils.dart @@ -0,0 +1,84 @@ +import 'dart:math'; + +import 'package:solitaire_game/entities/tile.dart'; +import 'package:solitaire_game/provider/data.dart'; + +class BoardUtils { + static printGrid(List cells) { + String textBoard = ' '; + String textHole = '·'; + String textPeg = 'o'; + + print(''); + print('-------'); + for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) { + String row = ''; + for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) { + String textCell = textBoard; + Tile? tile = cells[rowIndex][colIndex]; + if (tile != null) { + textCell = tile.hasPeg ? textPeg : textHole; + } + row += textCell; + } + print(row); + } + print('-------'); + print(''); + } + + static List<List<Tile?>> createBoardFromSavedState(Data myProvider, String savedBoard) { + List<List<Tile?>> board = []; + int boardSize = pow((savedBoard.length), 1 / 2).round(); + myProvider.updateBoardSize(boardSize); + + String textBoard = ' '; + String textPeg = 'o'; + + int index = 0; + for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) { + List<Tile?> row = []; + for (var colIndex = 0; colIndex < boardSize; colIndex++) { + String stringValue = savedBoard[index++]; + if (stringValue == textBoard) { + row.add(null); + } else { + row.add(Tile(rowIndex, colIndex, (stringValue == textPeg))); + } + } + board.add(row); + } + + return board; + } + + static createNewBoard(Data myProvider) { + List<String> templateEnglish = [ + ' ooo ', + ' ooo ', + 'ooooooo', + 'ooo·ooo', + 'ooooooo', + ' ooo ', + ' ooo ', + ]; + + List<List<Tile?>> grid = []; + int row = 0; + templateEnglish.forEach((String line) { + List<Tile?> gridLine = []; + int col = 0; + line.split("").forEach((String tileCode) { + gridLine.add(tileCode == ' ' ? null : new Tile(row, col, (tileCode == 'o'))); + col++; + }); + row++; + grid.add(gridLine); + }); + + printGrid(grid); + + myProvider.resetGame(); + myProvider.updateBoard(grid); + } +} diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart index de8996e50ccf4d4751747fa7763b7e8dfd1b3040..c618e456ea8f3a9cdc662dee00dec690c355c399 100644 --- a/lib/utils/game_utils.dart +++ b/lib/utils/game_utils.dart @@ -1,4 +1,6 @@ +import 'package:solitaire_game/entities/tile.dart'; import 'package:solitaire_game/provider/data.dart'; +import 'package:solitaire_game/utils/board_utils.dart'; class GameUtils { static Future<void> quitGame(Data myProvider) async { @@ -8,7 +10,7 @@ class GameUtils { static Future<void> startNewGame(Data myProvider) async { print('Starting game'); - myProvider.resetGame(); + BoardUtils.createNewBoard(myProvider); myProvider.updateGameIsRunning(true); } @@ -22,6 +24,8 @@ class GameUtils { if (savedState.isNotEmpty) { try { myProvider.setParameterValue('skin', savedState['skin']); + myProvider.updateBoard( + BoardUtils.createBoardFromSavedState(myProvider, savedState['boardValues'])); myProvider.updateGameIsRunning(true); } catch (e) { @@ -36,4 +40,66 @@ class GameUtils { startNewGame(myProvider); } } + + static bool isMoveAllowed(Data myProvider, List<int> source, List<int> target) { + List<List<Tile?>> board = myProvider.board; + int sourceCol = source[0]; + int sourceRow = source[1]; + int targetCol = target[0]; + int targetRow = target[1]; + + // ensure source exists and has a peg + if (board[sourceRow][sourceCol] == null || board[sourceRow][sourceCol]?.hasPeg == false) { + print('move forbidden: source peg does not exist'); + return false; + } + + // ensure target exists and is empty + if (board[targetRow][targetCol] == null || board[targetRow][targetCol]?.hasPeg == true) { + print('move forbidden: target does not exist or already with a peg'); + return false; + } + + // ensure source and target are in the same line/column + if ((targetCol != sourceCol) && (targetRow != sourceRow)) { + print('move forbidden: source and target are not in the same line or column'); + return false; + } + + // ensure source and target are separated by exactly one tile + if (((targetCol == sourceCol) && ((targetRow - sourceRow).abs() != 2)) || + ((targetRow == sourceRow) && ((targetCol - sourceCol).abs() != 2))) { + print('move forbidden: source and target must be separated by exactly one tile'); + return false; + } + + // ensure middle tile exists and has a peg + int middleRow = (sourceRow + ((targetRow - sourceRow) / 2)).round(); + int middleCol = (sourceCol + ((targetCol - sourceCol) / 2)).round(); + if (board[middleRow][middleCol] == null || board[middleRow][middleCol]?.hasPeg == false) { + print('move forbidden: tile between source and target does not contain a peg'); + return false; + } + + // ok, move is allowed + return true; + } + + static void move(Data myProvider, List<int> source, List<int> target) { + print('Move from ' + source.toString() + ' to ' + target.toString()); + int sourceCol = source[0]; + int sourceRow = source[1]; + int targetCol = target[0]; + int targetRow = target[1]; + + int middleRow = (sourceRow + ((targetRow - sourceRow) / 2)).round(); + int middleCol = (sourceCol + ((targetCol - sourceCol) / 2)).round(); + + // remove peg from source + myProvider.updatePegValue(sourceRow, sourceCol, false); + // put peg in target + myProvider.updatePegValue(targetRow, targetCol, true); + // remove peg from middle tile + myProvider.updatePegValue(middleRow, middleCol, false); + } }