diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d1b4edb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f26cb7c..2804f0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "@types/react": "^17.0.38", "@types/react-dom": "^17.0.11", "classnames": "^2.3.1", - "prettier": "^2.5.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0", @@ -28,6 +27,7 @@ "devDependencies": { "autoprefixer": "^10.4.2", "postcss": "^8.4.5", + "prettier": "^2.5.1", "tailwindcss": "^3.0.12" } }, @@ -12633,6 +12633,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, "bin": { "prettier": "bin-prettier.js" }, @@ -24864,7 +24865,8 @@ "prettier": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==" + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true }, "pretty-bytes": { "version": "5.6.0", diff --git a/src/App.tsx b/src/App.tsx index 832d089..c75838a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,7 +9,7 @@ import { InfoModal } from './components/modals/InfoModal' import { WinModal } from './components/modals/WinModal' import { StatsModal } from './components/modals/StatsModal' import { isWordInWordList, isWinningWord, solution } from './lib/words' -import { addEvent, loadStats } from './lib/stats' +import { addStatsForCompletedGame, loadStats } from './lib/stats' import { loadGameStateFromLocalStorage, saveGameStateToLocalStorage, @@ -37,15 +37,12 @@ function App() { return loaded.guesses }) - const [stats, setStats] = useState(() => { - const loaded = loadStats() - return loaded - }) - + const [stats, setStats] = useState(() => loadStats()) + useEffect(() => { saveGameStateToLocalStorage({ guesses, solution }) }, [guesses]) - + useEffect(() => { if (isGameWon) { setIsWinModalOpen(true) @@ -69,7 +66,7 @@ function App() { setIsNotEnoughLetters(false) }, 2000) } - + if (!isWordInWordList(currentGuess)) { setIsWordNotFoundAlertOpen(true) return setTimeout(() => { @@ -84,12 +81,12 @@ function App() { setCurrentGuess('') if (winningWord) { - setStats(addEvent(stats, guesses.length)) + setStats(addStatsForCompletedGame(stats, guesses.length)) return setIsGameWon(true) } if (guesses.length === 5) { - setStats(addEvent(stats, guesses.length + 1)) + setStats(addStatsForCompletedGame(stats, guesses.length + 1)) setIsGameLost(true) return setTimeout(() => { setIsGameLost(false) @@ -148,7 +145,7 @@ function App() { setIsStatsModalOpen(false)} - stats={stats} + gameStats={stats} /> { - const min = 10 - const max = Math.ceil(Math.max.apply(null, data)*1.2) - return( -
- { data.map(( value, i ) => ( - - )) - } -
- ) -} diff --git a/src/components/histogram/progress.tsx b/src/components/histogram/progress.tsx deleted file mode 100644 index 53e3e3b..0000000 --- a/src/components/histogram/progress.tsx +++ /dev/null @@ -1,23 +0,0 @@ - -type Props = { - index: number, - size: number, - label: string -} - -export const Progress = ( {index, size, label}: Props ) => { - return( -
-
{index+1}
-
-
{label} -
-
-
- ) -} - - diff --git a/src/components/modals/StatsModal.tsx b/src/components/modals/StatsModal.tsx index 03edce2..a2e08a2 100644 --- a/src/components/modals/StatsModal.tsx +++ b/src/components/modals/StatsModal.tsx @@ -1,21 +1,17 @@ import { Fragment } from 'react' import { Dialog, Transition } from '@headlessui/react' import { XCircleIcon } from '@heroicons/react/outline' -import { trys, successRate, currentStreak, bestStreak } from '../../lib/stats' -import { Histogram } from '../histogram/histogram' -import { StatLine } from '../statline/statline' +import { StatBar } from '../stats/StatBar' +import { Histogram } from '../stats/Histogram' +import { GameStats } from '../../lib/localStorage' type Props = { isOpen: boolean handleClose: () => void - stats: number[] + gameStats: GameStats } -export const StatsModal = ({ isOpen, handleClose, stats }: Props) => { - const labels = ["Total trys", "Success rate", - "Current streak", "Best streak"] - const values = [String(trys(stats)), String(successRate(stats))+'%', - String(currentStreak(stats)), String(bestStreak(stats))] +export const StatsModal = ({ isOpen, handleClose, gameStats }: Props) => { return ( { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > -
+ transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6" + >
handleClose()} + onClick={handleClose} />
@@ -69,14 +67,11 @@ export const StatsModal = ({ isOpen, handleClose, stats }: Props) => { > Statistics - - - Guess Distribution - - + +

+ Guess Distribution +

+
diff --git a/src/components/statline/statline.tsx b/src/components/statline/statline.tsx deleted file mode 100644 index 5e1d07d..0000000 --- a/src/components/statline/statline.tsx +++ /dev/null @@ -1,19 +0,0 @@ - - -type Props = { - labels: string[] - values: string[] -} - -export const StatLine = ({labels, values}: Props) => { - return ( -
- {values.map((value,i ) => ( -
-
{value}
-
{labels[i]}
-
- ))} -
- ) -} diff --git a/src/components/stats/Histogram.tsx b/src/components/stats/Histogram.tsx new file mode 100644 index 0000000..5f3aca7 --- /dev/null +++ b/src/components/stats/Histogram.tsx @@ -0,0 +1,23 @@ +import { GameStats } from '../../lib/localStorage' +import { Progress } from './Progress' + +type Props = { + gameStats: GameStats +} + +export const Histogram = ({ gameStats }: Props) => { + const { totalGames, winDistribution } = gameStats + + return ( +
+ {winDistribution.map((value, i) => ( + + ))} +
+ ) +} diff --git a/src/components/stats/Progress.tsx b/src/components/stats/Progress.tsx new file mode 100644 index 0000000..79af368 --- /dev/null +++ b/src/components/stats/Progress.tsx @@ -0,0 +1,21 @@ +type Props = { + index: number + size: number + label: string +} + +export const Progress = ({ index, size, label }: Props) => { + return ( +
+
{index + 1}
+
+
+ {label} +
+
+
+ ) +} diff --git a/src/components/stats/StatBar.tsx b/src/components/stats/StatBar.tsx new file mode 100644 index 0000000..076afc8 --- /dev/null +++ b/src/components/stats/StatBar.tsx @@ -0,0 +1,31 @@ +import { GameStats } from '../../lib/localStorage' + +type Props = { + gameStats: GameStats +} + +const StatItem = ({ + label, + value, +}: { + label: string + value: string | number +}) => { + return ( +
+
{value}
+
{label}
+
+ ) +} + +export const StatBar = ({ gameStats }: Props) => { + return ( +
+ + + + +
+ ) +} diff --git a/src/lib/localStorage.ts b/src/lib/localStorage.ts index 81fca8e..3b04d7a 100644 --- a/src/lib/localStorage.ts +++ b/src/lib/localStorage.ts @@ -16,17 +16,20 @@ export const loadGameStateFromLocalStorage = () => { const gameStatKey = 'gameStats' -type StoredGameStats = { - distribution: number[] - current: number - best: number +export type GameStats = { + winDistribution: number[] + gamesFailed: number + currentStreak: number + bestStreak: number + totalGames: number + successRate: number } -export const saveStatsToLocalStorage = ( gameStats: StoredGameStats) => { +export const saveStatsToLocalStorage = (gameStats: GameStats) => { localStorage.setItem(gameStatKey, JSON.stringify(gameStats)) } export const loadStatsFromLocalStorage = () => { const stats = localStorage.getItem(gameStatKey) - return stats ? (JSON.parse(stats) as StoredGameStats) : null + return stats ? (JSON.parse(stats) as GameStats) : null } diff --git a/src/lib/stats.ts b/src/lib/stats.ts index c0e08f6..ee07753 100644 --- a/src/lib/stats.ts +++ b/src/lib/stats.ts @@ -1,67 +1,56 @@ -/** -** An attempt at a statistics object and its interface -**/ - import { + GameStats, loadStatsFromLocalStorage, - saveStatsToLocalStorage + saveStatsToLocalStorage, } from './localStorage' // In stats array elements 0-5 are successes in 1-6 trys -// stats[6] is the number of failures -// stats[7] is the currentStreak -// stats[8] is the bestStreak -export const failures = (stats: number[] ) => { return stats[6] } -export const currentStreak = (stats: number[] ) => { return stats[7] } -export const bestStreak = (stats: number[] ) => { return stats[8] } +export const addStatsForCompletedGame = ( + gameStats: GameStats, + count: number +) => { + // Count is number of incorrect guesses before end. + const stats = { ...gameStats } -export const addEvent = (stats: number[], count: number) => { - // Count is number of incorrect guesses before end. - if(count < 0) { count = 0 } // Should not really need this - if( count > 5 ){ // A fail situation - stats[7] = 0 // End current streak - stats[6] += 1 // Increase number of fails + stats.totalGames += 1 + + if (count > 5) { + // A fail situation + stats.currentStreak = 0 + stats.gamesFailed += 1 } else { - stats[count] += 1 // Increase counters - stats[7] += 1 - if( bestStreak(stats) < currentStreak(stats) ){ - stats[8] = currentStreak(stats) + stats.winDistribution[count] += 1 + stats.currentStreak += 1 + + if (stats.bestStreak < stats.currentStreak) { + stats.bestStreak = stats.currentStreak } } - saveStats(stats) + + stats.successRate = getSuccessRate(stats) + + saveStatsToLocalStorage(stats) return stats } -export const resetStats = () => { - return [0,0,0,0,0,0,0,0,0] -} - -export const saveStats = (stats: number[]) => { - const distribution = stats.slice(0,7) - const current = currentStreak(stats) - const best = bestStreak(stats) - saveStatsToLocalStorage({ distribution , current, best }) +const defaultStats: GameStats = { + winDistribution: [0, 0, 0, 0, 0, 0], + gamesFailed: 0, + currentStreak: 0, + bestStreak: 0, + totalGames: 0, + successRate: 0, } export const loadStats = () => { - const loaded = loadStatsFromLocalStorage() - var stats = resetStats() - if( loaded ){ - stats = loaded.distribution - stats[7] = loaded.current - stats[8] = loaded.best - } - return ( stats ) + return loadStatsFromLocalStorage() || defaultStats } -export const trys = (stats: number[] ) => { - return(stats.slice(0,7).reduce((a,b) => a+b , 0 )) +const getSuccessRate = (gameStats: GameStats) => { + const { totalGames, gamesFailed } = gameStats + + return Math.round( + (100 * (totalGames - gamesFailed)) / Math.max(totalGames, 1) + ) } - -export const successRate = (stats: number[] ) => { - return(Math.round((100*(trys(stats) - failures(stats)))/Math.max(trys(stats),1))) -} - - -