Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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();
}
}
}
}
}