Skip to content

Commit

Permalink
app: replace normalized clue value with value on board
Browse files Browse the repository at this point in the history
Trust that the .jep.json file has the correct value for each clue, regardless
of whether it is wagerable on unrevealed.
  • Loading branch information
cmnord committed Jul 25, 2023
1 parent 2ae4cfc commit a84d93f
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 40 deletions.
12 changes: 3 additions & 9 deletions app/components/board/board.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useEngineContext } from "~/engine";
import type { Board, Clue } from "~/models/convert.server";
import { useSoloAction } from "~/utils/use-solo-action";
import useGameSound from "~/utils/use-sound";
import { getNormalizedClueValue } from "~/utils/utils";
import { Category } from "./category";
import { ClueComponent } from "./clue";

Expand All @@ -19,7 +18,6 @@ function BoardComponent({
onClickClue,
onFocusClue,
onKeyDownClue,
round,
tbodyRef,
}: {
board: Board;
Expand All @@ -28,20 +26,18 @@ function BoardComponent({
onClickClue: (i: number, j: number) => void;
onFocusClue: (i: number, j: number) => void;
onKeyDownClue: (event: React.KeyboardEvent, i: number, j: number) => void;
round: number;
tbodyRef: React.RefObject<HTMLTableSectionElement>;
}) {
const clueRows = new Map<number, Clue[]>();
for (const category of board.categories) {
const clues = category.clues;
for (let i = 0; i < clues.length; i++) {
const clue = clues[i];
const clueValue = getNormalizedClueValue(i, round);
const clueRow = clueRows.get(clueValue);
const clueRow = clueRows.get(clue.value);
if (clueRow) {
clueRow.push(clue);
} else {
clueRows.set(clueValue, [clue]);
clueRows.set(clue.value, [clue]);
}
}
}
Expand Down Expand Up @@ -102,8 +98,7 @@ export function ConnectedBoardComponent({
userId: string;
roomName: string;
}) {
const { board, round, boardControl, isAnswered, soloDispatch } =
useEngineContext();
const { board, boardControl, isAnswered, soloDispatch } = useEngineContext();
const fetcher = useFetcher<Action>();
useSoloAction(fetcher, soloDispatch);

Expand Down Expand Up @@ -185,7 +180,6 @@ export function ConnectedBoardComponent({
board={board}
tbodyRef={tbodyRef}
hasBoardControl={hasBoardControl}
round={round}
isAnswered={isAnswered}
onClickClue={handleClickClue}
onFocusClue={(i, j) => {
Expand Down
2 changes: 1 addition & 1 deletion app/components/board/clue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import classNames from "classnames";
import * as React from "react";

import Popover from "~/components/popover";
import { UNREVEALED_CLUE } from "~/engine/engine";
import { UNREVEALED_CLUE } from "~/engine";
import type { Clue } from "~/models/convert.server";

interface Props {
Expand Down
14 changes: 7 additions & 7 deletions app/components/prompt/wager-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import * as React from "react";

import Button from "~/components/button";
import type { Action, Player } from "~/engine";
import { useEngineContext } from "~/engine";
import { getHighestClueValue, useEngineContext } from "~/engine";
import { useSoloAction } from "~/utils/use-solo-action";
import { getNormalizedClueValue } from "~/utils/utils";

const formatter = Intl.NumberFormat("en-US", {
style: "currency",
Expand Down Expand Up @@ -100,8 +99,7 @@ export default function ConnectedWagerForm({
roomName: string;
userId: string;
}) {
const { board, players, round, soloDispatch, activeClue } =
useEngineContext();
const { board, players, soloDispatch, activeClue } = useEngineContext();
const fetcher = useFetcher<Action>();
useSoloAction(fetcher, soloDispatch);
const loading = fetcher.state === "loading";
Expand All @@ -110,16 +108,18 @@ export default function ConnectedWagerForm({

const score = players.get(userId)?.score ?? 0;

const numRows = board?.categories[0].clues.length ?? 0;
const highestClueValueInRound = getNormalizedClueValue(numRows - 1, round);
const highestClueValue = React.useMemo(
() => getHighestClueValue(board),
[board]
);

return (
<fetcher.Form method="post" action={`/room/${roomName}/wager`}>
<input type="hidden" value={userId} name="userId" />
<input type="hidden" value={i} name="i" />
<input type="hidden" value={j} name="j" />
<WagerForm
highestClueValue={highestClueValueInRound}
highestClueValue={highestClueValue}
loading={loading}
players={Array.from(players.values())}
score={score}
Expand Down
38 changes: 28 additions & 10 deletions app/engine/engine.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Game } from "~/models/convert.server";
import { generateGrid, getNormalizedClueValue } from "~/utils/utils";
import type { Board, Game } from "~/models/convert.server";
import { generateGrid } from "~/utils/utils";
import {
isAnswerAction,
isBuzzAction,
Expand Down Expand Up @@ -123,7 +123,11 @@ function setIsAnswered(
*/
export function getClueValue(state: State, [i, j]: [number, number]) {
const board = state.game.boards.at(state.round);
if (board?.categories.at(j)?.clues.at(i)?.wagerable) {
const clue = board?.categories.at(j)?.clues.at(i);
if (!clue) {
throw new Error(`No clue exists at (${i}, ${j})`);
}
if (clue.wagerable) {
const wager = state.isAnswered.at(i)?.at(j)?.wager;
if (wager === undefined) {
throw new Error(
Expand All @@ -132,7 +136,25 @@ export function getClueValue(state: State, [i, j]: [number, number]) {
}
return wager;
}
return getNormalizedClueValue(i, state.round);
return clue.value;
}

/** getHighestClueValue gets the highest clue value on the board. */
export function getHighestClueValue(board: Board | undefined) {
if (!board) {
return 0;
}
let max = 0;
for (let j = 0; j < board.categories.length; j++) {
const category = board.categories[j];
for (let i = 0; i < category.clues.length; i++) {
const clue = category.clues[i];
if (clue.value > max) {
max = clue.value;
}
}
}
return max;
}

export function createInitialState(game: Game): State {
Expand Down Expand Up @@ -274,12 +296,8 @@ export function gameEngine(state: State, action: Action): State {
if (wager < 5) {
throw new Error("Wager must be at least $5");
}
const numRows =
state.game.boards.at(state.round)?.categories[0].clues.length ?? 0;
const highestClueValueInRound = getNormalizedClueValue(
numRows - 1,
state.round
);
const board = state.game.boards.at(state.round);
const highestClueValueInRound = getHighestClueValue(board);
const maxWager = Math.max(
state.players.get(userId)?.score ?? 0,
highestClueValueInRound
Expand Down
7 changes: 6 additions & 1 deletion app/engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
CLUE_TIMEOUT_MS,
gameEngine,
GameState,
getHighestClueValue,
UNREVEALED_CLUE,
} from "./engine";
import { GameEngineContext, useEngineContext } from "./use-engine-context";
import { useGameEngine } from "./use-game-engine";
import { useGameEngine, useSoloGameEngine } from "./use-game-engine";

export {
Action,
Expand All @@ -17,8 +19,11 @@ export {
gameEngine,
GameEngineContext,
GameState,
getHighestClueValue,
Player,
State,
UNREVEALED_CLUE,
useEngineContext,
useGameEngine,
useSoloGameEngine,
};
3 changes: 1 addition & 2 deletions app/routes/$gameId.solo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

import GameComponent from "~/components/game";
import { GameEngineContext } from "~/engine";
import { useSoloGameEngine } from "~/engine/use-game-engine";
import { GameEngineContext, useSoloGameEngine } from "~/engine";
import { getGame } from "~/models/game.server";
import { getOrCreateUserSession } from "~/session.server";
import { getRandomName } from "~/utils/name";
Expand Down
3 changes: 1 addition & 2 deletions app/routes/mock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

import GameComponent from "~/components/game";
import { GameEngineContext } from "~/engine";
import { useSoloGameEngine } from "~/engine/use-game-engine";
import { GameEngineContext, useSoloGameEngine } from "~/engine";
import { getMockGame } from "~/models/mock.server";

export const meta: MetaFunction<typeof loader> = ({ data }) => ({
Expand Down
8 changes: 0 additions & 8 deletions app/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@ const gameWords =
const SATURATION = 60;
const LIGHTNESS = 85;

/** getNormalizedClueValue gets the normalized clue value based on its position in the
* board. Otherwise, the clue value for wagerable or unrevealed clues would be
* 0.
*/
export function getNormalizedClueValue(i: number, round: number) {
return (i + 1) * 200 * (round + 1);
}

export function getRandomWord() {
return gameWords[Math.floor(Math.random() * gameWords.length)];
}
Expand Down

0 comments on commit a84d93f

Please sign in to comment.