React + Express.js で構築したオセロゲームです。
フロントエンドはReact Router v7、バックエンドはExpress.jsを使用しています。
- Node.js 18以上
- npm または yarn
- React 19: 最新のUIライブラリ
- React Router v7: SPAのルーティング(ファイルベースルーティング)
- TypeScript: 静的型付けによる開発体験向上
- Tailwind CSS v4: ユーティリティファーストなCSSフレームワーク
- Vite: 高速なビルドツールとHMR対応
- Express.js 4: Node.js用の軽量Webフレームワーク
- TypeScript: サーバーサイドでも静的型付け
- CORS: クロスオリジンリクエスト対応
- UUID: ゲームID生成
- tsx: TypeScript実行環境(開発用)
- Docker: アプリケーションのコンテナ化
- Docker Compose: マルチコンテナアプリケーションの管理
- serve: 静的ファイル配信(プロダクション)
dohack-example-express/
├── backend/ # Express.js APIサーバー
│ ├── src/
│ │ ├── server.ts # メインサーバーファイル
│ │ ├── othelloEngine.ts # ゲームロジック
│ │ └── types.ts # 型定義
│ ├── package.json
│ ├── tsconfig.json
│ ├── Dockerfile
│ └── .dockerignore
├── frontend/ # React アプリケーション
│ ├── app/
│ │ ├── routes/ # ページコンポーネント
│ │ │ ├── home.tsx # スタート画面
│ │ │ ├── game.tsx # ゲーム画面
│ │ │ └── result.tsx # 結果画面
│ │ ├── hooks/
│ │ │ └── useOthelloAPI.ts # API通信フック
│ │ ├── app.css
│ │ ├── root.tsx
│ │ └── routes.ts
│ ├── public/
│ │ └── favicon.ico
│ ├── package.json
│ ├── vite.config.ts
│ ├── tsconfig.json
│ ├── react-router.config.ts
│ ├── Dockerfile
│ └── .dockerignore
├── docker-compose.yml
├── .env.example
├── .dockerignore
└── README.md
Express.jsサーバーのメインファイル。7つのAPIエンドポイントを提供:
POST /api/games- 新しいゲーム作成GET /api/games/:gameId- ゲーム状態取得POST /api/games/:gameId/moves- プレイヤーの手POST /api/games/:gameId/computer-move- AIの手DELETE /api/games/:gameId- ゲーム削除GET /api/games- 全ゲーム一覧GET /health- ヘルスチェック
オセロゲームのコアロジック:
- ゲーム状態管理: Mapベースのメモリ内ストレージ
- ルール実装: 有効手判定、石の反転処理
- AI戦略: コーナー優先 → エッジ優先 → 最大取得数
TypeScript型定義:
export type Player = 1 | -1;
export type Board = (Player | 0)[][];
export interface GameState {
id: string;
board: Board;
currentPlayer: Player;
gameOver: boolean;
blackCount: number;
whiteCount: number;
createdAt: Date;
lastMove?: { row: number; col: number };
}React Router v7のファイルベースルーティング:
home.tsx: スタート画面(ゲーム説明とスタートボタン)game.tsx: メインゲーム画面(8x8ボード、AI対戦)result.tsx: 結果表示画面(勝敗と石数)
API通信を管理するカスタムフック:
export function useOthelloAPI() {
const [gameState, setGameState] = useState<GameState | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const initGame = useCallback(async () => { /* API呼び出し */ }, []);
const makeMove = useCallback(async (row: number, col: number) => { /* API呼び出し */ }, []);
return { gameState, loading, error, initGame, makeMove, /* ... */ };
}# バックエンドディレクトリに移動
cd backend
# 依存関係をインストール
npm install
# 開発サーバーを起動(ポート3001)
npm run dev# フロントエンドディレクトリに移動(新しいターミナル)
cd frontend
# 依存関係をインストール
npm install
# 開発サーバーを起動(ポート5173)
npm run dev- フロントエンド:
http://localhost:5173 - バックエンドAPI:
http://localhost:3001/health
# 開発環境で起動(デフォルト)
docker-compose up -d
# ログを確認
docker-compose logs -f
# 停止
docker-compose downアクセス先:
- フロントエンド:
http://localhost:5173 - バックエンドAPI:
http://localhost:3001
# プロダクション環境で起動
NODE_ENV=production FRONTEND_PORT=3000 docker-compose up -dアクセス先:
- フロントエンド:
http://localhost:3000 - バックエンドAPI:
http://localhost:3001
# .env ファイルを作成(オプション)
cp .env.example .env
# カスタム設定例
NODE_ENV=development
FRONTEND_PORT=5173# バックエンド
cd backend
npm run build
npm start
# フロントエンド
cd frontend
npm run build- 新しいゲーム作成: REST APIでゲーム状態を管理
- プレイヤー vs AI: 戦略的AIによる自動対戦
- リアルタイム更新: APIを通じた状態同期
- ゲーム履歴: 最後の手の記録
- コーナー優先: 角を取れる場合は最優先
- エッジ戦略: 端を取れる場合は次に優先
- 最大取得: 最も多くの石を取れる手を選択
- 状態管理: カスタムフックによるAPI状態管理
- エラーハンドリング: 通信エラーの適切な処理
- ローディング状態: 非同期処理中の表示
- レスポンシブデザイン: モバイル対応
// backend/src/server.ts
app.get('/api/stats', (req, res) => {
try {
const stats = othello.getGameStats();
res.json({ success: true, stats });
} catch (error) {
res.status(500).json({ success: false, error: 'エラーメッセージ' });
}
});現在はメモリ内で状態管理していますが、以下で永続化可能:
// 例: MongoDB連携
import { MongoClient } from 'mongodb';
class OthelloEngine {
private db: Db;
async createGame(): Promise<GameState> {
const game = { /* ゲーム状態 */ };
await this.db.collection('games').insertOne(game);
return game;
}
}frontend/app/routes/に新しい.tsxファイルを作成frontend/app/routes.tsにルートを追加
// frontend/app/routes.ts
export default [
index("routes/home.tsx"),
route("/game", "routes/game.tsx"),
route("/result", "routes/result.tsx"),
route("/settings", "routes/settings.tsx"), // 新しいページ
] satisfies RouteConfig;// frontend/app/hooks/useOthelloAPI.ts
const getGameStats = useCallback(async () => {
const response = await fetch(`${API_BASE_URL}/stats`);
const data = await response.json();
return data;
}, []);# ポートが使用中の場合
PORT=3002 npm run dev
# 依存関係を再インストール
rm -rf node_modules package-lock.json
npm install- CORS設定を確認:
backend/src/server.ts - API URL を確認:
frontend/app/hooks/useOthelloAPI.tsのAPI_BASE_URL - 両方のサーバーが起動しているか確認
# コンテナとイメージをクリーンアップ
docker-compose down
docker system prune -f
# 再ビルド
docker-compose up --build# TypeScript設定を確認
npm run typecheck
# 型定義を再生成
rm -rf node_modules package-lock.json
npm install- ユーザー認証機能
- ゲーム履歴の永続化(データベース)
- マルチプレイヤー対応
- リアルタイム通信(WebSocket)
- CI/CD パイプライン
- テストの追加(Jest, Cypress)
- パフォーマンス最適化
- セキュリティ強化
- クラウドデプロイ(AWS、GCP、Azure)
# バックエンド
cd backend
npm run dev # 開発サーバー起動
npm run build # ビルド
npm start # プロダクション起動
# フロントエンド
cd frontend
npm run dev # 開発サーバー起動
npm run build # ビルド
npm run typecheck # 型チェック
# Docker
docker-compose up -d # 開発環境起動
NODE_ENV=production FRONTEND_PORT=3000 docker-compose up -d # プロダクション起動
docker-compose down # 停止
docker-compose logs -f # ログ表示- 1つのdocker-compose.yml: 環境変数で開発/プロダクションを切り替え
- 1つのDockerfile: 各サービスで環境に応じて動作を変更
- othello-backend: Express.js APIサーバー(ポート3001)
- othello-frontend: React アプリ(開発:ポート5173、プロダクション:ポート3000)
# 開発環境(デフォルト)
docker-compose up -d
# プロダクション環境
NODE_ENV=production FRONTEND_PORT=3000 docker-compose up -dハッピーハッキング! 🚀