Skip to content
Snippets Groups Projects
Commit 84e3453f authored by Benoît Harrault's avatar Benoît Harrault
Browse files

Improve game model / conception

parent aa0e5f50
No related branches found
No related tags found
1 merge request!68Resolve "Improve game architecture"
Pipeline #5453 passed
Showing
with 530 additions and 226 deletions
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
app.versionName=0.1.16 app.versionName=0.1.17
app.versionCode=65 app.versionCode=66
Improve game architecture.
Amélioration de la conception du jeu.
...@@ -5,27 +5,30 @@ import 'package:sudoku/utils/board_animate.dart'; ...@@ -5,27 +5,30 @@ import 'package:sudoku/utils/board_animate.dart';
import 'package:sudoku/utils/board_utils.dart'; import 'package:sudoku/utils/board_utils.dart';
class Cell { class Cell {
int value; const Cell({
bool isFixed; required this.row,
int conflictsCount = 0; required this.col,
required this.value,
required this.isFixed,
});
bool isAnimated = false; final int row;
final int col;
final int value;
final bool isFixed;
Cell( static const Cell none = Cell(row: 0, col: 0, value: 0, isFixed: true);
this.value,
this.isFixed,
);
/* /*
* Build widget for board cell, with interactions * Build widget for board cell, with interactions
*/ */
Container widget(Data myProvider, int row, int col) { Widget widget(Data myProvider) {
final String imageAsset = getImageAssetName(myProvider); final String imageAsset = getImageAssetName(myProvider);
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: getBackgroundColor(myProvider), color: getBackgroundColor(myProvider),
border: getCellBorders(myProvider, row, col), border: getCellBorders(myProvider),
), ),
child: GestureDetector( child: GestureDetector(
child: AnimatedSwitcher( child: AnimatedSwitcher(
...@@ -40,7 +43,7 @@ class Cell { ...@@ -40,7 +43,7 @@ class Cell {
), ),
), ),
onTap: () { onTap: () {
if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) { if (col != myProvider.selectedCellCol || row != myProvider.selectedCellRow) {
myProvider.selectCell(col, row); myProvider.selectCell(col, row);
} else { } else {
myProvider.selectCell(null, null); myProvider.selectCell(null, null);
...@@ -62,14 +65,14 @@ class Cell { ...@@ -62,14 +65,14 @@ class Cell {
Color backgroundColor = Colors.grey.shade200; Color backgroundColor = Colors.grey.shade200;
if (myProvider.showConflicts && if (myProvider.showConflicts &&
myProvider.currentCellCol != null && myProvider.selectedCellCol != null &&
myProvider.currentCellRow != null) { myProvider.selectedCellRow != null) {
final List<List<Cell>> cells = myProvider.cells; final Board cells = myProvider.board;
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
final int blockSizeVertical = myProvider.blockSizeVertical; final int blockSizeVertical = myProvider.blockSizeVertical;
if (!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical, if (!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical,
myProvider.currentCellCol, myProvider.currentCellRow, value)) { myProvider.selectedCellCol, myProvider.selectedCellRow, value)) {
backgroundColor = Colors.pink.shade100; backgroundColor = Colors.pink.shade100;
} }
} }
...@@ -85,9 +88,9 @@ class Cell { ...@@ -85,9 +88,9 @@ class Cell {
child: GestureDetector( child: GestureDetector(
child: Image(image: AssetImage(imageAsset), fit: BoxFit.fill), child: Image(image: AssetImage(imageAsset), fit: BoxFit.fill),
onTap: () { onTap: () {
if (myProvider.currentCellCol != null && myProvider.currentCellRow != null) { if (myProvider.selectedCellCol != null && myProvider.selectedCellRow != null) {
myProvider.updateCellValue( myProvider.updateCellValue(
myProvider.currentCellCol, myProvider.currentCellRow, value); myProvider.selectedCellCol, myProvider.selectedCellRow, value);
} }
myProvider.selectCell(null, null); myProvider.selectCell(null, null);
if (BoardUtils.checkBoardIsSolved(myProvider)) { if (BoardUtils.checkBoardIsSolved(myProvider)) {
...@@ -126,6 +129,8 @@ class Cell { ...@@ -126,6 +129,8 @@ class Cell {
backgroundColor = fixedCellColor; backgroundColor = fixedCellColor;
} }
final int conflictsCount = myProvider.boardConflicts[row][col];
if (myProvider.showConflicts && (conflictsCount != 0)) { if (myProvider.showConflicts && (conflictsCount != 0)) {
if (isFixed) { if (isFixed) {
backgroundColor = fixedCellColorConflict; backgroundColor = fixedCellColorConflict;
...@@ -134,7 +139,7 @@ class Cell { ...@@ -134,7 +139,7 @@ class Cell {
} }
} }
if (myProvider.showConflicts && (value == myProvider.currentCellValue)) { if (myProvider.showConflicts && (value == myProvider.selectedCellValue)) {
if (isFixed) { if (isFixed) {
backgroundColor = fixedSelectedValueColor; backgroundColor = fixedSelectedValueColor;
} else { } else {
...@@ -142,6 +147,8 @@ class Cell { ...@@ -142,6 +147,8 @@ class Cell {
} }
} }
final bool isAnimated = myProvider.boardAnimated[row][col];
if (isAnimated) { if (isAnimated) {
if (isFixed) { if (isFixed) {
backgroundColor = fixedAnimated; backgroundColor = fixedAnimated;
...@@ -154,7 +161,9 @@ class Cell { ...@@ -154,7 +161,9 @@ class Cell {
} }
// Compute cell borders, from board size and cell state // Compute cell borders, from board size and cell state
Border getCellBorders(Data myProvider, int row, int col) { Border getCellBorders(Data myProvider) {
// final int row = , int col;
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
final int blockSizeVertical = myProvider.blockSizeVertical; final int blockSizeVertical = myProvider.blockSizeVertical;
...@@ -184,7 +193,7 @@ class Cell { ...@@ -184,7 +193,7 @@ class Cell {
); );
// Update cell borders if not currently selected cell // Update cell borders if not currently selected cell
if (col != myProvider.currentCellCol || row != myProvider.currentCellRow) { if (col != myProvider.selectedCellCol || row != myProvider.selectedCellRow) {
borders = Border( borders = Border(
top: BorderSide( top: BorderSide(
width: cellBorderWidth, width: cellBorderWidth,
......
...@@ -4,7 +4,7 @@ import 'package:provider/provider.dart'; ...@@ -4,7 +4,7 @@ import 'package:provider/provider.dart';
import 'package:overlay_support/overlay_support.dart'; import 'package:overlay_support/overlay_support.dart';
import 'package:sudoku/provider/data.dart'; import 'package:sudoku/provider/data.dart';
import 'package:sudoku/screens/home.dart'; import 'package:sudoku/ui/screens/home.dart';
void main() { void main() {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
...@@ -27,9 +27,6 @@ class MyApp extends StatelessWidget { ...@@ -27,9 +27,6 @@ class MyApp extends StatelessWidget {
visualDensity: VisualDensity.adaptivePlatformDensity, visualDensity: VisualDensity.adaptivePlatformDensity,
), ),
home: const Home(), home: const Home(),
routes: {
Home.id: (context) => const Home(),
},
), ),
); );
}), }),
......
...@@ -6,6 +6,11 @@ import 'package:shared_preferences/shared_preferences.dart'; ...@@ -6,6 +6,11 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:sudoku/entities/cell.dart'; import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/utils/tools.dart'; import 'package:sudoku/utils/tools.dart';
typedef Board = List<List<Cell>>;
typedef ConflictsCount = List<List<int>>;
typedef AnimatedBoard = List<List<bool>>;
typedef AnimatedBoardSequence = List<AnimatedBoard>;
class Data extends ChangeNotifier { class Data extends ChangeNotifier {
// Configuration available values // Configuration available values
final List<String> _availableParameters = ['level', 'size', 'skin']; final List<String> _availableParameters = ['level', 'size', 'skin'];
...@@ -39,15 +44,17 @@ class Data extends ChangeNotifier { ...@@ -39,15 +44,17 @@ class Data extends ChangeNotifier {
bool _animationInProgress = false; bool _animationInProgress = false;
int _blockSizeVertical = 0; int _blockSizeVertical = 0;
int _blockSizeHorizontal = 0; int _blockSizeHorizontal = 0;
List<List<Cell>> _cells = []; Board _board = [];
List<List<Cell>> _cellsSolved = []; Board _boardSolved = [];
ConflictsCount _boardConflicts = [];
AnimatedBoard _boardAnimated = [];
List<int> _shuffledCellValues = []; List<int> _shuffledCellValues = [];
int? _currentCellCol; int? _selectedCellCol;
int? _currentCellRow; int? _selectedCellRow;
int? _currentCellValue; int? _selectedCellValue;
bool _showConflicts = false; bool _showConflicts = false;
int _givenTipsCount = 0; int _givenTipsCount = 0;
String _currentState = ''; String _currentSavedState = '';
void updateParameterLevel(String parameterLevel) { void updateParameterLevel(String parameterLevel) {
_parameterLevel = parameterLevel; _parameterLevel = parameterLevel;
...@@ -115,15 +122,15 @@ class Data extends ChangeNotifier { ...@@ -115,15 +122,15 @@ class Data extends ChangeNotifier {
setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault); setParameterValue('skin', prefs.getString('skin') ?? _parameterSkinDefault);
} }
String get currentState => _currentState; String get currentSavedState => _currentSavedState;
String computeCurrentGameState() { String computeCurrentGameState() {
String cellsValues = ''; String cellsValues = '';
const String stringValues = '0123456789ABCDEFG'; const String stringValues = '0123456789ABCDEFG';
for (int rowIndex = 0; rowIndex < _cells.length; rowIndex++) { for (int rowIndex = 0; rowIndex < _board.length; rowIndex++) {
for (int colIndex = 0; colIndex < _cells[rowIndex].length; colIndex++) { for (int colIndex = 0; colIndex < _board[rowIndex].length; colIndex++) {
cellsValues += stringValues[_cells[rowIndex][colIndex].value]; cellsValues += stringValues[_board[rowIndex][colIndex].value];
cellsValues += _cells[rowIndex][colIndex].isFixed ? 'x' : ' '; cellsValues += _board[rowIndex][colIndex].isFixed ? 'x' : ' ';
} }
} }
...@@ -140,35 +147,35 @@ class Data extends ChangeNotifier { ...@@ -140,35 +147,35 @@ class Data extends ChangeNotifier {
void saveCurrentGameState() async { void saveCurrentGameState() async {
if (_gameIsRunning) { if (_gameIsRunning) {
_currentState = computeCurrentGameState(); _currentSavedState = computeCurrentGameState();
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setString('savedState', _currentState); prefs.setString('savedState', _currentSavedState);
} else { } else {
resetCurrentSavedState(); resetCurrentSavedState();
} }
} }
void resetCurrentSavedState() async { void resetCurrentSavedState() async {
_currentState = ''; _currentSavedState = '';
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
prefs.setString('savedState', _currentState); prefs.setString('savedState', _currentSavedState);
notifyListeners(); notifyListeners();
} }
void loadCurrentSavedState() async { void loadCurrentSavedState() async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
_currentState = prefs.getString('savedState') ?? ''; _currentSavedState = prefs.getString('savedState') ?? '';
} }
bool hasCurrentSavedState() { bool hasCurrentSavedState() {
return (_currentState != ''); return (_currentSavedState != '');
} }
Map<String, dynamic> getCurrentSavedState() { Map<String, dynamic> getCurrentSavedState() {
if (_currentState != '') { if (_currentSavedState != '') {
Map<String, dynamic> savedState = json.decode(_currentState); Map<String, dynamic> savedState = json.decode(_currentSavedState);
if (savedState.isNotEmpty) { if (savedState.isNotEmpty) {
return savedState; return savedState;
} }
...@@ -187,17 +194,20 @@ class Data extends ChangeNotifier { ...@@ -187,17 +194,20 @@ class Data extends ChangeNotifier {
_assetsPreloaded = assetsPreloaded; _assetsPreloaded = assetsPreloaded;
} }
List<List<Cell>> get cells => _cells; Board get board => _board;
void updateCells(List<List<Cell>> cells) { void updateCells(Board board) {
_cells = cells; _board = board;
notifyListeners(); notifyListeners();
} }
List<List<Cell>> get cellsSolved => _cellsSolved; Board get boardSolved => _boardSolved;
void updateCellsSolved(List<List<Cell>> cells) { void updateCellsSolved(Board boardSolved) {
_cellsSolved = cells; _boardSolved = boardSolved;
} }
AnimatedBoard get boardAnimated => _boardAnimated;
ConflictsCount get boardConflicts => _boardConflicts;
void shuffleCellValues() { void shuffleCellValues() {
const int maxCellValue = 16; const int maxCellValue = 16;
final List<int> values = List<int>.generate(maxCellValue, (i) => i + 1); final List<int> values = List<int>.generate(maxCellValue, (i) => i + 1);
...@@ -218,21 +228,21 @@ class Data extends ChangeNotifier { ...@@ -218,21 +228,21 @@ class Data extends ChangeNotifier {
return _shuffledCellValues[originalValue - 1]; return _shuffledCellValues[originalValue - 1];
} }
int? get currentCellCol => _currentCellCol; int? get selectedCellCol => _selectedCellCol;
set updateCurrentCellCol(int? currentCellCol) { set updateCelectedCellCol(int? selectedCellCol) {
_currentCellCol = currentCellCol; _selectedCellCol = selectedCellCol;
notifyListeners(); notifyListeners();
} }
int? get currentCellRow => _currentCellRow; int? get selectedCellRow => _selectedCellRow;
set updateCurrentCellRow(int? currentCellRow) { set updateSelectedCellRow(int? selectedCellRow) {
_currentCellRow = currentCellRow; _selectedCellRow = selectedCellRow;
notifyListeners(); notifyListeners();
} }
int? get currentCellValue => _currentCellValue; int? get selectedCellValue => _selectedCellValue;
set updateCurrentCellValue(int? currentCellValue) { set updateSelectedCellValue(int? selectedCellValue) {
_currentCellValue = currentCellValue; _selectedCellValue = selectedCellValue;
notifyListeners(); notifyListeners();
} }
...@@ -253,21 +263,43 @@ class Data extends ChangeNotifier { ...@@ -253,21 +263,43 @@ class Data extends ChangeNotifier {
} }
selectCell(int? col, int? row) { selectCell(int? col, int? row) {
_currentCellCol = col; _selectedCellCol = col;
_currentCellRow = row; _selectedCellRow = row;
_currentCellValue = null; _selectedCellValue = null;
if (row != null && col != null) { if (row != null && col != null) {
if (_cells[row][col].value != 0) { if (_board[row][col].value != 0) {
_currentCellValue = _cells[row][col].value; _selectedCellValue = _board[row][col].value;
} }
} }
notifyListeners(); notifyListeners();
} }
void initConflictsBoard() {
ConflictsCount nonConflictedBoard = [];
final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
for (int row = 0; row < boardSideLength; row++) {
List<int> line = [];
for (int col = 0; col < boardSideLength; col++) {
line.add(0);
}
nonConflictedBoard.add(line);
}
_boardConflicts = nonConflictedBoard;
}
void updateConflicts(ConflictsCount conflictsCount) {
_boardConflicts = conflictsCount;
}
updateCellValue(int? col, int? row, int value) { updateCellValue(int? col, int? row, int value) {
if ((col != null) && (row != null)) { if ((col != null) && (row != null)) {
if (!_cells[row][col].isFixed) { if (!_board[row][col].isFixed) {
_cells[row][col].value = value; _board[row][col] = Cell(
row: row,
col: col,
value: value,
isFixed: false,
);
saveCurrentGameState(); saveCurrentGameState();
notifyListeners(); notifyListeners();
...@@ -292,11 +324,24 @@ class Data extends ChangeNotifier { ...@@ -292,11 +324,24 @@ class Data extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void initAnimatedBackground() {
AnimatedBoard staticBoard = [];
final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
for (int row = 0; row < boardSideLength; row++) {
List<bool> line = [];
for (int col = 0; col < boardSideLength; col++) {
line.add(false);
}
staticBoard.add(line);
}
_boardAnimated = staticBoard;
}
void setAnimatedBackground(List animatedCellsPattern) { void setAnimatedBackground(List animatedCellsPattern) {
final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical; final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
for (int row = 0; row < boardSideLength; row++) { for (int row = 0; row < boardSideLength; row++) {
for (int col = 0; col < boardSideLength; col++) { for (int col = 0; col < boardSideLength; col++) {
_cells[row][col].isAnimated = animatedCellsPattern[row][col]; _boardAnimated[row][col] = animatedCellsPattern[row][col];
} }
} }
notifyListeners(); notifyListeners();
...@@ -306,7 +351,7 @@ class Data extends ChangeNotifier { ...@@ -306,7 +351,7 @@ class Data extends ChangeNotifier {
final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical; final int boardSideLength = _blockSizeHorizontal * _blockSizeVertical;
for (int row = 0; row < boardSideLength; row++) { for (int row = 0; row < boardSideLength; row++) {
for (int col = 0; col < boardSideLength; col++) { for (int col = 0; col < boardSideLength; col++) {
_cells[row][col].isAnimated = false; _boardAnimated[row][col] = false;
} }
} }
} }
......
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/provider/data.dart'; import 'package:sudoku/provider/data.dart';
class Board { class BoardLayout extends StatelessWidget {
static Container buildGameBoard(Data myProvider) { const BoardLayout({super.key, required this.myProvider});
final Data myProvider;
@override
Container build(BuildContext context) {
const Color borderColor = Colors.black; const Color borderColor = Colors.black;
final int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
final Widget gameTileset = Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
for (int row = 0; row < boardSize; row++)
TableRow(
children: [
for (int col = 0; col < boardSize; col++)
Column(
children: [myProvider.board[row][col].widget(myProvider)],
),
],
),
],
);
return Container( return Container(
margin: const EdgeInsets.all(2), margin: const EdgeInsets.all(2),
...@@ -20,22 +40,9 @@ class Board { ...@@ -20,22 +40,9 @@ class Board {
), ),
child: Column( child: Column(
children: [ children: [
buildGameTileset(myProvider), gameTileset,
], ],
), ),
); );
} }
static Widget buildGameTileset(Data myProvider) {
final int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
final List<List<Cell>> cells = myProvider.cells;
return Table(defaultColumnWidth: const IntrinsicColumnWidth(), children: [
for (int row = 0; row < boardSize; row++)
TableRow(children: [
for (int col = 0; col < boardSize; col++)
Column(children: [cells[row][col].widget(myProvider, row, col)]),
]),
]);
}
} }
import 'package:flutter/material.dart';
import 'package:sudoku/ui/layout/board.dart';
import 'package:sudoku/ui/widgets/message_game_end.dart';
import 'package:sudoku/ui/widgets/bar_select_cell_value.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/board_utils.dart';
class Game extends StatelessWidget {
const Game({super.key, required this.myProvider});
final Data myProvider;
@override
Widget build(BuildContext context) {
final bool gameIsFinished = BoardUtils.checkBoardIsSolved(myProvider);
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
BoardLayout(myProvider: myProvider),
const SizedBox(height: 2),
gameIsFinished
? EndGameMessage(myProvider: myProvider)
: SelectCellValueBar(myProvider: myProvider),
],
);
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sudoku/provider/data.dart'; import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/game_utils.dart'; import 'package:sudoku/ui/widgets/button_game_resume.dart';
import 'package:sudoku/ui/widgets/button_game_start_new.dart';
class Parameters extends StatelessWidget {
const Parameters({super.key, required this.myProvider});
final Data myProvider;
class Parameters {
static const double separatorHeight = 2.0; static const double separatorHeight = 2.0;
static const double blockMargin = 3.0; static const double blockMargin = 3.0;
static const double blockPadding = 2.0; static const double blockPadding = 2.0;
...@@ -15,7 +20,8 @@ class Parameters { ...@@ -15,7 +20,8 @@ class Parameters {
static const double buttonPadding = 0.0; static const double buttonPadding = 0.0;
static const double buttonMargin = 0.0; static const double buttonMargin = 0.0;
static Widget buildParametersSelector(Data myProvider) { @override
Widget build(BuildContext context) {
List<Widget> lines = []; List<Widget> lines = [];
List<String> parameters = myProvider.availableParameters; List<String> parameters = myProvider.availableParameters;
...@@ -26,8 +32,8 @@ class Parameters { ...@@ -26,8 +32,8 @@ class Parameters {
myProvider.loadCurrentSavedState(); myProvider.loadCurrentSavedState();
Widget buttonsBlock = myProvider.hasCurrentSavedState() Widget buttonsBlock = myProvider.hasCurrentSavedState()
? buildResumeGameButton(myProvider) ? ResumeGameButton(myProvider: myProvider)
: buildStartNewGameButton(myProvider); : StartNewGameButton(myProvider: myProvider);
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
...@@ -73,66 +79,7 @@ class Parameters { ...@@ -73,66 +79,7 @@ class Parameters {
); );
} }
static Container buildStartNewGameButton(Data myProvider) { Widget buildParameterSelector(Data myProvider, String parameterCode) {
return Container(
margin: const EdgeInsets.all(blockMargin),
padding: const EdgeInsets.all(blockPadding),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
buildDecorationImageWidget(),
Column(
children: [
TextButton(
child: buildImageContainerWidget('button_start'),
onPressed: () => GameUtils.startNewGame(myProvider),
),
],
),
buildDecorationImageWidget(),
],
),
],
),
);
}
static Container buildResumeGameButton(Data myProvider) {
return Container(
margin: const EdgeInsets.all(blockMargin),
padding: const EdgeInsets.all(blockPadding),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
Column(
children: [
TextButton(
child: buildImageContainerWidget('button_delete_saved_game'),
onPressed: () => GameUtils.deleteSavedGame(myProvider),
),
],
),
Column(
children: [
TextButton(
child: buildImageContainerWidget('button_resume_game'),
onPressed: () => GameUtils.resumeSavedGame(myProvider),
),
],
),
buildDecorationImageWidget(),
],
),
],
),
);
}
static Widget buildParameterSelector(Data myProvider, String parameterCode) {
List<String> availableValues = myProvider.getParameterAvailableValues(parameterCode); List<String> availableValues = myProvider.getParameterAvailableValues(parameterCode);
if (availableValues.length == 1) { if (availableValues.length == 1) {
...@@ -156,8 +103,7 @@ class Parameters { ...@@ -156,8 +103,7 @@ class Parameters {
); );
} }
static Widget _buildParameterButton( Widget _buildParameterButton(Data myProvider, String parameterCode, String parameterValue) {
Data myProvider, String parameterCode, String parameterValue) {
String currentValue = myProvider.getParameterValue(parameterCode).toString(); String currentValue = myProvider.getParameterValue(parameterCode).toString();
bool isActive = (parameterValue == currentValue); bool isActive = (parameterValue == currentValue);
......
...@@ -3,14 +3,12 @@ import 'package:provider/provider.dart'; ...@@ -3,14 +3,12 @@ import 'package:provider/provider.dart';
import 'package:badges/badges.dart' as badges; import 'package:badges/badges.dart' as badges;
import 'package:overlay_support/overlay_support.dart'; import 'package:overlay_support/overlay_support.dart';
import 'package:sudoku/layout/game.dart'; import 'package:sudoku/ui/layout/game.dart';
import 'package:sudoku/layout/parameters.dart'; import 'package:sudoku/ui/layout/parameters.dart';
import 'package:sudoku/provider/data.dart'; import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/game_utils.dart'; import 'package:sudoku/utils/game_utils.dart';
class Home extends StatefulWidget { class Home extends StatefulWidget {
static const String id = 'home';
const Home({super.key}); const Home({super.key});
@override @override
...@@ -69,7 +67,7 @@ class HomeState extends State<Home> { ...@@ -69,7 +67,7 @@ class HomeState extends State<Home> {
final Data myProvider = Provider.of<Data>(context); final Data myProvider = Provider.of<Data>(context);
if (!myProvider.assetsPreloaded) { if (!myProvider.assetsPreloaded) {
List<String> assets = getImagesAssets(myProvider); final List<String> assets = getImagesAssets(myProvider);
for (String asset in assets) { for (String asset in assets) {
precacheImage(AssetImage(asset), context); precacheImage(AssetImage(asset), context);
} }
...@@ -155,8 +153,8 @@ class HomeState extends State<Home> { ...@@ -155,8 +153,8 @@ class HomeState extends State<Home> {
body: SafeArea( body: SafeArea(
child: Center( child: Center(
child: myProvider.gameIsRunning child: myProvider.gameIsRunning
? Game.buildGameWidget(myProvider) ? Game(myProvider: myProvider)
: Parameters.buildParametersSelector(myProvider), : Parameters(myProvider: myProvider),
), ),
), ),
); );
......
...@@ -3,35 +3,21 @@ import 'dart:math'; ...@@ -3,35 +3,21 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sudoku/entities/cell.dart'; import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/layout/board.dart';
import 'package:sudoku/provider/data.dart'; import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/board_utils.dart';
import 'package:sudoku/utils/game_utils.dart';
class Game { class SelectCellValueBar extends StatelessWidget {
static Widget buildGameWidget(Data myProvider) { const SelectCellValueBar({super.key, required this.myProvider});
final bool gameIsFinished = BoardUtils.checkBoardIsSolved(myProvider);
return Column( final Data myProvider;
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Board.buildGameBoard(myProvider),
const SizedBox(height: 2),
gameIsFinished
? Game.buildEndGameMessage(myProvider)
: Game.buildSelectCellValueBar(myProvider),
],
);
}
static Container buildSelectCellValueBar(Data myProvider) { @override
final List<List<Cell>> cells = myProvider.cells; Widget build(BuildContext context) {
final Board cells = myProvider.board;
final bool isCellSelected = final bool isCellSelected =
(myProvider.currentCellCol != null && myProvider.currentCellRow != null); (myProvider.selectedCellCol != null && myProvider.selectedCellRow != null);
final bool isUpdatableCellSelected = isCellSelected final bool isUpdatableCellSelected = isCellSelected
? !cells[myProvider.currentCellRow ?? 0][myProvider.currentCellCol ?? 0].isFixed ? !cells[myProvider.selectedCellRow ?? 0][myProvider.selectedCellCol ?? 0].isFixed
: false; : false;
final int maxValue = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; final int maxValue = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
...@@ -53,9 +39,12 @@ class Game { ...@@ -53,9 +39,12 @@ class Game {
value++) value++)
Column( Column(
children: [ children: [
Cell(isUpdatableCellSelected ? (value <= maxValue ? value : -1) : -1, Cell(
false) row: 1,
.widgetUpdateValue(myProvider) col: value,
value: isUpdatableCellSelected ? (value <= maxValue ? value : -1) : -1,
isFixed: false,
).widgetUpdateValue(myProvider)
], ],
), ),
], ],
...@@ -64,48 +53,4 @@ class Game { ...@@ -64,48 +53,4 @@ class Game {
), ),
); );
} }
static Widget buildRestartGameButton(Data myProvider) {
return TextButton(
child: const Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
onPressed: () => GameUtils.quitGame(myProvider),
);
}
static Container buildEndGameMessage(Data myProvider) {
const Image decorationImage = Image(
image: AssetImage('assets/icons/game_win.png'),
fit: BoxFit.fill,
);
return Container(
margin: const EdgeInsets.all(2),
padding: const EdgeInsets.all(2),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
const Column(
children: [decorationImage],
),
Column(
children: [
myProvider.animationInProgress
? decorationImage
: buildRestartGameButton(myProvider)
],
),
const Column(
children: [decorationImage],
),
],
),
],
),
);
}
} }
import 'package:flutter/material.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/game_utils.dart';
class RestartGameButton extends StatelessWidget {
const RestartGameButton({super.key, required this.myProvider});
final Data myProvider;
@override
Widget build(BuildContext context) {
return TextButton(
child: const Image(
image: AssetImage('assets/icons/button_back.png'),
fit: BoxFit.fill,
),
onPressed: () => GameUtils.quitGame(myProvider),
);
}
}
import 'package:flutter/material.dart';
import 'package:sudoku/ui/layout/parameters.dart';
import 'package:sudoku/utils/game_utils.dart';
class ResumeGameButton extends Parameters {
const ResumeGameButton({super.key, required super.myProvider});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(Parameters.blockMargin),
padding: const EdgeInsets.all(Parameters.blockPadding),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
Column(
children: [
TextButton(
child: Parameters.buildImageContainerWidget('button_delete_saved_game'),
onPressed: () => GameUtils.deleteSavedGame(myProvider),
),
],
),
Column(
children: [
TextButton(
child: Parameters.buildImageContainerWidget('button_resume_game'),
onPressed: () => GameUtils.resumeSavedGame(myProvider),
),
],
),
Parameters.buildDecorationImageWidget(),
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:sudoku/provider/data.dart';
import 'package:sudoku/ui/layout/parameters.dart';
import 'package:sudoku/utils/game_utils.dart';
class StartNewGameButton extends StatelessWidget {
const StartNewGameButton({super.key, required this.myProvider});
final Data myProvider;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.all(Parameters.blockMargin),
padding: const EdgeInsets.all(Parameters.blockPadding),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
Parameters.buildDecorationImageWidget(),
Column(
children: [
TextButton(
child: Parameters.buildImageContainerWidget('button_start'),
onPressed: () => GameUtils.startNewGame(myProvider),
),
],
),
Parameters.buildDecorationImageWidget(),
],
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:sudoku/ui/widgets/button_game_restart.dart';
import 'package:sudoku/provider/data.dart';
class EndGameMessage extends StatelessWidget {
const EndGameMessage({super.key, required this.myProvider});
final Data myProvider;
@override
Widget build(BuildContext context) {
const Image decorationImage = Image(
image: AssetImage('assets/icons/game_win.png'),
fit: BoxFit.fill,
);
return Container(
margin: const EdgeInsets.all(2),
padding: const EdgeInsets.all(2),
child: Table(
defaultColumnWidth: const IntrinsicColumnWidth(),
children: [
TableRow(
children: [
const Column(
children: [decorationImage],
),
Column(
children: [
myProvider.animationInProgress
? decorationImage
: RestartGameButton(myProvider: myProvider)
],
),
const Column(
children: [decorationImage],
),
],
),
],
),
);
}
}
...@@ -4,14 +4,14 @@ import 'package:sudoku/provider/data.dart'; ...@@ -4,14 +4,14 @@ import 'package:sudoku/provider/data.dart';
class BoardAnimate { class BoardAnimate {
// Start game animation: blinking tiles // Start game animation: blinking tiles
static List<List<List<bool>>> createStartGameAnimationPatterns(Data myProvider) { static AnimatedBoardSequence createStartGameAnimationPatterns(Data myProvider) {
List<List<List<bool>>> patterns = []; AnimatedBoardSequence patterns = [];
int patternsCount = 3; int patternsCount = 3;
int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) { for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
List<List<bool>> pattern = []; AnimatedBoard pattern = [];
for (int row = 0; row < boardSideLength; row++) { for (int row = 0; row < boardSideLength; row++) {
List<bool> patternRow = []; List<bool> patternRow = [];
for (int col = 0; col < boardSideLength; col++) { for (int col = 0; col < boardSideLength; col++) {
...@@ -26,14 +26,14 @@ class BoardAnimate { ...@@ -26,14 +26,14 @@ class BoardAnimate {
} }
// Win game animation: fill board with colored rows, from bottom to top // Win game animation: fill board with colored rows, from bottom to top
static List<List<List<bool>>> createWinGameAnimationPatterns(Data myProvider) { static AnimatedBoardSequence createWinGameAnimationPatterns(Data myProvider) {
List<List<List<bool>>> patterns = []; AnimatedBoardSequence patterns = [];
int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
int patternsCount = boardSideLength + 6; int patternsCount = boardSideLength + 6;
for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) { for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
List<List<bool>> pattern = []; AnimatedBoard pattern = [];
for (int row = 0; row < boardSideLength; row++) { for (int row = 0; row < boardSideLength; row++) {
List<bool> patternRow = []; List<bool> patternRow = [];
for (int col = 0; col < boardSideLength; col++) { for (int col = 0; col < boardSideLength; col++) {
...@@ -48,14 +48,14 @@ class BoardAnimate { ...@@ -48,14 +48,14 @@ class BoardAnimate {
} }
// Default multi-purpose animation: sliding stripes, from top left to right bottom // Default multi-purpose animation: sliding stripes, from top left to right bottom
static List<List<List<bool>>> createDefaultAnimationPatterns(Data myProvider) { static AnimatedBoardSequence createDefaultAnimationPatterns(Data myProvider) {
List<List<List<bool>>> patterns = []; AnimatedBoardSequence patterns = [];
int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical; int boardSideLength = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
int patternsCount = boardSideLength; int patternsCount = boardSideLength;
for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) { for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) {
List<List<bool>> pattern = []; AnimatedBoard pattern = [];
for (int row = 0; row < boardSideLength; row++) { for (int row = 0; row < boardSideLength; row++) {
List<bool> patternRow = []; List<bool> patternRow = [];
for (int col = 0; col < boardSideLength; col++) { for (int col = 0; col < boardSideLength; col++) {
...@@ -70,7 +70,7 @@ class BoardAnimate { ...@@ -70,7 +70,7 @@ class BoardAnimate {
} }
static void startAnimation(Data myProvider, String animationType) { static void startAnimation(Data myProvider, String animationType) {
List<List<List<bool>>> patterns = []; AnimatedBoardSequence patterns = [];
switch (animationType) { switch (animationType) {
case 'start': case 'start':
......
...@@ -6,7 +6,7 @@ import 'package:sudoku/utils/random_pick_grid.dart'; ...@@ -6,7 +6,7 @@ import 'package:sudoku/utils/random_pick_grid.dart';
import 'package:sudoku/utils/tools.dart'; import 'package:sudoku/utils/tools.dart';
class BoardUtils { class BoardUtils {
static printGrid(List<List<Cell>> cells, List<List<Cell>> solvedCells) { static printGrid(Board cells, Board solvedCells) {
String stringValues = '0123456789ABCDEFG'; String stringValues = '0123456789ABCDEFG';
printlog(''); printlog('');
printlog('-------'); printlog('-------');
...@@ -42,16 +42,21 @@ class BoardUtils { ...@@ -42,16 +42,21 @@ class BoardUtils {
myProvider.updateCellsSolved(BoardUtils.getSolvedGrid(myProvider)); myProvider.updateCellsSolved(BoardUtils.getSolvedGrid(myProvider));
myProvider.selectCell(null, null); myProvider.selectCell(null, null);
printGrid(myProvider.cells, myProvider.cellsSolved); printGrid(myProvider.board, myProvider.boardSolved);
} }
} }
static List<List<Cell>> createEmptyBoard(final int boardSize) { static Board createEmptyBoard(final int boardSize) {
final List<List<Cell>> cells = []; final Board cells = [];
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
final List<Cell> row = []; final List<Cell> row = [];
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
row.add(Cell(0, false)); row.add(Cell(
row: rowIndex,
col: colIndex,
value: 0,
isFixed: false,
));
} }
cells.add(row); cells.add(row);
} }
...@@ -59,8 +64,8 @@ class BoardUtils { ...@@ -59,8 +64,8 @@ class BoardUtils {
return cells; return cells;
} }
static List<List<Cell>> createBoardFromSavedState(Data myProvider, String savedBoard) { static Board createBoardFromSavedState(Data myProvider, String savedBoard) {
final List<List<Cell>> cells = []; final Board cells = [];
final int boardSize = int.parse(pow((savedBoard.length / 2), 1 / 2).toStringAsFixed(0)); final int boardSize = int.parse(pow((savedBoard.length / 2), 1 / 2).toStringAsFixed(0));
const String stringValues = '0123456789ABCDEFG'; const String stringValues = '0123456789ABCDEFG';
...@@ -75,7 +80,12 @@ class BoardUtils { ...@@ -75,7 +80,12 @@ class BoardUtils {
final String isFixedString = savedBoard[index++]; final String isFixedString = savedBoard[index++];
final bool isFixed = (isFixedString != ' '); final bool isFixed = (isFixedString != ' ');
row.add(Cell(value, isFixed)); row.add(Cell(
row: rowIndex,
col: colIndex,
value: value,
isFixed: isFixed,
));
} }
cells.add(row); cells.add(row);
} }
...@@ -83,12 +93,17 @@ class BoardUtils { ...@@ -83,12 +93,17 @@ class BoardUtils {
return cells; return cells;
} }
static List<List<Cell>> copyBoard(List cells) { static Board copyBoard(List cells) {
final List<List<Cell>> copiedGrid = []; final Board copiedGrid = [];
for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) { for (int rowIndex = 0; rowIndex < cells.length; rowIndex++) {
final List<Cell> row = []; final List<Cell> row = [];
for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) { for (int colIndex = 0; colIndex < cells[rowIndex].length; colIndex++) {
row.add(Cell(cells[rowIndex][colIndex].value, false)); row.add(Cell(
row: rowIndex,
col: colIndex,
value: cells[rowIndex][colIndex].value,
isFixed: false,
));
} }
copiedGrid.add(row); copiedGrid.add(row);
} }
...@@ -96,8 +111,8 @@ class BoardUtils { ...@@ -96,8 +111,8 @@ class BoardUtils {
return copiedGrid; return copiedGrid;
} }
static List<List<Cell>> createBoardFromTemplate(String grid, bool isSymetric) { static Board createBoardFromTemplate(String grid, bool isSymetric) {
List<List<Cell>> cells = []; Board cells = [];
final int boardSize = int.parse(pow(grid.length, 1 / 2).toStringAsFixed(0)); final int boardSize = int.parse(pow(grid.length, 1 / 2).toStringAsFixed(0));
const String stringValues = '0123456789ABCDEFG'; const String stringValues = '0123456789ABCDEFG';
...@@ -108,7 +123,12 @@ class BoardUtils { ...@@ -108,7 +123,12 @@ class BoardUtils {
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
final String stringValue = grid[index++]; final String stringValue = grid[index++];
final int value = stringValues.indexOf(stringValue); final int value = stringValues.indexOf(stringValue);
row.add(Cell(value, (value != 0))); row.add(Cell(
row: rowIndex,
col: colIndex,
value: value,
isFixed: (value != 0),
));
} }
cells.add(row); cells.add(row);
} }
...@@ -131,11 +151,15 @@ class BoardUtils { ...@@ -131,11 +151,15 @@ class BoardUtils {
switch (flip) { switch (flip) {
case 'horizontal': case 'horizontal':
{ {
final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize); final Board transformedBoard = createEmptyBoard(boardSize);
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = transformedBoard[rowIndex][colIndex] = Cell(
cells[boardSize - rowIndex - 1][colIndex].value; row: rowIndex,
col: colIndex,
value: cells[boardSize - rowIndex - 1][colIndex].value,
isFixed: false,
);
} }
} }
cells = transformedBoard; cells = transformedBoard;
...@@ -143,11 +167,15 @@ class BoardUtils { ...@@ -143,11 +167,15 @@ class BoardUtils {
break; break;
case 'vertical': case 'vertical':
{ {
final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize); final Board transformedBoard = createEmptyBoard(boardSize);
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = transformedBoard[rowIndex][colIndex] = Cell(
cells[rowIndex][boardSize - colIndex - 1].value; row: rowIndex,
col: colIndex,
value: cells[rowIndex][boardSize - colIndex - 1].value,
isFixed: false,
);
} }
} }
cells = transformedBoard; cells = transformedBoard;
...@@ -158,11 +186,15 @@ class BoardUtils { ...@@ -158,11 +186,15 @@ class BoardUtils {
switch (rotate) { switch (rotate) {
case 'left': case 'left':
{ {
final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize); final Board transformedBoard = createEmptyBoard(boardSize);
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = transformedBoard[rowIndex][colIndex] = Cell(
cells[colIndex][boardSize - rowIndex - 1].value; row: rowIndex,
col: colIndex,
value: cells[colIndex][boardSize - rowIndex - 1].value,
isFixed: false,
);
} }
} }
cells = transformedBoard; cells = transformedBoard;
...@@ -170,11 +202,15 @@ class BoardUtils { ...@@ -170,11 +202,15 @@ class BoardUtils {
break; break;
case 'right': case 'right':
{ {
final List<List<Cell>> transformedBoard = createEmptyBoard(boardSize); final Board transformedBoard = createEmptyBoard(boardSize);
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
transformedBoard[rowIndex][colIndex].value = transformedBoard[rowIndex][colIndex] = Cell(
cells[boardSize - colIndex - 1][rowIndex].value; row: rowIndex,
col: colIndex,
value: cells[boardSize - colIndex - 1][rowIndex].value,
isFixed: false,
);
} }
} }
cells = transformedBoard; cells = transformedBoard;
...@@ -185,8 +221,12 @@ class BoardUtils { ...@@ -185,8 +221,12 @@ class BoardUtils {
// Fix cells fixed states // Fix cells fixed states
for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) { for (int rowIndex = 0; rowIndex < boardSize; rowIndex++) {
for (int colIndex = 0; colIndex < boardSize; colIndex++) { for (int colIndex = 0; colIndex < boardSize; colIndex++) {
cells[rowIndex][colIndex].isFixed = cells[rowIndex][colIndex] = Cell(
(cells[rowIndex][colIndex].value != 0) ? true : false; row: rowIndex,
col: colIndex,
value: cells[rowIndex][colIndex].value,
isFixed: (cells[rowIndex][colIndex].value != 0) ? true : false,
);
} }
} }
...@@ -194,7 +234,8 @@ class BoardUtils { ...@@ -194,7 +234,8 @@ class BoardUtils {
} }
static bool checkBoardIsSolved(Data myProvider) { static bool checkBoardIsSolved(Data myProvider) {
final List<List<Cell>> cells = myProvider.cells; final Board cells = myProvider.board;
final ConflictsCount conflicts = myProvider.boardConflicts;
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
final int blockSizeVertical = myProvider.blockSizeVertical; final int blockSizeVertical = myProvider.blockSizeVertical;
...@@ -207,7 +248,7 @@ class BoardUtils { ...@@ -207,7 +248,7 @@ class BoardUtils {
// check grid is fully completed and does not contain conflict // check grid is fully completed and does not contain conflict
for (int row = 0; row < boardSize; row++) { for (int row = 0; row < boardSize; row++) {
for (int col = 0; col < boardSize; col++) { for (int col = 0; col < boardSize; col++) {
if (cells[row][col].value == 0 || cells[row][col].conflictsCount != 0) { if (cells[row][col].value == 0 || conflicts[row][col] != 0) {
return false; return false;
} }
} }
...@@ -219,7 +260,7 @@ class BoardUtils { ...@@ -219,7 +260,7 @@ class BoardUtils {
} }
static bool isValueAllowed( static bool isValueAllowed(
List<List<Cell>> cells, Board cells,
int blockSizeHorizontal, int blockSizeHorizontal,
int blockSizeVertical, int blockSizeVertical,
int? candidateCol, int? candidateCol,
...@@ -300,7 +341,8 @@ class BoardUtils { ...@@ -300,7 +341,8 @@ class BoardUtils {
} }
static void computeConflictsInBoard(Data myProvider) { static void computeConflictsInBoard(Data myProvider) {
final List<List<Cell>> cells = myProvider.cells; final Board cells = myProvider.board;
final ConflictsCount conflicts = myProvider.boardConflicts;
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
final int blockSizeVertical = myProvider.blockSizeVertical; final int blockSizeVertical = myProvider.blockSizeVertical;
...@@ -310,7 +352,7 @@ class BoardUtils { ...@@ -310,7 +352,7 @@ class BoardUtils {
// reset conflict states // reset conflict states
for (int row = 0; row < boardSize; row++) { for (int row = 0; row < boardSize; row++) {
for (int col = 0; col < boardSize; col++) { for (int col = 0; col < boardSize; col++) {
cells[row][col].conflictsCount = 0; conflicts[row][col] = 0;
} }
} }
...@@ -328,7 +370,7 @@ class BoardUtils { ...@@ -328,7 +370,7 @@ class BoardUtils {
printlog('line $row contains duplicates'); printlog('line $row contains duplicates');
// Add line to cells in conflict // Add line to cells in conflict
for (int col = 0; col < boardSize; col++) { for (int col = 0; col < boardSize; col++) {
cells[row][col].conflictsCount++; conflicts[row][col]++;
} }
} }
} }
...@@ -347,7 +389,7 @@ class BoardUtils { ...@@ -347,7 +389,7 @@ class BoardUtils {
printlog('column $col contains duplicates'); printlog('column $col contains duplicates');
// Add column to cells in conflict // Add column to cells in conflict
for (int row = 0; row < boardSize; row++) { for (int row = 0; row < boardSize; row++) {
cells[row][col].conflictsCount++; conflicts[row][col]++;
} }
} }
} }
...@@ -378,17 +420,19 @@ class BoardUtils { ...@@ -378,17 +420,19 @@ class BoardUtils {
for (int colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) { for (int colInBlock = 0; colInBlock < blockSizeHorizontal; colInBlock++) {
int row = (blockRow * blockSizeVertical) + rowInBlock; int row = (blockRow * blockSizeVertical) + rowInBlock;
int col = (blockCol * blockSizeHorizontal) + colInBlock; int col = (blockCol * blockSizeHorizontal) + colInBlock;
cells[row][col].conflictsCount++; conflicts[row][col]++;
} }
} }
} }
} }
} }
myProvider.updateConflicts(conflicts);
} }
static List<List<int>> getCellsWithWrongValue( static List<List<int>> getCellsWithWrongValue(
final List<List<Cell>> cells, final Board cells,
final List<List<Cell>> cellsSolved, final Board cellsSolved,
final int blockSizeHorizontal, final int blockSizeHorizontal,
final int blockSizeVertical, final int blockSizeVertical,
) { ) {
...@@ -407,31 +451,8 @@ class BoardUtils { ...@@ -407,31 +451,8 @@ class BoardUtils {
return cellsWithWrongValue; return cellsWithWrongValue;
} }
static List<List<int>> getConflictingCells(
final List<List<Cell>> cells,
final int blockSizeHorizontal,
final int blockSizeVertical,
) {
final List<List<int>> conflictingCells = [];
final int boardSize = blockSizeHorizontal * blockSizeVertical;
for (int row = 0; row < boardSize; row++) {
for (int col = 0; col < boardSize; col++) {
if (!cells[row][col].isFixed && cells[row][col].value != 0) {
if (cells[row][col].conflictsCount != 0 &&
!BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical, col,
row, cells[row][col].value)) {
conflictingCells.add([col, row]);
}
}
}
}
return conflictingCells;
}
static List<List<int>> getCellsWithUniqueAvailableValue( static List<List<int>> getCellsWithUniqueAvailableValue(
List<List<Cell>> cells, Board cells,
final int blockSizeHorizontal, final int blockSizeHorizontal,
final int blockSizeVertical, final int blockSizeVertical,
) { ) {
...@@ -460,8 +481,8 @@ class BoardUtils { ...@@ -460,8 +481,8 @@ class BoardUtils {
return candidateCells; return candidateCells;
} }
static List<List<Cell>> getSolvedGrid(Data myProvider) { static Board getSolvedGrid(Data myProvider) {
final List<List<Cell>> cells = copyBoard(myProvider.cells); final Board cells = copyBoard(myProvider.board);
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
int blockSizeVertical = myProvider.blockSizeVertical; int blockSizeVertical = myProvider.blockSizeVertical;
...@@ -479,7 +500,12 @@ class BoardUtils { ...@@ -479,7 +500,12 @@ class BoardUtils {
int col = cellsWithUniqueAvailableValue[i][0]; int col = cellsWithUniqueAvailableValue[i][0];
int row = cellsWithUniqueAvailableValue[i][1]; int row = cellsWithUniqueAvailableValue[i][1];
int value = cellsWithUniqueAvailableValue[i][2]; int value = cellsWithUniqueAvailableValue[i][2];
cells[row][col].value = value; cells[row][col] = Cell(
row: row,
col: col,
value: value,
isFixed: cells[row][col].isFixed,
);
} }
} while (true); } while (true);
......
import 'package:sudoku/entities/cell.dart';
import 'package:sudoku/provider/data.dart'; import 'package:sudoku/provider/data.dart';
import 'package:sudoku/utils/board_animate.dart'; import 'package:sudoku/utils/board_animate.dart';
import 'package:sudoku/utils/board_utils.dart'; import 'package:sudoku/utils/board_utils.dart';
...@@ -19,6 +18,8 @@ class GameUtils { ...@@ -19,6 +18,8 @@ class GameUtils {
myProvider.shuffleCellValues(); myProvider.shuffleCellValues();
myProvider.updateCells(BoardUtils.createEmptyBoard( myProvider.updateCells(BoardUtils.createEmptyBoard(
myProvider.blockSizeHorizontal * myProvider.blockSizeVertical)); myProvider.blockSizeHorizontal * myProvider.blockSizeVertical));
myProvider.initAnimatedBackground();
myProvider.initConflictsBoard();
BoardUtils.pickGrid(myProvider); BoardUtils.pickGrid(myProvider);
BoardAnimate.startAnimation(myProvider, 'start'); BoardAnimate.startAnimation(myProvider, 'start');
} }
...@@ -57,7 +58,7 @@ class GameUtils { ...@@ -57,7 +58,7 @@ class GameUtils {
} }
static void showTip(Data myProvider) { static void showTip(Data myProvider) {
if (myProvider.currentCellCol == null || myProvider.currentCellRow == null) { if (myProvider.selectedCellCol == null || myProvider.selectedCellRow == null) {
// no selected cell -> pick one // no selected cell -> pick one
GameUtils.helpSelectCell(myProvider); GameUtils.helpSelectCell(myProvider);
} else { } else {
...@@ -68,14 +69,14 @@ class GameUtils { ...@@ -68,14 +69,14 @@ class GameUtils {
} }
static void helpSelectCell(Data myProvider) { static void helpSelectCell(Data myProvider) {
final List<List<Cell>> cells = myProvider.cells; final Board cells = myProvider.board;
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
final int blockSizeVertical = myProvider.blockSizeVertical; final int blockSizeVertical = myProvider.blockSizeVertical;
// pick one of wrong value cells, if found // pick one of wrong value cells, if found
final List<List<int>> wrongValueCells = BoardUtils.getCellsWithWrongValue( final List<List<int>> wrongValueCells = BoardUtils.getCellsWithWrongValue(
cells, cells,
myProvider.cellsSolved, myProvider.boardSolved,
blockSizeHorizontal, blockSizeHorizontal,
blockSizeVertical, blockSizeVertical,
); );
...@@ -116,7 +117,7 @@ class GameUtils { ...@@ -116,7 +117,7 @@ class GameUtils {
} }
static void helpFillCell(Data myProvider) { static void helpFillCell(Data myProvider) {
final List<List<Cell>> cells = myProvider.cells; final Board cells = myProvider.board;
final int blockSizeHorizontal = myProvider.blockSizeHorizontal; final int blockSizeHorizontal = myProvider.blockSizeHorizontal;
final int blockSizeVertical = myProvider.blockSizeVertical; final int blockSizeVertical = myProvider.blockSizeVertical;
...@@ -129,13 +130,13 @@ class GameUtils { ...@@ -129,13 +130,13 @@ class GameUtils {
int allowedValuesCount = 0; int allowedValuesCount = 0;
for (int value = 1; value <= boardSize; value++) { for (int value = 1; value <= boardSize; value++) {
if (BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical, if (BoardUtils.isValueAllowed(cells, blockSizeHorizontal, blockSizeVertical,
myProvider.currentCellCol, myProvider.currentCellRow, value)) { myProvider.selectedCellCol, myProvider.selectedCellRow, value)) {
allowedValuesCount++; allowedValuesCount++;
eligibleValue = value; eligibleValue = value;
} }
} }
myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, myProvider.updateCellValue(myProvider.selectedCellCol, myProvider.selectedCellRow,
allowedValuesCount == 1 ? eligibleValue : 0); allowedValuesCount == 1 ? eligibleValue : 0);
myProvider.selectCell(null, null); myProvider.selectCell(null, null);
if (BoardUtils.checkBoardIsSolved(myProvider)) { if (BoardUtils.checkBoardIsSolved(myProvider)) {
......
name: sudoku name: sudoku
description: A sudoku game application. description: A sudoku game application.
publish_to: 'none' publish_to: 'none'
version: 0.1.16+65 version: 0.1.17+66
environment: environment:
sdk: '^3.0.0' sdk: '^3.0.0'
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment