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
Select Git revision

Target

Select target project
  • android/org.benoitharrault.sudoku
1 result
Select Git revision
Show changes
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../provider/data.dart';
class Cell {
int value;
final int col;
final int row;
bool isFixed;
int conflictsCount = 0;
Cell(
@required this.value,
@required this.col,
@required this.row,
@required this.isFixed,
);
Container widget(Data myProvider) {
static double cellBorderWidth = 3;
static Color cellBorderDarkColor = Colors.black;
static Color cellBorderLightColor = Colors.grey;
static Color cellBorderSelectedColor = Colors.red;
Container widget(Data myProvider, Border borders, int row, int col) {
String imageAsset = 'assets/skins/empty.png';
if (this.value > 0) {
imageAsset = 'assets/skins/' + myProvider.skin + '_' + this.value.toString() + '.png';
}
int size = myProvider.size;
double borderWidth = 3;
Color borderDarkColor = Colors.black;
Color borderLightColor = Colors.grey;
Color borderSelectedColor = Colors.red;
Color backgroundColor = this.getBackgroundColor(myProvider);
Border borders = Border(
top: BorderSide(width: borderWidth, color: ((this.row % size) == 0) ? borderDarkColor : borderLightColor),
left: BorderSide(width: borderWidth, color: ((this.col % size) == 0) ? borderDarkColor : borderLightColor),
right: BorderSide(width: borderWidth, color: (((this.col + 1) % size) == 0) ? borderDarkColor : borderLightColor),
bottom: BorderSide(width: borderWidth, color: (((this.row + 1) % size) == 0) ? borderDarkColor : borderLightColor),
);
if (this.col == myProvider.currentCellCol && this.row == myProvider.currentCellRow) {
borders = Border.all(
color: borderSelectedColor,
width: borderWidth,
);
}
return Container(
decoration: BoxDecoration(
color: backgroundColor,
color: this.getBackgroundColor(myProvider),
border: borders,
),
child: GestureDetector(
......@@ -56,9 +34,9 @@ class Cell {
fit: BoxFit.fill
),
onTap: () {
if (this.col != null && this.row != null) {
if (!this.isFixed && (this.col != myProvider.currentCellCol || this.row != myProvider.currentCellRow)) {
myProvider.selectCell(this.col, this.row);
if (col != null && row != null) {
if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) {
myProvider.selectCell(col, row);
} else {
myProvider.selectCell(null, null);
}
......@@ -73,6 +51,8 @@ class Cell {
Color editableCellColorConflict = Colors.pink[100];
Color fixedCellColor = Colors.grey[300];
Color fixedCellColorConflict = Colors.pink[200];
Color editableSelectedValueColor = Colors.green[100];
Color fixedSelectedValueColor = Colors.green[300];
Color backgroundColor = editableCellColor;
......@@ -88,6 +68,14 @@ class Cell {
}
}
if (myProvider.showConflicts && (this.value == myProvider.currentCellValue)) {
if (this.isFixed) {
backgroundColor = fixedSelectedValueColor;
} else {
backgroundColor = editableSelectedValueColor;
}
}
return backgroundColor;
}
......@@ -111,12 +99,34 @@ class Cell {
fit: BoxFit.fill
),
onTap: () {
if (this.col != null && this.row != null) {
myProvider.updateCellValue(this.col, this.row, this.value);
if (myProvider.currentCellCol != null && myProvider.currentCellRow != null) {
myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, this.value);
}
myProvider.selectCell(null, null);
},
)
);
}
static Border getCellBorders(Data myProvider, int row, int col) {
int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical;
Border borders = Border.all(
color: cellBorderSelectedColor,
width: cellBorderWidth,
);
if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) {
borders = Border(
top: BorderSide(width: cellBorderWidth, color: ((row % blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor),
left: BorderSide(width: cellBorderWidth, color: ((col % blockSizeHorizontal) == 0) ? cellBorderDarkColor : cellBorderLightColor),
right: BorderSide(width: cellBorderWidth, color: (((col + 1) % blockSizeHorizontal) == 0) ? cellBorderDarkColor : cellBorderLightColor),
bottom: BorderSide(width: cellBorderWidth, color: (((row + 1) % blockSizeVertical) == 0) ? cellBorderDarkColor : cellBorderLightColor),
);
}
return borders;
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import '../entities/cell.dart';
import '../provider/data.dart';
import '../utils/board_utils.dart';
class Board {
static Container buildGameBoard(Data myProvider) {
Color borderColor = BoardUtils.checkBoardIsSolved(myProvider) ? Colors.green : Colors.orange;
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: borderColor,
borderRadius: BorderRadius.circular(2),
border: Border.all(
color: borderColor,
width: 2,
),
),
child: buildGameTileset(myProvider),
);
}
static Table buildGameTileset(Data myProvider) {
int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
List cells = myProvider.cells;
return Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
for (var row = 0; row < boardSize; row++)
TableRow(children: [
for (var col = 0; col < boardSize; col++)
Column(children: [
cells[row][col].widget(
myProvider,
Cell.getCellBorders(myProvider, row, col),
row,
col
)
]),
]),
]
);
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import '../entities/cell.dart';
import '../layout/board.dart';
import '../provider/data.dart';
import '../utils/board_utils.dart';
class Game {
static Container buildGameWidget(Data myProvider) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Board.buildGameBoard(myProvider),
SizedBox(height: 2),
Game.buildSelectCellValueBar(myProvider)
],
),
);
}
static Container buildSelectCellValueBar(Data myProvider) {
List cells = myProvider.cells;
Color borderColor = Colors.blue;
bool isCellSelected = (myProvider.currentCellCol != null && myProvider.currentCellRow != null);
bool isUpdatableCellSelected = isCellSelected ? !myProvider.cells[myProvider.currentCellRow][myProvider.currentCellCol].isFixed : false;
int maxValue = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
for (var value = 0; value <= maxValue; value++)
Column(
children: [
Cell(
isUpdatableCellSelected ? value : 0,
false
).widgetUpdateValue(myProvider)
]
),
]
),
]
),
);
}
}
import 'package:flutter/material.dart';
import '../provider/data.dart';
import '../utils/game_utils.dart';
class Parameters {
static const double _parameterButtonSize = 70;
static const double _startButtonSize = 150;
static Container buildParametersSelector(Data myProvider) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Parameters.buildParameterSelector(myProvider, 'difficulty'),
Parameters.buildParameterSelector(myProvider, 'size'),
Parameters.buildParameterSelector(myProvider, 'skin'),
Parameters.buildStartGameButton(myProvider),
],
),
);
}
static Column buildStartGameButton(Data myProvider) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Image(
image: AssetImage('assets/icons/button_start.png'),
width: _startButtonSize,
height: _startButtonSize,
fit: BoxFit.fill
),
onPressed: () => GameUtils.startGame(myProvider),
)
],
);
}
static Column buildParameterSelector(Data myProvider, String parameterCode) {
List availableValues = myProvider.getParameterAvailableValues(parameterCode);
return Column(
children: [
Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
for (var index = 0; index < availableValues.length; index++)
Column(
children: [
_buildParameterButton(myProvider, parameterCode, availableValues[index])
]
),
],
),
],
),
SizedBox(height: 20),
]
);
}
static FlatButton _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) {
String currentValue = myProvider.getParameterValue(parameterCode).toString();
bool isActive = (parameterValue == currentValue);
String imageAsset = 'assets/icons/' + parameterCode + '_' + parameterValue + '.png';
return FlatButton(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isActive ? Colors.blue : Colors.white,
width: 10,
),
),
child: Image(
image: AssetImage(imageAsset),
width: _parameterButtonSize,
height: _parameterButtonSize,
fit: BoxFit.fill
),
),
onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
);
}
}
......@@ -4,7 +4,7 @@ class Data extends ChangeNotifier {
// Configuration available values
List _availableDifficultyLevels = ['easy', 'medium', 'hard'];
List _availableSizes = [2, 3];
List _availableSizes = ['2x2', '3x2', '3x3'];
List _availableSkins = ['default', 'food', 'nature'];
List get availableDifficultyLevels => _availableDifficultyLevels;
......@@ -13,15 +13,18 @@ class Data extends ChangeNotifier {
// Application default configuration
String _level = 'medium';
int _size = 3;
String _size = '3x3';
String _skin = 'default';
bool _showConflicts = false;
// Game data
bool _stateRunning = false;
int _blockSizeVertical = null;
int _blockSizeHorizontal = null;
List _cells = [];
int _currentCellCol = null;
int _currentCellRow = null;
int _currentCellValue = null;
String get level => _level;
set updateLevel(String level) {
......@@ -29,9 +32,13 @@ class Data extends ChangeNotifier {
notifyListeners();
}
int get size => _size;
set updateSize(int size) {
String get size => _size;
int get blockSizeVertical => _blockSizeVertical;
int get blockSizeHorizontal => _blockSizeHorizontal;
set updateSize(String size) {
_size = size;
_blockSizeHorizontal = int.parse(_size.split('x')[0]);
_blockSizeVertical = int.parse(_size.split('x')[1]);
notifyListeners();
}
......@@ -69,7 +76,7 @@ class Data extends ChangeNotifier {
}
}
setParameterValue(String parameterCode, var parameterValue) {
setParameterValue(String parameterCode, String parameterValue) {
switch(parameterCode) {
case 'difficulty': { updateLevel = parameterValue; }
break;
......@@ -98,9 +105,21 @@ class Data extends ChangeNotifier {
notifyListeners();
}
int get currentCellValue => _currentCellValue;
set updateCurrentCellValue(int currentCellValue) {
_currentCellValue = currentCellValue;
notifyListeners();
}
selectCell(int col, int row) {
_currentCellCol = col;
_currentCellRow = row;
_currentCellValue = null;
if (row != null && col != null) {
if (_cells[row][col].value != 0) {
_currentCellValue = _cells[row][col].value;
}
}
notifyListeners();
}
......
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../entities/cell.dart';
import '../layout/game.dart';
import '../layout/parameters.dart';
import '../provider/data.dart';
import '../utils/random_pick_grid.dart';
import '../utils/game_utils.dart';
class Home extends StatelessWidget {
static const String id = 'home';
static const double _parameterButtonSize = 70;
static const double _startButtonSize = 150;
Future<void> resetGame(Data myProvider) async {
myProvider.updateStateRunning = false;
}
Future<void> startGame(Data myProvider) async {
myProvider.updateStateRunning = true;
myProvider.updateCells = createEmptyBoard(myProvider.size);
pickGrid(myProvider);
}
printGrid(List cells) {
print('');
print('-------');
for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
String row = '';
for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
row += cells[rowIndex][colIndex].value.toString();
}
print(row);
}
print('-------');
print('');
}
Future<void> pickGrid(Data myProvider) async {
int size = myProvider.size;
String grid;
RandomPickGrid randomPickGrid;
randomPickGrid = RandomPickGrid();
await randomPickGrid.init(myProvider.level, size);
if (randomPickGrid.grid != null) {
grid = randomPickGrid.grid;
}
if (grid.length == pow(size, 4)) {
myProvider.updateCells = createBoardFromTemplate(grid);
}
}
List createBoardFromTemplate(String grid) {
List cells = [];
int size = int.parse(pow(grid.length, 1/4).toStringAsFixed(0));
int sideLength = pow(size, 2);
int index = 0;
for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) {
List row = [];
for (var colIndex = 0; colIndex < sideLength; colIndex++) {
int value = int.parse(grid[index++]);
row.add(Cell(value, colIndex, rowIndex, (value != 0)));
}
cells.add(row);
}
List<String> allowedFlip = ['', 'horizontal', 'vertical'];
List<String> allowedRotate = ['', 'left', 'right'];
var rand = new Random();
String flip = allowedFlip[rand.nextInt(allowedFlip.length)];
String rotate = allowedRotate[rand.nextInt(allowedRotate.length)];
switch(flip) {
case 'horizontal': {
List transformedBoard = createEmptyBoard(size);
for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) {
for (var colIndex = 0; colIndex < sideLength; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[sideLength - rowIndex - 1][colIndex].value;
}
}
cells = transformedBoard;
}
break;
case 'vertical': {
List transformedBoard = createEmptyBoard(size);
for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) {
for (var colIndex = 0; colIndex < sideLength; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[rowIndex][sideLength - colIndex - 1].value;
}
}
cells = transformedBoard;
}
break;
}
switch(rotate) {
case 'left': {
List transformedBoard = createEmptyBoard(size);
for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) {
for (var colIndex = 0; colIndex < sideLength; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[colIndex][sideLength - rowIndex - 1].value;
}
}
cells = transformedBoard;
}
break;
case 'right': {
List transformedBoard = createEmptyBoard(size);
for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) {
for (var colIndex = 0; colIndex < sideLength; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[sideLength - colIndex - 1][rowIndex].value;
}
}
cells = transformedBoard;
}
break;
}
// Fix cells fixed states
for (var rowIndex = 0; rowIndex < sideLength; rowIndex++) {
for (var colIndex = 0; colIndex < sideLength; colIndex++) {
cells[rowIndex][colIndex].isFixed = (cells[rowIndex][colIndex].value != 0) ? true : false;
}
}
return cells;
}
List createEmptyBoard(int size) {
int index = 0;
List cells = [];
for (var rowIndex = 0; rowIndex < pow(size, 2); rowIndex++) {
List row = [];
for (var colIndex = 0; colIndex < pow(size, 2); colIndex++) {
row.add(Cell(0, colIndex, rowIndex, false));
}
cells.add(row);
}
return cells;
}
Container _buildParametersSelector(Data myProvider) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildParameterSelector(myProvider, 'difficulty'),
_buildParameterSelector(myProvider, 'size'),
_buildParameterSelector(myProvider, 'skin'),
_buildStartGameButton(myProvider),
],
),
);
}
Column _buildStartGameButton(Data myProvider) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
FlatButton(
child: Image(
image: AssetImage('assets/icons/button_start.png'),
width: _startButtonSize,
height: _startButtonSize,
fit: BoxFit.fill
),
onPressed: () => startGame(myProvider),
)
],
);
}
Column _buildParameterSelector(Data myProvider, String parameterCode) {
List availableValues = myProvider.getParameterAvailableValues(parameterCode);
return Column(
children: [
Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
for (var index = 0; index < availableValues.length; index++)
Column(
children: [
_buildParameterButton(myProvider, parameterCode, availableValues[index])
]
),
],
),
],
),
SizedBox(height: 20),
]
);
}
FlatButton _buildParameterButton(Data myProvider, String parameterCode, var parameterValue) {
String currentValue = myProvider.getParameterValue(parameterCode).toString();
bool isActive = (parameterValue.toString() == currentValue);
String imageAsset = 'assets/icons/' + parameterCode + '_' + parameterValue.toString() + '.png';
return FlatButton(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isActive ? Colors.blue : Colors.white,
width: 10,
),
),
child: Image(
image: AssetImage(imageAsset),
width: _parameterButtonSize,
height: _parameterButtonSize,
fit: BoxFit.fill
),
),
onPressed: () => myProvider.setParameterValue(parameterCode, parameterValue),
);
}
Container _buildGameWidget(Data myProvider) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildGameBoard(myProvider),
SizedBox(height: 2),
_buildSelectCellValueBar(myProvider)
],
),
);
}
Container _buildGameBoard(Data myProvider) {
List cells = myProvider.cells;
int size = myProvider.size;
Color borderColor = _checkBoardIsSolved(myProvider) ? Colors.green : Colors.orange;
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
decoration: BoxDecoration(
color: borderColor,
borderRadius: BorderRadius.circular(2),
border: Border.all(
color: borderColor,
width: 2,
),
),
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
for (var row = 0; row < pow(size, 2); row++)
TableRow(children: [
for (var col = 0; col < pow(size, 2); col++)
Column(children: [
cells[row][col].widget(myProvider)
]),
]),
]
),
);
}
Container _buildSelectCellValueBar(Data myProvider) {
List cells = myProvider.cells;
int size = myProvider.size;
Color borderColor = Colors.blue;
bool isCellSelected = (myProvider.currentCellCol != null && myProvider.currentCellRow != null);
return Container(
margin: EdgeInsets.all(2),
padding: EdgeInsets.all(2),
child: Table(
defaultColumnWidth: IntrinsicColumnWidth(),
children: [
TableRow(
children: [
for (var value = 0; value < (pow(size, 2) + 1); value++)
Column(
children: [
Cell(
isCellSelected ? value : 0,
isCellSelected ? myProvider.currentCellCol : null,
isCellSelected ? myProvider.currentCellRow : null,
false
).widgetUpdateValue(myProvider)
]
),
]
),
]
),
);
}
bool _checkBoardIsSolved(Data myProvider) {
List cells = myProvider.cells;
int size = myProvider.size;
int sideLength = pow(size, 2);
bool isSolved = true;
// reset conflict states
for (var row = 0; row < sideLength; row++) {
for (var col = 0; col < sideLength; col++) {
cells[row][col].conflictsCount = 0;
}
}
// check grid is fully completed
for (var row = 0; row < sideLength; row++) {
for (var col = 0; col < sideLength; col++) {
if (cells[row][col].value == 0) {
isSolved = false;
}
}
}
// check lines does not contains a value twice
for (var row = 0; row < sideLength; row++) {
List values = [];
for (var col = 0; col < sideLength; col++) {
int value = cells[row][col].value;
if (value != 0) {
values.add(value);
}
}
List distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
print('line ' + row.toString() + ' contains duplicates');
// Add line to cells in conflict
for (var col = 0; col < sideLength; col++) {
cells[row][col].conflictsCount++;
}
isSolved = false;
}
}
// check columns does not contains a value twice
for (var col = 0; col < sideLength; col++) {
List values = [];
for (var row = 0; row < sideLength; row++) {
int value = cells[row][col].value;
if (value != 0) {
values.add(value);
}
}
List distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
print('column ' + col.toString() + ' contains duplicates');
// Add column to cells in conflict
for (var row = 0; row < sideLength; row++) {
cells[row][col].conflictsCount++;
}
isSolved = false;
}
}
// check blocks does not contains a value twice
for (var blockRow = 0; blockRow < size; blockRow++) {
for (var blockCol = 0; blockCol < size; blockCol++) {
List values = [];
for (var rowInBlock = 0; rowInBlock < size; rowInBlock++) {
for (var colInBlock = 0; colInBlock < size; colInBlock++) {
int row = (blockRow * size) + rowInBlock;
int col = (blockCol * size) + colInBlock;
int value = cells[row][col].value;
if (value != 0) {
values.add(value);
}
}
}
List distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
print('block [' + blockCol.toString() + ',' + blockRow.toString() + '] contains duplicates');
// Add blocks to cells in conflict
for (var rowInBlock = 0; rowInBlock < size; rowInBlock++) {
for (var colInBlock = 0; colInBlock < size; colInBlock++) {
int row = (blockRow * size) + rowInBlock;
int col = (blockCol * size) + colInBlock;
cells[row][col].conflictsCount++;
}
}
isSolved = false;
}
}
}
if (isSolved) {
print('-> ok sudoku solved!');
}
return isSolved;
}
@override
Widget build(BuildContext context) {
Data myProvider = Provider.of<Data>(context);
......@@ -424,7 +16,7 @@ class Home extends StatelessWidget {
List<Widget> menuActions = [];
if (myProvider.stateRunning) {
menuActions.add(
menuActions = [
FlatButton(
child: Container(
decoration: BoxDecoration(
......@@ -440,29 +32,39 @@ class Home extends StatelessWidget {
fit: BoxFit.fill
),
),
onPressed: () { myProvider.updateShowConflicts = !myProvider.showConflicts; },
)
);
menuActions.add(
onPressed: () {
myProvider.updateShowConflicts = !myProvider.showConflicts;
},
),
FlatButton(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: Colors.blue,
width: 4,
),
),
margin: EdgeInsets.all(8),
child: Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill
),
onPressed: () => resetGame(myProvider),
),
);
onPressed: () => GameUtils.resetGame(myProvider),
),
];
}
return Scaffold(
appBar: AppBar(
title: Text('🔢'),
actions: menuActions,
),
body: SafeArea(
child: Center(
child: myProvider.stateRunning ? _buildGameWidget(myProvider) : _buildParametersSelector(myProvider)
child: myProvider.stateRunning
? Game.buildGameWidget(myProvider)
: Parameters.buildParametersSelector(myProvider)
),
)
);
......
import 'dart:math';
import '../entities/cell.dart';
import '../utils/random_pick_grid.dart';
import '../provider/data.dart';
class BoardUtils {
static printGrid(List cells) {
print('');
print('-------');
for (var rowIndex = 0; rowIndex < cells.length; rowIndex++) {
String row = '';
for (var colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
row += cells[rowIndex][colIndex].value.toString();
}
print(row);
}
print('-------');
print('');
}
static Future<void> pickGrid(Data myProvider) async {
String grid;
RandomPickGrid randomPickGrid;
randomPickGrid = RandomPickGrid();
await randomPickGrid.init(myProvider.level, myProvider.size);
if (randomPickGrid.grid != null) {
grid = randomPickGrid.grid;
}
int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical;
if (grid.length == pow(blockSizeHorizontal * blockSizeVertical, 2)) {
print('Picked grid from template: ' + grid);
bool isSymetric = (blockSizeHorizontal == blockSizeVertical);
myProvider.updateCells = BoardUtils.createBoardFromTemplate(grid, isSymetric);
}
}
static List createEmptyBoard(int boardSize) {
int index = 0;
List cells = [];
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
List row = [];
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
row.add(Cell(0, false));
}
cells.add(row);
}
return cells;
}
static List createBoardFromTemplate(String grid, bool isSymetric) {
List cells = [];
int boardSize = int.parse(pow(grid.length, 1/2).toStringAsFixed(0));
int index = 0;
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
List row = [];
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
int value = int.parse(grid[index++]);
row.add(Cell(value, (value != 0)));
}
cells.add(row);
}
List<String> allowedFlip = ['none', 'horizontal', 'vertical'];
List<String> allowedRotate = ['none', 'left', 'right'];
// Forbid rotation if blocks are not symetric
if (!isSymetric) {
allowedRotate = ['none'];
}
var rand = new Random();
String flip = allowedFlip[rand.nextInt(allowedFlip.length)];
String rotate = allowedRotate[rand.nextInt(allowedRotate.length)];
print('flip board: ' + flip);
print('rotate board: ' + rotate);
switch(flip) {
case 'horizontal': {
List transformedBoard = createEmptyBoard(boardSize);
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[boardSize - rowIndex - 1][colIndex].value;
}
}
cells = transformedBoard;
}
break;
case 'vertical': {
List transformedBoard = createEmptyBoard(boardSize);
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[rowIndex][boardSize - colIndex - 1].value;
}
}
cells = transformedBoard;
}
break;
}
switch(rotate) {
case 'left': {
List transformedBoard = createEmptyBoard(boardSize);
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[colIndex][boardSize - rowIndex - 1].value;
}
}
cells = transformedBoard;
}
break;
case 'right': {
List transformedBoard = createEmptyBoard(boardSize);
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = cells[boardSize - colIndex - 1][rowIndex].value;
}
}
cells = transformedBoard;
}
break;
}
// Fix cells fixed states
for (var rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (var colIndex = 0; colIndex < boardSize; colIndex++) {
cells[rowIndex][colIndex].isFixed = (cells[rowIndex][colIndex].value != 0) ? true : false;
}
}
printGrid(cells);
return cells;
}
static bool checkBoardIsSolved(Data myProvider) {
List cells = myProvider.cells;
int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical;
int boardSize = blockSizeHorizontal * blockSizeVertical;
bool isSolved = true;
// reset conflict states
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
cells[row][col].conflictsCount = 0;
}
}
// check grid is fully completed
for (var row = 0; row < boardSize; row++) {
for (var col = 0; col < boardSize; col++) {
if (cells[row][col].value == 0) {
isSolved = false;
}
}
}
// check lines does not contains a value twice
for (var row = 0; row < boardSize; row++) {
List values = [];
for (var col = 0; col < boardSize; col++) {
int value = cells[row][col].value;
if (value != 0) {
values.add(value);
}
}
List distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
print('line ' + row.toString() + ' contains duplicates');
// Add line to cells in conflict
for (var col = 0; col < boardSize; col++) {
cells[row][col].conflictsCount++;
}
isSolved = false;
}
}
// check columns does not contains a value twice
for (var col = 0; col < boardSize; col++) {
List values = [];
for (var row = 0; row < boardSize; row++) {
int value = cells[row][col].value;
if (value != 0) {
values.add(value);
}
}
List distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
print('column ' + col.toString() + ' contains duplicates');
// Add column to cells in conflict
for (var row = 0; row < boardSize; row++) {
cells[row][col].conflictsCount++;
}
isSolved = false;
}
}
// check blocks does not contains a value twice
int horizontalBlocksCount = blockSizeVertical;
int verticalBlocksCount = blockSizeHorizontal;
for (var blockRow = 0; blockRow < verticalBlocksCount; blockRow++) {
for (var blockCol = 0; blockCol < horizontalBlocksCount; blockCol++) {
List values = [];
for (var rowInBlock = 0; rowInBlock < blockSizeVertical; rowInBlock++) {
for (var colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) {
int row = (blockRow * blockSizeVertical) + rowInBlock;
int col = (blockCol * blockSizeHorizontal) + colInBlock;
int value = cells[row][col].value;
if (value != 0) {
values.add(value);
}
}
}
List distinctValues = values.toSet().toList();
if (values.length != distinctValues.length) {
print('block [' + blockCol.toString() + ',' + blockRow.toString() + '] contains duplicates');
// Add blocks to cells in conflict
for (var rowInBlock = 0; rowInBlock < blockSizeVertical; rowInBlock++) {
for (var colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) {
int row = (blockRow * blockSizeVertical) + rowInBlock;
int col = (blockCol * blockSizeHorizontal) + colInBlock;
cells[row][col].conflictsCount++;
}
}
isSolved = false;
}
}
}
if (isSolved) {
print('-> ok sudoku solved!');
}
return isSolved;
}
}
import '../provider/data.dart';
import '../utils/board_utils.dart';
class GameUtils {
static Future<void> resetGame(Data myProvider) async {
myProvider.updateStateRunning = false;
}
static Future<void> startGame(Data myProvider) async {
print('Start new game: ' + myProvider.size);
myProvider.updateSize = myProvider.size;
myProvider.updateStateRunning = true;
myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical);
BoardUtils.pickGrid(myProvider);
}
}
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
......@@ -7,27 +6,25 @@ class RandomPickGrid {
String _grid;
init(String difficulty, int size) async {
init(String difficulty, String size) async {
_grid = '';
await gridFromLocalFile(difficulty, size);
}
Future<void> gridFromLocalFile(String difficulty, int size) async {
String sizeAsString = size.toString() + 'x' + size.toString();
Future<void> gridFromLocalFile(String difficulty, String size) async {
// Get global grids list
List grids;
try {
String jsonString = await rootBundle.loadString('assets/files/templates.json');
final jsonResponse = await json.decode(jsonString);
grids = jsonResponse['templates'][sizeAsString][difficulty];
grids = jsonResponse['templates'][size][difficulty];
} catch (e) {
print("$e");
}
// Check we have enough grids
if (grids.length < 1) {
print('Not enough grids [' + sizeAsString + ', ' + difficulty + '] in templates.');
print('Not enough grids [' + size + ', ' + difficulty + '] in templates.');
}
// Randomize grids list
......
......@@ -7,7 +7,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.6.1"
version: "2.7.0"
boolean_selector:
dependency: transitive
description:
......@@ -73,7 +73,7 @@ packages:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
version: "1.4.0"
nested:
dependency: transitive
description:
......@@ -141,7 +141,7 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.0"
version: "0.4.0"
typed_data:
dependency: transitive
description:
......