From 7d6c95d81da3d441c05271f6eac60234dfe209ed Mon Sep 17 00:00:00 2001 From: Galla Date: Sun, 27 Oct 2024 19:43:06 -0400 Subject: [PATCH] Resolving the issues with types --- app/Api/Drills.ts | 25 - app/Api/Lessons.ts | 468 ++++++++---------- app/Api/Profile.ts | 125 +++-- app/Api/Puzzles.ts | 115 ++--- app/Api/Statistics.ts | 110 ++-- app/Components/Home/LessonPanel.tsx | 22 +- app/Components/Statistics/TotalStatistics.tsx | 1 + .../SudokuBoard/Components/Cell.tsx | 1 + .../SudokuBoard/Components/EndGameModal.tsx | 1 + .../SudokuBoard/Components/PauseButton.tsx | 1 + .../SudokuBoard/Functions/BoardFunctions.ts | 8 +- .../SudokuBoard/Functions/Difficulty.ts | 6 +- .../SudokuBoard/Functions/generateGame.ts | 10 +- app/Components/SudokuBoard/SudokuBoard.tsx | 8 +- app/Contexts/InitializeContext.ts | 20 +- app/Data/puzzles/amateur_puzzles.ts | 5 +- app/Data/puzzles/grandmaster_puzzles.ts | 4 +- app/Data/puzzles/layman_puzzles.ts | 4 +- app/Data/puzzles/master_puzzles.ts | 4 +- app/Data/puzzles/novice_puzzles.ts | 4 +- app/Data/puzzles/professional_puzzles.ts | 4 +- app/Data/puzzles/protege_puzzles.ts | 4 +- app/Data/puzzles/pundit_puzzles.ts | 4 +- app/Data/puzzles/trainee_puzzles.ts | 4 +- app/Functions/LocalDatabase.ts | 70 +-- app/Navigation/DrawerNavigator.tsx | 1 + app/Pages/LearnPage.tsx | 4 +- app/Pages/Lesson.tsx | 34 +- app/Pages/PlayPage.tsx | 4 +- app/Pages/StatisticsPage.tsx | 31 +- docs/BackendApiCalls/api.md | 221 ++------- 31 files changed, 502 insertions(+), 821 deletions(-) delete mode 100644 app/Api/Drills.ts diff --git a/app/Api/Drills.ts b/app/Api/Drills.ts deleted file mode 100644 index e554d559..00000000 --- a/app/Api/Drills.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { calculateNotes, SudokuStrategy } from "sudokuru"; -import { puzzle } from "./Puzzle.Types"; -import { drill } from "./Puzzle.Types"; -import { returnLocalDrillGame } from "../Functions/LocalDatabase"; - -/** - * Functions to handle requesting drills - */ -export class Drills { - /** - * Drills.getGame retrieves the requested drill game from the device's local storage. - * @param strategy the name of the sudoku strategy to retrieve the relevant drill. - * @returns a drill object corresponding to the drill type being requested. - */ - public static async getGame(strategy: SudokuStrategy): Promise { - let data: puzzle = returnLocalDrillGame(strategy); - let boardString: string = data.puzzle; - let notes: string = calculateNotes(boardString); - return { - puzzleCurrentState: boardString, - puzzleCurrentNotesState: notes, - puzzleSolution: data.puzzleSolution, - }; - } -} diff --git a/app/Api/Lessons.ts b/app/Api/Lessons.ts index e64b1fcb..ee42a4f7 100644 --- a/app/Api/Lessons.ts +++ b/app/Api/Lessons.ts @@ -1,271 +1,207 @@ -export interface LessonOfflineMode { - mode: getLessonMode.Offline; -} - -export interface LessonOnlineMode { - mode: getLessonMode.Online; -} - -export enum getLessonMode { - Offline, - Online, -} +import { ImageURISource } from "react-native"; /** - * Functions to handle requesting lessons + * Returns a list of all the strategies that have lessons + * @returns string array of strategy names that getLessonSteps can be called with */ -export class Lessons { - /** - * Returns a list of all the strategies that have lessons - * @returns string array of strategy names that getSteps can be called with - */ - public static async getStrategies( - args: LessonOfflineMode | LessonOnlineMode - ): Promise { - if (args.mode === getLessonMode.Online) { - const response: Response = await fetch( - "https://sudokuru.s3.amazonaws.com/Lessons/strategies.json", - { cache: "no-cache" } - ); - const json = await response.json(); - return json; - } else { - return [ - "SUDOKU_101", - "AMEND_NOTES", - "NAKED_SINGLE", - "SIMPLIFY_NOTES", - "NAKED_SET", - "HIDDEN_SINGLE", - "HIDDEN_SET", - "POINTING_SET", - ]; - } - } +export const getStrategies = (): string[] => { + return [ + "SUDOKU_101", + "AMEND_NOTES", + "NAKED_SINGLE", + "SIMPLIFY_NOTES", + "NAKED_SET", + "HIDDEN_SINGLE", + "HIDDEN_SET", + "POINTING_SET", + ]; +}; - /** - * Given a strategy string (from getStrategies()) returns a 2d string array of steps for the strategy - * @param strategy - name of the strategy - * @returns 2d string array of steps with first value in each array being text and second being link to s3 image - */ - public static async getSteps( - strategy: string, - args: LessonOfflineMode | LessonOnlineMode - ): Promise { - if (args.mode === getLessonMode.Online) { - const response: Response = await fetch( - "https://sudokuru.s3.amazonaws.com/Lessons/" + strategy + ".json", - { cache: "no-cache" } - ); - const json = await response.json(); - return json; - } else { - if (strategy === "SUDOKU_101") { - return [ - [ - "Welcome to Sudokuru! As your Sudoku Guru I will teach you how to solve Sudoku puzzles. Shown above is a Sudoku puzzle. All Sudoku puzzles are made of 81 cells in a 9x9 grid.", - require("../../.assets/Lessons/SUDOKU_101_STEP_1.png"), - ], - [ - "Each puzzle can be divided into 9 horizontal rows. Every row needs to have the numbers 1 through 9 exactly once. For example, the 2nd row is highlighted in the puzzle above and is only missing 3, 6, and 7 which will go in the remaining three cells.", - require("../../.assets/Lessons/SUDOKU_101_STEP_2.png"), - ], - [ - "Each puzzle can be divided into 9 vertical columns. Every column needs to have the numbers 1 through 9 exactly once. For example, the 1st column is highlighted in the puzzle above and needs 1, 3, and 5-9 still.", - require("../../.assets/Lessons/SUDOKU_101_STEP_3.png"), - ], - [ - "Each puzzle can be divided into 9 3x3 boxes as denoted by the bolder grid lines. Every box needs to have the numbers 1 through 9 exactly once. The 4th box (counting left to right, top to bottom) is highlighted above and still needs 1, 2-5, and 7-9.", - require("../../.assets/Lessons/SUDOKU_101_STEP_4.png"), - ], - [ - "By making sure each row, column, and box has all nine numbers you can deduce the one valid solution to the puzzle.", - require("../../.assets/Lessons/SUDOKU_101_STEP_5.png"), - ], - ]; - } else if (strategy === "AMEND_NOTES") { - return [ - [ - "Notes are the list of remaining possibilities for a cell. To solve a puzzle we will start by filling in notes for each cell for each value that doesn't conflict with an already placed value.", - require("../../.assets/Lessons/AMEND_NOTES_STEP_1.png"), - ], - [ - "We will start by adding notes to the cell in the top left. We will add every number between 1 and 9 that hasn't already been placed in the cell's row, column or box.", - require("../../.assets/Lessons/AMEND_NOTES_STEP_2.png"), - ], - [ - "The small black numbers in the cell are the numbers that should be added as notes. The small red numbers are the numbers that shouldn't be added because they are already in the row, column, or box as shown in the gold highlighted cells.", - require("../../.assets/Lessons/AMEND_NOTES_STEP_3.png"), - ], - [ - "We've now finished entering all of the valid notes to the top left cell.", - require("../../.assets/Lessons/AMEND_NOTES_STEP_4.png"), - ], - [ - "You have now learned how to amend notes! If you ever make a mistake in a cell you can always erase the notes from it and amend again.", - require("../../.assets/Lessons/AMEND_NOTES_STEP_4.png"), - ], - ]; - } else if (strategy === "NAKED_SINGLE") { - return [ - [ - "To solve a Sudoku puzzle you have to correctly fill in all of the empty cells. You can do this by utilizing the naked single strategy. To use the naked single strategy you first have to find a cell with only one note left.", - require("../../.assets/Lessons/NAKED_SINGLE_STEP_1.png"), - ], - [ - "The highlighted cell in the top middle of the above puzzle is an example of a naked single. Since it only has an 8 left as a note you can fill the cell in with the value 8.", - require("../../.assets/Lessons/NAKED_SINGLE_STEP_2.png"), - ], - [ - "You have now learned how to use the naked single strategy to solve Sudoku puzzles!", - require("../../.assets/Lessons/NAKED_SINGLE_STEP_3.png"), - ], - ]; - } else if (strategy === "SIMPLIFY_NOTES") { - return [ - [ - "Using a placed value, simplify notes lets you to eliminate it from the notes of any cell that shares a row, column, and/or box with the placed value cell.", - require("../../.assets/Lessons/NAKED_SINGLE_STEP_4.png"), - ], - [ - "You can remove the red 8 as a note from the cell directly to the left of the highlighted placed 8 because they share a row (and box).", - require("../../.assets/Lessons/NAKED_SINGLE_STEP_5.png"), - ], - [ - "You have now learned how to use the simplify notes strategy to solve Sudoku puzzles! Part of what makes simplify notes so effective is that they can have a domino effect. For instance, the cell we just removed an 8 from is now a naked single.", - require("../../.assets/Lessons/NAKED_SINGLE_STEP_7.png"), - ], - ]; - } else if (strategy === "NAKED_SET") { - return [ - [ - "If you recall from a previous lesson naked singles are cells with only one possible value, which must be placed there, resulting in its removal from notes of cells sharing the same group.", - require("../../.assets/Lessons/NAKED_SET_STEP_1.png"), - ], - [ - "This rule can be extended to naked pairs, triplets, and quadruplets, where x cells have only x remaining combined possibilities, allowing for removal of these possibilities from the notes of cells in shared groups.", - require("../../.assets/Lessons/NAKED_SET_STEP_1.png"), - ], - [ - "Above we highlighted in gold a naked pair made up of the numbers 2 and 9. They form a naked pair because they are two cells that can only be filled with a combined two shared numbers.", - require("../../.assets/Lessons/NAKED_SET_STEP_2.png"), - ], - [ - "Therefore, one of them will eventually be filled with a 2 while the other will be filled with a 9. This lets you remove both 2 and a 9 from every cell in the column and box the cells share.", - require("../../.assets/Lessons/NAKED_SET_STEP_2.png"), - ], - [ - "We've now finished removing all of the notes from applying the naked pair.", - require("../../.assets/Lessons/NAKED_SET_STEP_3.png"), - ], - [ - "You've now learned how to use the naked set strategies! While you can't directly place values with naked sets other than singles you can remove lots of notes which lead to placing values like in the case of the 6 naked single revealed in the leftmost column by the naked pair we just applied.", - require("../../.assets/Lessons/NAKED_SET_STEP_4.png"), - ], - ]; - } else if (strategy === "HIDDEN_SINGLE") { - return [ - [ - "Hidden singles are when a single note is only left in a single cell in a row, column, or box. When you find a hidden single you can remove all of the notes other than the hidden single itself from the cell.", - require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_1.png"), - ], - [ - "In the highlighted column above the number 9 is absent from all of the gold highlighted cells appearing only in one cell. Since only one cell in the column has a nine left you can remove all of the other notes from it as shown highlighted red.", - require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_2.png"), - ], - [ - "We've now finished removing all of the excess notes from the hidden single cell.", - require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_3.png"), - ], - [ - "You've now learned how to use the hidden single strategy! Hidden singles are particularly useful because they always result in naked singles which can be used to place the value.", - require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_4.png"), - ], - ]; - } else if (strategy === "HIDDEN_SET") { - return [ - [ - "If you recall from a previous lesson hidden singles are based on the fact that if a row, column, or box only has a single location to place a value than it must be placed there. This idea can be generalized to any set size.", - require("../../.assets/Lessons/HIDDEN_SET_STEP_1.png"), - ], - [ - "For instance, you can have hidden pairs, triplets, and quadruplets. All hidden sets rely on there only being x places that x values can be placed in a given row, column, or box leading to each of them having to be placed in one of them eventually. This results in all other notes being removed from them.", - require("../../.assets/Lessons/HIDDEN_SET_STEP_1.png"), - ], - [ - "Above we have highlighted a hidden triplet made up of 1, 5, and 9. They are a hidden triplet because they are three cells containing a combined three numbers that are absent from every other cell in their shared column (other cells are highlighted in gold).", - require("../../.assets/Lessons/HIDDEN_SET_STEP_2.png"), - ], - [ - "Therefore, each of them will eventually be filled with one of those three numbers. This lets you remove every other number from their notes as highlighted in red.", - require("../../.assets/Lessons/HIDDEN_SET_STEP_2.png"), - ], - [ - "We've now finished removing all of the notes from applying the hidden triplet.", - require("../../.assets/Lessons/HIDDEN_SET_STEP_3.png"), - ], - [ - "You've now learned how to use the hidden set strategies! While you can't directly place values with hidden sets you can remove lots of notes which lead to placing values like in the case of the 1 naked single revealed in the column by the hidden triplet we just applied.", - require("../../.assets/Lessons/HIDDEN_SET_STEP_4.png"), - ], - ]; - } else if (strategy === "POINTING_SET") { - return [ - [ - "Pointing sets are when all cells containing a specific note in a box share the same row or column. When you find a pointing set you can remove the note from every cell in the shared row or column except those in the box itself.", - require("../../.assets/Lessons/POINTING_SET_1.png"), - ], - [ - "The cells highlighted in gold in the above puzzle form a pointing pair since they are the only cells in the highlighted box containing the note 4 and they share a column.", - require("../../.assets/Lessons/POINTING_SET_2.png"), - ], - [ - "Since you know one of the gold cells will eventually have a 4 placed in it you can remove the 4 from the notes of the other cells in the column as highlighted in red.", - require("../../.assets/Lessons/POINTING_SET_3.png"), - ], - [ - "You've now learned how to use the pointing pair strategy!", - require("../../.assets/Lessons/POINTING_SET_4.png"), - ], - [ - "There are also pointing triplets like the one highlighted in gold which contain the only 8's in the box letting you remove all other 8's from the shared row.", - require("../../.assets/Lessons/POINTING_SET_5.png"), - ], - [ - "You've now learned how to use pointing sets!", - require("../../.assets/Lessons/POINTING_SET_6.png"), - ], - ]; - } - } - } - - /** - * Returns a tutorial to teach new users the basics of Sudoku via the first few lessons - * @returns 2d string array of steps from the first few lessons with first value in each array being text and second being link to s3 image - */ - public static async getTutorial(): Promise { - const sudoku_101: Response = await fetch( - "https://sudokuru.s3.amazonaws.com/Lessons/SUDOKU_101.json", - { cache: "no-cache" } - ); - const lesson1 = await sudoku_101.json(); - const amend_notes: Response = await fetch( - "https://sudokuru.s3.amazonaws.com/Lessons/AMEND_NOTES.json", - { cache: "no-cache" } - ); - const lesson2 = await amend_notes.json(); - const naked_single = await fetch( - "https://sudokuru.s3.amazonaws.com/Lessons/NAKED_SINGLE.json", - { cache: "no-cache" } - ); - const lesson3 = await naked_single.json(); - const simplify_notes = await fetch( - "https://sudokuru.s3.amazonaws.com/Lessons/SIMPLIFY_NOTES.json", - { cache: "no-cache" } - ); - const lesson4 = await simplify_notes.json(); - const tutorial = lesson1.concat(lesson2, lesson3, lesson4); - return tutorial; +/** + * Given a strategy string (from getLessonStrategies()) returns a 2d string array of steps for the strategy + * @param strategy - name of the strategy + * @returns 2d string array of steps with first value in each array being text and second being link to s3 image + */ +export const getLessonSteps = ( + strategy: string +): [string, ImageURISource][] => { + if (strategy === "SUDOKU_101") { + return [ + [ + "Welcome to Sudokuru! As your Sudoku Guru I will teach you how to solve Sudoku puzzles. Shown above is a Sudoku puzzle. All Sudoku puzzles are made of 81 cells in a 9x9 grid.", + require("../../.assets/Lessons/SUDOKU_101_STEP_1.png"), + ], + [ + "Each puzzle can be divided into 9 horizontal rows. Every row needs to have the numbers 1 through 9 exactly once. For example, the 2nd row is highlighted in the puzzle above and is only missing 3, 6, and 7 which will go in the remaining three cells.", + require("../../.assets/Lessons/SUDOKU_101_STEP_2.png"), + ], + [ + "Each puzzle can be divided into 9 vertical columns. Every column needs to have the numbers 1 through 9 exactly once. For example, the 1st column is highlighted in the puzzle above and needs 1, 3, and 5-9 still.", + require("../../.assets/Lessons/SUDOKU_101_STEP_3.png"), + ], + [ + "Each puzzle can be divided into 9 3x3 boxes as denoted by the bolder grid lines. Every box needs to have the numbers 1 through 9 exactly once. The 4th box (counting left to right, top to bottom) is highlighted above and still needs 1, 2-5, and 7-9.", + require("../../.assets/Lessons/SUDOKU_101_STEP_4.png"), + ], + [ + "By making sure each row, column, and box has all nine numbers you can deduce the one valid solution to the puzzle.", + require("../../.assets/Lessons/SUDOKU_101_STEP_5.png"), + ], + ]; + } else if (strategy === "AMEND_NOTES") { + return [ + [ + "Notes are the list of remaining possibilities for a cell. To solve a puzzle we will start by filling in notes for each cell for each value that doesn't conflict with an already placed value.", + require("../../.assets/Lessons/AMEND_NOTES_STEP_1.png"), + ], + [ + "We will start by adding notes to the cell in the top left. We will add every number between 1 and 9 that hasn't already been placed in the cell's row, column or box.", + require("../../.assets/Lessons/AMEND_NOTES_STEP_2.png"), + ], + [ + "The small black numbers in the cell are the numbers that should be added as notes. The small red numbers are the numbers that shouldn't be added because they are already in the row, column, or box as shown in the gold highlighted cells.", + require("../../.assets/Lessons/AMEND_NOTES_STEP_3.png"), + ], + [ + "We've now finished entering all of the valid notes to the top left cell.", + require("../../.assets/Lessons/AMEND_NOTES_STEP_4.png"), + ], + [ + "You have now learned how to amend notes! If you ever make a mistake in a cell you can always erase the notes from it and amend again.", + require("../../.assets/Lessons/AMEND_NOTES_STEP_4.png"), + ], + ]; + } else if (strategy === "NAKED_SINGLE") { + return [ + [ + "To solve a Sudoku puzzle you have to correctly fill in all of the empty cells. You can do this by utilizing the naked single strategy. To use the naked single strategy you first have to find a cell with only one note left.", + require("../../.assets/Lessons/NAKED_SINGLE_STEP_1.png"), + ], + [ + "The highlighted cell in the top middle of the above puzzle is an example of a naked single. Since it only has an 8 left as a note you can fill the cell in with the value 8.", + require("../../.assets/Lessons/NAKED_SINGLE_STEP_2.png"), + ], + [ + "You have now learned how to use the naked single strategy to solve Sudoku puzzles!", + require("../../.assets/Lessons/NAKED_SINGLE_STEP_3.png"), + ], + ]; + } else if (strategy === "SIMPLIFY_NOTES") { + return [ + [ + "Using a placed value, simplify notes lets you to eliminate it from the notes of any cell that shares a row, column, and/or box with the placed value cell.", + require("../../.assets/Lessons/NAKED_SINGLE_STEP_4.png"), + ], + [ + "You can remove the red 8 as a note from the cell directly to the left of the highlighted placed 8 because they share a row (and box).", + require("../../.assets/Lessons/NAKED_SINGLE_STEP_5.png"), + ], + [ + "You have now learned how to use the simplify notes strategy to solve Sudoku puzzles! Part of what makes simplify notes so effective is that they can have a domino effect. For instance, the cell we just removed an 8 from is now a naked single.", + require("../../.assets/Lessons/NAKED_SINGLE_STEP_7.png"), + ], + ]; + } else if (strategy === "NAKED_SET") { + return [ + [ + "If you recall from a previous lesson naked singles are cells with only one possible value, which must be placed there, resulting in its removal from notes of cells sharing the same group.", + require("../../.assets/Lessons/NAKED_SET_STEP_1.png"), + ], + [ + "This rule can be extended to naked pairs, triplets, and quadruplets, where x cells have only x remaining combined possibilities, allowing for removal of these possibilities from the notes of cells in shared groups.", + require("../../.assets/Lessons/NAKED_SET_STEP_1.png"), + ], + [ + "Above we highlighted in gold a naked pair made up of the numbers 2 and 9. They form a naked pair because they are two cells that can only be filled with a combined two shared numbers.", + require("../../.assets/Lessons/NAKED_SET_STEP_2.png"), + ], + [ + "Therefore, one of them will eventually be filled with a 2 while the other will be filled with a 9. This lets you remove both 2 and a 9 from every cell in the column and box the cells share.", + require("../../.assets/Lessons/NAKED_SET_STEP_2.png"), + ], + [ + "We've now finished removing all of the notes from applying the naked pair.", + require("../../.assets/Lessons/NAKED_SET_STEP_3.png"), + ], + [ + "You've now learned how to use the naked set strategies! While you can't directly place values with naked sets other than singles you can remove lots of notes which lead to placing values like in the case of the 6 naked single revealed in the leftmost column by the naked pair we just applied.", + require("../../.assets/Lessons/NAKED_SET_STEP_4.png"), + ], + ]; + } else if (strategy === "HIDDEN_SINGLE") { + return [ + [ + "Hidden singles are when a single note is only left in a single cell in a row, column, or box. When you find a hidden single you can remove all of the notes other than the hidden single itself from the cell.", + require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_1.png"), + ], + [ + "In the highlighted column above the number 9 is absent from all of the gold highlighted cells appearing only in one cell. Since only one cell in the column has a nine left you can remove all of the other notes from it as shown highlighted red.", + require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_2.png"), + ], + [ + "We've now finished removing all of the excess notes from the hidden single cell.", + require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_3.png"), + ], + [ + "You've now learned how to use the hidden single strategy! Hidden singles are particularly useful because they always result in naked singles which can be used to place the value.", + require("../../.assets/Lessons/HIDDEN_SINGLE_STEP_4.png"), + ], + ]; + } else if (strategy === "HIDDEN_SET") { + return [ + [ + "If you recall from a previous lesson hidden singles are based on the fact that if a row, column, or box only has a single location to place a value than it must be placed there. This idea can be generalized to any set size.", + require("../../.assets/Lessons/HIDDEN_SET_STEP_1.png"), + ], + [ + "For instance, you can have hidden pairs, triplets, and quadruplets. All hidden sets rely on there only being x places that x values can be placed in a given row, column, or box leading to each of them having to be placed in one of them eventually. This results in all other notes being removed from them.", + require("../../.assets/Lessons/HIDDEN_SET_STEP_1.png"), + ], + [ + "Above we have highlighted a hidden triplet made up of 1, 5, and 9. They are a hidden triplet because they are three cells containing a combined three numbers that are absent from every other cell in their shared column (other cells are highlighted in gold).", + require("../../.assets/Lessons/HIDDEN_SET_STEP_2.png"), + ], + [ + "Therefore, each of them will eventually be filled with one of those three numbers. This lets you remove every other number from their notes as highlighted in red.", + require("../../.assets/Lessons/HIDDEN_SET_STEP_2.png"), + ], + [ + "We've now finished removing all of the notes from applying the hidden triplet.", + require("../../.assets/Lessons/HIDDEN_SET_STEP_3.png"), + ], + [ + "You've now learned how to use the hidden set strategies! While you can't directly place values with hidden sets you can remove lots of notes which lead to placing values like in the case of the 1 naked single revealed in the column by the hidden triplet we just applied.", + require("../../.assets/Lessons/HIDDEN_SET_STEP_4.png"), + ], + ]; + } else if (strategy === "POINTING_SET") { + return [ + [ + "Pointing sets are when all cells containing a specific note in a box share the same row or column. When you find a pointing set you can remove the note from every cell in the shared row or column except those in the box itself.", + require("../../.assets/Lessons/POINTING_SET_1.png"), + ], + [ + "The cells highlighted in gold in the above puzzle form a pointing pair since they are the only cells in the highlighted box containing the note 4 and they share a column.", + require("../../.assets/Lessons/POINTING_SET_2.png"), + ], + [ + "Since you know one of the gold cells will eventually have a 4 placed in it you can remove the 4 from the notes of the other cells in the column as highlighted in red.", + require("../../.assets/Lessons/POINTING_SET_3.png"), + ], + [ + "You've now learned how to use the pointing pair strategy!", + require("../../.assets/Lessons/POINTING_SET_4.png"), + ], + [ + "There are also pointing triplets like the one highlighted in gold which contain the only 8's in the box letting you remove all other 8's from the shared row.", + require("../../.assets/Lessons/POINTING_SET_5.png"), + ], + [ + "You've now learned how to use pointing sets!", + require("../../.assets/Lessons/POINTING_SET_6.png"), + ], + ]; + } else { + return []; } -} +}; diff --git a/app/Api/Profile.ts b/app/Api/Profile.ts index 1633655b..3699ee7a 100644 --- a/app/Api/Profile.ts +++ b/app/Api/Profile.ts @@ -1,4 +1,4 @@ -import { profile } from "./Puzzle.Types"; +import { Profile } from "./Puzzle.Types"; import { getKeyJSON, storeData } from "../Functions/AsyncStorage"; import { SUDOKU_STRATEGY_ARRAY } from "sudokuru"; @@ -10,74 +10,71 @@ type profileValue = | "highlightRow" | "previewMode" | "strategyHintOrder"; -export class Profile { - public static async getProfile(): Promise { - const value = await getKeyJSON("profile"); - // deep clone to avoid manipulation of const value. - const sudokuStrategyArray = JSON.parse( - JSON.stringify(SUDOKU_STRATEGY_ARRAY) - ); +export const getProfile = async (): Promise => { + const value = await getKeyJSON("profile"); - const defaultProfileValues: profile = { - theme: true, - highlightBox: true, - highlightColumn: true, - highlightRow: true, - highlightIdenticalValues: true, - previewMode: false, - strategyHintOrder: sudokuStrategyArray, - }; + // deep clone to avoid manipulation of const value. + const sudokuStrategyArray = JSON.parse(JSON.stringify(SUDOKU_STRATEGY_ARRAY)); - if (value == null) { - await this.setProfile(defaultProfileValues); - return defaultProfileValues; - } - - // handle initialization if some data is present but not other data - // Not having this code caused a crash on mobile (web was ok for some reason) - // when a user had some but not all profile settings defined in their app storage - // TODO we will want to conver this scenario with end to end tests in the future - Object.entries(defaultProfileValues).forEach(([key, defaultValue]) => { - if (value[key] === undefined) { - value[key] = defaultValue; - } - }); - return value; - } + const defaultProfileValues: Profile = { + theme: true, + highlightBox: true, + highlightColumn: true, + highlightRow: true, + highlightIdenticalValues: true, + previewMode: false, + strategyHintOrder: sudokuStrategyArray, + }; - public static async setProfile(profile: profile) { - storeData("profile", JSON.stringify(profile)); + if (value == null) { + await setProfile(defaultProfileValues); + return defaultProfileValues; } - public static async setProfileValue( - profileValue: profileValue, - newValue?: any - ) { - let value: profile = await this.getProfile(); - switch (profileValue) { - case "theme": - value.theme = !value.theme; - break; - case "highlightBox": - value.highlightBox = !value.highlightBox; - break; - case "highlightColumn": - value.highlightColumn = !value.highlightColumn; - break; - case "highlightIdenticalValues": - value.highlightIdenticalValues = !value.highlightIdenticalValues; - break; - case "highlightRow": - value.highlightRow = !value.highlightRow; - break; - case "previewMode": - value.previewMode = !value.previewMode; - break; - case "strategyHintOrder": - value.strategyHintOrder = newValue; - break; + // handle initialization if some data is present but not other data + // Not having this code caused a crash on mobile (web was ok for some reason) + // when a user had some but not all profile settings defined in their app storage + // TODO we will want to convert this scenario with end to end tests in the future + Object.entries(defaultProfileValues).forEach(([key, defaultValue]) => { + if (value[key] === undefined) { + value[key] = defaultValue; } - this.setProfile(value); + }); + return value; +}; + +export const setProfile = (profile: Profile) => { + storeData("profile", JSON.stringify(profile)); +}; + +export const setProfileValue = async ( + profileValue: profileValue, + newValue?: any +) => { + let value: Profile = await getProfile(); + switch (profileValue) { + case "theme": + value.theme = !value.theme; + break; + case "highlightBox": + value.highlightBox = !value.highlightBox; + break; + case "highlightColumn": + value.highlightColumn = !value.highlightColumn; + break; + case "highlightIdenticalValues": + value.highlightIdenticalValues = !value.highlightIdenticalValues; + break; + case "highlightRow": + value.highlightRow = !value.highlightRow; + break; + case "previewMode": + value.previewMode = !value.previewMode; + break; + case "strategyHintOrder": + value.strategyHintOrder = newValue; + break; } -} + setProfile(value); +}; diff --git a/app/Api/Puzzles.ts b/app/Api/Puzzles.ts index ac708d22..0c7ee4d3 100644 --- a/app/Api/Puzzles.ts +++ b/app/Api/Puzzles.ts @@ -1,78 +1,67 @@ -import { difficulty } from "./../Components/Home/Cards"; -import { statistics } from "./Puzzle.Types"; import { SudokuObjectProps } from "../Functions/LocalDatabase"; import { getKeyJSON, removeData, storeData } from "../Functions/AsyncStorage"; -import { Statistics } from "./Statistics"; import { GameDifficulty, returnGameOfDifficulty, } from "../Components/SudokuBoard/Functions/Difficulty"; +import { Statistics } from "./Puzzle.Types"; +import { getStatistics, saveStatisitics } from "./Statistics"; /** - * Functions to handle puzzle related operations + * Given a difficulty and an user auth token retrieves a random puzzle close to the difficulty that the user hasn't solved before + * @param difficulty - difficulty number (between 0 and 1) + * @param strategies - new game can have subset of these strategies + * @returns promise of puzzle JSON object */ -export class Puzzles { - /** - * Given a difficulty and an user auth token retrieves a random puzzle close to the difficulty that the user hasn't solved before - * @param difficulty - difficulty number (between 0 and 1) - * @param strategies - new game can have subset of these strategies - * @returns promise of puzzle JSON object - */ - public static async startGame( - difficulty: GameDifficulty - ): Promise { - return returnGameOfDifficulty(difficulty); - // !uncomment below for dev testing - // return returnGameOfDifficulty("dev"); - } - - /** - * Given an user auth token retrieves the users active game or returns null if the user doesn't have an active game - * @returns promise of activeGame JSON object - */ - public static async getGame(): Promise { - return await getKeyJSON("active_game"); - } +export const startGame = (difficulty: GameDifficulty): SudokuObjectProps => { + return returnGameOfDifficulty(difficulty); + // !uncomment below for dev testing + // return returnGameOfDifficulty("dev"); +}; - /** - * Given a game saves it to AsyncStorage - * @param game - activeGame JSON object - */ - public static async saveGame(game: SudokuObjectProps) { - storeData("active_game", JSON.stringify([game])); - } +/** + * Given an user auth token retrieves the users active game or returns null if the user doesn't have an active game + * @returns promise of activeGame JSON object + */ +export const getGame = (): Promise => { + return getKeyJSON("active_game"); +}; - /** - * Given deletes the users active game and returns game score - * @returns promise of game score - */ - public static async finishGame( - numHintsUsed: number, - numWrongCellsPlayed: number, - time: number, - score: number - ) { - // remove the game from storage - await removeData("active_game"); +/** + * Given a game saves it to AsyncStorage + * @param game - activeGame JSON object + */ +export const saveGame = (game: SudokuObjectProps) => { + storeData("active_game", JSON.stringify([game])); +}; - // Create or update user's statistics - let statistics: statistics = await Statistics.getStatistics(); +/** + * Given deletes the users active game and returns game score + * @returns promise of game score + */ +export const finishGame = async ( + numHintsUsed: number, + numWrongCellsPlayed: number, + time: number, + score: number +) => { + // remove the game from storage + await removeData("active_game"); - statistics.totalScore += score; - if ( - time < statistics.fastestSolveTime || - statistics.fastestSolveTime === 0 - ) { - statistics.fastestSolveTime = time; - } - statistics.totalSolveTime += time; - statistics.numGamesPlayed += 1; - statistics.numHintsUsed += numHintsUsed; - statistics.numWrongCellsPlayed += numWrongCellsPlayed; - statistics.averageSolveTime = Math.round( - statistics.totalSolveTime / statistics.numGamesPlayed - ); + // Create or update user's statistics + let statistics: Statistics = await getStatistics(); - Statistics.saveStatisitics(statistics); + statistics.totalScore += score; + if (time < statistics.fastestSolveTime || statistics.fastestSolveTime === 0) { + statistics.fastestSolveTime = time; } -} + statistics.totalSolveTime += time; + statistics.numGamesPlayed += 1; + statistics.numHintsUsed += numHintsUsed; + statistics.numWrongCellsPlayed += numWrongCellsPlayed; + statistics.averageSolveTime = Math.round( + statistics.totalSolveTime / statistics.numGamesPlayed + ); + + saveStatisitics(statistics); +}; diff --git a/app/Api/Statistics.ts b/app/Api/Statistics.ts index 17c9f6e2..b0d7a819 100644 --- a/app/Api/Statistics.ts +++ b/app/Api/Statistics.ts @@ -1,66 +1,64 @@ -import { statistics } from "./Puzzle.Types"; +import { Statistics } from "./Puzzle.Types"; import { getKeyJSON, removeData, storeData } from "../Functions/AsyncStorage"; -export class Statistics { - /** - * retrieves the user's learned lessons - * @returns promise of puzzle JSON object - */ - public static async getLearnedLessons(): Promise { - let value = await getKeyJSON("learned_lessons"); - if (value == undefined) { - return JSON.parse(JSON.stringify(["NONE"])); - } else { - return value; - } +/** + * retrieves the user's learned lessons + * @returns promise of puzzle JSON object + */ +export const getLearnedLessons = async (): Promise => { + const value = await getKeyJSON("learned_lessons"); + if (value == undefined) { + return JSON.parse(JSON.stringify(["NONE"])); + } else { + return value; } +}; - /** - * Given a user's learnedLessons, saves the user's learned lessons - * @param learnedLessons - A JSON object representing all lessons the user has learned - */ - public static async saveLearnedLessons(learnedLessons: string[]) { - // Removing NONE placeholder value if user has learned a lesson - if (learnedLessons.includes("NONE") && learnedLessons.length > 1) { - let index = learnedLessons.indexOf("NONE"); - if (index !== -1) { - learnedLessons.splice(index, 1); - } +/** + * Given a user's learnedLessons, saves the user's learned lessons + * @param learnedLessons - A JSON object representing all lessons the user has learned + */ +export const saveLearnedLessons = (learnedLessons: string[]) => { + // Removing NONE placeholder value if user has learned a lesson + if (learnedLessons.includes("NONE") && learnedLessons.length > 1) { + let index = learnedLessons.indexOf("NONE"); + if (index !== -1) { + learnedLessons.splice(index, 1); } - storeData("learned_lessons", JSON.stringify(learnedLessons)); } + storeData("learned_lessons", JSON.stringify(learnedLessons)); +}; - /** - * returns all statistics objects for given user - */ - public static async getStatistics(): Promise { - let value = await getKeyJSON("statistics"); - if (value == null) { - let statistics: statistics = { - totalScore: 0, - numGamesPlayed: 0, - fastestSolveTime: 0, - averageSolveTime: 0, - totalSolveTime: 0, - numHintsUsed: 0, - numWrongCellsPlayed: 0, - }; - await this.saveStatisitics(statistics); - return statistics; - } - return value; - } +export const saveStatisitics = (statistics: Statistics) => { + storeData("statistics", JSON.stringify(statistics)); +}; - public static async saveStatisitics(statistics: statistics) { - storeData("statistics", JSON.stringify(statistics)); +/** + * returns all statistics objects for given user + */ +export const getStatistics = async (): Promise => { + let value = await getKeyJSON("statistics"); + if (value == null) { + let statistics: Statistics = { + totalScore: 0, + numGamesPlayed: 0, + fastestSolveTime: 0, + averageSolveTime: 0, + totalSolveTime: 0, + numHintsUsed: 0, + numWrongCellsPlayed: 0, + }; + await saveStatisitics(statistics); + return statistics; } + return value; +}; - /** - * this function deletes the user's statistics - */ - public static async deleteStatistics() { - await removeData("statistics"); - await removeData("learned_lessons"); - await removeData("dismissDrillTutorial"); - } -} +/** + * this function deletes the user's statistics + */ +export const deleteStatistics = async () => { + await removeData("statistics"); + await removeData("learned_lessons"); + await removeData("dismissDrillTutorial"); +}; diff --git a/app/Components/Home/LessonPanel.tsx b/app/Components/Home/LessonPanel.tsx index a24aa53b..7482b5b0 100644 --- a/app/Components/Home/LessonPanel.tsx +++ b/app/Components/Home/LessonPanel.tsx @@ -7,12 +7,6 @@ import { formatOneLessonName, getLockedLessons, } from "../../Functions/learnedLessons"; -import { - Lessons, - getLessonMode, - LessonOfflineMode, - LessonOnlineMode, -} from "../../Api/Lessons"; import { useNavigation } from "@react-navigation/native"; import { CARD_IMAGE_HEIGHT, @@ -25,6 +19,7 @@ import { } from "./Cards"; import Alert from "react-native-awesome-alerts"; import { rgba } from "polished"; +import { getStrategies } from "../../Api/Lessons"; let lessonImages: ImageURISource[] = [ require("../../../.assets/CardImages/SUDOKU_101.png"), @@ -66,7 +61,7 @@ const LessonPanel = (props: any) => { const { learnedLessons } = React.useContext(PreferencesContext); - const [availableLessons, setAvailableLessons] = React.useState([]); + const [availableLessons, setAvailableLessons] = React.useState([]); const [isLoading, setIsLoading] = React.useState(true); const [lockedWarningVisible, setLockedWarningVisible] = useState(false); @@ -74,18 +69,10 @@ const LessonPanel = (props: any) => { const hideLockedWarning = () => setLockedWarningVisible(false); const [lockedLesson, setLockedLesson] = useState(-1); - // setting lesson mode to offline - let LESSON_MODE = getLessonMode.Offline; - let getlessonArgs: LessonOfflineMode | LessonOnlineMode = { - mode: LESSON_MODE, - }; - useFocusEffect( React.useCallback(() => { - Lessons.getStrategies(getlessonArgs).then((result: any) => { - setAvailableLessons(result); - setIsLoading(false); - }); + setAvailableLessons(getStrategies()); + setIsLoading(false); }, []) ); @@ -95,7 +82,6 @@ const LessonPanel = (props: any) => { // dynamically render in lesson buttons based on criteria let lessonButtonArray = []; let lockedLessons = getLockedLessons(learnedLessons, availableLessons); - let NUM_LESSONS_PER_ROW = 2; let subArray = []; let columnCount: number = calculateCardsPerRow( diff --git a/app/Components/Statistics/TotalStatistics.tsx b/app/Components/Statistics/TotalStatistics.tsx index 8a931525..45944de8 100644 --- a/app/Components/Statistics/TotalStatistics.tsx +++ b/app/Components/Statistics/TotalStatistics.tsx @@ -3,6 +3,7 @@ import { Text, useTheme } from "react-native-paper"; import { useWindowDimensions } from "react-native"; import Statistic from "./Statistic"; import { formatTime } from "../SudokuBoard/Functions/BoardFunctions"; +import React from "react"; interface TotalStatisticsProps { totalScore: number; diff --git a/app/Components/SudokuBoard/Components/Cell.tsx b/app/Components/SudokuBoard/Components/Cell.tsx index ff512c95..fda7f422 100644 --- a/app/Components/SudokuBoard/Components/Cell.tsx +++ b/app/Components/SudokuBoard/Components/Cell.tsx @@ -62,6 +62,7 @@ const Cell = (props: RenderCellProps) => { onPress={(event: any) => { onClick(r, c, event); }} + // @ts-ignore style={{ outline: "none" }} > void; diff --git a/app/Components/SudokuBoard/Functions/BoardFunctions.ts b/app/Components/SudokuBoard/Functions/BoardFunctions.ts index ae662e8f..b1d02a5d 100644 --- a/app/Components/SudokuBoard/Functions/BoardFunctions.ts +++ b/app/Components/SudokuBoard/Functions/BoardFunctions.ts @@ -1,7 +1,7 @@ import { useWindowDimensions } from "react-native"; -import { Puzzles } from "../../../Api/Puzzles"; import { SudokuObjectProps } from "../../../Functions/LocalDatabase"; import { calculateGameScore, GameDifficulty } from "./Difficulty"; +import { finishGame } from "../../../Api/Puzzles"; /** * This is a temporary place to store functions * todo functions will be documented, sorted, and optimized @@ -60,7 +60,7 @@ export const formatTime = (inputSeconds: number) => { }; export async function saveGame(activeGame: SudokuObjectProps) { - Puzzles.saveGame(activeGame).then((res: any) => { + saveGame(activeGame).then((res: any) => { if (res) { console.log("Game progress was saved successfully!"); } @@ -76,7 +76,7 @@ export async function saveGame(activeGame: SudokuObjectProps) { * @param time * @returns */ -export function finishGame( +export function finishSudokuGame( difficulty: GameDifficulty, numHintsUsed: number, numWrongCellsPlayed: number, @@ -91,7 +91,7 @@ export function finishGame( ); // removes game from localstorage and updates statistics page - Puzzles.finishGame(numHintsUsed, numWrongCellsPlayed, time, score); + finishGame(numHintsUsed, numWrongCellsPlayed, time, score); return score; } diff --git a/app/Components/SudokuBoard/Functions/Difficulty.ts b/app/Components/SudokuBoard/Functions/Difficulty.ts index 18dd28ca..10a7b010 100644 --- a/app/Components/SudokuBoard/Functions/Difficulty.ts +++ b/app/Components/SudokuBoard/Functions/Difficulty.ts @@ -9,7 +9,7 @@ import { PUNDIT_PUZZLES } from "../../../Data/puzzles/pundit_puzzles"; import { TRAINEE_PUZZLES } from "../../../Data/puzzles/trainee_puzzles"; import { convertPuzzleToSudokuObject, - Puzzle, + InputPuzzle, SudokuObjectProps, } from "../../../Functions/LocalDatabase"; @@ -120,7 +120,7 @@ export function calculateGameScore( * @param PUZZLES An array of puzzles * @returns a random puzzle from an array of puzzles */ -const retrieveRandomPuzzle = (PUZZLES: Puzzle[]): Puzzle => { +const retrieveRandomPuzzle = (PUZZLES: InputPuzzle[]): InputPuzzle => { return PUZZLES[Math.floor(Math.random() * PUZZLES.length)]; }; @@ -131,7 +131,7 @@ const retrieveRandomPuzzle = (PUZZLES: Puzzle[]): Puzzle => { */ export const returnPuzzleOfDifficulty = ( difficulty: GameDifficulty | "dev" -): Puzzle => { +): InputPuzzle => { switch (difficulty) { // "dev" difficulty is a custom difficulty that always returns the same puzzle. case "dev": diff --git a/app/Components/SudokuBoard/Functions/generateGame.ts b/app/Components/SudokuBoard/Functions/generateGame.ts index 9084dbf6..1959cbed 100644 --- a/app/Components/SudokuBoard/Functions/generateGame.ts +++ b/app/Components/SudokuBoard/Functions/generateGame.ts @@ -1,4 +1,4 @@ -import { Puzzles } from "../../../Api/Puzzles"; +import { getGame, startGame } from "../../../Api/Puzzles"; import { SudokuObjectProps } from "../../../Functions/LocalDatabase"; import { SudokuBoardProps } from "../SudokuBoard"; @@ -6,13 +6,9 @@ export async function generateGame(props: SudokuBoardProps) { let gameData = null; if (props.action == "StartGame") { - gameData = await Puzzles.startGame(props.difficulty).then( - (game: SudokuObjectProps) => { - return game; - } - ); + return startGame(props.difficulty); } else if (props.action == "ResumeGame") { - gameData = await Puzzles.getGame().then((game: SudokuObjectProps[]) => { + gameData = await getGame().then((game: SudokuObjectProps[]) => { // If game object is not returned, you get redirected to Main Page if (game == null) { //navigation.navigate("Home"); diff --git a/app/Components/SudokuBoard/SudokuBoard.tsx b/app/Components/SudokuBoard/SudokuBoard.tsx index e4729688..1ae8740c 100644 --- a/app/Components/SudokuBoard/SudokuBoard.tsx +++ b/app/Components/SudokuBoard/SudokuBoard.tsx @@ -1,6 +1,10 @@ import React, { useEffect, useState } from "react"; import { View } from "react-native"; -import { finishGame, handlePause, saveGame } from "./Functions/BoardFunctions"; +import { + finishSudokuGame, + handlePause, + saveGame, +} from "./Functions/BoardFunctions"; import { areCellsInSameBox, areCellsInSameColumn, @@ -187,7 +191,7 @@ const SudokuBoard = (props: SudokuBoardProps) => { saveGame(sudokuBoard); if (!sudokuBoard.inNoteMode && isGameSolved()) { - const score = finishGame( + const score = finishSudokuGame( sudokuBoard.statistics.difficulty, sudokuBoard.statistics.numHintsUsed, sudokuBoard.statistics.numWrongCellsPlayed, diff --git a/app/Contexts/InitializeContext.ts b/app/Contexts/InitializeContext.ts index 77da37b0..29b6a0f9 100644 --- a/app/Contexts/InitializeContext.ts +++ b/app/Contexts/InitializeContext.ts @@ -3,9 +3,9 @@ import { CombinedDarkTheme, CombinedDefaultTheme, } from "../Styling/ThemeColors"; -import { profile } from "../Api/Puzzle.Types"; -import { Profile } from "../Api/Profile"; +import { getProfile, setProfileValue } from "../Api/Profile"; import { SUDOKU_STRATEGY_ARRAY, SudokuStrategyArray } from "sudokuru"; +import { Profile } from "../Api/Puzzle.Types"; const InitializeContext = () => { const [darkThemeSetting, setDarkThemeSetting] = React.useState(true); @@ -26,7 +26,7 @@ const InitializeContext = () => { // set initial values of theme React.useEffect(() => { - Profile.getProfile().then((data: profile) => { + getProfile().then((data: Profile) => { setDarkThemeSetting(data.theme); setHighlightIdenticalValuesSetting(data.highlightIdenticalValues); setHighlightBoxSetting(data.highlightBox); @@ -40,7 +40,7 @@ const InitializeContext = () => { const theme = darkThemeSetting ? CombinedDarkTheme : CombinedDefaultTheme; const toggleTheme = React.useCallback(() => { - Profile.setProfileValue("theme"); + setProfileValue("theme"); return setDarkThemeSetting(!darkThemeSetting); }, [darkThemeSetting]); @@ -59,33 +59,33 @@ const InitializeContext = () => { ); const toggleHighlightIdenticalValues = React.useCallback(() => { - Profile.setProfileValue("highlightIdenticalValues"); + setProfileValue("highlightIdenticalValues"); return setHighlightIdenticalValuesSetting(!highlightIdenticalValuesSetting); }, [highlightIdenticalValuesSetting]); const toggleHighlightBox = React.useCallback(() => { - Profile.setProfileValue("highlightBox"); + setProfileValue("highlightBox"); return setHighlightBoxSetting(!highlightBoxSetting); }, [highlightBoxSetting]); const toggleHighlightRow = React.useCallback(() => { - Profile.setProfileValue("highlightRow"); + setProfileValue("highlightRow"); return setHighlightRowSetting(!highlightRowSetting); }, [highlightRowSetting]); const toggleHighlightColumn = React.useCallback(() => { - Profile.setProfileValue("highlightColumn"); + setProfileValue("highlightColumn"); return setHighlightColumnSetting(!highlightColumnSetting); }, [highlightColumnSetting]); const toggleFeaturePreview = React.useCallback(() => { - Profile.setProfileValue("previewMode"); + setProfileValue("previewMode"); return setFeaturePreviewSetting(!featurePreviewSetting); }, [featurePreviewSetting]); const updateStrategyHintOrder = React.useCallback( (props: React.SetStateAction) => { - Profile.setProfileValue("strategyHintOrder", props); + setProfileValue("strategyHintOrder", props); return setStrategyHintOrderSetting(props); }, [strategyHintOrderSetting] diff --git a/app/Data/puzzles/amateur_puzzles.ts b/app/Data/puzzles/amateur_puzzles.ts index 62645bdc..e3a1f487 100644 --- a/app/Data/puzzles/amateur_puzzles.ts +++ b/app/Data/puzzles/amateur_puzzles.ts @@ -1,5 +1,6 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const AMATEUR_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; + +export const AMATEUR_PUZZLES: InputPuzzle[] = [ { p: "000000007059020041320514896060905004900430670080760050001670405006208703874153962", s: "148396527659827341327514896762985134915432678483761259231679485596248713874153962", diff --git a/app/Data/puzzles/grandmaster_puzzles.ts b/app/Data/puzzles/grandmaster_puzzles.ts index db76ba38..07787eeb 100644 --- a/app/Data/puzzles/grandmaster_puzzles.ts +++ b/app/Data/puzzles/grandmaster_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const GRANDMASTER_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const GRANDMASTER_PUZZLES: InputPuzzle[] = [ { p: "000000000000002053000514902400070290501000800009000000900100030004080000070300600", s: "295738164148962753637514982463871295521493876789256341956127438314689527872345619", diff --git a/app/Data/puzzles/layman_puzzles.ts b/app/Data/puzzles/layman_puzzles.ts index 02beab99..4a6d4dde 100644 --- a/app/Data/puzzles/layman_puzzles.ts +++ b/app/Data/puzzles/layman_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const LAYMAN_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const LAYMAN_PUZZLES: InputPuzzle[] = [ { p: "000000000010090850857000194790653000080104600040820005205708906109300000470960510", s: "924581367316497852857236194792653481583174629641829735235718946169345278478962513", diff --git a/app/Data/puzzles/master_puzzles.ts b/app/Data/puzzles/master_puzzles.ts index 7c0edc56..60405a80 100644 --- a/app/Data/puzzles/master_puzzles.ts +++ b/app/Data/puzzles/master_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const MASTER_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const MASTER_PUZZLES: InputPuzzle[] = [ { p: "000000000000000037105930040006804010810006000002000700203000901000305000000700008", s: "738542169429681537165937842396874215817256493542193786273468951981325674654719328", diff --git a/app/Data/puzzles/novice_puzzles.ts b/app/Data/puzzles/novice_puzzles.ts index 7835ff31..207eb45b 100644 --- a/app/Data/puzzles/novice_puzzles.ts +++ b/app/Data/puzzles/novice_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const NOVICE_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const NOVICE_PUZZLES: InputPuzzle[] = [ { p: "000002957017854023352760108260973480890125736503648219780491062136007094029500871", s: "648312957917854623352769148261973485894125736573648219785491362136287594429536871", diff --git a/app/Data/puzzles/professional_puzzles.ts b/app/Data/puzzles/professional_puzzles.ts index 36c5ba05..d689739d 100644 --- a/app/Data/puzzles/professional_puzzles.ts +++ b/app/Data/puzzles/professional_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const PROFESSIONAL_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const PROFESSIONAL_PUZZLES: InputPuzzle[] = [ { p: "000000000008051074030970001100090060900460020600083000000005030009000000070340800", s: "751234689298651374436978251143592768985467123627183495864715932319826547572349816", diff --git a/app/Data/puzzles/protege_puzzles.ts b/app/Data/puzzles/protege_puzzles.ts index 799dfdbd..0959f75f 100644 --- a/app/Data/puzzles/protege_puzzles.ts +++ b/app/Data/puzzles/protege_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const PROTEGE_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const PROTEGE_PUZZLES: InputPuzzle[] = [ { p: "000000000000000650091576008003905060105008043000063205824000001000200090000800432", s: "658392714372481659491576328283945167165728943749163285824639571537214896916857432", diff --git a/app/Data/puzzles/pundit_puzzles.ts b/app/Data/puzzles/pundit_puzzles.ts index 02c0501d..66980029 100644 --- a/app/Data/puzzles/pundit_puzzles.ts +++ b/app/Data/puzzles/pundit_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const PUNDIT_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const PUNDIT_PUZZLES: InputPuzzle[] = [ { p: "000000000000028710260700090600000000050000308700904000004003500900810000023007000", s: "147569832395428716268731495681352974459176328732984651814293567976815243523647189", diff --git a/app/Data/puzzles/trainee_puzzles.ts b/app/Data/puzzles/trainee_puzzles.ts index 7b40ff2f..19666fb8 100644 --- a/app/Data/puzzles/trainee_puzzles.ts +++ b/app/Data/puzzles/trainee_puzzles.ts @@ -1,5 +1,5 @@ -import { Puzzle } from "../../Functions/LocalDatabase"; -export const TRAINEE_PUZZLES: Puzzle[] = [ +import { InputPuzzle } from "../../Functions/LocalDatabase"; +export const TRAINEE_PUZZLES: InputPuzzle[] = [ { p: "000000000009000310005360798903200400257600083104570902000905000098137040531000079", s: "376891524829754316415362798963218457257649183184573962742985631698137245531426879", diff --git a/app/Functions/LocalDatabase.ts b/app/Functions/LocalDatabase.ts index 07ca8ac3..3b3318de 100644 --- a/app/Functions/LocalDatabase.ts +++ b/app/Functions/LocalDatabase.ts @@ -1,8 +1,8 @@ import { SudokuStrategy } from "sudokuru"; -import { puzzle } from "../Api/Puzzle.Types"; import { GameDifficulty } from "../Components/SudokuBoard/Functions/Difficulty"; +import { Puzzle } from "../Api/Puzzle.Types"; -export interface Puzzle { +export interface InputPuzzle { p: string; // initial puzzle string s: string; // solution string d: number; // difficulty @@ -13,7 +13,7 @@ export interface Puzzle { * @param puzzle Puzzle object */ export const convertPuzzleToSudokuObject = ( - puzzle: Puzzle, + puzzle: InputPuzzle, difficulty: GameDifficulty ): SudokuObjectProps => { let game: SudokuObjectProps = { @@ -55,52 +55,6 @@ export const convertPuzzleToSudokuObject = ( return JSON.parse(JSON.stringify(game)); }; -export function returnLocalDrillGame(strategy: SudokuStrategy): puzzle { - // if (strategy === "NAKED_SINGLE") { - // return NAKED_SINGLE_DRILL_GAMES[ - // Math.floor(Math.random() * NAKED_SINGLE_DRILL_GAMES.length) - // ]; - // } - if (strategy === "NAKED_PAIR") { - return NAKED_PAIR_DRILL_GAMES[ - Math.floor(Math.random() * NAKED_PAIR_DRILL_GAMES.length) - ]; - } else if (strategy === "NAKED_TRIPLET") { - return NAKED_TRIPLET_DRILL_GAMES[ - Math.floor(Math.random() * NAKED_TRIPLET_DRILL_GAMES.length) - ]; - } else if (strategy === "NAKED_QUADRUPLET") { - return NAKED_QUADRUPLET_DRILL_GAMES[ - Math.floor(Math.random() * NAKED_QUADRUPLET_DRILL_GAMES.length) - ]; - } else if (strategy === "HIDDEN_SINGLE") { - return HIDDEN_SINGLE_DRILL_GAMES[ - Math.floor(Math.random() * HIDDEN_SINGLE_DRILL_GAMES.length) - ]; - } else if (strategy === "HIDDEN_PAIR") { - return HIDDEN_PAIR_DRILL_GAMES[ - Math.floor(Math.random() * HIDDEN_PAIR_DRILL_GAMES.length) - ]; - } else if (strategy === "HIDDEN_TRIPLET") { - return HIDDEN_TRIPLET_DRILL_GAMES[ - Math.floor(Math.random() * HIDDEN_TRIPLET_DRILL_GAMES.length) - ]; - } else if (strategy === "HIDDEN_QUADRUPLET") { - return HIDDEN_QUADRUPLET_DRILL_GAMES[ - Math.floor(Math.random() * HIDDEN_QUADRUPLET_DRILL_GAMES.length) - ]; - } else if (strategy === "POINTING_PAIR") { - return POINTING_PAIR_DRILL_GAMES[ - Math.floor(Math.random() * POINTING_PAIR_DRILL_GAMES.length) - ]; - } else if (strategy === "POINTING_TRIPLET") { - return POINTING_TRIPLET_DRILL_GAMES[ - Math.floor(Math.random() * POINTING_TRIPLET_DRILL_GAMES.length) - ]; - } - return JSON.parse("{}"); -} - export interface SudokuObjectProps { variant: GameVariant; version: string; @@ -611,7 +565,7 @@ const NAKED_SINGLE_DRILL_GAMES: SudokuObjectProps[] = [ // } ]; -const NAKED_PAIR_DRILL_GAMES: puzzle[] = [ +const NAKED_PAIR_DRILL_GAMES: Puzzle[] = [ { puzzle: "120000000400009017780500034060000003000065000800070002000000400500900001300080050", @@ -700,7 +654,7 @@ const NAKED_PAIR_DRILL_GAMES: puzzle[] = [ }, ]; -const NAKED_TRIPLET_DRILL_GAMES: puzzle[] = [ +const NAKED_TRIPLET_DRILL_GAMES: Puzzle[] = [ { puzzle: "020400000006009130000015200000080000300000500800064700014702805000001090000000000", @@ -797,7 +751,7 @@ const NAKED_TRIPLET_DRILL_GAMES: puzzle[] = [ }, ]; -const NAKED_QUADRUPLET_DRILL_GAMES: puzzle[] = [ +const NAKED_QUADRUPLET_DRILL_GAMES: Puzzle[] = [ { puzzle: "020700080050091007709304000000500098004010000290000040008060300000180004900000070", @@ -886,7 +840,7 @@ const NAKED_QUADRUPLET_DRILL_GAMES: puzzle[] = [ }, ]; -const HIDDEN_SINGLE_DRILL_GAMES: puzzle[] = [ +const HIDDEN_SINGLE_DRILL_GAMES: Puzzle[] = [ { puzzle: "020000008000700900000000010007009100000008056240006000005302000890004030300800560", @@ -987,7 +941,7 @@ const HIDDEN_SINGLE_DRILL_GAMES: puzzle[] = [ }, ]; -const HIDDEN_PAIR_DRILL_GAMES: puzzle[] = [ +const HIDDEN_PAIR_DRILL_GAMES: Puzzle[] = [ { puzzle: "020609780050000090709050340071008000004000000060020000030001000900007020000000160", @@ -1088,7 +1042,7 @@ const HIDDEN_PAIR_DRILL_GAMES: puzzle[] = [ }, ]; -const HIDDEN_TRIPLET_DRILL_GAMES: puzzle[] = [ +const HIDDEN_TRIPLET_DRILL_GAMES: Puzzle[] = [ { puzzle: "120000000400009017780500034060000003000065000800070002000000400500900001300080050", @@ -1177,7 +1131,7 @@ const HIDDEN_TRIPLET_DRILL_GAMES: puzzle[] = [ }, ]; -const HIDDEN_QUADRUPLET_DRILL_GAMES: puzzle[] = [ +const HIDDEN_QUADRUPLET_DRILL_GAMES: Puzzle[] = [ { puzzle: "020400000006009130000015200000080000300000500800064700014702805000001090000000000", @@ -1255,7 +1209,7 @@ const HIDDEN_QUADRUPLET_DRILL_GAMES: puzzle[] = [ }, ]; -const POINTING_PAIR_DRILL_GAMES: puzzle[] = [ +const POINTING_PAIR_DRILL_GAMES: Puzzle[] = [ { puzzle: "003070040006002301089000000000107080517000006000400000271009005095000000000020000", @@ -1345,7 +1299,7 @@ const POINTING_PAIR_DRILL_GAMES: puzzle[] = [ }, ]; -const POINTING_TRIPLET_DRILL_GAMES: puzzle[] = [ +const POINTING_TRIPLET_DRILL_GAMES: Puzzle[] = [ { puzzle: "003070040006002301089000000000107080517000006000400000271009005095000000000020000", diff --git a/app/Navigation/DrawerNavigator.tsx b/app/Navigation/DrawerNavigator.tsx index 99900c9f..0668f369 100644 --- a/app/Navigation/DrawerNavigator.tsx +++ b/app/Navigation/DrawerNavigator.tsx @@ -56,6 +56,7 @@ const DrawerNavigator = () => { + {/* @ts-ignore */} diff --git a/app/Pages/LearnPage.tsx b/app/Pages/LearnPage.tsx index cfb1637f..425c7444 100644 --- a/app/Pages/LearnPage.tsx +++ b/app/Pages/LearnPage.tsx @@ -6,7 +6,7 @@ import Alert from "react-native-awesome-alerts"; import { PreferencesContext } from "../Contexts/PreferencesContext"; import LessonPanel from "../Components/Home/LessonPanel"; import { rgba } from "polished"; -import { Statistics } from "../Api/Statistics"; +import { getLearnedLessons } from "../Api/Statistics"; import { useNewWindowDimensions } from "../Functions/WindowDimensions"; const LearnPage = () => { @@ -27,7 +27,7 @@ const LearnPage = () => { useCallback(() => { // This determines what lessons the user has learned and conditionally displays everything. async function getUserLearnedLessons() { - await Statistics.getLearnedLessons().then((lessons: any) => { + await getLearnedLessons().then((lessons: any) => { if (lessons !== null) { // prevent the infinite loop if (learnedLessons != lessons && !areLessonsLoaded) { diff --git a/app/Pages/Lesson.tsx b/app/Pages/Lesson.tsx index b7a1aded..527b992b 100644 --- a/app/Pages/Lesson.tsx +++ b/app/Pages/Lesson.tsx @@ -5,6 +5,7 @@ import { useWindowDimensions, Pressable, ScrollView, + ImageURISource, } from "react-native"; import { Text, useTheme, Button, Card } from "react-native-paper"; import { useNavigation } from "@react-navigation/native"; @@ -12,15 +13,10 @@ import { MaterialCommunityIcons } from "@expo/vector-icons"; import Alert from "react-native-awesome-alerts"; import { useFocusEffect } from "@react-navigation/core"; import { PreferencesContext } from "../Contexts/PreferencesContext"; -import { - Lessons, - getLessonMode, - LessonOfflineMode, - LessonOnlineMode, -} from "../Api/Lessons"; -import { Statistics } from "../Api/Statistics"; import { CARD_PADDING } from "../Components/Home/Cards"; import { toTitle } from "../Components/SudokuBoard/sudoku"; +import { getLessonSteps } from "../Api/Lessons"; +import { saveLearnedLessons } from "../Api/Statistics"; const Lesson = (props: { route: { params: { params: any } } }) => { //Brings in name of strategy from carousel @@ -39,40 +35,22 @@ const Lesson = (props: { route: { params: { params: any } } }) => { const showLearnHelp = () => setLearnHelpVisible(true); const hideLearnHelp = () => setLearnHelpVisible(false); - const [steps, setSteps] = React.useState([]); + const [steps, setSteps] = React.useState<[string, ImageURISource][]>([]); const theme = useTheme(); let title = toTitle(name); - if (Lessons == null) { - return; - } - - // setting lesson mode to offline - let LESSON_MODE = getLessonMode.Offline; - let getlessonArgs: LessonOfflineMode | LessonOnlineMode = { - mode: LESSON_MODE, - }; - //2d array - [[],[]]. 1st array indicates which step, 2nd array indicates text or image. // This useFocusEffect stores the steps in state when page is loaded in. useFocusEffect( React.useCallback(() => { - Lessons.getSteps(name, getlessonArgs).then((result: any) => { - setSteps(result); - }); + setSteps(getLessonSteps(name)); }, []) ); async function saveUserLearnedLessons(learnedLessons: string[]) { - await Statistics.saveLearnedLessons(learnedLessons).then((res: any) => { - if (res) { - console.log("Lessons save successfully!"); - } else { - console.log("Lesson not saved"); - } - }); + await saveLearnedLessons(learnedLessons); } const clickCheckMark = () => { diff --git a/app/Pages/PlayPage.tsx b/app/Pages/PlayPage.tsx index 74496ebf..c84a7c1a 100644 --- a/app/Pages/PlayPage.tsx +++ b/app/Pages/PlayPage.tsx @@ -8,7 +8,7 @@ import { useMinWindowDimensions, useNewWindowDimensions, } from "../Functions/WindowDimensions"; -import { Puzzles } from "../Api/Puzzles"; +import { getGame } from "../Api/Puzzles"; import { SudokuObjectProps } from "../Functions/LocalDatabase"; import DifficultyPanel from "../Components/Home/DifficultyPanel"; @@ -28,7 +28,7 @@ const PlayPage = () => { React.useCallback(() => { // This determines if user has active game and displays resume button conditionally. async function grabCurrentGame() { - await Puzzles.getGame().then((game: SudokuObjectProps[]) => { + await getGame().then((game: SudokuObjectProps[]) => { if (game != null) { showResumeButton(); } else { diff --git a/app/Pages/StatisticsPage.tsx b/app/Pages/StatisticsPage.tsx index 61b29754..bdde44c4 100644 --- a/app/Pages/StatisticsPage.tsx +++ b/app/Pages/StatisticsPage.tsx @@ -5,12 +5,11 @@ import { useWindowDimensions } from "react-native"; import { useNavigation } from "@react-navigation/native"; import { PreferencesContext } from "../Contexts/PreferencesContext"; import { useFocusEffect } from "@react-navigation/core"; -import TotalStatistics, { - StatisticsProps, -} from "../Components/Statistics/TotalStatistics"; import Alert from "react-native-awesome-alerts"; import { rgba } from "polished"; -import { Statistics } from "../Api/Statistics"; +import { deleteStatistics, getStatistics } from "../Api/Statistics"; +import TotalStatistics from "../Components/Statistics/TotalStatistics"; +import { Statistics } from "../Api/Puzzle.Types"; const StatisticsPage = () => { const theme = useTheme(); @@ -23,30 +22,28 @@ const StatisticsPage = () => { React.useContext(PreferencesContext); const [isLoading, setLoading] = React.useState(true); - const [totalStatistics, setTotalStatistics] = React.useState( - { - totalScore: 0, - averageSolveTime: 0, - fastestSolveTime: 0, - numGamesPlayed: 0, - numHintsUsed: 0, - numWrongCellsPlayed: 0, - totalSolveTime: 0, - } - ); + const [totalStatistics, setTotalStatistics] = React.useState({ + totalScore: 0, + averageSolveTime: 0, + fastestSolveTime: 0, + numGamesPlayed: 0, + numHintsUsed: 0, + numWrongCellsPlayed: 0, + totalSolveTime: 0, + }); const [warningVisible, setWarningVisible] = React.useState(false); const showWarningButton = () => setWarningVisible(true); const hideWarningButton = () => setWarningVisible(false); async function deleteUserStatistics() { - await Statistics.deleteStatistics().then(() => { + await deleteStatistics().then(() => { updateLearnedLessons([]); }); } async function getUserStatistics() { - await Statistics.getStatistics().then((res: any) => { + await getStatistics().then((res: any) => { if (res) { setTotalStatistics(res); } else { diff --git a/docs/BackendApiCalls/api.md b/docs/BackendApiCalls/api.md index 070495e7..764d37ec 100644 --- a/docs/BackendApiCalls/api.md +++ b/docs/BackendApiCalls/api.md @@ -1,28 +1,19 @@ # Table of Contents -- [Puzzles Class](#puzzles-class) - - [Setup](#setup) - - [Puzzles.startGame()](#puzzlesstartgame) - - [Puzzles.getGame()](#puzzlesgetgame) - - [Puzzles.saveGame()](#puzzlessavegame) - - [Puzzles.finishGame()](#puzzlesfinishgame) - - [Puzzles.getRandomGame()](#puzzlesgetrandomgame) -- [Drills Class](#drills-class) - - [Setup](#setup-1) - - [Drills.strategies](#drillsstrategies) - - [Drills.getGame()](#drillsgetgame) - - [How to Use Drills](#how-to-use-drills) -- [Lessons Class](#lessons-class) - - [Setup](#setup-2) - - [Lessons.getStrategies()](#lessonsgetstrategies) - - [Lessons.getSteps()](#lessonsgetsteps) - - [Lessons.getTutorial()](#lessonsgettutorial) -- [Statistics Class](#statistics-class) - - [Setup](#setup-3) - - [Statistics.getLearnedLessons()](#statisticsgetlearnedlessons) - - [Statistics.saveLearnedLessons()](#statisticssavelearnedlessons) - - [Statistics.getStatistics()](#statisticsgetstatistics) - - [Statistics.deleteStatistics()](statisticsdeletestatistics) +- [Puzzles](#puzzles) + - [startGame()](#puzzlesstartgame) + - [getGame()](#puzzlesgetgame) + - [saveGame()](#puzzlessavegame) + - [finishGame()](#puzzlesfinishgame) +- [Lessons](#lessons) + - [getStrategies()](#lessonsgetstrategies) + - [getSteps()](#lessonsgetsteps) + - [getTutorial()](#lessonsgettutorial) +- [Statistics](#statistics) + - [getLearnedLessons()](#statisticsgetlearnedlessons) + - [saveLearnedLessons()](#statisticssavelearnedlessons) + - [getStatistics()](#statisticsgetstatistics) + - [deleteStatistics()](statisticsdeletestatistics) - [Statistics Object Properties](#statistics-object-properties) - [userID](#userid) - [dateRange](#daterange) @@ -55,194 +46,82 @@ - [numHintsUsed](#numhintsused) - [numWrongCellsPlayed](#numwrongcellsplayed) -### Puzzles Class +### Puzzles -#### Setup - -```typescript -import { Puzzles } from "sudokuru"; -``` - -#### Puzzles.startGame() +#### startGame() 1. Description: Returns puzzle only containing strategies specified, hasn't been solved by user, and has difficulty as close to the specified difficulty as possible. 2. Syntax ```typescript - Puzzles.startGame(url, difficulty, strategies, token).then(game => { - if (game !== null) { - console.log(game); - } - else { - console.log("Unexpected error when starting game"); - } + startGame(difficulty); ``` 3. Parameters - - url: Server url e.g. "http://localhost:3100/" - difficulty: integer representing rd score, see Report.txt to see example values - - strategies: array of strategies that are allowed to be in returned puzzle e.g. [ "NAKED_SINGLE" ] - - token: string authentication token 4. Return Value: [activeGame](#activegame-object-properties) JSON object -#### Puzzles.getGame() +#### getGame() 1. Description: Retrieves users active game if they have one, otherwise returns null 2. Syntax ```typescript - Puzzles.getGame(url, token).then((game) => { - if (game !== null) { - console.log(game); - } else { - console.log("User doesn't have an activeGame"); - } - }); + const game = await getGame(); ``` -3. Parameters: - - url: Server url e.g. "http://localhost:3100/" - - token: string authentication token -4. Return Value: [activeGame](#activegame-object-properties) JSON object if user has an active game, otherwise null +3. Return Value: [activeGame](#activegame-object-properties) JSON object if user has an active game, otherwise null -#### Puzzles.saveGame() +#### saveGame() 1. Description: Saves changes to users active game and returns true if successful 2. Syntax ```typescript - Puzzles.saveGame(url, activeGame, puzzle, token).then((res) => { - if (res) { - console.log("Game progress was saved successfully"); - } - }); + saveGame(activeGame); ``` 3. Parameters: - - url: Server url e.g. "http://localhost:3100/" - activeGame: [activeGame](#activegame-object-properties) JSON object containing only properties that are being updated - - puzzle: a string containing the initial puzzle state - - token: string authentication token -4. Return Value: true if game was saved successfully +4. Return Value: None -#### Puzzles.finishGame() +#### finishGame() 1. Description: Deletes users active game and returns true if successful 2. Syntax ```typescript - Puzzles.finishGame(url, puzzle, token).then((res) => { - if (res) { - console.log("Game was deleted successfully"); - } - }); - ``` -3. Parameters: - - url: Server url e.g. "http://localhost:3100/" - - puzzle: a string containing the initial puzzle state - - token: string authentication token - [finishGame](#finishgame-object-properties) JSON object if game exists and has been ended, otherwise null - -#### Puzzles.getRandomGame() - -1. Description: Returns a random game to be used by landing page display -2. Syntax - ```typescript - Puzzles.getRandomGame(); - ``` -3. Return Value: [activeGame](#activegame-object-properties) JSON object - -### Drills Class - -#### Setup - -```typescript -import { Drills } from "sudokuru"; -``` - -#### Drills.strategies - -1. Description: 2d array, subarrays contain strategy strings that drills are available for, the first element in each subarray with more than one element is the name of the group of strategies e.g. [["NAKED_SET", "NAKED_SINGLE", "NAKED_PAIR", ...]]. - -#### Drills.getGame() - -1. Description: Returns board and notes state for a drill of the given strategy type if there is one -2. Syntax - ```typescript - Drills.getGame(url, strategy, token).then((drill) => { - if (drill !== null) { - console.log(drill); - } else { - console.log("No drill was found for the given strategy type"); - } - }); + await finishGame(numHintsUsed: number, numWrongCellsPlayed: number, time: number, score: number); ``` 3. Parameters: - - url: Server url e.g. "http://localhost:3100/" - - strategy: string representing strategy type, can be any from Drills.getDrillStrategies() - - token: string authentication token -4. Return Value: JSON object containing puzzleCurrentState,puzzleCurrentNotesState, and puzzleSolution as described in [activeGame](#activegame-object-properties) if drill found, otherwise null - -#### How to Use Drills + - numHintsUsed: the number of hints used from the game + - numWrongCellsPlayed: the number of wrong cells from the game + - time: the time from the game + - score: the score from the game -Once you get a drill game using Drills.getGame() and one of the supported strategies from Drills.strategies you just need to get a hint. To do that you can use [Puzzles.getHint()](#puzzlesgethint) using the board and notes from Drills.getGame() and the strategy you are using put inside of an array. - -### Lessons Class - -#### Setup - -```typescript -import { Lessons } from "sudokuru"; -``` +### Lessons -#### Lessons.getStrategies() +#### getLessonStrategies() 1. Description: Returns an array containing strategy strings that lessons are available for 2. Syntax ```typescript - Lessons.getStrategies().then((strategies) => { - console.log(strategies); - }); + const strategies = getLessonStrategies(); ``` 3. Return Value: string array e.g. ["AMEND_NOTES", "SIMPLIFY_NOTES", "NAKED_SET", ...] -#### Lessons.getSteps() +#### getLessonSteps() 1. Description: Returns a 2d array containing "steps" which are arrays containing two strings, the first of which is text describing the image which is linked to by the url which is the second string in the subarray. 2. Syntax ```typescript - Lessons.getSteps(strategy).then((steps) => { - console.log(steps); - }); + const steps = getLessonSteps(strategy); ``` 3. Parameters: - strategy: string representing type, can be any from Lessons.strategies 4. Return Value: 2d array e.g. [["Here is an example of the simplify notes strategy", "https://sudokuru.s3.amazonaws.com/hintExample2-V2.png"]]. -#### Lessons.getTutorial() - -1. Description: Returns a 2d string array containing "steps" which are arrays containing two strings, the first of which is text describing the image which is linked to by the url which is the second string in the subarray for the first few lessons. -2. Syntax - ```typescript - Lessons.getTutorial().then((tutorial) => { - console.log(tutorial); - }); - ``` -3. Return Value: 2d array e.g. [["Here is an example of the simplify notes strategy", "https://sudokuru.s3.amazonaws.com/hintExample2-V2.png"]]. - -### Statistics Class - -#### Setup - -```shell -import {Statistics} from 'sudokuru'; -``` +### Statistics -#### Statistics.getLearnedLessons() +#### getLearnedLessons() 1. Description: Returns an JSON object containing the strategies a user has learned 2. Syntax ```typescript - Statistics.getLearnedLessons(url, token).then((lessons) => { - if (lessons !== null) { - console.log(lessons); - } else { - console.log("User has not completed any lessons"); - } - }); + const learnedLessons = await getLearnedLessons(); ``` 3. Return Value: JSON array: Example: @@ -253,44 +132,30 @@ import {Statistics} from 'sudokuru'; } ``` -#### Statistics.saveLearnedLessons() +#### saveLearnedLessons() 1. Description: Returns an JSON object containing the strategies a user has learned 2. Syntax ```typescript - Statistics.saveLearnedLessons(url, learnedLessons, token).then((res) => { - if (res) { - console.log("User's learned lessons were updated successfully!"); - } - }); + saveLearnedLessons(learnedLessons); ``` 3. Return Value: boolean -#### Statistics.getStatistics() +#### getStatistics() 1. Description: Returns an JSON object containing the strategies a user has learned 2. Syntax ```typescript - Statistics.getStatistics(url, token).then((statistics) => { - if (statistics !== null) { - console.log(statistics); - } else { - console.log("User does not have any game statistics!"); - } - }); + const statistics = await getStatistics(); ``` 3. Return Value: [statistics](#statistics-object-properties) -#### Statistics.deleteStatistics() +#### deleteStatistics() 1. Description: Returns an JSON object containing the strategies a user has learned 2. Syntax ```typescript - Statistics.deleteStatistics(url, token).then((res) => { - if (res) { - console.log("User's statistics were deleted successfully!"); - } - }); + await deleteStatistics(); ``` 3. Return Value: boolean