Skip to content

Commit

Permalink
Adding hints back to sudoku board (#343)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas-Gallant authored Nov 1, 2024
1 parent 35183e8 commit 962275b
Show file tree
Hide file tree
Showing 79 changed files with 16,029 additions and 1,685 deletions.
19 changes: 17 additions & 2 deletions Changelog.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
[
{
"version": "1.26.0",
"date": "October 31st, 2024",
"summary": "🎃 Adding hints back to sudoku board. Adding ability to change hint strategy order.",
"features": [
"User can now click a hint. There are 5 stages to the hint which show the user how to continue the sudoku puzzle.",
"UI interface for changing the hint priority order is moved out of feature preview and is visible on profile page.",
"User can now see the number of hints they used after completing a game, and the total hints they have used on the statistics page.",
"User can now see the number of hints used for each strategy after completing a game, and the total number they have used on the statistics page.",
"User can use hotkeys 'h' or 'H' for triggering a hint"
],
"targets": ["web", "mobile", "desktop"],
"contributors": ["Thomas-Gallant"]
},
{
"version": "1.25.0",
"date": "October 24th, 2024",
Expand Down Expand Up @@ -204,14 +218,15 @@
{
"version": "1.16.0",
"date": "December 3rd, 2023",
"summary": "Rebuilt the Sudoku board logic from the ground up.",
"summary": "Rebuilt the Sudoku board logic from the ground up. Removed hint logic, Drills, and Demo board.",
"features": [
"Move history is now preserved through browser refreshes, and is stored on the user's device.",
"0 key can now be used to erase values or notes in a Sudoku cell",
"Users can now unselect the currently selected cell.",
"The end game modal is replaced with an end game screen.",
"Improved performance of the Sudoku board",
"Demo board and Drills are no longer functional and will be added back in a later release."
"Demo board and Drills are no longer functional and will be added back in a later release.",
"Hints are no longer functional and will be added back in a later release."
],
"bug fixes": [
"Erase button is now disabled if a cell is not selected. User can no longer erase correct cells.",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
- 🎓 Learn how to play Sudoku with lessons from the basics all the way to advanced strategies
- 📊 Statistics to track your progress
- ⚙️ Sensible default settings for casual players with options to customize the playing experience for users with different playstyles
- 📅 _Upcoming_: custom strategy based hints from the Sudokuru npm library module
- 💡Custom strategy based hints from the Sudokuru npm library module
- 📅 _Upcoming_: drills which let you practice individual strategies also powered by the Sudokuru npm library module

# 🖥️ Supported Platforms
Expand Down
6 changes: 2 additions & 4 deletions app/Api/Profile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { profile } from "./Puzzle.Types";
import { getKeyJSON, storeData } from "../Functions/AsyncStorage";
import { SUDOKU_STRATEGY_ARRAY } from "sudokuru";
import { returnSudokuStrategyArray } from "../Contexts/PreferencesContext";

type profileValue =
| "theme"
Expand All @@ -15,9 +15,7 @@ export class Profile {
const value = await getKeyJSON("profile");

// deep clone to avoid manipulation of const value.
const sudokuStrategyArray = JSON.parse(
JSON.stringify(SUDOKU_STRATEGY_ARRAY)
);
const sudokuStrategyArray = returnSudokuStrategyArray();

const defaultProfileValues: profile = {
theme: true,
Expand Down
8 changes: 6 additions & 2 deletions app/Api/Puzzle.Types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SudokuStrategyArray } from "sudokuru";
import { SudokuStrategy, SudokuStrategyArray } from "sudokuru";

export interface Puzzle {
puzzle: string;
Expand Down Expand Up @@ -45,8 +45,12 @@ export interface Statistics {
fastestSolveTime: number;
averageSolveTime: number;
totalSolveTime: number;
numHintsUsed: number;
numWrongCellsPlayed: number;
numHintsUsed: number;
numHintsUsedPerStrategy: {
hintStrategy: SudokuStrategy;
numHintsUsed: number;
}[];
}

export interface Profile {
Expand Down
34 changes: 32 additions & 2 deletions app/Api/Puzzles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
GameDifficulty,
returnGameOfDifficulty,
} from "../Components/SudokuBoard/Functions/Difficulty";
import { SudokuStrategy } from "sudokuru";

/**
* Functions to handle puzzle related operations
Expand Down Expand Up @@ -43,11 +44,24 @@ export class Puzzles {
}

/**
* Given deletes the users active game and returns game score
* @returns promise of game score
* Completes the game by removing the active game from storage and updating user statistics.
*
* @param numHintsUsed - The total number of hints used during the game.
* @param numHintsUsedPerStrategy - An array of objects detailing the number of hints used per strategy.
* @param numWrongCellsPlayed - The total number of incorrect cells played during the game.
* @param time - The total time taken to complete the game.
* @param score - The score achieved in the game.
*
* This function removes the current active game from storage and updates the user's statistics
* including total score, fastest and total solve times, average solve time, and the number of games played.
* It also updates the number of hints used for each strategy applied during the game.
*/
public static async finishGame(
numHintsUsed: number,
numHintsUsedPerStrategy: {
hintStrategy: SudokuStrategy;
numHintsUsed: number;
}[],
numWrongCellsPlayed: number,
time: number,
score: number
Expand All @@ -73,6 +87,22 @@ export class Puzzles {
statistics.totalSolveTime / statistics.numGamesPlayed
);

// Create or update user's total number of hints used per strategy statistics
for (const newHintStrategies of numHintsUsedPerStrategy) {
const existingHintStrategies = statistics.numHintsUsedPerStrategy.find(
(strategy: { hintStrategy: SudokuStrategy; numHintsUsed: number }) =>
strategy.hintStrategy === newHintStrategies.hintStrategy
);
if (existingHintStrategies) {
existingHintStrategies.numHintsUsed += newHintStrategies.numHintsUsed;
} else {
statistics.numHintsUsedPerStrategy.push({
hintStrategy: newHintStrategies.hintStrategy,
numHintsUsed: newHintStrategies.numHintsUsed,
});
}
}

Statistics.saveStatisitics(statistics);
}
}
3 changes: 2 additions & 1 deletion app/Api/Statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ export class Statistics {
fastestSolveTime: 0,
averageSolveTime: 0,
totalSolveTime: 0,
numHintsUsed: 0,
numWrongCellsPlayed: 0,
numHintsUsed: 0,
numHintsUsedPerStrategy: [],
};
await this.saveStatisitics(statistics);
return statistics;
Expand Down
46 changes: 46 additions & 0 deletions app/Components/NumHintsUsedPerStrategy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Statistic from "./Statistics/Statistic";
import { formatOneLessonName } from "../Functions/learnedLessons";
import React from "react";
import { SudokuStrategy } from "sudokuru";

interface NumHintsUsedPerStrategyProps {
numHintsUsedPerStrategy: {
hintStrategy: SudokuStrategy;
numHintsUsed: number;
}[];
}

/**
* Renders a list of JSX Statistic components displaying the number of hints used per strategy.
*
* @param numHintsUsedPerStrategy - Array of objects detailing hints used for each strategy,
* sorted by most hints used.
* @returns An array of JSX elements representing the number of hints used per strategy.
*/
export const NumHintsUsedPerStrategy = (
props: NumHintsUsedPerStrategyProps
) => {
// sort by most number of hints
const numHintsUsedPerStrategyClone = [...props.numHintsUsedPerStrategy].sort(
(a, b) => {
return b.numHintsUsed - a.numHintsUsed;
}
);

// Generates the JSX elements for the number of hints used per strategy
const strategyHints: React.JSX.Element[] = [];
for (const strategyHint of numHintsUsedPerStrategyClone) {
strategyHints.push(
<Statistic
statisticName={
" " + formatOneLessonName(strategyHint.hintStrategy) + ": "
}
statisticValue={strategyHint.numHintsUsed}
testID={"hintsUsed" + strategyHint.hintStrategy}
key={strategyHint.hintStrategy}
/>
);
}

return strategyHints;
};
15 changes: 8 additions & 7 deletions app/Components/Profile/StrategyOrder.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from "react";
import { View, TouchableOpacity } from "react-native";
import { IconButton, Text } from "react-native-paper";
import { SUDOKU_STRATEGY_ARRAY, SudokuStrategyArray } from "sudokuru";
import { SudokuStrategyArray } from "sudokuru";
import { formatOneLessonName } from "../../Functions/learnedLessons";
import { PreferencesContext } from "../../Contexts/PreferencesContext";
import {
PreferencesContext,
returnSudokuStrategyArray,
} from "../../Contexts/PreferencesContext";

/**
* This is a component for the user to select what order hints for strategies should be given in
Expand Down Expand Up @@ -192,11 +195,9 @@ const StrategyOrder = () => {
borderColor: "#025E73",
}}
// need a deep copy of SUDOKU_STRATEGY_ARRAY otherwise it becomes mutated
onPress={() =>
updateStrategyHintOrder(
JSON.parse(JSON.stringify(SUDOKU_STRATEGY_ARRAY))
)
}
onPress={() => {
updateStrategyHintOrder(returnSudokuStrategyArray());
}}
/>
<Text style={{ color: "#025E73" }}>RESET</Text>
</View>
Expand Down
20 changes: 15 additions & 5 deletions app/Components/Statistics/TotalStatistics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import { Text, useTheme } from "react-native-paper";
import { useWindowDimensions } from "react-native";
import Statistic from "./Statistic";
import { formatTime } from "../SudokuBoard/Functions/BoardFunctions";
import { SudokuStrategy } from "sudokuru";
import React from "react";
import { NumHintsUsedPerStrategy } from "../NumHintsUsedPerStrategy";

interface TotalStatisticsProps {
export interface TotalStatisticsProps {
totalScore: number;
numGamesPlayed: number;
fastestSolveTime: number;
averageSolveTime: number;
totalSolveTime: number;
numHintsUsed: number;
numHintsUsedPerStrategy: {
hintStrategy: SudokuStrategy;
numHintsUsed: number;
}[];
numWrongCellsPlayed: number;
}

Expand Down Expand Up @@ -64,15 +71,18 @@ const TotalStatistics = (props: TotalStatisticsProps) => {
statisticValue={formatTime(props.totalSolveTime)}
testID="totalSolveTime"
/>
<Statistic
statisticName="Total Mistakes Made: "
statisticValue={props.numWrongCellsPlayed}
testID="numWrongCellsPlayed"
/>
<Statistic
statisticName="Total Hints Used: "
statisticValue={props.numHintsUsed}
testID="numHintsUsed"
/>
<Statistic
statisticName="Total Mistakes Made: "
statisticValue={props.numWrongCellsPlayed}
testID="numWrongCellsPlayed"
<NumHintsUsedPerStrategy
numHintsUsedPerStrategy={props.numHintsUsedPerStrategy}
/>
</View>
</View>
Expand Down
15 changes: 15 additions & 0 deletions app/Components/SudokuBoard/Components/ActionRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface ActionRowProps {
undo: () => void;
toggleNoteMode: () => void;
eraseSelected: () => void;
getHint: () => void;
boardHasConflict: boolean;
}

const ActionRow = (props: ActionRowProps) => {
Expand All @@ -21,6 +23,8 @@ const ActionRow = (props: ActionRowProps) => {
undo,
toggleNoteMode,
eraseSelected,
getHint,
boardHasConflict,
} = props;
const cellSize = getCellSize();
const theme = useTheme();
Expand Down Expand Up @@ -77,6 +81,17 @@ const ActionRow = (props: ActionRowProps) => {
size={cellSize / sizeConst}
/>
</Pressable>
<Pressable
testID={"hintButton"}
disabled={boardHasConflict}
onPress={getHint}
>
<MaterialCommunityIcons
color={theme.colors.onBackground}
name="help"
size={cellSize / sizeConst}
/>
</Pressable>
</View>
);
};
Expand Down
Loading

0 comments on commit 962275b

Please sign in to comment.