feat: upgrade Guess

This commit is contained in:
TrèsAbhi committed 2025-09-01 19:22:38 -05:00
1 parent 7d164e415a
commit f56c31fb48
5 files changed
+80 -82

No files matched your search

@@ -1,13 +1,13 @@
import { Box } from '@radix-ui/themes'; import { Box } from "@radix-ui/themes";
import { useEffect } from 'react'; import { useEffect } from "react";
import { awaitableModelDefinitions } from '../../core/awaitables/modelDefinitions'; import { awaitableModelDefinitions } from "../../core/awaitables/modelDefinitions";
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions'; import { awaitableProvisionDefinitions } from "../../core/awaitables/provisionDefinitions";
import { tankToDuelMember } from '../../core/blitzkit/tankToDuelMember'; import { tankToDuelMember } from "../../core/blitzkit/tankToDuelMember";
import { Var } from '../../core/radix/var'; import { Var } from "../../core/radix/var";
import { Duel } from '../../stores/duel'; import { Duel } from "../../stores/duel";
import { GuessEphemeral } from '../../stores/guessEphemeral'; import { Guess } from "../../stores/guessEphemeral";
import { TankopediaEphemeral } from '../../stores/tankopediaEphemeral'; import { TankopediaEphemeral } from "../../stores/tankopediaEphemeral";
import './index.css'; import "./index.css";
const [modelDefinitions, provisionDefinitions] = await Promise.all([ const [modelDefinitions, provisionDefinitions] = await Promise.all([
awaitableModelDefinitions, awaitableModelDefinitions,
@@ -15,8 +15,8 @@ const [modelDefinitions, provisionDefinitions] = await Promise.all([
]); ]);
export function GuessBackground() { export function GuessBackground() {
const guessState = GuessEphemeral.use((state) => state.guessState); const guessState = Guess.use((state) => state.guessState);
const tank = GuessEphemeral.use((state) => state.tank); const tank = Guess.use((state) => state.tank);
const mutateDuel = Duel.useMutation(); const mutateDuel = Duel.useMutation();
useEffect(() => { useEffect(() => {
@@ -37,13 +37,13 @@ export function GuessBackground() {
top="0" top="0"
left="0" left="0"
style={{ style={{
transitionDuration: '2s', transitionDuration: "2s",
backgroundColor: Var( backgroundColor: Var(
guessState === null guessState === null
? 'accent-2' ? "accent-2"
: guessState : guessState
? 'jade-3' ? "jade-3"
: 'tomato-3', : "tomato-3"
), ),
}} }}
> >
@@ -51,7 +51,7 @@ export function GuessBackground() {
width="100%" width="100%"
height="100%" height="100%"
style={{ style={{
background: `radial-gradient(circle, ${Var('color-panel-translucent')}, ${Var('color-panel-solid')})`, background: `radial-gradient(circle, ${Var("color-panel-translucent")}, ${Var("color-panel-solid")})`,
}} }}
/> />
</Box> </Box>
@@ -65,13 +65,13 @@ export function GuessBackground() {
left="0" left="0"
style={{ style={{
backgroundImage: ` backgroundImage: `
radial-gradient(circle, ${Var('white-a1')} 20%, transparent 20%), radial-gradient(circle, ${Var("white-a1")} 20%, transparent 20%),
radial-gradient(circle at 0 0, ${Var('white-a1')} 10%, transparent 10%), radial-gradient(circle at 0 0, ${Var("white-a1")} 10%, transparent 10%),
radial-gradient(circle at 100% 0, ${Var('white-a1')} 10%, transparent 10%), radial-gradient(circle at 100% 0, ${Var("white-a1")} 10%, transparent 10%),
radial-gradient(circle at 0 100%, ${Var('white-a1')} 10%, transparent 10%), radial-gradient(circle at 0 100%, ${Var("white-a1")} 10%, transparent 10%),
radial-gradient(circle at 100% 100%, ${Var('white-a1')} 10%, transparent 10%) radial-gradient(circle at 100% 100%, ${Var("white-a1")} 10%, transparent 10%)
`, `,
backgroundSize: '4rem 4rem', backgroundSize: "4rem 4rem",
}} }}
/> />
</> </>
@@ -1,22 +1,22 @@
import { GuessEphemeral } from '../stores/guessEphemeral'; import { Guess } from "../stores/guessEphemeral";
import { SmartCanvas } from './SmartCanvas'; import { SmartCanvas } from "./SmartCanvas";
import { Controls } from './Tankopedia/HeroSection/components/TankSandbox/components/Control'; import { Controls } from "./Tankopedia/HeroSection/components/TankSandbox/components/Control";
import { Lighting } from './Tankopedia/HeroSection/components/TankSandbox/components/Lighting'; import { Lighting } from "./Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { TankModel } from './Tankopedia/HeroSection/components/TankSandbox/components/TankModel'; import { TankModel } from "./Tankopedia/HeroSection/components/TankSandbox/components/TankModel";
const BRIGHTNESS = 2 ** -5.5; const BRIGHTNESS = 2 ** -5.5;
const CONTRAST = 2 ** 2; const CONTRAST = 2 ** 2;
export function GuessRenderer() { export function GuessRenderer() {
const guessState = GuessEphemeral.use((state) => state.guessState); const guessState = Guess.use((state) => state.guessState);
const isRevealed = guessState !== null; const isRevealed = guessState !== null;
return ( return (
<SmartCanvas <SmartCanvas
style={{ style={{
filter: `grayscale(${isRevealed ? 0 : 1}) contrast(${isRevealed ? 1 : CONTRAST}) brightness(${isRevealed ? 1 : BRIGHTNESS})`, filter: `grayscale(${isRevealed ? 0 : 1}) contrast(${isRevealed ? 1 : CONTRAST}) brightness(${isRevealed ? 1 : BRIGHTNESS})`,
transitionDuration: isRevealed ? '2s' : undefined, transitionDuration: isRevealed ? "2s" : undefined,
transitionProperty: 'filter', transitionProperty: "filter",
}} }}
camera={{ position: [-5, 5, -5] }} camera={{ position: [-5, 5, -5] }}
> >
+26 -27
View File
@@ -2,13 +2,13 @@ import {
SEARCH_KEYS, SEARCH_KEYS,
TankDefinition, TankDefinition,
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { literals } from '@blitzkit/i18n'; import { literals } from "@blitzkit/i18n";
import { import {
ArrowRightIcon, ArrowRightIcon,
MagnifyingGlassIcon, MagnifyingGlassIcon,
PaperPlaneIcon, PaperPlaneIcon,
} from '@radix-ui/react-icons'; } from "@radix-ui/react-icons";
import { import {
Box, Box,
Button, Button,
@@ -17,16 +17,16 @@ import {
Spinner, Spinner,
Text, Text,
TextField, TextField,
} from '@radix-ui/themes'; } from "@radix-ui/themes";
import fuzzysort from 'fuzzysort'; import fuzzysort from "fuzzysort";
import { debounce } from 'lodash-es'; import { debounce } from "lodash-es";
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from "react";
import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions'; import { awaitableTankDefinitions } from "../core/awaitables/tankDefinitions";
import { awaitableTankNames } from '../core/awaitables/tankNames'; import { awaitableTankNames } from "../core/awaitables/tankNames";
import { useLocale } from '../hooks/useLocale'; import { useLocale } from "../hooks/useLocale";
import { GuessEphemeral } from '../stores/guessEphemeral'; import { Guess } from "../stores/guessEphemeral";
import { classIcons } from './ClassIcon'; import { classIcons } from "./ClassIcon";
import { SearchResults } from './SearchResults'; import { SearchResults } from "./SearchResults";
const { go } = fuzzysort; const { go } = fuzzysort;
@@ -38,16 +38,15 @@ const [tankNames, tankDefinitions] = await Promise.all([
const ids = Object.keys(tankDefinitions.tanks); const ids = Object.keys(tankDefinitions.tanks);
export function Guesser() { export function Guesser() {
const tank = GuessEphemeral.use((state) => state.tank); const tank = Guess.use((state) => state.tank);
const guessState = GuessEphemeral.use((state) => state.guessState); const guessState = Guess.use((state) => state.guessState);
const correctGuesses = GuessEphemeral.use((state) => state.correctGuesses); const correctGuesses = Guess.use((state) => state.correctGuesses);
const totalGuesses = GuessEphemeral.use((state) => state.totalGuesses); const totalGuesses = Guess.use((state) => state.totalGuesses);
const { strings, unwrap } = useLocale(); const { strings, unwrap } = useLocale();
const input = useRef<HTMLInputElement>(null); const input = useRef<HTMLInputElement>(null);
const [searching, setSearching] = useState(false); const [searching, setSearching] = useState(false);
const [results, setResults] = useState<TankDefinition[] | null>(null); const [results, setResults] = useState<TankDefinition[] | null>(null);
const [selected, setSelected] = useState<TankDefinition | null>(null); const [selected, setSelected] = useState<TankDefinition | null>(null);
const mutateGuessEphemeral = GuessEphemeral.useMutation();
const requestSearch = useCallback(() => { const requestSearch = useCallback(() => {
setSelected(null); setSelected(null);
@@ -72,10 +71,10 @@ export function Guesser() {
}); });
setResults( setResults(
searchResults.map((result) => tankDefinitions.tanks[result.obj.id]), searchResults.map((result) => tankDefinitions.tanks[result.obj.id])
); );
}, 500), }, 500),
[], []
); );
useEffect(() => { useEffect(() => {
@@ -88,7 +87,7 @@ export function Guesser() {
position="absolute" position="absolute"
bottom="0" bottom="0"
left="50%" left="50%"
style={{ transform: 'translateX(-50%)' }} style={{ transform: "translateX(-50%)" }}
width="100%" width="100%"
p="4" p="4"
maxWidth="25rem" maxWidth="25rem"
@@ -138,7 +137,7 @@ export function Guesser() {
</Card> </Card>
)} )}
<Flex justify="center" style={{ userSelect: 'none' }}> <Flex justify="center" style={{ userSelect: "none" }}>
<Text> <Text>
{literals(strings.website.tools.guess.stats, [ {literals(strings.website.tools.guess.stats, [
`${correctGuesses}`, `${correctGuesses}`,
@@ -164,12 +163,12 @@ export function Guesser() {
<Button <Button
size="3" size="3"
color={guessState === null && selected === null ? 'red' : undefined} color={guessState === null && selected === null ? "red" : undefined}
onClick={() => { onClick={() => {
if (guessState === null) { if (guessState === null) {
const correct = selected !== null && selected.id === tank.id; const correct = selected !== null && selected.id === tank.id;
mutateGuessEphemeral((draft) => { Guess.mutate((draft) => {
draft.guessState = correct; draft.guessState = correct;
draft.totalGuesses++; draft.totalGuesses++;
draft.correctGuesses += correct ? 1 : 0; draft.correctGuesses += correct ? 1 : 0;
@@ -178,12 +177,12 @@ export function Guesser() {
const id = Number(ids[Math.floor(Math.random() * ids.length)]); const id = Number(ids[Math.floor(Math.random() * ids.length)]);
const tank = tankDefinitions.tanks[id]; const tank = tankDefinitions.tanks[id];
mutateGuessEphemeral((draft) => { Guess.mutate((draft) => {
if (!input.current) return; if (!input.current) return;
draft.tank = tank; draft.tank = tank;
draft.guessState = null; draft.guessState = null;
input.current.value = ''; input.current.value = "";
}); });
} }
}} }}
@@ -192,7 +191,7 @@ export function Guesser() {
<> <>
{ {
strings.website.tools.guess.search[ strings.website.tools.guess.search[
selected === null ? 'skip' : 'guess' selected === null ? "skip" : "guess"
] ]
} }
{selected === null ? <ArrowRightIcon /> : <PaperPlaneIcon />} {selected === null ? <ArrowRightIcon /> : <PaperPlaneIcon />}
@@ -13,7 +13,7 @@ import {
useLocale, useLocale,
} from "../../../hooks/useLocale"; } from "../../../hooks/useLocale";
import { Duel } from "../../../stores/duel"; import { Duel } from "../../../stores/duel";
import { GuessEphemeral } from "../../../stores/guessEphemeral"; import { Guess } from "../../../stores/guessEphemeral";
import { TankopediaEphemeral } from "../../../stores/tankopediaEphemeral"; import { TankopediaEphemeral } from "../../../stores/tankopediaEphemeral";
const [tankDefinitions, modelDefinitions, provisionDefinitions] = const [tankDefinitions, modelDefinitions, provisionDefinitions] =
@@ -26,20 +26,20 @@ const [tankDefinitions, modelDefinitions, provisionDefinitions] =
const ids = Object.keys(tankDefinitions.tanks); const ids = Object.keys(tankDefinitions.tanks);
export function Page({ locale }: LocaleAcceptorProps) { export function Page({ locale }: LocaleAcceptorProps) {
const initialId = Number(ids[Math.floor(Math.random() * ids.length)]); const id = Number(ids[Math.floor(Math.random() * ids.length)]);
const initialTank = tankDefinitions.tanks[initialId]; const tank = tankDefinitions.tanks[id];
Guess.useInitialization(tank);
return ( return (
<GuessEphemeral.Provider data={initialTank}> <LocaleProvider locale={locale}>
<LocaleProvider locale={locale}> <Container />
<Container /> </LocaleProvider>
</LocaleProvider>
</GuessEphemeral.Provider>
); );
} }
function Container() { function Container() {
const tank = GuessEphemeral.use((state) => state.tank); const tank = Guess.use((state) => state.tank);
const model = modelDefinitions.models[tank.id]; const model = modelDefinitions.models[tank.id];
TankopediaEphemeral.useInitialization(model); TankopediaEphemeral.useInitialization(model);
@@ -53,8 +53,8 @@ function Container() {
function Content() { function Content() {
const { unwrap } = useLocale(); const { unwrap } = useLocale();
const tank = GuessEphemeral.use((state) => state.tank); const tank = Guess.use((state) => state.tank);
const guessState = GuessEphemeral.use((state) => state.guessState); const guessState = Guess.use((state) => state.guessState);
const isRevealed = guessState !== null; const isRevealed = guessState !== null;
const name = unwrap(tank.name); const name = unwrap(tank.name);
const fontSize = `min(48vh, ${55 / name.length}vw)`; const fontSize = `min(48vh, ${55 / name.length}vw)`;
+12 -13
View File
@@ -1,20 +1,19 @@
import type { TankDefinition } from '@blitzkit/core'; import type { TankDefinition } from "@blitzkit/core";
import { create } from 'zustand'; import { Varuna } from "varuna";
import { createContextualStore } from '../core/zustand/createContextualStore';
export interface GuessEphemeral { export interface Guess {
tank: TankDefinition; tank: TankDefinition;
// true = correct, false = incorrect, null = not guessed /**
* true = correct, false = incorrect, null = not guessed
*/
guessState: boolean | null; guessState: boolean | null;
totalGuesses: number; totalGuesses: number;
correctGuesses: number; correctGuesses: number;
} }
export const GuessEphemeral = createContextualStore((tank: TankDefinition) => export const Guess = new Varuna<Guess, TankDefinition>((tank) => ({
create<GuessEphemeral>()(() => ({ tank,
tank, guessState: null,
guessState: null, totalGuesses: 0,
totalGuesses: 0, correctGuesses: 0,
correctGuesses: 0, }));
})),
);