diff --git a/android/gradle.properties b/android/gradle.properties index 663881258a10822c0b4abc064b6e0bc0ccf48833..81949dfd2077495aaea8a6bc81ad9c75442f9ebb 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -app.versionName=0.0.13 -app.versionCode=13 +app.versionName=0.0.14 +app.versionCode=14 diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart index 369a5f7f9030cee73c12fd6a4e07d88c68f47077..e4d2cf5e93e0b9b57e9cfb5d4f9010b74579456f 100644 --- a/lib/entities/cell.dart +++ b/lib/entities/cell.dart @@ -1,23 +1,23 @@ 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'; @@ -25,29 +25,9 @@ class Cell { 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 +36,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 (!this.isFixed && (col != myProvider.currentCellCol || row != myProvider.currentCellRow)) { + myProvider.selectCell(col, row); } else { myProvider.selectCell(null, null); } @@ -111,12 +91,33 @@ 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 size = myProvider.size; + + Border borders = Border.all( + color: cellBorderSelectedColor, + width: cellBorderWidth, + ); + + if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) { + borders = Border( + top: BorderSide(width: cellBorderWidth, color: ((row % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), + left: BorderSide(width: cellBorderWidth, color: ((col % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), + right: BorderSide(width: cellBorderWidth, color: (((col + 1) % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), + bottom: BorderSide(width: cellBorderWidth, color: (((row + 1) % size) == 0) ? cellBorderDarkColor : cellBorderLightColor), + ); + } + + return borders; + } + } diff --git a/lib/layout/board.dart b/lib/layout/board.dart new file mode 100644 index 0000000000000000000000000000000000000000..cd4905eebcc311c65a44d58f0fe5c507210457cc --- /dev/null +++ b/lib/layout/board.dart @@ -0,0 +1,53 @@ +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 size = myProvider.size; + List cells = myProvider.cells; + + return 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, + Cell.getCellBorders(myProvider, row, col), + row, + col + ) + ]), + ]), + ] + ); + } + +} diff --git a/lib/layout/game.dart b/lib/layout/game.dart new file mode 100644 index 0000000000000000000000000000000000000000..bda08eb0cc9725813dde59daa799e28da34b8974 --- /dev/null +++ b/lib/layout/game.dart @@ -0,0 +1,61 @@ +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; + int size = myProvider.size; + + Color borderColor = Colors.blue; + + bool isCellSelected = (myProvider.currentCellCol != null && myProvider.currentCellRow != null); + int maxValue = pow(size, 2) + 1; + + 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( + isCellSelected ? value : 0, + false + ).widgetUpdateValue(myProvider) + ] + ), + ] + ), + ] + ), + ); + } + +} diff --git a/lib/layout/parameters.dart b/lib/layout/parameters.dart new file mode 100644 index 0000000000000000000000000000000000000000..b4bb302380b2f77ad5baedf5a5c40422f3ee2e59 --- /dev/null +++ b/lib/layout/parameters.dart @@ -0,0 +1,101 @@ +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, 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), + ); + } + + +} diff --git a/lib/screens/home.dart b/lib/screens/home.dart index ce7543317a7ebe0e2fe312e842ef1a6b2a35d5e1..7b5b549216bebe778c83bbd47726e3cf36916fe2 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -1,422 +1,14 @@ -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,19 +32,18 @@ class Home extends StatelessWidget { fit: BoxFit.fill ), ), - onPressed: () { myProvider.updateShowConflicts = !myProvider.showConflicts; }, - ) - ); - - menuActions.add( + onPressed: () { + myProvider.updateShowConflicts = !myProvider.showConflicts; + }, + ), FlatButton( child: Image( image: AssetImage('assets/icons/button_back.png'), fit: BoxFit.fill ), - onPressed: () => resetGame(myProvider), + onPressed: () => GameUtils.resetGame(myProvider), ), - ); + ]; } return Scaffold( @@ -462,7 +53,9 @@ class Home extends StatelessWidget { ), body: SafeArea( child: Center( - child: myProvider.stateRunning ? _buildGameWidget(myProvider) : _buildParametersSelector(myProvider) + child: myProvider.stateRunning + ? Game.buildGameWidget(myProvider) + : Parameters.buildParametersSelector(myProvider) ), ) ); diff --git a/lib/utils/board_utils.dart b/lib/utils/board_utils.dart new file mode 100644 index 0000000000000000000000000000000000000000..539bf4da6314131c07774df64324da05e77ecbd8 --- /dev/null +++ b/lib/utils/board_utils.dart @@ -0,0 +1,241 @@ +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 { + 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 = BoardUtils.createBoardFromTemplate(grid); + } + } + + + static 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, false)); + } + cells.add(row); + } + + return cells; + } + + + static 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, (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; + } + } + + printGrid(cells); + + return cells; + } + + + static 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; + } + +} diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart new file mode 100644 index 0000000000000000000000000000000000000000..dd9d43a09f0296a1207398a708c503c2855e558d --- /dev/null +++ b/lib/utils/game_utils.dart @@ -0,0 +1,15 @@ +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 { + myProvider.updateStateRunning = true; + myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.size); + BoardUtils.pickGrid(myProvider); + } +} diff --git a/lib/utils/random_pick_grid.dart b/lib/utils/random_pick_grid.dart index aa08feb3b9f389156b938ec0a9ba54c60abef75c..5348f2747aa601da0fa65529413df9443e1ee65d 100644 --- a/lib/utils/random_pick_grid.dart +++ b/lib/utils/random_pick_grid.dart @@ -1,4 +1,3 @@ -import 'dart:async'; import 'dart:convert'; import 'package:flutter/services.dart';