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); } 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)) { List cells = createBoardFromTemplate(grid); myProvider.updateCells = cells; } } List createBoardFromTemplate(String grid) { List cells = []; int size = int.parse(pow(grid.length, 1/4).toStringAsFixed(0)); int index = 0; for (var rowIndex = 0; rowIndex < pow(size, 2); rowIndex++) { List row = []; for (var colIndex = 0; colIndex < pow(size, 2); colIndex++) { int value = int.parse(grid[index++]); row.add(Cell(value, colIndex, rowIndex, (value != 0))); } cells.add(row); } // TODO: shuffle/flip/rotate grid 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; // check grid is fully completed for (var row = 0; row < pow(size, 2); row++) { for (var col = 0; col < pow(size, 2); col++) { if (cells[row][col].value == 0) { print('grid not complete'); return false; } } } print('ok grid complete complete'); // check lines does not contains a value twice for (var row = 0; row < pow(size, 2); row++) { List values = []; for (var col = 0; col < pow(size, 2); col++) { values.add(cells[row][col].value); } List distinctValues = values.toSet().toList(); if (values.length != distinctValues.length) { print('line ' + row.toString() + ' contains duplicates'); return false; } } print('ok no line with duplicates'); // check columns does not contains a value twice for (var col = 0; col < pow(size, 2); col++) { List values = []; for (var row = 0; row < pow(size, 2); row++) { values.add(cells[row][col].value); } List distinctValues = values.toSet().toList(); if (values.length != distinctValues.length) { print('column ' + col.toString() + ' contains duplicates'); return false; } } print('ok no column with duplicates'); // 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; values.add(cells[row][col].value); } } List distinctValues = values.toSet().toList(); if (values.length != distinctValues.length) { print('block [' + blockCol.toString() + ',' + blockRow.toString() + '] contains duplicates'); return false; } } } print('ok no block with duplicates'); print('-> ok sudoku solved!'); return true; } @override Widget build(BuildContext context) { Data myProvider = Provider.of<Data>(context); return Scaffold( appBar: AppBar( title: Text('🔢'), actions: [ if (myProvider.stateRunning) FlatButton( child: Text('◀️'), onPressed: () => resetGame(myProvider), ), ], ), body: SafeArea( child: Center( child: myProvider.stateRunning ? _buildGameWidget(myProvider) : _buildParametersSelector(myProvider) ), ) ); } }