Skip to content

Yuuga2001/CircleTactics

Repository files navigation

CircleTactics

CircleTactics Logo

○×ゲームを進化させた、戦略ボードゲーム
A strategic board game evolved from Tic-Tac-Toe

Play Now

機能ルール技術スタックアーキテクチャセットアップ


関連プロジェクト | Related Projects

プラットフォーム リポジトリ 技術スタック
Web 版(本リポジトリ) CircleTactics React 18 + Vite、AWS Amplify + Lambda + DynamoDB
Android 版 circle-tactics-android React Native 0.81 + Expo SDK 54
iOS 版 circle-tactics-ios SwiftUI ネイティブ(iOS 16+)

オンライン対戦バックエンド(AWS Lambda + DynamoDB)は本リポジトリの infra/ が管理し、3 プラットフォームで共有。


概要 | Overview

CircleTactics は、古典的な○×ゲーム(Tic-Tac-Toe)に革新的なルールを追加した戦略ボードゲームです。

4×4マス3サイズのコマ (S / M / L)2種類の勝利条件最大4人対戦 で、より奥深い駆け引きと戦略性を実現しました。同一端末で 1〜4 人の対戦に加え、オンライン対戦(ルームコード or QRで合流)もサポート。空席は AI が自動で担当します。

CircleTactics is a strategic board game that adds modern rules to the classic Tic-Tac-Toe. Up to 4 players can play locally on the same device or online via a 6-digit room code / QR. A 4×4 board, 3 piece sizes, 2 win conditions, and AI-filled empty seats keep every match dynamic.


プレイ画像 | Play Screen

CircleTactics Screenshot


機能一覧 | Features

ゲームプレイ

  • 🎮 1〜4人マルチプレイ (ローカル + オンライン) — タイトル画面でモード選択。空席は AI が自動補充
  • 🌐 オンライン対戦 — ホストが 6 桁ルームコードを発行、参加者はコード入力 / QR スキャン / ?room=XXXXXX 共有 URL から合流
  • 🚀 途中参加 & 観戦キュー — ゲーム開始前・開始後を問わずルームに入室可能。4席が埋まっていれば自動的に観戦キューに並び、空きが出次第プレイヤーに昇格
  • 🔄 観戦者の自動昇格 — 切断プレイヤーの席が AI になると、待機中の観戦者が優先的に昇格(AI より観戦者を優先)。現在ターン中の席が空いた場合は、昇格した観戦者がそのままターンを引き継ぐ
  • 🎲 先攻決定ルーレット (全員同期) — スタート時・Play Again 時に色がぐるぐる回って先攻を決めるアニメーション。ホストだけでなく非ホスト・観戦者全員に表示
  • 🧠 戦略的 AI — 「勝てる手 → 相手をブロック → 有効手」の優先度で思考。サイズ選択 → 配置の 2 段階で動き、人間らしいテンポ
  • 30秒ターン制限 (オンラインのみ) — 残り秒数表示付き、超過すると駒を置かずに次のプレイヤーへ
  • 🏆 2種類の勝利条件 — マス内勝利 & 4マス1列勝利
  • 勝利マスのハイライト — 勝利条件を満たしたセルが白〜黄色のグロウで発光し、決め手が一目で分かる
  • 🔁 進行中ゲーム自動復帰 — タブを閉じても 6 時間以内ならルームに復帰可能。元いた席が AI のままなら同じ色に戻り、他プレイヤーが取っていれば別の AI 席に移る
  • 🤖 AI takeover — オンラインで切断したプレイヤーは 30 秒後に AI が引き継ぎ。観戦者がいる場合は AI ではなく観戦者が優先昇格
  • 🎨 3サイズのコマ — S / M / L(サイズ差を大きく取り、一目で区別可能)
  • 🤝 引き分け判定 — 全 16×3 スロットが埋まると自動的にドローでゲーム終了
  • 💡 有効マスハイライト — 現在のターンプレイヤーが置けるマスのみ枠がパルスで光る
  • ターン自動スキップ — 手札不足や置き場所がない場合は「SKIP」アニメーションを表示してターンを自動でパス

サウンド

  • 🎵 BGM — Web Audio API で合成した著作権フリーの 8 小節ループ BGM(Cメジャーペンタトニック、BPM 98)。デフォルトは ON
  • 🔊 効果音 (SE) — コマ置き(ポヨン)・選択・先攻発表・ルーレット・スキップ・勝利・引き分け、加えて 全ボタン共通のタップ音 までアクションごとに効果音。デフォルトは ON
  • ⚙️ メニューから即時切替 — 常時表示の「メニュー」ボタン(右上)から BGM / SE を ON/OFF トグル。設定は localStorage に保存

ユーザー体験

  • 🌐 15言語対応 — 日本語・英語・中国語(簡体/繁体)・韓国語・スペイン語・フランス語・ドイツ語・イタリア語・ポルトガル語・ロシア語・アラビア語・ヒンディー語・トルコ語・インドネシア語。左上の言語ボタンで即時切替
  • 📋 常時表示メニュー — 全画面の右上に「メニュー」ボタンを固定表示。タイトルへ戻る・新しいゲーム(ローカル中のみ)・BGM / SE 設定を一箇所に集約。観戦待機画面なども離脱動作はメニュー経由に統一されており、画面ごとの導線分岐がない
  • 👥 入室人数バッジ — ヘッダーのルームコード横に対戦者+観戦者の合計人数をリアルタイム表示
  • 📊 手札サマリ — 画面上部に全プレイヤーの残り駒数を一覧表示。[You] / [AI] バッジで役割が一目瞭然
  • 🎯 手札ハイライトは自分のターンのみ — 黄色のアクティブ枠・カウントダウン警告は自分のターンにのみ表示。他プレイヤーのターン中は残り秒数を小さく表示
  • 📱 レスポンシブデザイン — PC・タブレット・スマートフォン対応。盤面は手札表・手札ボードと常に同じ横幅を維持。ブラウザのナビゲーションバーが表示されてもスクロールで対応し盤面が縮まない
  • 🍞 トースト通知 + 押下フィードバック — API エラー等をトーストで明示。全ボタンに統一の押下感
  • 🔄 Play Again / Menu — 終了後にホスト権限不要で誰でも再開、またはタイトルへ戻る
  • 🎬 タイトル画面デモ盤面 — タイトル下部に AI 同士が自動対戦するデモ盤面を表示。ゲームのイメージを直感的に伝える

ゲームルール | Game Rules

プレイヤーとコマ

各プレイヤーは固定の色を持ち、S / M / L の3サイズの駒をそれぞれ4個ずつ(計12個)所持します。

プレイヤー 役割 (例: 1人モード) コマ数
🔴 RED あなた 各サイズ4個 = 12個
🔵 BLUE AI 各サイズ4個 = 12個
🟡 YELLOW AI 各サイズ4個 = 12個
🟢 GREEN AI 各サイズ4個 = 12個

プレイヤー数を増やすと色順 (RED → BLUE → YELLOW → GREEN) でプレイヤーが担当します。
ターン順はゲーム開始時に毎回ランダムでシャッフルされます。

コマのサイズ

L  ●●● (大)
M  ●●  (中)
S  ●   (小)

勝利条件

① Cell Win(マス内勝利)

1つのマス内に、同じプレイヤーの3サイズ全て (S / M / L) が揃う

② Board Win(ボード勝利)

盤面全体で、4つのマスが縦・横・斜めのいずれかに一列に揃う

ゲームの流れ

1. タイトルで Local Play / Online Play を選択
2. ローカル: プレイヤー数 (1〜4) を選択
   オンライン: Create Room でコード発行 → 共有 → Start
3. ルーレットでランダムに先攻プレイヤーが決定
4. ターンプレイヤーが S / M / L を選び、4×4 のマスをタップして配置
5. シャッフルされたターン順で次のプレイヤーへ
6. 勝利条件を満たしたプレイヤーの勝ち!決め手のマスが光って表示

技術スタック | Tech Stack

フロントエンド

カテゴリ 技術
フレームワーク React 18
言語 TypeScript 5
ビルドツール Vite 7
スタイリング CSS Modules + CSS Custom Properties
状態管理 (ローカル) React useReducer
状態管理 (オンライン) サーバ集中 + ポーリング (画面別 1.5〜3 秒間隔)
QR 生成 qrcode
デプロイ AWS Amplify Hosting

バックエンド (オンライン対戦用)

カテゴリ 技術
API Amazon API Gateway HTTP API
コンピュート AWS Lambda (Node.js 22)
データベース DynamoDB (TTL によるスライディング自動削除)
IaC AWS CDK (TypeScript)
共有ロジック tsconfig paths でフロントエンドの src/logic を直接 import

アーキテクチャ | Architecture

Browser (Vite SPA, Amplify Hosting)
   │
   │  HTTPS/JSON  (ポーリング 1.5〜3 秒 + アクション)
   ▼
API Gateway (HTTP API)  →  Lambda (gameHandler)  →  DynamoDB (TTL 2h)
                                  │
                                  └─ 共有純粋関数 (winConditions, ai, seating)
                                     を src/logic から esbuild バンドルで取込

ポーリング間隔は画面ごとに調整: ロビー待機は 1.5 秒・ホスト画面は 3 秒・対戦/観戦中は 2 秒(FINISHED 後は 5 秒に減速)。

  • サーバが 唯一の真実 (server authoritative) : 勝利判定 / ターン進行 / AI 着手をすべて Lambda で確定
  • 各 AI 着手は「サイズ選択 → 配置」の 2 ステップに分かれ、ポーリングごとに 1 ステップ進行 → ローカル版と同じ "考えてから置く" テンポ
  • 切断検出・AI 交代・観戦者昇格・host 移譲・ターンタイムアウトはすべて getGame ポーリングの副作用で実行されるため、追加スケジューラ不要
  • 観戦キュー: waitQueue[] を DynamoDB に保持。AI 席が発生した瞬間に先頭の観戦者を昇格(WAITING 中は空きスロットへ追加、PLAYING 中は AI 席を引き継ぎ)
  • 全員同期ルーレット: クライアントは startedAt の変化を監視し、ラウンド開始 / Play Again のたびに先攻決定ルーレットをリプレイ。ホスト・非ホスト・観戦者で表示が一致する

ディレクトリ構成

.
├── src/
│   ├── App.tsx                  # モード分岐 (Title / Local / Online lobby / OnlineGame / Spectator)
│   ├── components/
│   │   ├── TitleScreen.tsx       # Local / Online + プレイヤー数選択
│   │   ├── Game.tsx              # ローカル対戦
│   │   ├── OnlineGame.tsx        # オンライン対戦 (ポーリング + 楽観的更新)
│   │   ├── SpectatorView.tsx     # 観戦・待機キュー画面
│   │   ├── Board.tsx             # 4×4 盤面
│   │   ├── Cell.tsx              # セル (勝利ハイライト + :active 押下感)
│   │   ├── Piece.tsx             # 駒 (3 サイズ + 配置アニメ)
│   │   ├── PlayerHand.tsx        # 現ターンの手札 (S / M / L 選択)
│   │   ├── HandsSummary.tsx      # 全員の残り駒数表 + [You] / [AI] バッジ
│   │   ├── Toast.tsx             # 統一トースト通知
│   │   └── Lobby/
│   │       ├── LobbyScreen.tsx   # Create / Join 分岐
│   │       ├── HostScreen.tsx    # ルームコード + QR + Start
│   │       ├── JoinScreen.tsx    # コード入力 (URL からの自動入力対応)
│   │       └── WaitingRoom.tsx   # ゲスト待機画面 (席確定・ゲーム開始待ち)
│   ├── logic/
│   │   ├── gameReducer.ts        # ローカル状態遷移
│   │   ├── seating.ts            # 人数→人間プレイヤー割当 + ターン順シャッフル
│   │   ├── winConditions.ts      # 勝利判定 (種別 + 達成セル位置)
│   │   └── ai.ts                 # AI 思考エンジン
│   ├── online/
│   │   ├── api.ts                # fetch クライアント + エラー翻訳
│   │   ├── types.ts              # GameSession 型
│   │   ├── clientId.ts           # localStorage UUID
│   │   ├── activeGame.ts         # 進行中ゲームの保存・復帰 (gameId / roomCode / color)
│   │   ├── usePolling.ts         # ポーリング (1.5〜3 秒 / visibilitychange / FINISHED 減速)
│   │   └── useHeartbeat.ts       # 10秒毎 heartbeat
│   └── types/index.ts            # 共有型 (Player, GameState, WinInfo, ...)
└── infra/                       # AWS CDK (Lambda + DynamoDB + HTTP API)
    ├── bin/circletactics-infra.ts
    ├── lib/circletactics-stack.ts
    └── lambda/
        ├── gameHandler.ts        # ルーター + 観戦キュー昇格ロジック
        └── domain/
            ├── session.ts        # GameSession ドメイン型 + DDB 変換
            ├── moves.ts          # selectSize / placePiece
            ├── aiTakeover.ts     # AI 連鎖・ホスト移譲・ターンタイムアウト
            └── roomCode.ts       # 6 桁コード生成 + 衝突チェック

セットアップ | Getting Started

必要な環境

  • Node.js 18.0.0 以上
  • npm
  • (バックエンドを動かす場合) AWS CLI + AWS CDK

フロントエンド開発

# 1. リポジトリをクローン
git clone https://github.com/Yuuga2001/CircleTactics.git
cd CircleTactics

# 2. 依存関係をインストール
npm install

# 3. 環境変数を設定 (オンライン対戦を使う場合)
echo 'VITE_API_BASE_URL=https://hsqetnkx8k.execute-api.ap-northeast-1.amazonaws.com' > .env

# 4. 開発サーバを起動
npm run dev   # → http://localhost:5173

オンライン対戦機能を使わずローカル対戦のみ動かすなら .env は不要です。

テスト

npm run test            # unit / integration テスト (Vitest, ~470件)
npm run test:coverage   # カバレッジレポート生成
npm run test:e2e        # E2E テスト (Playwright, 4ファイル) ※開発サーバが必要

テストはテストピラミッド構成で、src/test/ 以下に配置されています。

src/test/
├── unit/          # ゲームロジック・API・オーディオなど純粋関数
├── integration/   # React コンポーネント・フック (Testing Library)
└── e2e/           # ブラウザ全体の E2E フロー (Playwright)

バックエンド (CDK) のデプロイ

cd infra
npm install
npm run synth                 # CloudFormation テンプレートの生成
npm run deploy:dev            # CircleTacticsStack-Dev をデプロイ
# 出力された ApiUrl をフロント側の .env / Amplify env に設定

利用可能なスクリプト

コマンド 説明
npm run dev 開発サーバを起動
npm run build tsc 型チェック (tsconfig.build.json、テストは除外) + Vite 本番ビルド
npm run preview ビルド結果をプレビュー
npm run lint ESLint でコードをチェック
npm run test unit / integration テストを実行 (Vitest)
npm run test:watch テストをウォッチモードで実行
npm run test:coverage カバレッジレポートを生成
npm run test:e2e E2E テストを実行 (Playwright)
npm run test:e2e:ui Playwright UI モードで実行
cd infra && npm run synth CDK シンセサイズ
cd infra && npm run deploy:dev Dev スタックのデプロイ
cd infra && npm run deploy:prod Prod スタックのデプロイ

Made with ❤️ using React + TypeScript + Vite + AWS CDK

About

CircleTactics is a strategic board game that adds innovative rules to the classic Tic-Tac-Toe.:CircleTacticsは、○×ゲームをベースにしたボードゲーム 。コマの「サイズ」という概念を導入し、 盤面全体での勝利・1マス内での勝利 という 2種類の勝利条件を持つ。

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages