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/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/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; 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()); } } 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..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 @@ -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) { @@ -179,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("/"); @@ -196,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); 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 { 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 dcc5976..2316cda 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); @@ -149,8 +150,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; @@ -487,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); 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 c46dfd8..566ad3b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlysudoku/ui/MainActivity.java @@ -388,6 +388,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: @@ -409,7 +410,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(); diff --git a/app/src/main/res/layout/activity_daily_sudoku.xml b/app/src/main/res/layout/activity_daily_sudoku.xml index 13f345c..73322b0 100644 --- a/app/src/main/res/layout/activity_daily_sudoku.xml +++ b/app/src/main/res/layout/activity_daily_sudoku.xml @@ -1,249 +1,233 @@ - - + - + 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" + app:layout_constraintVertical_bias="0.25" /> - - + + 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.75" /> - + + + + + + + + + + + + + + + + + + + + + + + + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + android:clipChildren="false" + app:layout_scrollFlags="enterAlways" + app:popupTheme="@style/AppTheme.PopupOverlay"> - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -253,10 +237,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