From b98014ad883232ad3fe9e6aee57f90ee12414833 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 11 Jul 2020 14:33:23 +0200 Subject: [PATCH 01/11] Fix bug that caused the GameActivity to assign the difficulty "unspecified" to all imported sudokus --- .../java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 79237e0..4c2ebff 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -149,8 +149,9 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig difficultyCheck.setPuzzle(container.getFixedValues()); difficultyCheck.solve(); - startGame = difficultyCheck.hasUniqueSolution(); container.parseDifficulty(difficultyCheck.getDifficulty().toString()); + startGame = difficultyCheck.hasUniqueSolution(); + } catch (IllegalArgumentException e) { startGame = false; From b12d085f27adf561392207d6ee9aff7cfb999c8b Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 11 Jul 2020 14:40:38 +0200 Subject: [PATCH 02/11] Fix bug in MainActivity that caused the MainActivity to be re-opened, rather than the GameActivity being opened, upon successfully veriying an imported sudoku --- .../java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java index e8a25c9..17066e0 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java @@ -385,7 +385,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig boolean solvable = CreateSudokuActivity.verify(MainActivity.this, GameType.Default_9x9, input); if (solvable) { Toast.makeText(MainActivity.this, R.string.finished_verifying_custom_sudoku_toast, Toast.LENGTH_LONG).show(); - final Intent intent = new Intent(this, MainActivity.class); + final Intent intent = new Intent(this, GameActivity.class); intent.setData(Uri.parse(GameActivity.URL_SCHEME_WITHOUT_HOST + "://" + input)); startActivity(intent); finish(); From 1c6be6e5c4aa851b974ab679c59883755caa321c Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 14 Jul 2020 09:29:13 +0200 Subject: [PATCH 03/11] Add boolean 'isCustom' variable to GameInfoContainer and GameController --- .../privacyfriendlysudoku/controller/GameController.java | 5 +++++ .../controller/helper/GameInfoContainer.java | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java index 1a0fbbc..88e2472 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameController.java @@ -64,6 +64,7 @@ public class GameController implements IModelChangedListener, Parcelable { private GameType gameType; private GameDifficulty difficulty; private CellConflictList errorList = new CellConflictList(); + private boolean gameIsCustom; // Undo Redo private UndoRedoManager undoRedoManager; @@ -91,6 +92,7 @@ public class GameController implements IModelChangedListener, Parcelable { public GameController(GameType type, SharedPreferences pref, Context context) { this.context = context; this.gameBoard = new GameBoard(type); + this.gameIsCustom = false; setGameType(type); setSettings(pref); @@ -102,6 +104,8 @@ public class GameController implements IModelChangedListener, Parcelable { return gameID; } + public boolean gameIsCustom() { return gameIsCustom; } + public void loadNewLevel(GameType type, GameDifficulty difficulty) { NewLevelManager newLevelManager = NewLevelManager.getInstance(context, settings); @@ -141,6 +145,7 @@ public class GameController implements IModelChangedListener, Parcelable { this.difficulty = gic.getDifficulty(); this.time = gic.getTimePlayed(); this.usedHints = gic.getHintsUsed(); + this.gameIsCustom = gic.isCustom(); setGameType(gic.getGameType()); this.gameBoard = new GameBoard(gic.getGameType()); diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java index 9365a39..a2d6ade 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java @@ -25,6 +25,7 @@ public class GameInfoContainer { int[] setValues; boolean[][] setNotes; int hintsUsed; + boolean isCustom; public GameInfoContainer() {} public GameInfoContainer(int ID, GameDifficulty difficulty, GameType gameType, int[] fixedValues, int[] setValues, boolean[][] setNotes) { @@ -40,12 +41,17 @@ public class GameInfoContainer { this.setValues = setValues; this.setNotes = setNotes; this.hintsUsed = hintsUsed; + isCustom = false; } public void setID(int ID) { this.ID = ID; } + public void setCustom (boolean isCustom) { this.isCustom = isCustom; } + + public boolean isCustom () { return isCustom; } + public void parseGameType(String s) { gameType = Enum.valueOf(GameType.class, s); if(gameType == null) { From 2df51ac5ba8f5aa8127ae1a517955d06ac6059d4 Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 14 Jul 2020 09:50:48 +0200 Subject: [PATCH 04/11] Change the way a custom sudoku is saved (add 'true' at the end of the byte stream) so that the GameStateManager can recognise custom levels when loading the sudokus the user has saved on his device --- .../privacyfriendlysudoku/controller/GameStateManager.java | 4 ++++ .../controller/helper/GameInfoContainer.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java index c50ba6c..a4e9af1 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/GameStateManager.java @@ -89,6 +89,10 @@ public class GameStateManager { gic.parseNotes(values[i++]); gic.parseHintsUsed(values[i++]); + if (values.length > i) { + gic.setCustom(true); + } + if (gic.getID() == GameController.DAILY_SUDOKU_ID) { includesDaily = true; } diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java index a2d6ade..79cc687 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/helper/GameInfoContainer.java @@ -185,6 +185,7 @@ public class GameInfoContainer { public static String getGameInfo(GameController controller) { StringBuilder sb = new StringBuilder(); Date today = new Date(); + boolean custom = controller.gameIsCustom(); sb.append(controller.getGameType().name()); sb.append("/"); @@ -202,6 +203,11 @@ public class GameInfoContainer { sb.append("/"); sb.append(controller.getUsedHints()); + if (custom) { + sb.append("/"); + sb.append(custom); + } + String result = sb.toString(); Log.d("getGameInfo", result); From 54a4024dac0122ab306b3fdcbbc8933786ddd506 Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 14 Jul 2020 09:53:03 +0200 Subject: [PATCH 05/11] Add extra 'isCustom' to Intent from CreateSudokuActivity so that the GameActivity will know that it is handling a custom sudoku --- .../secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java index d1603ec..8fc014f 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/CreateSudokuActivity.java @@ -131,6 +131,7 @@ public class CreateSudokuActivity extends BaseActivity implements IFinalizeDialo Toast.makeText(CreateSudokuActivity.this, R.string.finished_verifying_custom_sudoku_toast, Toast.LENGTH_LONG).show(); final Intent intent = new Intent(this, GameActivity.class); intent.setData(Uri.parse(GameActivity.URL_SCHEME_WITHOUT_HOST + "://" + boardContent)); + intent.putExtra("isCustom", true); startActivity(intent); finish(); } else { From f9d053a06924271822db9dd0c646220bcd00424e Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 14 Jul 2020 09:54:41 +0200 Subject: [PATCH 06/11] If the GameActivity receives an Intent carrying an imported sudoku containing the key 'isCustom', set 'isCustom' attribute of the created GameInfoContainer to 'true' --- .../privacyfriendlysudoku/ui/GameActivity.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java index 4c2ebff..c5f6c3d 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/GameActivity.java @@ -140,6 +140,7 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig QQWing difficultyCheck; GameInfoContainer container = new GameInfoContainer(0, GameDifficulty.Unspecified, GameType.Unspecified, new int [boardSize], new int [boardSize], new boolean [boardSize][sectionSize]); + container.setCustom(extras.getBoolean("isCustom", false)); try { container.parseGameType("Default_" + sectionSize + "x" + sectionSize); @@ -488,12 +489,19 @@ public class GameActivity extends BaseActivity implements NavigationView.OnNavig editor.apply(); } - //Show time hints new plus old best time + //Don't save statistics if game is custom + boolean isNewBestTime; - statistics.saveGameStats(); + if (!gameController.gameIsCustom()) { + //Show time hints new plus old best time + statistics.saveGameStats(); + isNewBestTime = gameController.getUsedHints() == 0 + && statistics.loadStats(gameController.getGameType(),gameController.getDifficulty()).getMinTime() >= gameController.getTime(); + + } else { + isNewBestTime = false; + } - boolean isNewBestTime = gameController.getUsedHints() == 0 - && statistics.loadStats(gameController.getGameType(),gameController.getDifficulty()).getMinTime() >= gameController.getTime(); dialog = new WinDialog(this, R.style.WinDialog , timeToString(gameController.getTime()), String.valueOf(gameController.getUsedHints()), isNewBestTime); From 8027b8b4091eb1715f6e68508cfd8a6c15ab964c Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 14 Jul 2020 10:08:42 +0200 Subject: [PATCH 07/11] Only add time and hints used in a Sudoku to HighscoreInfoContainer if sudoku isn't custom --- .../privacyfriendlysudoku/controller/SaveLoadStatistics.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java index 7819531..b519ace 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/SaveLoadStatistics.java @@ -204,12 +204,12 @@ public class SaveLoadStatistics implements ITimerListener, IHintListener { @Override public void onTick(int time) { - incTime(gc.getDifficulty(), gc.getGameType()); + if (!gc.gameIsCustom()) incTime(gc.getDifficulty(), gc.getGameType()); //gc.getUsedHints(); } @Override public void onHintUsed() { - incHints(gc.getDifficulty(),gc.getGameType()); + if (!gc.gameIsCustom()) incHints(gc.getDifficulty(),gc.getGameType()); } } From 110013f8ca306c1aee638cf81ba82f04e58e8771 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 18 Jul 2020 15:43:17 +0200 Subject: [PATCH 08/11] Flatten hierarchy of the DailySudokuActivity's portrait mode layout --- .../main/res/layout/activity_daily_sudoku.xml | 437 +++++++++--------- 1 file changed, 213 insertions(+), 224 deletions(-) diff --git a/app/src/main/res/layout/activity_daily_sudoku.xml b/app/src/main/res/layout/activity_daily_sudoku.xml index 13f345c..b2d4158 100644 --- a/app/src/main/res/layout/activity_daily_sudoku.xml +++ b/app/src/main/res/layout/activity_daily_sudoku.xml @@ -1,249 +1,236 @@ - - + + - + app:layout_constraintTop_toBottomOf="@+id/appbar" /> - + + + + android:layout_marginStart="16dp" + android:layout_marginLeft="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:adjustViewBounds="true" + android:src="@drawable/icon_default_9x9" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/view" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - - + + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:numStars="4" + android:scaleX="1.5" + android:scaleY="1.5" + app:layout_constraintBottom_toBottomOf="@+id/view" + app:layout_constraintEnd_toEndOf="@+id/statistic_image" + app:layout_constraintStart_toStartOf="@+id/statistic_image" + app:layout_constraintTop_toBottomOf="@+id/statistic_image" + app:layout_constraintVertical_bias="0.50" /> - + + + + + + + + + + + + + + + + + + + + + + + + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + android:clipChildren="false" + app:layout_scrollFlags="enterAlways" + app:popupTheme="@style/AppTheme.PopupOverlay"> - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -253,10 +240,12 @@ android:layout_height="0dp" app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="@+id/linearLayout6" - app:layout_constraintTop_toBottomOf="@+id/linearLayout6" > + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="parent"> - \ No newline at end of file + + \ No newline at end of file From efa6394f5774ca138a27c91dd9fe87bc88a54009 Mon Sep 17 00:00:00 2001 From: uykek Date: Sat, 18 Jul 2020 15:46:12 +0200 Subject: [PATCH 09/11] Override transition when navigating from MainActivity to DailySudokuActivity --- .../java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java index 17066e0..7992063 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java @@ -364,6 +364,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig case R.id.nav_dailySudoku_main: intent = new Intent(this, DailySudokuActivity.class); startActivity(intent); + overridePendingTransition(0, 0); break; default: From a4b80b9eccad78eee1b1432ce5ef89c70131f6d0 Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 21 Jul 2020 09:50:37 +0200 Subject: [PATCH 10/11] Fix a few constraints in the portrait layout of the DailySudokuActivity --- app/src/main/res/layout/activity_daily_sudoku.xml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/layout/activity_daily_sudoku.xml b/app/src/main/res/layout/activity_daily_sudoku.xml index b2d4158..73322b0 100644 --- a/app/src/main/res/layout/activity_daily_sudoku.xml +++ b/app/src/main/res/layout/activity_daily_sudoku.xml @@ -7,7 +7,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto"> - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.25" /> + app:layout_constraintVertical_bias="0.75" /> From 3db311061805b2bd37057c266d3be4a3724812a6 Mon Sep 17 00:00:00 2001 From: uykek Date: Tue, 21 Jul 2020 10:43:54 +0200 Subject: [PATCH 11/11] Adjust generation of the daily sudoku so that challenges have a lesser probability of being generated than other sudokus, but generating them on the days they do appear won't take as long as before --- .../controller/NewLevelManager.java | 13 +++------- .../controller/QQWingController.java | 26 ++++++++++++++----- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java index 7aca04a..47ec3a8 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/NewLevelManager.java @@ -40,7 +40,8 @@ public class NewLevelManager { public static int PRE_SAVES_MIN = 3; public static int PRE_SAVES_MAX = 10; - private final double CHALLENGE_GENERATION_PROBABILITY = 0.9; + private final double CHALLENGE_GENERATION_PROBABILITY = 0.25; + private final int CHALLENGE_ITERATIONS = 4; public static NewLevelManager getInstance(Context context, SharedPreferences settings) { @@ -84,16 +85,8 @@ public class NewLevelManager { public int[] loadDailySudoku() { DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); String toHash = "Sudoku/.PrivacyFriendly/." + dateFormat.format(new Date()); - boolean generateChallenge = new Random(toHash.hashCode()).nextDouble() >= CHALLENGE_GENERATION_PROBABILITY; QQWingController controller = new QQWingController(); - int[] result; - - if (generateChallenge) { - result = controller.generateFromSeed(toHash.hashCode(), GameDifficulty.Challenge); - } else { - result = controller.generateFromSeed(toHash.hashCode(), GameDifficulty.Unspecified); - } - return result; + return controller.generateFromSeed(toHash.hashCode(), CHALLENGE_GENERATION_PROBABILITY, CHALLENGE_ITERATIONS); } public int[] loadLevel(GameType type, GameDifficulty diff) { diff --git a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java index eb78863..87a81b1 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/controller/QQWingController.java @@ -4,6 +4,7 @@ import android.os.Parcelable; import android.util.Log; import java.util.LinkedList; +import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.Date; @@ -53,20 +54,27 @@ public class QQWingController { return generated; } - public int[] generateFromSeed(int seed, GameDifficulty difficulty) { + public int[] generateFromSeed(int seed) { + return generateFromSeed(seed, 1, 1); + } + + public int[] generateFromSeed(int seed, double challengePermission, int challengeIterations) { generated.clear(); QQWing generator = new QQWing(GameType.Default_9x9, GameDifficulty.Unspecified); - boolean havePuzzle = false; + boolean continueSearch = true; + Random random = new Random(seed); + int seedFactor = 2; - while(!havePuzzle) { - seed++; + while(continueSearch && challengeIterations > 0) { + seed *= seedFactor; generator.setRandom(seed); generator.setRecordHistory(true); generator.generatePuzzle(); - if (difficulty == GameDifficulty.Unspecified && generator.getDifficulty() != GameDifficulty.Challenge - || difficulty == generator.getDifficulty()) { - havePuzzle = true; + if (generator.getDifficulty() != GameDifficulty.Challenge || random.nextDouble() < challengePermission) { + continueSearch = false; + } else { + challengeIterations--; } } @@ -223,6 +231,10 @@ public class QQWingController { } } + public boolean isImpossible() { + return solveImpossible; + } + private static class QQWingOptions { // defaults for options boolean needNow = false;