From f7fa4bfc9829537276516e802c197454c8161204 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Fri, 7 Nov 2025 12:19:00 +0800 Subject: [PATCH 1/9] create CLAUDE.md --- CLAUDE.md | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..47293d6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,181 @@ +# Hitori Mahjong - Project Architecture & Documentation + +This document provides a comprehensive analysis of the Hitori Mahjong web application, detailing its architecture, technology choices, and development practices. + +## Project Overview + +Hitori Mahjong is a modern web application that implements a single-player version of Mahjong. The project demonstrates a full-stack approach using cutting-edge web technologies with a focus on performance, developer experience, and scalability. + +## Architecture Philosophy + +The application follows a **full-stack React** approach, leveraging React Router v7's capabilities for both client-side and server-side rendering. This unified approach provides: + +- **Code sharing** between client and server +- **Type safety** throughout the entire stack +- **Performance optimization** through SSR and edge deployment +- **Developer experience** with hot reloading and modern tooling + +## Technology Stack + +### Core Framework +- **React Router v7**: Serves as both the frontend framework and backend runtime +- **Vite**: Build tool and development server +- **TypeScript**: Primary language for type safety and developer experience + +### Runtime & Deployment +- **Cloudflare Workers**: Edge runtime for serverless deployment +- **Bun**: Package manager and JavaScript runtime for development + +### Data Layer +- **Drizzle ORM**: Type-safe database interactions +- **Better Auth**: Authentication and session management +- **Redis**: Caching and session storage + +### Styling & UI +- **Tailwind CSS**: Utility-first CSS framework +- **daisyUI**: Component library built on Tailwind + +### Development Tools +- **Biome**: Fast linter and formatter +- **Lefthook**: Git hooks for code quality +- **Docker Compose**: Local development environment + +## Directory Structure Analysis + +### `/app` - Application Core +The heart of the application, following React Router v7 conventions: + +``` +app/ +├── root.tsx # Root application component +├── entry.server.tsx # SSR entry point +├── routes/ # File-based routing +│ ├── _index.tsx # Home page +│ ├── learn.tsx # Tutorial/learning interface +│ └── api.auth.$.ts # Authentication API endpoints +└── lib/ # Core business logic + ├── auth.ts # Server-side auth logic + ├── auth-client.ts # Client-side auth utilities + ├── hai.ts # Mahjong tile logic (牌) + ├── redis.ts # Cache layer + ├── db/ # Database layer + └── components/ # Reusable UI components +``` + +### `/public` - Static Assets +Organized by feature with Mahjong-specific assets: + +``` +public/ +├── hai/ # Mahjong tile images (牌) +├── tutorial/ # Educational content assets +├── background/ # UI background images +├── favicon.ico # Browser icon +└── logo.svg # Application branding +``` + +### `/workers` - Edge Runtime +Contains the Cloudflare Worker that serves the application at the edge. + +## Key Design Decisions + +### 1. Full-Stack React Architecture +- **Rationale**: Unified development experience with shared code between client and server +- **Benefits**: Type safety, reduced context switching, better performance through SSR +- **Trade-offs**: Learning curve for developers new to full-stack React + +### 2. Edge-First Deployment +- **Rationale**: Global performance and reduced latency +- **Benefits**: Fast response times worldwide, automatic scaling +- **Trade-offs**: Runtime limitations of edge environments + +### 3. File-Based Routing +- **Rationale**: Convention over configuration, reduced boilerplate +- **Benefits**: Intuitive project structure, automatic route generation +- **Trade-offs**: Less flexibility for complex routing patterns + +### 4. Type-Safe Database Layer +- **Rationale**: Prevent runtime errors, improve developer experience +- **Benefits**: Compile-time checks, better refactoring support, IntelliSense +- **Trade-offs**: Additional build-time complexity + +## Development Workflow + +### Local Development +1. **Environment Setup**: Docker Compose provides local services (likely database/Redis) +2. **Development Server**: Vite provides fast HMR and development experience +3. **Code Quality**: Biome handles linting and formatting +4. **Git Hooks**: Lefthook ensures code quality before commits + +### Deployment Pipeline +1. **CI/CD**: GitHub Actions handle automated testing and deployment +2. **Edge Deployment**: Wrangler deploys to Cloudflare Workers +3. **Database Migrations**: Drizzle handles schema changes + +## Performance Considerations + +### Client-Side Optimizations +- **Code Splitting**: Route-based chunking through React Router +- **Asset Optimization**: Vite handles bundling and minification +- **Caching**: Strategic use of Redis for frequently accessed data + +### Server-Side Optimizations +- **SSR**: Faster initial page loads and better SEO +- **Edge Deployment**: Reduced latency through global distribution +- **Database Queries**: Type-safe, optimized queries through Drizzle + +## Security Features + +### Authentication +- **Better Auth**: Modern, secure authentication system +- **Session Management**: Secure session handling with Redis backing +- **API Protection**: Authentication middleware for protected routes + +### Data Security +- **Type Safety**: Prevents many classes of runtime errors +- **Input Validation**: Handled through TypeScript interfaces and runtime checks +- **Environment Variables**: Secure configuration management + +## Mahjong-Specific Implementation + +### Tile System (`hai.ts`) +The core game logic is encapsulated in the `hai.ts` module, handling: +- Tile representation and validation +- Game state management +- Scoring algorithms +- Rule enforcement + +### Visual Assets +- **Tile Images**: Complete set of Mahjong tile graphics in `/public/hai/` +- **Tutorial Graphics**: Educational materials in `/public/tutorial/` +- **Responsive Design**: Tailwind CSS ensures mobile-friendly gameplay + +## Future Considerations + +### Scalability +- **Database**: Current setup supports horizontal scaling through Cloudflare D1 +- **Caching**: Redis layer can be expanded for complex caching strategies +- **CDN**: Static assets benefit from Cloudflare's global CDN + +### Feature Expansion +- **Multiplayer Support**: Architecture supports WebSocket integration for real-time gameplay +- **Analytics**: Event tracking can be added through the existing API layer +- **Internationalization**: React Router v7 supports i18n out of the box + +## Maintenance & Operations + +### Monitoring +- **Error Tracking**: Can be integrated through Cloudflare Workers analytics +- **Performance Monitoring**: Built-in metrics through Cloudflare dashboard +- **Uptime**: Edge deployment provides inherent high availability + +### Updates & Maintenance +- **Dependency Management**: Bun provides fast, reliable package management +- **Database Migrations**: Drizzle provides safe, versioned schema changes +- **Rollback Strategy**: Cloudflare Workers support instant rollbacks + +## Conclusion + +This project represents a modern, well-architected web application that leverages the latest in web development practices. The choice of React Router v7 as a full-stack framework, combined with edge deployment through Cloudflare Workers, provides an excellent foundation for a performant, scalable Mahjong game. + +The clean separation of concerns, type safety throughout the stack, and thoughtful tooling choices create a maintainable codebase that can evolve with changing requirements while providing an excellent user experience. \ No newline at end of file From d9b806215d961b19ec754abb4113ff21dbc220c8 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Fri, 7 Nov 2025 13:30:43 +0800 Subject: [PATCH 2/9] tmp --- .../src/utils => app/lib/hai}/judgeAgari.ts | 0 app/lib/{hai.ts => hai/utils.ts} | 0 app/lib/redis.ts | 4 +-- app/routes/play.agari.ts | 0 app/routes/play.ryukyoku.ts | 0 app/routes/play.tsx | 36 ++++++++++++++++++- 6 files changed, 37 insertions(+), 3 deletions(-) rename {old-workspaces/web/src/utils => app/lib/hai}/judgeAgari.ts (100%) rename app/lib/{hai.ts => hai/utils.ts} (100%) create mode 100644 app/routes/play.agari.ts create mode 100644 app/routes/play.ryukyoku.ts diff --git a/old-workspaces/web/src/utils/judgeAgari.ts b/app/lib/hai/judgeAgari.ts similarity index 100% rename from old-workspaces/web/src/utils/judgeAgari.ts rename to app/lib/hai/judgeAgari.ts diff --git a/app/lib/hai.ts b/app/lib/hai/utils.ts similarity index 100% rename from app/lib/hai.ts rename to app/lib/hai/utils.ts diff --git a/app/lib/redis.ts b/app/lib/redis.ts index e34464e..acf4905 100644 --- a/app/lib/redis.ts +++ b/app/lib/redis.ts @@ -1,6 +1,6 @@ import { createClient } from "redis"; -import type { Hai } from "./hai"; -import { sortTehai } from "./hai"; +import type { Hai } from "./hai/utils"; +import { sortTehai } from "./hai/utils"; export function getRedisClient(env: Env) { const client = createClient({ diff --git a/app/routes/play.agari.ts b/app/routes/play.agari.ts new file mode 100644 index 0000000..e69de29 diff --git a/app/routes/play.ryukyoku.ts b/app/routes/play.ryukyoku.ts new file mode 100644 index 0000000..e69de29 diff --git a/app/routes/play.tsx b/app/routes/play.tsx index 3794876..2259d24 100644 --- a/app/routes/play.tsx +++ b/app/routes/play.tsx @@ -3,7 +3,8 @@ import { Form } from "react-router"; import { getAuth } from "~/lib/auth"; import { getDB } from "~/lib/db"; import { hai, haiyama } from "~/lib/db/schema"; -import { dbHaiToHai, sortTehai } from "~/lib/hai"; +import judgeAgari from "~/lib/hai/judgeAgari"; +import { dbHaiToHai, sortTehai } from "~/lib/hai/utils"; import { type GameState, getRedisClient, init } from "~/lib/redis"; import type { Route } from "./+types/play"; @@ -73,6 +74,9 @@ export async function loader({ export default function Page({ loaderData }: Route.ComponentProps) { let { haiyama, sutehai, tsumohai, junme, kyoku, tehai } = loaderData; tehai = sortTehai(tehai); + const isAgari = + tehai && tsumohai ? judgeAgari(sortTehai([...tehai, tsumohai])) : false; + const isRyukyoku = junme === 19; const indexedSutehai = sutehai.map((hai, index) => ({ ...hai, index: index, @@ -84,6 +88,36 @@ export default function Page({ loaderData }: Route.ComponentProps) { return (
+ {isAgari && ( + +
+

ツモ!

+
+
+ + +
+
+
+
+ )} + {isRyukyoku && ( + +
+

流局

+
+
+ +
+
+
+
+ )} +

Play Page - 局 {kyoku} 巡目 {junme}

From 24c5a4ac55ad002090461715cea4e6e43b264b67 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Mon, 24 Nov 2025 12:24:10 +0800 Subject: [PATCH 3/9] Delete CLAUDE.md --- CLAUDE.md | 181 ------------------------------------------------------ 1 file changed, 181 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 47293d6..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,181 +0,0 @@ -# Hitori Mahjong - Project Architecture & Documentation - -This document provides a comprehensive analysis of the Hitori Mahjong web application, detailing its architecture, technology choices, and development practices. - -## Project Overview - -Hitori Mahjong is a modern web application that implements a single-player version of Mahjong. The project demonstrates a full-stack approach using cutting-edge web technologies with a focus on performance, developer experience, and scalability. - -## Architecture Philosophy - -The application follows a **full-stack React** approach, leveraging React Router v7's capabilities for both client-side and server-side rendering. This unified approach provides: - -- **Code sharing** between client and server -- **Type safety** throughout the entire stack -- **Performance optimization** through SSR and edge deployment -- **Developer experience** with hot reloading and modern tooling - -## Technology Stack - -### Core Framework -- **React Router v7**: Serves as both the frontend framework and backend runtime -- **Vite**: Build tool and development server -- **TypeScript**: Primary language for type safety and developer experience - -### Runtime & Deployment -- **Cloudflare Workers**: Edge runtime for serverless deployment -- **Bun**: Package manager and JavaScript runtime for development - -### Data Layer -- **Drizzle ORM**: Type-safe database interactions -- **Better Auth**: Authentication and session management -- **Redis**: Caching and session storage - -### Styling & UI -- **Tailwind CSS**: Utility-first CSS framework -- **daisyUI**: Component library built on Tailwind - -### Development Tools -- **Biome**: Fast linter and formatter -- **Lefthook**: Git hooks for code quality -- **Docker Compose**: Local development environment - -## Directory Structure Analysis - -### `/app` - Application Core -The heart of the application, following React Router v7 conventions: - -``` -app/ -├── root.tsx # Root application component -├── entry.server.tsx # SSR entry point -├── routes/ # File-based routing -│ ├── _index.tsx # Home page -│ ├── learn.tsx # Tutorial/learning interface -│ └── api.auth.$.ts # Authentication API endpoints -└── lib/ # Core business logic - ├── auth.ts # Server-side auth logic - ├── auth-client.ts # Client-side auth utilities - ├── hai.ts # Mahjong tile logic (牌) - ├── redis.ts # Cache layer - ├── db/ # Database layer - └── components/ # Reusable UI components -``` - -### `/public` - Static Assets -Organized by feature with Mahjong-specific assets: - -``` -public/ -├── hai/ # Mahjong tile images (牌) -├── tutorial/ # Educational content assets -├── background/ # UI background images -├── favicon.ico # Browser icon -└── logo.svg # Application branding -``` - -### `/workers` - Edge Runtime -Contains the Cloudflare Worker that serves the application at the edge. - -## Key Design Decisions - -### 1. Full-Stack React Architecture -- **Rationale**: Unified development experience with shared code between client and server -- **Benefits**: Type safety, reduced context switching, better performance through SSR -- **Trade-offs**: Learning curve for developers new to full-stack React - -### 2. Edge-First Deployment -- **Rationale**: Global performance and reduced latency -- **Benefits**: Fast response times worldwide, automatic scaling -- **Trade-offs**: Runtime limitations of edge environments - -### 3. File-Based Routing -- **Rationale**: Convention over configuration, reduced boilerplate -- **Benefits**: Intuitive project structure, automatic route generation -- **Trade-offs**: Less flexibility for complex routing patterns - -### 4. Type-Safe Database Layer -- **Rationale**: Prevent runtime errors, improve developer experience -- **Benefits**: Compile-time checks, better refactoring support, IntelliSense -- **Trade-offs**: Additional build-time complexity - -## Development Workflow - -### Local Development -1. **Environment Setup**: Docker Compose provides local services (likely database/Redis) -2. **Development Server**: Vite provides fast HMR and development experience -3. **Code Quality**: Biome handles linting and formatting -4. **Git Hooks**: Lefthook ensures code quality before commits - -### Deployment Pipeline -1. **CI/CD**: GitHub Actions handle automated testing and deployment -2. **Edge Deployment**: Wrangler deploys to Cloudflare Workers -3. **Database Migrations**: Drizzle handles schema changes - -## Performance Considerations - -### Client-Side Optimizations -- **Code Splitting**: Route-based chunking through React Router -- **Asset Optimization**: Vite handles bundling and minification -- **Caching**: Strategic use of Redis for frequently accessed data - -### Server-Side Optimizations -- **SSR**: Faster initial page loads and better SEO -- **Edge Deployment**: Reduced latency through global distribution -- **Database Queries**: Type-safe, optimized queries through Drizzle - -## Security Features - -### Authentication -- **Better Auth**: Modern, secure authentication system -- **Session Management**: Secure session handling with Redis backing -- **API Protection**: Authentication middleware for protected routes - -### Data Security -- **Type Safety**: Prevents many classes of runtime errors -- **Input Validation**: Handled through TypeScript interfaces and runtime checks -- **Environment Variables**: Secure configuration management - -## Mahjong-Specific Implementation - -### Tile System (`hai.ts`) -The core game logic is encapsulated in the `hai.ts` module, handling: -- Tile representation and validation -- Game state management -- Scoring algorithms -- Rule enforcement - -### Visual Assets -- **Tile Images**: Complete set of Mahjong tile graphics in `/public/hai/` -- **Tutorial Graphics**: Educational materials in `/public/tutorial/` -- **Responsive Design**: Tailwind CSS ensures mobile-friendly gameplay - -## Future Considerations - -### Scalability -- **Database**: Current setup supports horizontal scaling through Cloudflare D1 -- **Caching**: Redis layer can be expanded for complex caching strategies -- **CDN**: Static assets benefit from Cloudflare's global CDN - -### Feature Expansion -- **Multiplayer Support**: Architecture supports WebSocket integration for real-time gameplay -- **Analytics**: Event tracking can be added through the existing API layer -- **Internationalization**: React Router v7 supports i18n out of the box - -## Maintenance & Operations - -### Monitoring -- **Error Tracking**: Can be integrated through Cloudflare Workers analytics -- **Performance Monitoring**: Built-in metrics through Cloudflare dashboard -- **Uptime**: Edge deployment provides inherent high availability - -### Updates & Maintenance -- **Dependency Management**: Bun provides fast, reliable package management -- **Database Migrations**: Drizzle provides safe, versioned schema changes -- **Rollback Strategy**: Cloudflare Workers support instant rollbacks - -## Conclusion - -This project represents a modern, well-architected web application that leverages the latest in web development practices. The choice of React Router v7 as a full-stack framework, combined with edge deployment through Cloudflare Workers, provides an excellent foundation for a performant, scalable Mahjong game. - -The clean separation of concerns, type safety throughout the stack, and thoughtful tooling choices create a maintainable codebase that can evolve with changing requirements while providing an excellent user experience. \ No newline at end of file From 80d4073ab7a9f3bc6272c628037a865304976c80 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Mon, 24 Nov 2025 12:24:27 +0800 Subject: [PATCH 4/9] fix import path --- app/lib/hai/judgeAgari.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/hai/judgeAgari.ts b/app/lib/hai/judgeAgari.ts index 7a0b590..ec31626 100644 --- a/app/lib/hai/judgeAgari.ts +++ b/app/lib/hai/judgeAgari.ts @@ -1,4 +1,4 @@ -import { type Hai, haiToIndex } from "shared/hai.ts"; +import { type Hai, haiToIndex } from "./utils"; function deleteSyuntsu(remainingTehai: TehaiIndex): number { let extractCount = 0; From eca81edfd3c5f3f2ba48ecf20086b17c50c1a775 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Mon, 24 Nov 2025 12:24:43 +0800 Subject: [PATCH 5/9] delete redis connection check --- app/routes/_index.tsx | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 6443792..91c8397 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,30 +1,11 @@ -import { drizzle as drizzleNeon } from "drizzle-orm/neon-http"; -import { drizzle as drizzlePg } from "drizzle-orm/node-postgres"; -import type { c } from "node_modules/better-auth/dist/shared/better-auth.C9FmyZ5W.cjs"; import { Link, useNavigate } from "react-router"; import { authClient } from "~/lib/auth-client"; -import { getRedisClient } from "~/lib/redis"; -import type { Route } from "./+types/_index"; - -export async function loader({ context }: Route.LoaderArgs) { - const { env } = context.cloudflare; - const db = - env.NODE_ENV === "development" - ? drizzlePg(env.DATABASE_URL) - : drizzleNeon(env.DATABASE_URL); - const redisClient = getRedisClient(env); - redisClient.on("error", (err) => console.log("Redis Client Error", err)); - await redisClient.connect(); - await redisClient.set("key", "value"); - const value = await redisClient.get("key"); - console.log(value); -} export default function Page() { const navigate = useNavigate(); const anonymousLoginAndStart = async () => { const user = await authClient.getSession(); - if (!user) { + if (!user.data) { const _user = await authClient.signIn.anonymous(); } navigate("/play"); From 00241f7a047702e76fdd9d8b4300843b2783665d Mon Sep 17 00:00:00 2001 From: tknkaa Date: Mon, 24 Nov 2025 12:56:48 +0800 Subject: [PATCH 6/9] install redis cli --- .envrc | 1 + .gitignore | 2 ++ flake.lock | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 20 ++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index cd15247..3f0fdbb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ worker-configuration.d.ts !.dev.vars.example !.env.example + +.direnv diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6967c77 --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1763806073, + "narHash": "sha256-FHsEKDvfWpzdADWj99z7vBk4D716Ujdyveo5+A048aI=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "878e468e02bfabeda08c79250f7ad583037f2227", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..b7ebe54 --- /dev/null +++ b/flake.nix @@ -0,0 +1,20 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.default = pkgs.mkShell { + packages = with pkgs; [ redis ]; + }; + } + ); +} From 760c134a35247bedc71a909e60048c2cfa5af9a6 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Mon, 24 Nov 2025 12:57:09 +0800 Subject: [PATCH 7/9] fix anonymous login logic --- app/routes/_index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 91c8397..2dfbe27 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -5,9 +5,10 @@ export default function Page() { const navigate = useNavigate(); const anonymousLoginAndStart = async () => { const user = await authClient.getSession(); - if (!user.data) { - const _user = await authClient.signIn.anonymous(); + if (user.data) { + const _user = await authClient.signOut(); } + const _user = await authClient.signIn.anonymous(); navigate("/play"); }; return ( From dc3841e4d33d3399f6c514b7b0e9b2e4d2622d93 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Tue, 25 Nov 2025 16:53:23 +0800 Subject: [PATCH 8/9] fix index bug --- app/lib/redis.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/lib/redis.ts b/app/lib/redis.ts index acf4905..0df3ca0 100644 --- a/app/lib/redis.ts +++ b/app/lib/redis.ts @@ -73,8 +73,9 @@ export const tedashi = async ( 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]; + const sortedTehai = sortTehai(state.tehai); + const deletedTehai = sortedTehai.filter((_, i) => i !== index); + const discardedHai = sortedTehai[index]; const newGameState: GameState = { ...state, From 8886462b2b5e9032aa4e7ee6c17ab651833774c6 Mon Sep 17 00:00:00 2001 From: tknkaa Date: Tue, 25 Nov 2025 17:01:25 +0800 Subject: [PATCH 9/9] display dialog --- app/routes/play.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/routes/play.tsx b/app/routes/play.tsx index 2259d24..bab529f 100644 --- a/app/routes/play.tsx +++ b/app/routes/play.tsx @@ -89,7 +89,7 @@ export default function Page({ loaderData }: Route.ComponentProps) { return (
{isAgari && ( - +

ツモ!

@@ -104,7 +104,7 @@ export default function Page({ loaderData }: Route.ComponentProps) {
)} {isRyukyoku && ( - +

流局