diff --git a/lefthook.yml b/lefthook.yml index 85dc788..4477b36 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -1,5 +1,5 @@ pre-commit: commands: - check: - glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}" - run: bunx @biomejs/biome check --write {staged_files} && git add {staged_files} + format: + glob: "*.{ts,tsx,json,jsonc}" + run: bunx @biomejs/biome check {staged_files} --write && git add {staged_files} diff --git a/rr/app/lib/db/schema.ts b/rr/app/lib/db/schema.ts index c169b9f..68c1c8d 100644 --- a/rr/app/lib/db/schema.ts +++ b/rr/app/lib/db/schema.ts @@ -35,7 +35,6 @@ export const hai = pgTable("hai", { }); // relation between user and haiyama -// TODO: index export const kyoku = pgTable( "kyoku", { diff --git a/rr/app/lib/store.ts b/rr/app/lib/store.ts new file mode 100644 index 0000000..618d48f --- /dev/null +++ b/rr/app/lib/store.ts @@ -0,0 +1,65 @@ +import { create } from "zustand"; +import type { Hai } from "./hai"; +import { sortTehai } from "./hai"; + +interface GameState { + kyoku: number; + junme: number; + haiyama: Hai[]; + sutehai: Hai[]; + // Hai * 13 + tehai: Hai[]; + // null as an initial value + tsumohai: Hai | null; +} + +interface PlayerAction { + // tsumo just after tedashi or tsumogiri + tedashi: (index: number) => void; + tsumogiri: () => void; +} + +const useGameStore = create()((set, get) => ({ + kyoku: 1, + junme: 1, + haiyama: [], + sutehai: [], + tehai: [], + tsumohai: null, + + tedashi: (index) => { + const state = get(); + if (!state.tsumohai) { + throw new Error("syohai"); + } + const tsumohai = state.tsumohai; + + if (index < 0 || 12 < index) { + throw new Error("index out of tehai length"); + } + const deletedTehai = state.tehai.filter((_, i) => i !== index); + const discardedHai = state.tehai[index]; + set(() => ({ + kyoku: state.kyoku + 1, + junme: state.junme + 1, + haiyama: state.haiyama.slice(1), + sutehai: [...state.sutehai, discardedHai], + tehai: sortTehai([...deletedTehai, tsumohai]), + tsumohai: state.haiyama[0], + })); + }, + tsumogiri: () => { + const state = get(); + if (!state.tsumohai) { + throw new Error("syohai"); + } + const tsumohai = state.tsumohai; + set(() => ({ + kyoku: state.kyoku + 1, + junme: state.junme + 1, + haiyama: state.haiyama.slice(1), + sutehai: [...state.sutehai, tsumohai], + tsumohai: state.haiyama[0], + })); + }, +})); diff --git a/rr/bun.lock b/rr/bun.lock index 16e659e..2ee8d63 100644 --- a/rr/bun.lock +++ b/rr/bun.lock @@ -13,6 +13,7 @@ "react": "^19.1.1", "react-dom": "^19.1.1", "react-router": "^7.9.2", + "zustand": "^5.0.8", }, "devDependencies": { "@cloudflare/vite-plugin": "^1.13.5", @@ -753,6 +754,8 @@ "zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], + "zustand": ["zustand@5.0.8", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], diff --git a/rr/package.json b/rr/package.json index 4ab5c67..8ce762d 100644 --- a/rr/package.json +++ b/rr/package.json @@ -20,7 +20,8 @@ "pg": "^8.16.3", "react": "^19.1.1", "react-dom": "^19.1.1", - "react-router": "^7.9.2" + "react-router": "^7.9.2", + "zustand": "^5.0.8" }, "devDependencies": { "@cloudflare/vite-plugin": "^1.13.5",