import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../entities/cell.dart'; import '../provider/data.dart'; import '../utils/random_pick_grid.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); List<Widget> menuActions = []; if (myProvider.stateRunning) { menuActions.add( FlatButton( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all( color: myProvider.showConflicts ? Colors.white : Colors.blue, width: 4, ), ), margin: EdgeInsets.all(8), child: Image( image: AssetImage('assets/icons/button_show_conflicts.png'), fit: BoxFit.fill ), ), onPressed: () { myProvider.updateShowConflicts = !myProvider.showConflicts; }, ) ); menuActions.add( FlatButton( child: Image( image: AssetImage('assets/icons/button_back.png'), fit: BoxFit.fill ), onPressed: () => resetGame(myProvider), ), ); } return Scaffold( appBar: AppBar( title: Text('🔢'), actions: menuActions, ), body: SafeArea( child: Center( child: myProvider.stateRunning ? _buildGameWidget(myProvider) : _buildParametersSelector(myProvider) ), ) ); } }