diff --git a/fastlane/metadata/android/en-US/changelogs/3.txt b/fastlane/metadata/android/en-US/changelogs/3.txt
new file mode 100644
index 0000000000000000000000000000000000000000..23cb99cfd2751705cbe2e209ed3505973b08787a
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/3.txt
@@ -0,0 +1 @@
+Improve/fix grid solver.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/3.txt b/fastlane/metadata/android/fr-FR/changelogs/3.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dd898d8d2bcee2dcb85a83ecc0ff77cd5f99c6de
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/3.txt
@@ -0,0 +1 @@
+Amélioration/correction du résolveur de grille.
diff --git a/lib/models/activity/activity.dart b/lib/models/activity/activity.dart
index c13811d9970edd17c95af252aa41f2beafbee623..c633981f5338d76828d543c75e9362676db110fe 100644
--- a/lib/models/activity/activity.dart
+++ b/lib/models/activity/activity.dart
@@ -8,7 +8,9 @@ import 'package:suguru/data/game_data.dart';
 import 'package:suguru/models/activity/board.dart';
 import 'package:suguru/models/activity/cell.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/utils/suguru_solver.dart';
 
 class Activity {
   Activity({
@@ -139,16 +141,12 @@ class Activity {
   bool get canGiveTip => (buttonTipsCountdown == 0);
 
   bool checkBoardIsSolved() {
-    for (CellLocation location in board.getCellLocations()) {
-      if (board.get(location).value == 0 ||
-          board.get(location).value != board.solvedCells[location.row][location.col].value) {
-        return false;
-      }
+    if (board.isSolved()) {
+      printlog('-> ok suguru solved!');
+      return true;
     }
 
-    printlog('-> ok suguru solved!');
-
-    return true;
+    return false;
   }
 
   ConflictsCount computeConflictsInBoard() {
@@ -227,7 +225,7 @@ class Activity {
       tipGiven = helpSelectCell(activityCubit);
     } else {
       // currently selected cell -> set value
-      tipGiven = helpFillCell(activityCubit);
+      tipGiven = helpFillCell(activityCubit, selectedCell?.location);
     }
 
     if (tipGiven) {
@@ -236,56 +234,66 @@ class Activity {
   }
 
   bool helpSelectCell(ActivityCubit activityCubit) {
+    CellLocation? cellLocationToFix;
+
     // pick one of wrong value cells, if found
-    final List<List<int>> wrongValueCells = getCellsWithWrongValue();
+    final List<CellLocation> wrongValueCells = getCellsWithWrongValue();
     if (wrongValueCells.isNotEmpty) {
       printlog('pick from wrongValueCells');
-      return pickRandomFromList(activityCubit, wrongValueCells);
+      wrongValueCells.shuffle();
+      cellLocationToFix = wrongValueCells[0];
     }
 
     // pick one of conflicting cells, if found
-    final List<List<int>> conflictingCells = getCellsWithConflicts();
+    final List<CellLocation> conflictingCells = getCellsWithConflicts();
     if (conflictingCells.isNotEmpty) {
       printlog('pick from conflictingCells');
-      return pickRandomFromList(activityCubit, conflictingCells);
+      conflictingCells.shuffle();
+      cellLocationToFix = conflictingCells[0];
     }
 
-    // pick one from "easy" cells (unique empty cell in block)
-    final List<List<int>> easyFillableCells = board.getLastEmptyCellsInBlocks();
-    if (easyFillableCells.isNotEmpty) {
-      printlog('pick from easyFillableCells');
-      return pickRandomFromList(activityCubit, easyFillableCells);
+    if (cellLocationToFix != null) {
+      printlog('picked cell with wrong or conflicting value...');
+      activityCubit.selectCell(cellLocationToFix);
+      return true;
     }
 
-    // pick one from cells with unique non-conflicting candidate value
-    final List<List<int>> candidateCells = board.getEmptyCellsWithUniqueAvailableValue();
-    if (candidateCells.isNotEmpty) {
-      printlog('pick from candidateCells');
-      return pickRandomFromList(activityCubit, candidateCells);
+    final Move? nextMove = SuguruSolver.pickNextMove(board);
+
+    if (nextMove == null) {
+      printlog('no easy cell to select...');
+      return false;
     }
 
-    // pick one from "only cell in this block for this value"
-    final List<List<int>> onlyCellsWithoutConflict = board.getOnlyCellInBlockWithoutConflict();
-    if (onlyCellsWithoutConflict.isNotEmpty) {
-      printlog('pick from onlyCellsWithoutConflict');
-      return pickRandomFromList(activityCubit, onlyCellsWithoutConflict);
+    activityCubit.selectCell(nextMove.location);
+    return true;
+  }
+
+  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;
     }
 
-    printlog('no easy cell to select...');
-    return false;
+    activityCubit.updateCellValue(nextMove.location, nextMove.value);
+    activityCubit.unselectCell();
+    return true;
   }
 
-  List<List<int>> getCellsWithWrongValue() {
+  List<CellLocation> getCellsWithWrongValue() {
     final BoardCells cells = board.cells;
     final BoardCells cellsSolved = board.solvedCells;
 
-    List<List<int>> cellsWithWrongValue = [];
+    List<CellLocation> cellsWithWrongValue = [];
 
     for (int row = 0; row < boardSizeVertical; row++) {
       for (int col = 0; col < boardSizeHorizontal; col++) {
         if (cells[row][col].value != 0 &&
             cells[row][col].value != cellsSolved[row][col].value) {
-          cellsWithWrongValue.add([row, col]);
+          cellsWithWrongValue.add(CellLocation.go(row, col));
         }
       }
     }
@@ -293,13 +301,13 @@ class Activity {
     return cellsWithWrongValue;
   }
 
-  List<List<int>> getCellsWithConflicts() {
-    List<List<int>> cellsWithConflict = [];
+  List<CellLocation> getCellsWithConflicts() {
+    List<CellLocation> cellsWithConflict = [];
 
     for (int row = 0; row < boardSizeVertical; row++) {
       for (int col = 0; col < boardSizeHorizontal; col++) {
         if (boardConflicts[row][col] != 0) {
-          cellsWithConflict.add([row, col]);
+          cellsWithConflict.add(CellLocation.go(row, col));
         }
       }
     }
@@ -307,73 +315,6 @@ class Activity {
     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() {
     printlog('');
     printlog('## Current game dump:');
diff --git a/lib/models/activity/board.dart b/lib/models/activity/board.dart
index 663efc3d7fb68a12841d7491bac2a0f31b8cd0a0..3b7bc38fd397179848eaa40055444b24caeb5b1b 100644
--- a/lib/models/activity/board.dart
+++ b/lib/models/activity/board.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_location.dart';
+import 'package:suguru/models/activity/move.dart';
 import 'package:suguru/models/activity/types.dart';
+import 'package:suguru/utils/suguru_solver.dart';
 
 class Board {
   Board({
@@ -35,6 +37,9 @@ class Board {
   void createFromTemplate({
     required String template,
   }) {
+    printlog('Creating board from template:');
+    printlog(template);
+
     final List<String> templateParts = template.split(';');
     if (templateParts.length != 3) {
       printlog('Failed to get grid template (wrong format)...');
@@ -69,49 +74,8 @@ class Board {
       cells.add(row);
     }
 
-    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;
-    }
+    // Do some transformations to board
+    transformBoard();
 
     // Force cells fixed states (all cells with value != 0)
     for (CellLocation location in getCellLocations()) {
@@ -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
@@ -166,6 +135,55 @@ class Board {
     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() {
     final BoardCells transformedBoard = copyCells();
     final int boardSizeVertical = cells.length;
@@ -244,18 +262,25 @@ class Board {
     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) {
-    if (location.row < cells.length) {
-      if (location.col < cells[location.row].length) {
-        return cells[location.row][location.col];
-      }
+    if (inBoard(location)) {
+      return cells[location.row][location.col];
     }
 
     return Cell.none;
   }
 
   void setCell(CellLocation location, Cell cell) {
-    cells[location.row][location.col] = cell;
+    if (inBoard(location)) {
+      cells[location.row][location.col] = cell;
+    }
   }
 
   void setValue(CellLocation location, int value) {
@@ -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> blockIds = [];
     for (CellLocation location in getCellLocations()) {
@@ -314,48 +344,8 @@ class Board {
     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() {
-    // check grid is fully completed
+    // first, check grid is fully completed
     for (CellLocation location in getCellLocations()) {
       if (get(location).value == 0) {
         return false;
@@ -387,6 +377,10 @@ class Board {
       }
     }
 
+    if (boardHasSiblingWithSameValue()) {
+      return false;
+    }
+
     return true;
   }
 
@@ -415,8 +409,8 @@ class Board {
     return missingValues;
   }
 
-  List<List<int>> getLastEmptyCellsInBlocks() {
-    List<List<int>> candidateCells = [];
+  List<Move> getLastEmptyCellsInBlocks() {
+    List<Move> candidateCells = [];
 
     for (CellLocation location in getCellLocations()) {
       final Cell cell = get(location);
@@ -433,7 +427,7 @@ class Board {
               candidateValue = value;
             }
           }
-          candidateCells.add([location.row, location.col, candidateValue]);
+          candidateCells.add(Move(location: location, value: candidateValue));
         }
       }
     }
@@ -441,8 +435,8 @@ class Board {
     return candidateCells;
   }
 
-  List<List<int>> getOnlyCellInBlockWithoutConflict() {
-    List<List<int>> candidateCells = [];
+  List<Move> getOnlyCellInBlockWithoutConflict() {
+    List<Move> candidateCells = [];
 
     for (String blockId in getBlockIds()) {
       List<int> missingValuesInBlock = getMissingValuesInBlock(blockId);
@@ -460,7 +454,7 @@ class Board {
 
         if (allowedCellsForThisValue.length == 1) {
           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 {
     return candidateCells;
   }
 
-  List<List<int>> getEmptyCellsWithUniqueAvailableValue() {
-    List<List<int>> candidateCells = [];
+  List<Move> getEmptyCellsWithUniqueAvailableValue() {
+    List<Move> candidateCells = [];
 
     for (CellLocation location in getCellLocations()) {
       if (get(location).value == 0) {
@@ -485,7 +479,7 @@ class Board {
         }
 
         if (allowedValuesCount == 1) {
-          candidateCells.add([location.row, location.col, candidateValue]);
+          candidateCells.add(Move(location: location, value: candidateValue));
         }
       }
     }
@@ -531,24 +525,15 @@ class Board {
       return false;
     }
 
-    final int boardSizeVertical = cells.length;
-    final int boardSizeHorizontal = cells[0].length;
-
     final int value = candidateValue ?? get(cellLocation).value;
     if (value != 0) {
-      for (int deltaRow in [-1, 0, 1]) {
-        for (int deltaCol in [-1, 0, 1]) {
-          if (cellLocation.row + deltaRow >= 0 &&
-              cellLocation.row + deltaRow < boardSizeHorizontal &&
-              cellLocation.col + deltaCol >= 0 &&
-              cellLocation.col + deltaCol < boardSizeVertical &&
-              !(deltaRow == 0 && deltaCol == 0)) {
-            final CellLocation candidateLocation =
-                CellLocation.go(cellLocation.row + deltaRow, cellLocation.col + deltaCol);
-
-            final int siblingValue = get(candidateLocation).value;
-
-            if (siblingValue == value) {
+      for (int deltaCol in [-1, 0, 1]) {
+        for (int deltaRow in [-1, 0, 1]) {
+          final CellLocation siblingLocation =
+              CellLocation.go(cellLocation.row + deltaRow, cellLocation.col + deltaCol);
+
+          if (inBoard(siblingLocation) && !(deltaRow == 0 && deltaCol == 0)) {
+            if (get(siblingLocation).value == value) {
               return true;
             }
           }
@@ -596,7 +581,12 @@ class Board {
           if (solvedCells.isEmpty) {
             rowSolved += '*';
           } else {
-            rowSolved += stringValues[solvedCells[rowIndex][colIndex].value];
+            final int solvedValue = solvedCells[rowIndex][colIndex].value;
+            if (solvedValue == 0) {
+              rowSolved += ' ';
+            } else {
+              rowSolved += stringValues[solvedCells[rowIndex][colIndex].value];
+            }
           }
         }
         printlog('$rowBlocks | $rowValues | $rowSolved');
diff --git a/lib/models/activity/move.dart b/lib/models/activity/move.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e6c6410d82136520320bbb01ba01dcc329172ca7
--- /dev/null
+++ b/lib/models/activity/move.dart
@@ -0,0 +1,32 @@
+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,
+    };
+  }
+}
diff --git a/lib/ui/pages/game.dart b/lib/ui/pages/game.dart
index a4c5464f6c516adf919bec0a794272ea835b689a..e5923741d7b97df1d7ad59937dabe6d6dd5a1a47 100644
--- a/lib/ui/pages/game.dart
+++ b/lib/ui/pages/game.dart
@@ -29,11 +29,6 @@ class PageGame extends StatelessWidget {
               const GameBoardWidget(),
               const SizedBox(height: 8),
               const GameBottomWidget(),
-              //   StyledButton.text(
-              //     onPressed: () => currentActivity.checkAllTemplates(),
-              //     caption: '[debug] test all templates',
-              //     color: Colors.red,
-              //   ),
               const Expanded(child: SizedBox.shrink()),
               currentActivity.isFinished ? const GameEndWidget() : const SizedBox.shrink(),
             ],
diff --git a/lib/utils/suguru_solver.dart b/lib/utils/suguru_solver.dart
new file mode 100644
index 0000000000000000000000000000000000000000..ed2709e4236cfc690fee4a70d9b2c1ac1bc3fdf7
--- /dev/null
+++ b/lib/utils/suguru_solver.dart
@@ -0,0 +1,114 @@
+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();
+        }
+      }
+    }
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index f03814e44b4a200f7ef46a4f59884f8ef0a72d4e..982dfe16ae68a58a15ab0e374dd7b72a9fcaebfb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: A suguru game application.
 
 publish_to: "none"
 
-version: 0.0.2+2
+version: 0.0.3+3
 
 environment:
   sdk: "^3.0.0"