Skip to content
Snippets Groups Projects
Commit 63a9814c authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Improve grid solver

parent 6fafd712
No related branches found
No related tags found
1 merge request!4Resolve "Improve grid solver"
Pipeline #7777 passed
Improve/fix grid solver.
Amélioration/correction du résolveur de grille.
...@@ -8,7 +8,9 @@ import 'package:suguru/data/game_data.dart'; ...@@ -8,7 +8,9 @@ import 'package:suguru/data/game_data.dart';
import 'package:suguru/models/activity/board.dart'; import 'package:suguru/models/activity/board.dart';
import 'package:suguru/models/activity/cell.dart'; import 'package:suguru/models/activity/cell.dart';
import 'package:suguru/models/activity/cell_location.dart'; import 'package:suguru/models/activity/cell_location.dart';
import 'package:suguru/models/activity/move.dart';
import 'package:suguru/models/activity/types.dart'; import 'package:suguru/models/activity/types.dart';
import 'package:suguru/utils/suguru_solver.dart';
class Activity { class Activity {
Activity({ Activity({
...@@ -139,18 +141,14 @@ class Activity { ...@@ -139,18 +141,14 @@ class Activity {
bool get canGiveTip => (buttonTipsCountdown == 0); bool get canGiveTip => (buttonTipsCountdown == 0);
bool checkBoardIsSolved() { bool checkBoardIsSolved() {
for (CellLocation location in board.getCellLocations()) { if (board.isSolved()) {
if (board.get(location).value == 0 ||
board.get(location).value != board.solvedCells[location.row][location.col].value) {
return false;
}
}
printlog('-> ok suguru solved!'); printlog('-> ok suguru solved!');
return true; return true;
} }
return false;
}
ConflictsCount computeConflictsInBoard() { ConflictsCount computeConflictsInBoard() {
final BoardCells cells = board.cells; final BoardCells cells = board.cells;
final ConflictsCount conflicts = boardConflicts; final ConflictsCount conflicts = boardConflicts;
...@@ -227,7 +225,7 @@ class Activity { ...@@ -227,7 +225,7 @@ class Activity {
tipGiven = helpSelectCell(activityCubit); tipGiven = helpSelectCell(activityCubit);
} else { } else {
// currently selected cell -> set value // currently selected cell -> set value
tipGiven = helpFillCell(activityCubit); tipGiven = helpFillCell(activityCubit, selectedCell?.location);
} }
if (tipGiven) { if (tipGiven) {
...@@ -236,56 +234,66 @@ class Activity { ...@@ -236,56 +234,66 @@ class Activity {
} }
bool helpSelectCell(ActivityCubit activityCubit) { bool helpSelectCell(ActivityCubit activityCubit) {
CellLocation? cellLocationToFix;
// pick one of wrong value cells, if found // pick one of wrong value cells, if found
final List<List<int>> wrongValueCells = getCellsWithWrongValue(); final List<CellLocation> wrongValueCells = getCellsWithWrongValue();
if (wrongValueCells.isNotEmpty) { if (wrongValueCells.isNotEmpty) {
printlog('pick from wrongValueCells'); printlog('pick from wrongValueCells');
return pickRandomFromList(activityCubit, wrongValueCells); wrongValueCells.shuffle();
cellLocationToFix = wrongValueCells[0];
} }
// pick one of conflicting cells, if found // pick one of conflicting cells, if found
final List<List<int>> conflictingCells = getCellsWithConflicts(); final List<CellLocation> conflictingCells = getCellsWithConflicts();
if (conflictingCells.isNotEmpty) { if (conflictingCells.isNotEmpty) {
printlog('pick from conflictingCells'); printlog('pick from conflictingCells');
return pickRandomFromList(activityCubit, conflictingCells); conflictingCells.shuffle();
cellLocationToFix = conflictingCells[0];
} }
// pick one from "easy" cells (unique empty cell in block) if (cellLocationToFix != null) {
final List<List<int>> easyFillableCells = board.getLastEmptyCellsInBlocks(); printlog('picked cell with wrong or conflicting value...');
if (easyFillableCells.isNotEmpty) { activityCubit.selectCell(cellLocationToFix);
printlog('pick from easyFillableCells'); return true;
return pickRandomFromList(activityCubit, easyFillableCells);
} }
// pick one from cells with unique non-conflicting candidate value final Move? nextMove = SuguruSolver.pickNextMove(board);
final List<List<int>> candidateCells = board.getEmptyCellsWithUniqueAvailableValue();
if (candidateCells.isNotEmpty) { if (nextMove == null) {
printlog('pick from candidateCells'); printlog('no easy cell to select...');
return pickRandomFromList(activityCubit, candidateCells); return false;
} }
// pick one from "only cell in this block for this value" activityCubit.selectCell(nextMove.location);
final List<List<int>> onlyCellsWithoutConflict = board.getOnlyCellInBlockWithoutConflict(); return true;
if (onlyCellsWithoutConflict.isNotEmpty) {
printlog('pick from onlyCellsWithoutConflict');
return pickRandomFromList(activityCubit, onlyCellsWithoutConflict);
} }
printlog('no easy cell to select...'); bool helpFillCell(ActivityCubit activityCubit, CellLocation? location) {
final Move? nextMove = SuguruSolver.pickNextMove(board, location);
if (nextMove == null) {
printlog('unable to compute cell value for $location');
activityCubit.unselectCell();
return false; return false;
} }
List<List<int>> getCellsWithWrongValue() { activityCubit.updateCellValue(nextMove.location, nextMove.value);
activityCubit.unselectCell();
return true;
}
List<CellLocation> getCellsWithWrongValue() {
final BoardCells cells = board.cells; final BoardCells cells = board.cells;
final BoardCells cellsSolved = board.solvedCells; final BoardCells cellsSolved = board.solvedCells;
List<List<int>> cellsWithWrongValue = []; List<CellLocation> cellsWithWrongValue = [];
for (int row = 0; row < boardSizeVertical; row++) { for (int row = 0; row < boardSizeVertical; row++) {
for (int col = 0; col < boardSizeHorizontal; col++) { for (int col = 0; col < boardSizeHorizontal; col++) {
if (cells[row][col].value != 0 && if (cells[row][col].value != 0 &&
cells[row][col].value != cellsSolved[row][col].value) { cells[row][col].value != cellsSolved[row][col].value) {
cellsWithWrongValue.add([row, col]); cellsWithWrongValue.add(CellLocation.go(row, col));
} }
} }
} }
...@@ -293,13 +301,13 @@ class Activity { ...@@ -293,13 +301,13 @@ class Activity {
return cellsWithWrongValue; return cellsWithWrongValue;
} }
List<List<int>> getCellsWithConflicts() { List<CellLocation> getCellsWithConflicts() {
List<List<int>> cellsWithConflict = []; List<CellLocation> cellsWithConflict = [];
for (int row = 0; row < boardSizeVertical; row++) { for (int row = 0; row < boardSizeVertical; row++) {
for (int col = 0; col < boardSizeHorizontal; col++) { for (int col = 0; col < boardSizeHorizontal; col++) {
if (boardConflicts[row][col] != 0) { if (boardConflicts[row][col] != 0) {
cellsWithConflict.add([row, col]); cellsWithConflict.add(CellLocation.go(row, col));
} }
} }
} }
...@@ -307,73 +315,6 @@ class Activity { ...@@ -307,73 +315,6 @@ class Activity {
return cellsWithConflict; return cellsWithConflict;
} }
bool pickRandomFromList(ActivityCubit activityCubit, List<List<int>> cellsCoordinates) {
if (cellsCoordinates.isNotEmpty) {
cellsCoordinates.shuffle();
final List<int> cell = cellsCoordinates[0];
activityCubit.selectCell(CellLocation.go(cell[0], cell[1]));
return true;
}
return false;
}
bool helpFillCell(ActivityCubit activityCubit) {
// Will clean cell if no eligible value found
int eligibleValue = 0;
// Ensure there is only one eligible value for this cell
int allowedValuesCount = 0;
final int maxValueForThisCell = board.getMaxValueForBlock(selectedCell?.blockId);
for (int value = 1; value <= maxValueForThisCell; value++) {
if (board.isValueAllowed(selectedCell?.location, value)) {
allowedValuesCount++;
eligibleValue = value;
}
}
if (allowedValuesCount == 1) {
activityCubit.updateCellValue(selectedCell!.location, eligibleValue);
activityCubit.unselectCell();
return true;
}
activityCubit.unselectCell();
return false;
}
void checkAllTemplates() {
printlog('###############################');
printlog('## ##');
printlog('## CHECK TEMPLATES ##');
printlog('## ##');
printlog('###############################');
final List<String> allowedLevels = ApplicationConfig.config
.getFromCode(ApplicationConfig.parameterCodeDifficultyLevel)
.allowedValues;
final List<String> allowedSizes = ApplicationConfig.config
.getFromCode(ApplicationConfig.parameterCodeBoardSize)
.allowedValues;
for (String level in allowedLevels) {
printlog('* level: $level');
for (String size in allowedSizes) {
printlog('** size: $size');
final List<String> templates = GameData.templates[size]?[level] ?? [];
printlog('*** templates count: ${templates.length}');
for (String template in templates) {
printlog(' checking $template');
final Board testBoard = Board.createEmpty();
testBoard.createFromTemplate(template: template);
testBoard.dump();
}
}
}
}
void dump() { void dump() {
printlog(''); printlog('');
printlog('## Current game dump:'); printlog('## Current game dump:');
... ...
......
...@@ -4,7 +4,9 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; ...@@ -4,7 +4,9 @@ import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:suguru/models/activity/cell.dart'; import 'package:suguru/models/activity/cell.dart';
import 'package:suguru/models/activity/cell_location.dart'; import 'package:suguru/models/activity/cell_location.dart';
import 'package:suguru/models/activity/move.dart';
import 'package:suguru/models/activity/types.dart'; import 'package:suguru/models/activity/types.dart';
import 'package:suguru/utils/suguru_solver.dart';
class Board { class Board {
Board({ Board({
...@@ -35,6 +37,9 @@ class Board { ...@@ -35,6 +37,9 @@ class Board {
void createFromTemplate({ void createFromTemplate({
required String template, required String template,
}) { }) {
printlog('Creating board from template:');
printlog(template);
final List<String> templateParts = template.split(';'); final List<String> templateParts = template.split(';');
if (templateParts.length != 3) { if (templateParts.length != 3) {
printlog('Failed to get grid template (wrong format)...'); printlog('Failed to get grid template (wrong format)...');
...@@ -69,49 +74,8 @@ class Board { ...@@ -69,49 +74,8 @@ class Board {
cells.add(row); cells.add(row);
} }
const List<String> allowedFlip = ['none', 'horizontal', 'vertical']; // Do some transformations to board
List<String> allowedRotate = ['none', 'left', 'right', 'upsidedown']; transformBoard();
// Limit rotation if board is not symetric
if (boardSizeVertical != boardSizeHorizontal) {
allowedRotate = ['none', 'upsidedown'];
}
final Random rand = Random();
final String flip = allowedFlip[rand.nextInt(allowedFlip.length)];
final String rotate = allowedRotate[rand.nextInt(allowedRotate.length)];
switch (flip) {
case 'horizontal':
{
transformFlipHorizontal();
}
break;
case 'vertical':
{
transformFlipVertical();
}
break;
}
switch (rotate) {
case 'left':
{
transformRotateLeft();
}
break;
case 'right':
{
transformRotateRight();
}
break;
case 'upsidedown':
{
transformFlipHorizontal();
transformFlipVertical();
}
break;
}
// Force cells fixed states (all cells with value != 0) // Force cells fixed states (all cells with value != 0)
for (CellLocation location in getCellLocations()) { for (CellLocation location in getCellLocations()) {
...@@ -124,7 +88,12 @@ class Board { ...@@ -124,7 +88,12 @@ class Board {
); );
} }
resolve(); final Board solvedBoard = SuguruSolver.resolve(this);
solvedCells = solvedBoard.cells;
// FIXME: for debug only
// to start with a board (almost) automatically solved
// cells = solvedCells;
} }
// Helper to create board from size, with "empty" cells // Helper to create board from size, with "empty" cells
...@@ -166,6 +135,55 @@ class Board { ...@@ -166,6 +135,55 @@ class Board {
return locations; return locations;
} }
void transformBoard() {
final int boardSizeVertical = cells.length;
final int boardSizeHorizontal = cells[0].length;
const List<String> allowedFlip = ['none', 'horizontal', 'vertical'];
List<String> allowedRotate = ['none', 'left', 'right', 'upsidedown'];
// Limit rotation if board is not symetric
if (boardSizeVertical != boardSizeHorizontal) {
allowedRotate = ['none', 'upsidedown'];
}
final Random rand = Random();
final String flip = allowedFlip[rand.nextInt(allowedFlip.length)];
final String rotate = allowedRotate[rand.nextInt(allowedRotate.length)];
switch (flip) {
case 'horizontal':
{
transformFlipHorizontal();
}
break;
case 'vertical':
{
transformFlipVertical();
}
break;
}
switch (rotate) {
case 'left':
{
transformRotateLeft();
}
break;
case 'right':
{
transformRotateRight();
}
break;
case 'upsidedown':
{
transformFlipHorizontal();
transformFlipVertical();
}
break;
}
}
void transformFlipHorizontal() { void transformFlipHorizontal() {
final BoardCells transformedBoard = copyCells(); final BoardCells transformedBoard = copyCells();
final int boardSizeVertical = cells.length; final int boardSizeVertical = cells.length;
...@@ -244,19 +262,26 @@ class Board { ...@@ -244,19 +262,26 @@ class Board {
cells = transformedBoard; cells = transformedBoard;
} }
bool inBoard(CellLocation location) {
return (location.row >= 0 &&
location.row < cells.length &&
location.col >= 0 &&
location.col < cells[location.row].length);
}
Cell get(CellLocation location) { Cell get(CellLocation location) {
if (location.row < cells.length) { if (inBoard(location)) {
if (location.col < cells[location.row].length) {
return cells[location.row][location.col]; return cells[location.row][location.col];
} }
}
return Cell.none; return Cell.none;
} }
void setCell(CellLocation location, Cell cell) { void setCell(CellLocation location, Cell cell) {
if (inBoard(location)) {
cells[location.row][location.col] = cell; cells[location.row][location.col] = cell;
} }
}
void setValue(CellLocation location, int value) { void setValue(CellLocation location, int value) {
Cell currentCell = get(location); Cell currentCell = get(location);
...@@ -271,6 +296,11 @@ class Board { ...@@ -271,6 +296,11 @@ class Board {
)); ));
} }
void applyMove(Move move) {
// printlog('put ${move.value} in ${move.location}');
setValue(move.location, move.value);
}
List<String> getBlockIds() { List<String> getBlockIds() {
List<String> blockIds = []; List<String> blockIds = [];
for (CellLocation location in getCellLocations()) { for (CellLocation location in getCellLocations()) {
...@@ -314,48 +344,8 @@ class Board { ...@@ -314,48 +344,8 @@ class Board {
return copiedGrid; return copiedGrid;
} }
resolve() {
final Board solvedBoard = Board(cells: copyCells(), solvedCells: []);
do {
// last cell in blocks
final List<List<int>> cellsLastEmptyInBlock = solvedBoard.getLastEmptyCellsInBlocks();
for (var cellData in cellsLastEmptyInBlock) {
solvedBoard.setValue(CellLocation.go(cellData[0], cellData[1]), cellData[2]);
}
// last cell in blocks
final List<List<int>> cellsSingleInBlockWithoutConflict =
solvedBoard.getOnlyCellInBlockWithoutConflict();
for (var cellData in cellsSingleInBlockWithoutConflict) {
solvedBoard.setValue(CellLocation.go(cellData[0], cellData[1]), cellData[2]);
}
// empty cells with unique available value
final List<List<int>> cellsWithUniqueAvailableValue =
solvedBoard.getEmptyCellsWithUniqueAvailableValue();
for (var cellData in cellsWithUniqueAvailableValue) {
solvedBoard.setValue(CellLocation.go(cellData[0], cellData[1]), cellData[2]);
}
// no more empty cell to fill
if (cellsLastEmptyInBlock.isEmpty && cellsWithUniqueAvailableValue.isEmpty) {
if (solvedBoard.isSolved()) {
printlog('ok compute solved board');
} else {
printlog('!!');
printlog('!! failed to resolve board');
printlog('!!');
}
break;
}
} while (true);
solvedCells = solvedBoard.cells;
}
bool isSolved() { bool isSolved() {
// check grid is fully completed // first, check grid is fully completed
for (CellLocation location in getCellLocations()) { for (CellLocation location in getCellLocations()) {
if (get(location).value == 0) { if (get(location).value == 0) {
return false; return false;
...@@ -387,6 +377,10 @@ class Board { ...@@ -387,6 +377,10 @@ class Board {
} }
} }
if (boardHasSiblingWithSameValue()) {
return false;
}
return true; return true;
} }
...@@ -415,8 +409,8 @@ class Board { ...@@ -415,8 +409,8 @@ class Board {
return missingValues; return missingValues;
} }
List<List<int>> getLastEmptyCellsInBlocks() { List<Move> getLastEmptyCellsInBlocks() {
List<List<int>> candidateCells = []; List<Move> candidateCells = [];
for (CellLocation location in getCellLocations()) { for (CellLocation location in getCellLocations()) {
final Cell cell = get(location); final Cell cell = get(location);
...@@ -433,7 +427,7 @@ class Board { ...@@ -433,7 +427,7 @@ class Board {
candidateValue = value; candidateValue = value;
} }
} }
candidateCells.add([location.row, location.col, candidateValue]); candidateCells.add(Move(location: location, value: candidateValue));
} }
} }
} }
...@@ -441,8 +435,8 @@ class Board { ...@@ -441,8 +435,8 @@ class Board {
return candidateCells; return candidateCells;
} }
List<List<int>> getOnlyCellInBlockWithoutConflict() { List<Move> getOnlyCellInBlockWithoutConflict() {
List<List<int>> candidateCells = []; List<Move> candidateCells = [];
for (String blockId in getBlockIds()) { for (String blockId in getBlockIds()) {
List<int> missingValuesInBlock = getMissingValuesInBlock(blockId); List<int> missingValuesInBlock = getMissingValuesInBlock(blockId);
...@@ -460,7 +454,7 @@ class Board { ...@@ -460,7 +454,7 @@ class Board {
if (allowedCellsForThisValue.length == 1) { if (allowedCellsForThisValue.length == 1) {
final CellLocation candidateLocation = allowedCellsForThisValue[0]; final CellLocation candidateLocation = allowedCellsForThisValue[0];
candidateCells.add([candidateLocation.row, candidateLocation.col, candidateValue]); candidateCells.add(Move(location: candidateLocation, value: candidateValue));
} }
} }
} }
...@@ -468,8 +462,8 @@ class Board { ...@@ -468,8 +462,8 @@ class Board {
return candidateCells; return candidateCells;
} }
List<List<int>> getEmptyCellsWithUniqueAvailableValue() { List<Move> getEmptyCellsWithUniqueAvailableValue() {
List<List<int>> candidateCells = []; List<Move> candidateCells = [];
for (CellLocation location in getCellLocations()) { for (CellLocation location in getCellLocations()) {
if (get(location).value == 0) { if (get(location).value == 0) {
...@@ -485,7 +479,7 @@ class Board { ...@@ -485,7 +479,7 @@ class Board {
} }
if (allowedValuesCount == 1) { if (allowedValuesCount == 1) {
candidateCells.add([location.row, location.col, candidateValue]); candidateCells.add(Move(location: location, value: candidateValue));
} }
} }
} }
...@@ -531,24 +525,15 @@ class Board { ...@@ -531,24 +525,15 @@ class Board {
return false; return false;
} }
final int boardSizeVertical = cells.length;
final int boardSizeHorizontal = cells[0].length;
final int value = candidateValue ?? get(cellLocation).value; final int value = candidateValue ?? get(cellLocation).value;
if (value != 0) { if (value != 0) {
for (int deltaRow in [-1, 0, 1]) {
for (int deltaCol in [-1, 0, 1]) { for (int deltaCol in [-1, 0, 1]) {
if (cellLocation.row + deltaRow >= 0 && for (int deltaRow in [-1, 0, 1]) {
cellLocation.row + deltaRow < boardSizeHorizontal && final CellLocation siblingLocation =
cellLocation.col + deltaCol >= 0 &&
cellLocation.col + deltaCol < boardSizeVertical &&
!(deltaRow == 0 && deltaCol == 0)) {
final CellLocation candidateLocation =
CellLocation.go(cellLocation.row + deltaRow, cellLocation.col + deltaCol); CellLocation.go(cellLocation.row + deltaRow, cellLocation.col + deltaCol);
final int siblingValue = get(candidateLocation).value; if (inBoard(siblingLocation) && !(deltaRow == 0 && deltaCol == 0)) {
if (get(siblingLocation).value == value) {
if (siblingValue == value) {
return true; return true;
} }
} }
...@@ -595,10 +580,15 @@ class Board { ...@@ -595,10 +580,15 @@ class Board {
rowValues += stringValues[cells[rowIndex][colIndex].value]; rowValues += stringValues[cells[rowIndex][colIndex].value];
if (solvedCells.isEmpty) { if (solvedCells.isEmpty) {
rowSolved += '*'; rowSolved += '*';
} else {
final int solvedValue = solvedCells[rowIndex][colIndex].value;
if (solvedValue == 0) {
rowSolved += ' ';
} else { } else {
rowSolved += stringValues[solvedCells[rowIndex][colIndex].value]; rowSolved += stringValues[solvedCells[rowIndex][colIndex].value];
} }
} }
}
printlog('$rowBlocks | $rowValues | $rowSolved'); printlog('$rowBlocks | $rowValues | $rowSolved');
} }
} }
... ...
......
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:suguru/models/activity/cell_location.dart';
class Move {
const Move({
required this.location,
required this.value,
});
final CellLocation location;
final int value;
void dump() {
printlog('$Move:');
printlog(' location: $location');
printlog(' value: $value');
printlog('');
}
@override
String toString() {
return '$Move(${toJson()})';
}
Map<String, dynamic>? toJson() {
return <String, dynamic>{
'location': location.toJson(),
'value': value,
};
}
}
...@@ -29,11 +29,6 @@ class PageGame extends StatelessWidget { ...@@ -29,11 +29,6 @@ class PageGame extends StatelessWidget {
const GameBoardWidget(), const GameBoardWidget(),
const SizedBox(height: 8), const SizedBox(height: 8),
const GameBottomWidget(), const GameBottomWidget(),
// StyledButton.text(
// onPressed: () => currentActivity.checkAllTemplates(),
// caption: '[debug] test all templates',
// color: Colors.red,
// ),
const Expanded(child: SizedBox.shrink()), const Expanded(child: SizedBox.shrink()),
currentActivity.isFinished ? const GameEndWidget() : const SizedBox.shrink(), currentActivity.isFinished ? const GameEndWidget() : const SizedBox.shrink(),
], ],
... ...
......
import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
import 'package:suguru/config/application_config.dart';
import 'package:suguru/data/game_data.dart';
import 'package:suguru/models/activity/board.dart';
import 'package:suguru/models/activity/cell_location.dart';
import 'package:suguru/models/activity/move.dart';
import 'package:suguru/models/activity/types.dart';
class SuguruSolver {
static Board resolve(Board board) {
printlog('solving grid...');
final BoardCells cells = board.copyCells();
final Board solvedBoard = Board(cells: cells, solvedCells: cells);
solvedBoard.dump();
do {
if (solvedBoard.isSolved()) {
printlog('ok compute solved board');
break;
} else {
final Move? nextMove = pickNextMove(solvedBoard);
if (nextMove != null) {
// found empty cell to fill
solvedBoard.applyMove(nextMove);
// solvedBoard.dump();
} else {
// no more empty cell to fill
if (!solvedBoard.isSolved()) {
printlog('!!');
printlog('!! failed to resolve board');
printlog('!!');
}
break;
}
}
} while (true);
return solvedBoard;
}
static Move? pickNextMove(Board board, [CellLocation? candidateCell]) {
// pick one from "easy" cells (unique empty cell in block)
final List<Move> easyFillableMoves = board.getLastEmptyCellsInBlocks();
if (easyFillableMoves.isNotEmpty) {
printlog('picked next move from easyFillableMoves');
return pickRandomFromList(easyFillableMoves);
}
// pick one from cells with unique non-conflicting candidate value
final List<Move> candidateMoves = board.getEmptyCellsWithUniqueAvailableValue();
if (candidateMoves.isNotEmpty) {
printlog('picked next move from candidateMoves');
return pickRandomFromList(candidateMoves);
}
// pick one from "only cell in this block for this value"
final List<Move> onlyCellsWithoutConflict = board.getOnlyCellInBlockWithoutConflict();
if (onlyCellsWithoutConflict.isNotEmpty) {
printlog('picked next move from onlyCellsWithoutConflict');
return pickRandomFromList(onlyCellsWithoutConflict);
}
printlog('unable to find next move...');
return null;
}
static Board applyMove(Board board, Move move) {
board.setValue(move.location, move.value);
return board;
}
static Move? pickRandomFromList(List<Move> moves) {
if (moves.isNotEmpty) {
moves.shuffle();
return moves[0];
}
return null;
}
static void checkAllTemplates() {
printlog('###############################');
printlog('## ##');
printlog('## CHECK TEMPLATES ##');
printlog('## ##');
printlog('###############################');
final List<String> allowedLevels = ApplicationConfig.config
.getFromCode(ApplicationConfig.parameterCodeDifficultyLevel)
.allowedValues;
final List<String> allowedSizes = ApplicationConfig.config
.getFromCode(ApplicationConfig.parameterCodeBoardSize)
.allowedValues;
for (String level in allowedLevels) {
printlog('* level: $level');
for (String size in allowedSizes) {
printlog('** size: $size');
final List<String> templates = GameData.templates[size]?[level] ?? [];
printlog('*** templates count: ${templates.length}');
for (String template in templates) {
printlog(' checking $template');
final Board testBoard = Board.createEmpty();
testBoard.createFromTemplate(template: template);
testBoard.dump();
}
}
}
}
}
...@@ -3,7 +3,7 @@ description: A suguru game application. ...@@ -3,7 +3,7 @@ description: A suguru game application.
publish_to: "none" publish_to: "none"
version: 0.0.2+2 version: 0.0.3+3
environment: environment:
sdk: "^3.0.0" sdk: "^3.0.0"
... ...
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment