import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:solitaire/data/game_data.dart'; import 'package:solitaire/models/game/cell.dart'; import 'package:solitaire/models/game/cell_location.dart'; import 'package:solitaire/models/settings/settings_game.dart'; typedef BoardCells = List<List<Cell>>; class Board { Board({ required this.cells, }); BoardCells cells = const []; factory Board.createEmpty() { return Board( cells: [], ); } factory Board.createNew({ required GameSettings gameSettings, }) { final List<String>? template = GameData.templates[gameSettings.layout]; final BoardCells grid = []; int row = 0; template?.forEach((String line) { final List<Cell> gridLine = []; int col = 0; line.split("").forEach((String tileCode) { gridLine.add(tileCode == ' ' ? Cell.none : Cell( location: CellLocation.go(row, col), hasHole: true, hasPeg: (tileCode == 'o'), )); col++; }); row++; grid.add(gridLine); }); return Board( cells: grid, ); } int get boardSize => cells.length; Cell get(CellLocation location) { if (location.row < cells.length) { if (location.col < cells[location.row].length) { return cells[location.row][location.col]; } } return Cell.none; } void set(CellLocation location, Cell cell) { cells[location.row][location.col] = cell; } bool isMoveAllowed({ required List<int> source, required List<int> target, }) { // printlog('(test) Pick from ' + source.toString() + ' and drop on ' + target.toString()); final int sourceCol = source[0]; final int sourceRow = source[1]; final int targetCol = target[0]; final int targetRow = target[1]; // ensure source and target are inside range if (sourceRow < 0 || sourceRow > (boardSize - 1) || sourceCol < 0 || sourceCol > (boardSize - 1)) { // printlog('move forbidden: source is out of board'); return false; } if (targetRow < 0 || targetRow > (boardSize - 1) || targetCol < 0 || targetCol > (boardSize - 1)) { // printlog('move forbidden: target is out of board'); return false; } // ensure source exists and has a peg if (cells[sourceRow][sourceCol].hasPeg == false) { // printlog('move forbidden: source peg does not exist'); return false; } // ensure target exists and is empty if (cells[targetRow][targetCol].hasPeg == true) { // printlog('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)) { // printlog('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))) { // printlog('move forbidden: source and target must be separated by exactly one tile'); return false; } // ensure middle tile exists and has a peg final int middleRow = (sourceRow + ((targetRow - sourceRow) / 2)).round(); final int middleCol = (sourceCol + ((targetCol - sourceCol) / 2)).round(); if (cells[middleRow][middleCol].hasPeg == false) { // printlog('move forbidden: tile between source and target does not contain a peg'); return false; } // ok, move is allowed return true; } void printGrid() { String textBoard = ' '; String textHole = 'ยท'; String textPeg = 'o'; printlog(''); printlog('-------'); for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) { String row = ''; for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) { String textCell = textBoard; Cell tile = cells[rowIndex][colIndex]; textCell = tile.hasPeg ? textPeg : textHole; row += textCell; } printlog(row); } printlog('-------'); printlog(''); } void dump() { printlog(''); printlog('$Board:'); printGrid(); printlog(''); } @override String toString() { return '$Board(${toJson()})'; } Map<String, dynamic>? toJson() { return <String, dynamic>{ 'cells': cells, }; } }