diff --git a/src/app/routes/settings/RulesetSettings.tsx b/src/app/routes/settings/RulesetSettings.tsx index 9f26299..d81015d 100644 --- a/src/app/routes/settings/RulesetSettings.tsx +++ b/src/app/routes/settings/RulesetSettings.tsx @@ -98,7 +98,7 @@ export const HandicapSlider = () => { className="mb-2" htmlFor={"handicap"} hint={ - "Defines the severity of the handicap a player will receive after winning a game. " + + "Defines the severity of the handicap a player will receive after winning multiple times in a row. " + "A value of 0 will turn the handicap off. " } > diff --git a/src/data/games.ts b/src/data/games.ts index dc270f0..ec8150a 100644 --- a/src/data/games.ts +++ b/src/data/games.ts @@ -5,6 +5,7 @@ import { createSlice, localStorage, useAtomValue, + createEffect, } from "~/lib/yaasl" import { createId } from "~/utils/createId" import { dateIsValid, timeBetween, timeSince, today } from "~/utils/date" @@ -31,33 +32,34 @@ export interface Game extends Omit { player?: Player } -const sortByDate = (games: RawGame[]) => - games.sort((a, b) => a.date.localeCompare(b.date)) +const sortByDate = createEffect({ + set: ({ set }) => { + set(games => games.sort((a, b) => a.date.localeCompare(b.date))) + }, +}) export const gamesSlice = createSlice({ defaultValue: [] as RawGame[], name: "games", - effects: [localStorage()], + effects: [sortByDate(), localStorage()], reducers: { - add: (state, game: RawGame) => sortByDate([...state, game]), + add: (state, game: RawGame) => [...state, game], edit: (state, id: string, data: Partial) => state.map(game => (game.id === id ? { ...game, ...data } : game)), remove: (state, id: string) => state.filter(game => game.id !== id), merge: (state, games: RawGame[]) => - sortByDate( - games.reduce( - (state, game) => { - const gameExists = state.some(({ name }) => name === game.name) - if (gameExists || !dateIsValid(game.date)) { - return state - } - state.push(game) + games.reduce( + (state, game) => { + const gameExists = state.some(({ name }) => name === game.name) + if (gameExists || !dateIsValid(game.date)) { return state - }, - [...state] - ) + } + state.push(game) + return state + }, + [...state] ), }, }) diff --git a/src/data/handicap.ts b/src/data/handicap.ts index 6a746e2..3b1928d 100644 --- a/src/data/handicap.ts +++ b/src/data/handicap.ts @@ -5,7 +5,10 @@ import { rulesetAtom } from "./ruleset" export const calcHandicap = (wins: number, max: number, severity = 0.75) => { if (severity <= 0) return 0 - const result = Math.pow((1 / max) * wins, 1 / severity) + const successive = wins - 1 + if (successive < 1) return 0 + + const result = Math.pow((1 / max) * successive, 1 / severity) return Math.round(result * 100) / 100 } @@ -18,12 +21,12 @@ export interface Handicap { const handicapAtom = createSelector( [gamesSlice, rulesetAtom], (games, ruleset): Handicap => { - const reversedGames = games.reverse() + const reversed = games.reverse() - let wins = 0 + let wins = 0 // ignore the first win let latestPlayer: string | undefined = undefined - while (!latestPlayer || reversedGames[wins]?.playerId === latestPlayer) { - const current = reversedGames[wins] + while (!latestPlayer || reversed[wins]?.playerId === latestPlayer) { + const current = reversed[wins] wins++ if (!current) break if (!latestPlayer) { @@ -32,7 +35,8 @@ const handicapAtom = createSelector( } return { - wins, + // Ignore one win + wins: wins - 1, amount: calcHandicap(wins, ruleset.gamesPerPerson, ruleset.handicap), playerId: latestPlayer, }