From 829619c3a2b9e01aa3d8dcf60b8f0602e970d34a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Harrault?= <benoit@harrault.fr>
Date: Wed, 14 Jul 2021 00:32:39 +0200
Subject: [PATCH] Add a tip button, show conflict to solve or next cell/value
 to fill

---
 android/gradle.properties                     |   4 +-
 assets/icons/button_help.png                  | Bin 0 -> 6037 bytes
 .../metadata/android/en-US/changelogs/33.txt  |   1 +
 .../metadata/android/fr-FR/changelogs/33.txt  |   1 +
 icons/build_game_icons.sh                     |   1 +
 icons/button_help.svg                         |   2 +
 lib/screens/home.dart                         |  17 ++++
 lib/utils/game_utils.dart                     |  83 +++++++++++++++++-
 8 files changed, 106 insertions(+), 3 deletions(-)
 create mode 100644 assets/icons/button_help.png
 create mode 100644 fastlane/metadata/android/en-US/changelogs/33.txt
 create mode 100644 fastlane/metadata/android/fr-FR/changelogs/33.txt
 create mode 100644 icons/button_help.svg

diff --git a/android/gradle.properties b/android/gradle.properties
index 4878903..9dfcc42 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.32
-app.versionCode=32
+app.versionName=0.0.33
+app.versionCode=33
diff --git a/assets/icons/button_help.png b/assets/icons/button_help.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd1bcd29a76f0624c9a6404f4b90bbde8f56ea3f
GIT binary patch
literal 6037
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

literal 0
HcmV?d00001

diff --git a/fastlane/metadata/android/en-US/changelogs/33.txt b/fastlane/metadata/android/en-US/changelogs/33.txt
new file mode 100644
index 0000000..7c583ba
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/33.txt
@@ -0,0 +1 @@
+Add a tip button, show conflict to solve or next cell/value to fill
diff --git a/fastlane/metadata/android/fr-FR/changelogs/33.txt b/fastlane/metadata/android/fr-FR/changelogs/33.txt
new file mode 100644
index 0000000..874c6fd
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/33.txt
@@ -0,0 +1 @@
+Ajout d'un bouton d'aide, montrant un conflit à régler ou la prochaine cellule à sélectionner/saisir.
diff --git a/icons/build_game_icons.sh b/icons/build_game_icons.sh
index 2cd7ac8..e05eb18 100755
--- a/icons/build_game_icons.sh
+++ b/icons/build_game_icons.sh
@@ -57,6 +57,7 @@ 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_help.svg ${BASE_DIR}/assets/icons/button_help.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
diff --git a/icons/button_help.svg b/icons/button_help.svg
new file mode 100644
index 0000000..f8a083b
--- /dev/null
+++ b/icons/button_help.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="10.769" fill="#dd00ec"/><circle cx="45.694" cy="45.91" r="33.217" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.885"/><g transform="matrix(.54508 0 0 .54508 20.441 -502.46)" fill="#db00eb" stroke="#00b244" stroke-width=".60926"><path d="m81.135 971.23c-1.1715-1.1715-3.0711-1.1715-4.2426 0l-5.6569 5.6569c-1.1716 1.1716-1.1715 3.0711 3e-6 4.2426 1.1716 1.1716 3.071 1.1716 4.2426 0l5.6568-5.6568c1.1715-1.1716 1.1716-3.071 1e-6 -4.2426zm-28.284-11.711c-0.54299-0.54299-1.2929-0.88389-2.1213-0.88388-1.6567-1e-5 -3.0053 1.3485-3.0052 3.0052l-3.5e-5 7.9991c1e-6 1.6569 1.3485 3.0052 3.0052 3.0052 1.6569 1e-5 3.0052-1.3483 3.0052-3.0052l3.5e-5 -7.9991c-7.1e-5 -0.82852-0.34104-1.5785-0.88388-2.1213zm39.996 39.996c-0.54284-0.54284-1.2928-0.88381-2.1213-0.88388l-7.9991 4e-5c-1.6569-1e-5 -3.0052 1.3483-3.0052 3.0052-1e-6 1.6568 1.3483 3.0053 3.0052 3.0053l7.9991-1e-4c1.6567 1e-4 3.0052-1.3484 3.0052-3.0052-3e-6 -0.8284-0.3409-1.5783-0.88389-2.1213zm-24.439-15.556c-9.7205-9.7205-26.656-11.528-37.366-0.8176-6.6684 6.6685-8.3484 13.631-9.325 19.313-0.97661 5.6818-1.386 9.783-4.9277 13.325l-8.4853 8.4853c-2.7878 2.7877-2.6328 7.2667 2.4e-5 9.8994l9.8995 9.8996c2.6328 2.6327 7.1117 2.7877 9.8995-2e-4l8.4853-8.4851c3.5418-3.5418 7.6429-3.9511 13.325-4.9278 5.6816-0.9764 12.644-2.6565 19.313-9.325 10.71-10.71 8.9029-27.646-0.81762-37.366zm-4.2426 4.2426c7.5261 7.5261 9.0328 20.666 0.81762 28.881-5.6097 5.6097-10.646 6.7364-16.065 7.6678-4.6199 0.794-9.7346 1.3621-14.275 4.6404l-11.667-11.667c3.2782-4.5401 3.8463-9.6548 4.6404-14.275 0.93136-5.4187 2.0581-10.455 7.6677-16.065 8.2152-8.2152 21.355-6.7085 28.881 0.8176zm-5.458-1.2154c-0.29098-0.31274-0.65457-0.5946-1.0606-0.83965-6.3377-2.8668-13.837-2.2586-19.666 1.812-1.2903 0.90814-1.6596 2.9083-0.75129 4.1984 0.90835 1.2902 2.9305 1.6378 4.2206 0.72917 4.084-2.8517 9.3179-3.2839 13.744-1.2816 1.2643 0.57898 2.9159 0.12349 3.7123-1.0164 0.93238-1.4424 0.67412-2.6636-0.19895-3.6019zm-34.14-15.755c-1.1715-1.1715-3.071-1.1716-4.2426 0-1.1715 1.1715-1.1715 3.0711 6e-6 4.2426l5.6569 5.6569c1.1716 1.1716 3.0711 1.1716 4.2426 0 1.1716-1.1716 1.1716-3.071-3e-6 -4.2426zm50.912 50.912c-1.1716-1.1716-3.0711-1.1715-4.2426 0-1.1716 1.1717-1.1716 3.071-1.9e-5 4.2427l5.6569 5.6568c1.1715 1.1716 3.0711 1.1716 4.2426 0 1.1715-1.1715 1.1715-3.071-2.8e-5 -4.2426zm-56.569 2e-4 11.314 11.314-1.4142 1.4141-11.314-11.314zm-5.6569 5.6568 11.314 11.314-0.70709 0.7071c-0.46222 0.4622-1.0623 0.3519-1.4142 0l-9.8995-9.8995c-0.35189-0.352-0.46222-0.952 7e-6 -1.4142z" color="#000000" enable-background="accumulate" fill="#db00eb" overflow="visible" stroke="none" stroke-width=".60926" style="text-indent:0;text-transform:none"/></g></svg>
diff --git a/lib/screens/home.dart b/lib/screens/home.dart
index 2cc23c8..8f76286 100644
--- a/lib/screens/home.dart
+++ b/lib/screens/home.dart
@@ -17,6 +17,23 @@ class Home extends StatelessWidget {
 
     if (myProvider.stateRunning) {
       menuActions = [
+        FlatButton(
+          child: Container(
+            decoration: BoxDecoration(
+              borderRadius: BorderRadius.circular(4),
+              border: Border.all(
+                color: Colors.blue,
+                width: 4,
+              ),
+            ),
+            margin: EdgeInsets.all(8),
+            child: Image(
+              image: AssetImage('assets/icons/button_help.png'),
+              fit: BoxFit.fill
+            ),
+          ),
+          onPressed: () => GameUtils.showTip(myProvider),
+        ),
         FlatButton(
           child: Container(
             decoration: BoxDecoration(
diff --git a/lib/utils/game_utils.dart b/lib/utils/game_utils.dart
index 6a32e1c..ef43797 100644
--- a/lib/utils/game_utils.dart
+++ b/lib/utils/game_utils.dart
@@ -1,5 +1,6 @@
 import '../provider/data.dart';
 import '../utils/board_utils.dart';
+import '../utils/game_utils.dart';
 
 class GameUtils {
 
@@ -8,10 +9,90 @@ class GameUtils {
   }
 
   static Future<void> startGame(Data myProvider) async {
-    print('Start new game: ' + myProvider.size);
     myProvider.updateSize = myProvider.size;
     myProvider.updateStateRunning = true;
     myProvider.updateCells = BoardUtils.createEmptyBoard(myProvider.blockSizeHorizontal * myProvider.blockSizeVertical);
     BoardUtils.pickGrid(myProvider);
   }
+
+  static void showTip(Data myProvider) {
+    if (myProvider.currentCellCol == null || myProvider.currentCellRow == null) {
+      // no selected cell -> pick one
+      GameUtils.helpSelectCell(myProvider);
+    } else {
+      // currently selected cell -> set value
+      GameUtils.helpFillCell(myProvider);
+    }
+  }
+
+  static void helpSelectCell(Data myProvider) {
+    List cells = myProvider.cells;
+    int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
+
+    // pick one of conflicting cells, if found
+    List conflictingCells = [];
+    for (var row = 0; row < boardSize; row++) {
+      for (var col = 0; col < boardSize; col++) {
+        if (!cells[row][col].isFixed && cells[row][col].value != 0) {
+
+          if (cells[row][col].conflictsCount != 0 && !BoardUtils.isValueAllowed(myProvider, col, row, cells[row][col].value)) {
+            conflictingCells.add([col, row]);
+          }
+        }
+      }
+    }
+    if (conflictingCells.length != 0) {
+      GameUtils.pickRandomFromList(myProvider, conflictingCells);
+      return;
+    }
+
+    //  pick one form cells with unique non-conflicting candidate value
+    List candidateCells = [];
+    for (var row = 0; row < boardSize; row++) {
+      for (var col = 0; col < boardSize; col++) {
+        if (cells[row][col].value == 0) {
+          int allowedValuesCount = 0;
+          for (var value = 1; value <= boardSize; value++) {
+            if (BoardUtils.isValueAllowed(myProvider, col, row, value)) {
+              allowedValuesCount++;
+            }
+          }
+          if (allowedValuesCount == 1) {
+            candidateCells.add([col, row]);
+          }
+        }
+      }
+    }
+    if (candidateCells.length != 0) {
+      GameUtils.pickRandomFromList(myProvider, candidateCells);
+      return;
+    }
+  }
+
+  static void pickRandomFromList(Data myProvider, List cellsCoordinates) {
+    if (cellsCoordinates.length > 0) {
+      cellsCoordinates.shuffle();
+      List cell = cellsCoordinates[0];
+      myProvider.selectCell(cell[0], cell[1]);
+    }
+  }
+
+  static void helpFillCell(Data myProvider) {
+    int boardSize = myProvider.blockSizeHorizontal * myProvider.blockSizeVertical;
+
+    int eligibleValue = 0;
+
+    // ensure there is only one eligible value for this cell
+    int allowedValuesCount = 0;
+    for (var value = 1; value <= boardSize; value++) {
+      if (BoardUtils.isValueAllowed(myProvider, myProvider.currentCellCol, myProvider.currentCellRow, value)) {
+        allowedValuesCount++;
+        eligibleValue = value;
+      }
+    }
+
+    myProvider.updateCellValue(myProvider.currentCellCol, myProvider.currentCellRow, allowedValuesCount == 1 ? eligibleValue : 0);
+    myProvider.selectCell(null, null);
+    BoardUtils.computeConflictsInBoard(myProvider);
+  }
 }
-- 
GitLab