Skip to content

Commit

Permalink
medal system
Browse files Browse the repository at this point in the history
  • Loading branch information
joshfeinsilber committed Jun 3, 2024
1 parent e662b04 commit 15f0d32
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 8 deletions.
34 changes: 34 additions & 0 deletions src/const/medals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export interface IMedal {
name: string
emoji: string
differenceFromOptimal: number
}

export const MEDALS: IMedal[] = [
{
name: 'Perfection',
emoji: 'πŸ†',
differenceFromOptimal: 0
},
{
name: 'Gold',
emoji: 'πŸ₯‡',
differenceFromOptimal: 1
},
{
name: 'Silver',
emoji: 'πŸ₯ˆ',
differenceFromOptimal: 2
},
{
name: 'Bronze',
emoji: 'πŸ₯‰',
differenceFromOptimal: 3
}
]

export const NONE_MEDAL: IMedal = {
name: 'No',
emoji: '🫀',
differenceFromOptimal: -1
}
21 changes: 15 additions & 6 deletions src/screens/game/WordCount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,35 @@ import { useThrottle } from '@uidotdev/usehooks'
import { useMemo } from 'react'
import { isWord } from '../../util/game/handleSubmit'
import classNames from 'classnames'
import { generateSolution } from '../../util/solution/generateSolution'
import { lettersForToday } from '../../util/lottery/letters'

export const WordCount = () => {
const wordCount = useAtomValue(words).length
const word = useAtomValue(currentWord)

const solutionNumberOfWords = useMemo(() => {
return generateSolution(lettersForToday()).length
}, [])

// Throttle the word so that we're not checking on every keystroke
const throttledWord = useThrottle(word, 150)
const isProperWord = useMemo(() => {
return isWord(throttledWord)
}, [throttledWord])

console.log(isProperWord)

return (
<div
className={classNames('absolute top-[24px] z-20 text-sm', {
'text-green-700 underline': isProperWord
})}
className={classNames(
'absolute top-[84px] flex w-full items-center justify-between px-7 text-sm'
)}
>
Word #{wordCount + 1}
<p className={classNames({ 'text-green-700 underline': isProperWord })}>
Word #{wordCount + 1}
</p>
<div className="tooltip tooltip-left" data-tip={`The perfect solution's number of words`}>
<p>πŸ† {solutionNumberOfWords}</p>
</div>
</div>
)
}
2 changes: 2 additions & 0 deletions src/screens/results/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { lettersForToday } from '../../util/lottery/letters'
import { useMemo } from 'react'
import { decodeResults } from '../../util/results/encodedInfo'
import { Bot } from './Bot'
import { Medal } from './Medal'

export const Results = () => {
const wordList = useAtomValue(words)
Expand All @@ -28,6 +29,7 @@ export const Results = () => {
className="w-full max-w-xl"
>
<Top />
<Medal />
{otherResults ? (
<Words title="Their Words" words={otherResults.words} letters={otherResults.letters} />
) : null}
Expand Down
43 changes: 43 additions & 0 deletions src/screens/results/Medal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { useMemo } from 'react'
import { MEDALS, NONE_MEDAL } from '../../const/medals'
import { generateSolution } from '../../util/solution/generateSolution'
import { lettersForToday } from '../../util/lottery/letters'
import { getMedal } from '../../util/results/getMedal'
import { useAtomValue } from 'jotai'
import { words } from '../../store/game'

export const Medal = () => {
const wordList = useAtomValue(words)

const medal = getMedal(wordList.length)

const perfectSolutionWordCount = useMemo(() => {
return generateSolution(lettersForToday()).length
}, [])

return (
<div className="center card mt-4 w-full select-text bg-base-100 shadow-xl">
<div className="card-body flex flex-col items-center">
<h3 className="mb-2 text-xl font-bold">Medal</h3>
<div className="flex flex-col items-center justify-center gap-5">
<div>
<h2 className="text-7xl font-bold ">{medal.emoji}</h2>
</div>

<div className="text-md opacity-90">
{MEDALS.map((medal) => {
return (
<p key={medal.name + '-medal'}>
{medal.emoji} {medal.differenceFromOptimal + perfectSolutionWordCount} Words
</p>
)
})}
<p>
{NONE_MEDAL.emoji} {perfectSolutionWordCount + 4}+ Words
</p>
</div>
</div>
</div>
</div>
)
}
4 changes: 3 additions & 1 deletion src/store/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export const words = atomWithSeedStorage('words', [] as string[])

export const resultHistory = atomWithStorage<
Array<{
seed: string
puzzleNum: number
wordsUsed: number
perfectSolutionWordsUsed: number
}>
>('history', [])
>('game-history', [], undefined, { getOnInit: true })
8 changes: 7 additions & 1 deletion src/util/game/handleSubmit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { SUCCESS_WORD_MESSAGES } from '../../const/messages'
import { wordsByStartingLetter } from '../../const/wordList'
import { Screen, screen } from '../../store/screen'
import { PUZZLE_NUMBER } from '../../const/puzzleNumber'
import { generateSolution } from '../solution/generateSolution'
import { seed } from '../lottery/seed'

export const isWord = (word: string) => {
const firstLetter = word[0]
Expand Down Expand Up @@ -56,15 +58,19 @@ export const handleSubmit = async () => {
}

const nextLetter = letters[lastLetterIndex + 1]

if (!nextLetter) {
store.set(screen, Screen.results)

// Push to our result history
store.set(resultHistory, (prev) => {
return [
...prev,
{
puzzleNum: PUZZLE_NUMBER,
wordsUsed: store.get(words).length
seed,
wordsUsed: store.get(words).length,
perfectSolutionWordsUsed: generateSolution(letters).length
}
]
})
Expand Down
16 changes: 16 additions & 0 deletions src/util/results/getMedal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MEDALS, NONE_MEDAL } from '../../const/medals'
import { lettersForToday } from '../lottery/letters'
import { generateSolution } from '../solution/generateSolution'

export const getMedal = (wordsUsed: number) => {
const solutionWordsUsed = generateSolution(lettersForToday()).length
const difference = Math.abs(solutionWordsUsed - wordsUsed)

let medal = MEDALS.find((medal) => {
return medal.differenceFromOptimal === difference
})
if (!medal) {
medal = NONE_MEDAL
}
return medal
}
3 changes: 3 additions & 0 deletions src/util/results/share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { words } from '../../store/game'
import { store } from '../../store/store'
import { toast } from 'sonner'
import { encodeResults } from './encodedInfo'
import { getMedal } from './getMedal'

const colors = ['🟦', '🟧', 'πŸŸͺ', '🟨', '🟩', '🟫', '⬜']

export const resultText = () => {
const wordsUsed = store.get(words)
const medal = getMedal(wordsUsed.length)

let text = `🎰 Charlottery No. ${PUZZLE_NUMBER}
βœ… Completed in ${wordsUsed.length} words
${medal.emoji} ${medal.name} Medal
`

let colorIndex = 0
Expand Down
13 changes: 13 additions & 0 deletions src/util/solution/generateSolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,16 @@ const findWordsMatchingOrder = (letters: string[], options?: { maxLettersPerWord
return matchingWords
}

const solutionCache = new Map<string, string[]>()

export const generateSolution = (letters: string[], options?: { maxLettersPerWord?: number }) => {
if (!options) {
const cacheKey = letters.join('')
if (solutionCache.has(cacheKey)) {
return solutionCache.get(cacheKey)!
}
}

// Create our root node which is an empty word at the start of the letters
const root = new WordNode({
word: '',
Expand Down Expand Up @@ -109,5 +118,9 @@ export const generateSolution = (letters: string[], options?: { maxLettersPerWor
}
}

if (!options) {
solutionCache.set(letters.join(''), solution)
}

return solution
}

0 comments on commit 15f0d32

Please sign in to comment.