Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • android/org.benoitharrault.suguru
1 result
Show changes
Commits on Source (6)
Showing
with 344 additions and 246 deletions
Fix display big vertical grids.
Improve/fix grid solver.
Improve grid display.
Correction sur l'affichage des grandes grilles verticales.
Amélioration/correction du résolveur de grille.
Amélioration de l'affichage de la 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,16 +141,12 @@ class Activity { ...@@ -139,16 +141,12 @@ 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 || printlog('-> ok suguru solved!');
board.get(location).value != board.solvedCells[location.row][location.col].value) { return true;
return false;
}
} }
printlog('-> ok suguru solved!'); return false;
return true;
} }
ConflictsCount computeConflictsInBoard() { ConflictsCount computeConflictsInBoard() {
...@@ -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); 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...'); activityCubit.updateCellValue(nextMove.location, nextMove.value);
return false; activityCubit.unselectCell();
return true;
} }
List<List<int>> getCellsWithWrongValue() { 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,18 +262,25 @@ class Board { ...@@ -244,18 +262,25 @@ 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) {
cells[location.row][location.col] = cell; if (inBoard(location)) {
cells[location.row][location.col] = cell;
}
} }
void setValue(CellLocation location, int value) { void setValue(CellLocation location, int value) {
...@@ -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]) { for (int deltaRow in [-1, 0, 1]) {
if (cellLocation.row + deltaRow >= 0 && final CellLocation siblingLocation =
cellLocation.row + deltaRow < boardSizeHorizontal && CellLocation.go(cellLocation.row + deltaRow, cellLocation.col + deltaCol);
cellLocation.col + deltaCol >= 0 &&
cellLocation.col + deltaCol < boardSizeVertical && if (inBoard(siblingLocation) && !(deltaRow == 0 && deltaCol == 0)) {
!(deltaRow == 0 && deltaCol == 0)) { if (get(siblingLocation).value == value) {
final CellLocation candidateLocation =
CellLocation.go(cellLocation.row + deltaRow, cellLocation.col + deltaCol);
final int siblingValue = get(candidateLocation).value;
if (siblingValue == value) {
return true; return true;
} }
} }
...@@ -596,7 +581,12 @@ class Board { ...@@ -596,7 +581,12 @@ class Board {
if (solvedCells.isEmpty) { if (solvedCells.isEmpty) {
rowSolved += '*'; rowSolved += '*';
} else { } 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'); 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 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
...@@ -77,19 +75,35 @@ class CellWidget extends StatelessWidget { ...@@ -77,19 +75,35 @@ class CellWidget extends StatelessWidget {
return 'assets/ui/cell_empty.png'; return 'assets/ui/cell_empty.png';
} }
Color getBaseColorFromBlockId(String blockId) {
const List<Color> paletteColors = [
Color.fromRGBO(0xbe, 0x7f, 0x51, 1),
Color.fromRGBO(0x50, 0x72, 0x8b, 1),
Color.fromRGBO(0xae, 0x70, 0x6f, 1),
Color.fromRGBO(0x91, 0x61, 0x87, 1),
Color.fromRGBO(0xe1, 0x8a, 0x2b, 1),
Color.fromRGBO(0x88, 0x77, 0x6d, 1),
Color.fromRGBO(0xd4, 0xbe, 0x1e, 1),
Color.fromRGBO(0xba, 0x96, 0x3a, 1),
Color.fromRGBO(0xa2, 0x9a, 0x5c, 1),
];
final int blockIdValue = blockId.codeUnits.first - 'A'.codeUnits.first;
return paletteColors[blockIdValue % paletteColors.length].lighten(20);
}
// Compute cell background color, from cell state // Compute cell background color, from cell state
Color getBackgroundColor(Activity activity) { Color getBackgroundColor(Activity activity) {
final Color editableCellColor = Colors.grey.shade100;
final Color editableCellColorConflict = Colors.pink.shade100; final Color editableCellColorConflict = Colors.pink.shade100;
final Color fixedCellColor = Colors.grey.shade300; final int fixedCellColorDarkAmount = 10;
final Color fixedCellColorConflict = Colors.pink.shade200; final Color fixedCellColorConflict = Colors.pink.shade200;
final Color editableAnimated = Colors.green.shade200; final Color editableAnimated = Colors.green.shade200;
final Color fixedAnimated = Colors.green.shade300; final Color fixedAnimated = Colors.green.shade300;
Color backgroundColor = editableCellColor; Color backgroundColor = getBaseColorFromBlockId(cell.blockId);
if (cell.isFixed == true) { if (cell.isFixed == true) {
backgroundColor = fixedCellColor; backgroundColor = backgroundColor.darken(fixedCellColorDarkAmount);
} }
final int conflictsCount = activity.boardConflicts[cell.location.row][cell.location.col]; final int conflictsCount = activity.boardConflicts[cell.location.row][cell.location.col];
...@@ -119,23 +133,14 @@ class CellWidget extends StatelessWidget { ...@@ -119,23 +133,14 @@ class CellWidget extends StatelessWidget {
// Compute cell borders, from board size and cell state // Compute cell borders, from board size and cell state
Border getCellBorders(Activity activity) { Border getCellBorders(Activity activity) {
final Color cellBorderDarkColor = Colors.grey.shade800; final Color baseColor = getBaseColorFromBlockId(cell.blockId);
final Color cellBorderLightColor = Colors.grey.shade400;
final Color cellBorderDarkColor = baseColor.darken(50);
final Color cellBorderLightColor = baseColor.lighten(10);
const Color cellBorderSelectedColor = Colors.red; const Color cellBorderSelectedColor = Colors.red;
Color cellBorderColor = cellBorderSelectedColor; Color cellBorderColor = cellBorderSelectedColor;
double cellBorderWidth = 4; const double cellBorderWidth = 8;
final int boardSizeReference =
max(activity.boardSizeHorizontal, activity.boardSizeVertical);
// Reduce cell border width on big boards
if (boardSizeReference > 8) {
cellBorderWidth = 2;
if (boardSizeReference > 10) {
cellBorderWidth = 1;
}
}
if (!activity.isRunning) { if (!activity.isRunning) {
cellBorderColor = Colors.green.shade700; cellBorderColor = Colors.green.shade700;
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:flutter_custom_toolbox/flutter_toolbox.dart';
...@@ -17,7 +18,11 @@ class GameBoardWidget extends StatelessWidget { ...@@ -17,7 +18,11 @@ class GameBoardWidget extends StatelessWidget {
final Color borderColor = Theme.of(context).colorScheme.onSurface; final Color borderColor = Theme.of(context).colorScheme.onSurface;
return Container( final Size size = MediaQuery.of(context).size;
final double width = size.width;
final double height = size.height;
final Container board = Container(
margin: const EdgeInsets.all(2), margin: const EdgeInsets.all(2),
padding: const EdgeInsets.all(2), padding: const EdgeInsets.all(2),
decoration: BoxDecoration( decoration: BoxDecoration(
...@@ -81,6 +86,16 @@ class GameBoardWidget extends StatelessWidget { ...@@ -81,6 +86,16 @@ class GameBoardWidget extends StatelessWidget {
], ],
), ),
); );
return ConstrainedBox(
constraints: BoxConstraints.tightFor(
width: width,
height: height * .6,
),
child: FittedBox(
child: board,
), //Text
);
}, },
); );
} }
......
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.1+1 version: 0.0.4+4
environment: environment:
sdk: "^3.0.0" sdk: "^3.0.0"
......