diff --git a/android/gradle.properties b/android/gradle.properties index d9abd55731010fe508f39321892e8002f10e79ef..663881258a10822c0b4abc064b6e0bc0ccf48833 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.12 -app.versionCode=12 +app.versionName=0.0.13 +app.versionCode=13 diff --git a/assets/icons/button_back.png b/assets/icons/button_back.png new file mode 100644 index 0000000000000000000000000000000000000000..370c0893bebe5c38c698f1c04c58a341efcb0707 Binary files /dev/null and b/assets/icons/button_back.png differ diff --git a/assets/icons/button_show_conflicts.png b/assets/icons/button_show_conflicts.png new file mode 100644 index 0000000000000000000000000000000000000000..060a9da706a71e0d67272f42f2a2d3b3c7bc8dce Binary files /dev/null and b/assets/icons/button_show_conflicts.png differ diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh index f17a2ad302a028c70d14bc5a7c1a592e2000e1a1..41dc2c480389c423c99a3fa1c11bcf90942ae6e6 100755 --- a/icons/build_game_icons.sh +++ b/icons/build_game_icons.sh @@ -54,6 +54,8 @@ function build_icon_for_skin() { } # Game icons +build_icon ${CURRENT_DIR}/button_back.svg ${BASE_DIR}/assets/icons/button_back.png +build_icon ${CURRENT_DIR}/button_show_conflicts.svg ${BASE_DIR}/assets/icons/button_show_conflicts.png build_icon ${CURRENT_DIR}/button_start.svg ${BASE_DIR}/assets/icons/button_start.png build_icon ${CURRENT_DIR}/difficulty_easy.svg ${BASE_DIR}/assets/icons/difficulty_easy.png build_icon ${CURRENT_DIR}/difficulty_medium.svg ${BASE_DIR}/assets/icons/difficulty_medium.png diff --git a/icons/button_back.svg b/icons/button_back.svg new file mode 100644 index 0000000000000000000000000000000000000000..0d152c5af3c012f2d6a76fa1e387686680bc6724 --- /dev/null +++ b/icons/button_back.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 93.665 93.676" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m46.834 11.248c-19.624 0-35.588 15.964-35.588 35.592 0 19.624 15.964 35.588 35.588 35.588s35.584-15.964 35.584-35.588c0-19.629-15.96-35.592-35.584-35.592zm0 63.607c-15.471 0-28.02-12.544-28.02-28.014 0-15.477 12.549-28.019 28.02-28.019s28.018 12.541 28.018 28.019c0 15.471-12.547 28.014-28.018 28.014z" stroke-width=".75985"/><path d="m55.403 64.285c0.85468 0.85879 3.0403 0.85879 3.0403 0v-34.893c0-0.85985-2.1908-0.85985-3.0486 0l-28.641 15.895c-0.8578 0.85468-0.87224 2.2454-0.0165 3.1032z" fill="#ed9138" stroke-width="1.031"/></svg> diff --git a/icons/button_show_conflicts.svg b/icons/button_show_conflicts.svg new file mode 100644 index 0000000000000000000000000000000000000000..dd7ee503a3e821da49e56a76b6cda6a05be0993e --- /dev/null +++ b/icons/button_show_conflicts.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 91.389 91.821" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect width="91.389" height="91.821" ry="0" fill="#e2b300"/><path d="m79.855 13.662a1.4083 1.4083 0 0 0-1.8775-0.11982l-26.365 20.293-1.1585-12.723a1.4009 1.4009 0 0 0-1.0985-1.2583 1.4367 1.4367 0 0 0-1.558 0.65904l-6.8708 11.644-9.6071-9.5472a1.4562 1.4562 0 0 0-1.6578-0.25965 1.4737 1.4737 0 0 0-0.77887 1.2782 0.972 0.972 0 0 0 0.02006 0.23979l2.177 13.182-21.052-3.4553a1.4314 1.4314 0 0 0-0.99866 2.6166l17.576 11.345-10.825 7.6299a1.4314 1.4314 0 0 0 0.81885 2.5965l12.783 0.13983-13.981 21.751a1.4236 1.4236 0 0 0 2.0972 1.8775l22.23-17.956 5.5725 6.4713a1.4509 1.4509 0 0 0 1.3582 0.45946 1.3606 1.3606 0 0 0 1.0786-0.9588l2.3967-7.1703 12.483 5.7723a1.4376 1.4376 0 0 0 1.6579-0.31951 1.451 1.451 0 0 0 0.19979-1.6777l-6.6512-11.784 13.062-3.5353a1.4274 1.4274 0 0 0 1.0586-1.3182 1.4045 1.4045 0 0 0-0.91882-1.3983l-12.583-4.7337 21.531-27.863a1.4082 1.4082 0 0 0-0.11982-1.8775z" stroke-width="2.0684"/></svg> diff --git a/lib/entities/cell.dart b/lib/entities/cell.dart index 89ea6536b9f81fcc21990c557eb0914cc083aa09..369a5f7f9030cee73c12fd6a4e07d88c68f47077 100644 --- a/lib/entities/cell.dart +++ b/lib/entities/cell.dart @@ -8,6 +8,7 @@ class Cell { final int col; final int row; bool isFixed; + int conflictsCount = 0; Cell( @required this.value, @@ -28,7 +29,7 @@ class Cell { Color borderDarkColor = Colors.black; Color borderLightColor = Colors.grey; Color borderSelectedColor = Colors.red; - Color backgroundColor = this.isFixed ? Colors.grey[300] : Colors.grey[100]; + Color backgroundColor = this.getBackgroundColor(myProvider); Border borders = Border( top: BorderSide(width: borderWidth, color: ((this.row % size) == 0) ? borderDarkColor : borderLightColor), @@ -67,6 +68,29 @@ class Cell { ); } + Color getBackgroundColor(Data myProvider) { + Color editableCellColor = Colors.grey[100]; + Color editableCellColorConflict = Colors.pink[100]; + Color fixedCellColor = Colors.grey[300]; + Color fixedCellColorConflict = Colors.pink[200]; + + Color backgroundColor = editableCellColor; + + if (this.isFixed) { + backgroundColor = fixedCellColor; + } + + if (myProvider.showConflicts && (this.conflictsCount != 0)) { + if (this.isFixed) { + backgroundColor = fixedCellColorConflict; + } else { + backgroundColor = editableCellColorConflict; + } + } + + return backgroundColor; + } + Container widgetUpdateValue(Data myProvider) { String imageAsset = 'assets/skins/empty.png'; if (this.value > 0) { diff --git a/lib/provider/data.dart b/lib/provider/data.dart index fd49afc6249b4c12a3986f5500ea5efbc9f06e98..ea939bfbbbf1c05f6557257cb951136d2ee1f84e 100644 --- a/lib/provider/data.dart +++ b/lib/provider/data.dart @@ -15,6 +15,7 @@ class Data extends ChangeNotifier { String _level = 'medium'; int _size = 3; String _skin = 'default'; + bool _showConflicts = false; // Game data bool _stateRunning = false; @@ -40,6 +41,12 @@ class Data extends ChangeNotifier { notifyListeners(); } + bool get showConflicts => _showConflicts; + set updateShowConflicts(bool showConflicts) { + _showConflicts = showConflicts; + notifyListeners(); + } + getParameterValue(String parameterCode) { switch(parameterCode) { case 'difficulty': { return _level; } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 74b014b081eca68527971dc4f8ca59b49d235cc6..ce7543317a7ebe0e2fe312e842ef1a6b2a35d5e1 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -318,45 +318,65 @@ class Home extends StatelessWidget { 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 < pow(size, 2); row++) { - for (var col = 0; col < pow(size, 2); col++) { + for (var row = 0; row < sideLength; row++) { + for (var col = 0; col < sideLength; col++) { if (cells[row][col].value == 0) { - print('grid not complete'); - return false; + isSolved = false; } } } - print('ok grid complete'); // check lines does not contains a value twice - for (var row = 0; row < pow(size, 2); row++) { + for (var row = 0; row < sideLength; row++) { List values = []; - for (var col = 0; col < pow(size, 2); col++) { - values.add(cells[row][col].value); + 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'); - return false; + // Add line to cells in conflict + for (var col = 0; col < sideLength; col++) { + cells[row][col].conflictsCount++; + } + isSolved = false; } } - print('ok no line with duplicates'); // check columns does not contains a value twice - for (var col = 0; col < pow(size, 2); col++) { + for (var col = 0; col < sideLength; col++) { List values = []; - for (var row = 0; row < pow(size, 2); row++) { - values.add(cells[row][col].value); + 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'); - return false; + // Add column to cells in conflict + for (var row = 0; row < sideLength; row++) { + cells[row][col].conflictsCount++; + } + isSolved = false; } } - print('ok no column with duplicates'); // check blocks does not contains a value twice for (var blockRow = 0; blockRow < size; blockRow++) { @@ -367,37 +387,78 @@ class Home extends StatelessWidget { for (var colInBlock = 0; colInBlock < size; colInBlock++) { int row = (blockRow * size) + rowInBlock; int col = (blockCol * size) + colInBlock; - values.add(cells[row][col].value); + 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'); - return false; + // 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; } } } - print('ok no block with duplicates'); - print('-> ok sudoku solved!'); - return true; + 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: [ - if (myProvider.stateRunning) - FlatButton( - child: Text('◀️'), - onPressed: () => resetGame(myProvider), - ), - ], + actions: menuActions, ), body: SafeArea( child: Center(