Skip to content

Commit

Permalink
feat(game): add daily bonus game
Browse files Browse the repository at this point in the history
  • Loading branch information
wolimst committed Apr 18, 2024
1 parent 91b3b34 commit 0794acf
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 37 deletions.
88 changes: 64 additions & 24 deletions src/components/wordle/Game.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
} from './message'
import { game, keyboard, ui } from './store'
import {
DAILY_BONUS_GUESS_COUNTS,
DOM_ID_GAME_CONTAINER,
WAIT_DURATION_TO_SHOW_STATS_MS,
} from '@/constants'
Expand Down Expand Up @@ -65,30 +66,69 @@
particleCount: Math.floor(count * particleRatio),
})
void fire(0.25, {
spread: 26,
startVelocity: 60,
})
void fire(0.2, {
spread: 35,
startVelocity: 50,
})
void fire(0.35, {
spread: 60,
startVelocity: 50,
decay: 0.91,
scalar: 0.8,
})
void fire(0.1, {
spread: 80,
startVelocity: 30,
decay: 0.92,
scalar: 1.2,
})
void fire(0.1, {
spread: 80,
startVelocity: 50,
})
const fireFromBottom = (velocity = 1) => {
void fire(0.25, {
spread: 26,
startVelocity: 60 * velocity,
})
void fire(0.2, {
spread: 35,
startVelocity: 50 * velocity,
})
void fire(0.35, {
spread: 60,
startVelocity: 50 * velocity,
decay: 0.91,
scalar: 0.8,
})
void fire(0.1, {
spread: 80,
startVelocity: 30 * velocity,
decay: 0.92,
scalar: 1.2,
})
void fire(0.1, {
spread: 80,
startVelocity: 50 * velocity,
})
}
const fireFromLeft = () => {
void confetti({
...defaults,
origin: {
x: 0.05,
y: 0.7,
},
angle: 70,
})
}
const fireFromRight = () => {
void confetti({
...defaults,
origin: {
x: 0.95,
y: 0.7,
},
angle: 110,
})
}
fireFromBottom()
if (
$game.data.guesses.length <=
DAILY_BONUS_GUESS_COUNTS[$game.data.config.nWordles][
$game.data.config.answerLength
]
) {
window.setTimeout(fireFromLeft, 500)
window.setTimeout(fireFromRight, 750)
window.setTimeout(fireFromLeft, 1450)
window.setTimeout(fireFromRight, 1450)
window.setTimeout(() => fireFromBottom(1.3), 1520)
}
}
}
Expand Down
41 changes: 35 additions & 6 deletions src/components/wordle/Statistics.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,23 @@
window.clearInterval(countdownIntervalId)
nextGameCountdownMillis = 0
if (gameMode.id === 'daily') {
const gameId = Wordle.generateConfigId(
const configId = Wordle.generateConfigId(
gameMode.id as Wordle.GameMode,
gameType.nWordles,
gameType.answerLength
)
const data = savedata.load(gameId)
if (data !== undefined && data.status !== 'playing') {
nextGameCountdownMillis = time.getMillisecondsToMidnightInKST()
countdownIntervalId = window.setInterval(countdownByOneSecond, 1000)
const data = savedata.loadByConfigId(configId).at(-1)
if (
data === undefined ||
(data.status === 'win' && Wordle.isDailyBonusAvailable(data)) ||
data.status === 'playing'
) {
return
}
nextGameCountdownMillis = time.getMillisecondsToMidnightInKST()
countdownIntervalId = window.setInterval(countdownByOneSecond, 1000)
}
}
Expand Down Expand Up @@ -137,6 +144,28 @@
void share.copy(text).then(() => ($open = false))
}
function getNextGameTypeString(): string {
if (gameMode.id === 'daily') {
const configId = Wordle.generateConfigId(
gameMode.id as Wordle.GameMode,
gameType.nWordles,
gameType.answerLength
)
const dailyGames = savedata.loadByConfigId(configId)
const latestGame = dailyGames.at(-1)
if (
dailyGames.length > 1 ||
(latestGame && Wordle.isDailyBonusAvailable(latestGame))
) {
return '보너스'
} else {
return '오늘의'
}
} else {
return '새로운'
}
}
onDestroy(() => {
window.clearInterval(countdownIntervalId)
})
Expand Down Expand Up @@ -236,7 +265,7 @@
>
<RefreshIcon width={22} />
<span class="tw-ml-1 tw-font-medium">
{gameMode.id === 'daily' ? '오늘의' : '새로운'} 문제 풀기
{getNextGameTypeString()} 문제 풀기
</span>
</LinkButton>
{/if}
Expand Down
14 changes: 14 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ export const N_GUESSES: readonly (readonly number[])[] = [
*/
export const N_WORDLES_PER_ROW = [NaN, 4, 4, 3, 2, 2] as const

/**
* A lookup table for the number of guesses to receive a bonus daily game.
*
* Row index represents the number of wordles in a game, and column index
* represents the answer length, e.g. if `nWordles = 2`, `answerLength = 1`
* then `count = DAILY_BONUS_GUESS_COUNTS[2][1]`
*/
export const DAILY_BONUS_GUESS_COUNTS: readonly (readonly number[])[] = [
[],
[NaN, NaN, 3],
[NaN, NaN, 4],
[NaN, NaN, 5],
] as const

export const ALERT_DURATION_MS = 3000
export const WAIT_DURATION_TO_SHOW_STATS_MS = 1500

Expand Down
69 changes: 63 additions & 6 deletions src/lib/wordle/game.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Keyboard } from './keyboard'
import type { GameData, GameMode, GuessError, Status } from './types'
import type {
GameData,
GameMode,
GameSaveData,
GuessError,
Status,
} from './types'
import { _Wordle } from './wordle'
import { getRandomAnswer, isInWordList } from './words'
import { GAME_MODES } from '@/constants'
import { DAILY_BONUS_GUESS_COUNTS, GAME_MODES } from '@/constants'
import * as Hangul from '@/lib/hangul'
import * as Path from '@/lib/path'
import { time } from '@/lib/utils'
Expand Down Expand Up @@ -131,10 +137,14 @@ export class Game {

if (config.useSave) {
const data = savedata.load(this.#id)
data?.guesses.forEach((guess) => {
const word = Hangul.toWord(guess.value)
this.#doSubmit(word)
})
if (data) {
data.guesses.forEach((guess) => {
const word = Hangul.toWord(guess.value)
this.#doSubmit(word)
})
} else {
savedata.save(this.data)
}
}
}

Expand Down Expand Up @@ -257,3 +267,50 @@ export function generateConfigId(
}
}
}

export function getGameId(config: GameConfig): string {
switch (config.mode) {
case 'daily': {
return getDailyGameId(config)
}
case 'free': {
return config.id
}
case 'custom':
return config.id
default: {
const _exhaustiveCheck: never = config.mode
return _exhaustiveCheck
}
}
}

function getDailyGameId(config: GameConfig): string {
if (config.mode !== 'daily') {
return config.id
}

const dailyGames = savedata.loadByConfigId(config.id)
const latestGame = dailyGames.at(-1)
if (!latestGame) {
return `${config.id}-000`
}

if (isDailyBonusAvailable(latestGame)) {
const index = String(dailyGames.length).padStart(3, '0')
return `${config.id}-${index}`
} else {
return latestGame.id
}
}

export function isDailyBonusAvailable(latestGame: GameData | GameSaveData) {
return (
latestGame.config.mode === 'daily' &&
latestGame.status === 'win' &&
latestGame.guesses.length <=
DAILY_BONUS_GUESS_COUNTS[latestGame.config.nWordles][
latestGame.config.answerLength
]
)
}
3 changes: 2 additions & 1 deletion src/routes/Wordle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
game.answerLength,
N_GUESSES[game.nWordles][game.answerLength]
)
initializeWordleStores(config, config.id)
const gameId = Wordle.getGameId(config)
initializeWordleStores(config, gameId)
pageStatus = 'success'
}
Expand Down

0 comments on commit 0794acf

Please sign in to comment.