HTML / CSS / JavaScriptで三目並べのWebアプリを開発する。MVPの開発完了を第一目標にし、時間的猶予があれば機能拡張を行う。
| フェーズ | 区分 | 内容 |
|---|---|---|
| 1・2(MVP) | 必須 | 同一端末での2人対戦・勝敗/引き分け判定・リセット・スコア保存 |
| 3(拡張) | 任意 | レスポンシブ対応・CPU対戦モード |
| 対象外 | — | 別端末でのオンライン対戦(バックエンド・WebSocketが必要なため対象外) |
フェーズ1(Issue #1〜#5)
それぞれの担当を並行して進める
↓
ミーティング:インターフェース仕様を全員で確認する(Discord or )
↓
フェーズ2(Issue #6〜#9)
各自の成果物をつなぎ合わせてMVPを完成させる
↓
フェーズ3(Issue #10〜#13)
仕上げ・レスポンシブ対応・CPU対戦
| 担当者 | 主な役割 | 担当ファイル |
|---|---|---|
| A | HTML構造・CSSスタイリング全般 | index.html, base.css, board.css, controls.css |
| B | ゲームロジック・データ管理 | config.js, gameLogic.js, storage.js |
| C | UIコンポーネント・全体統合 | Square.js, Board.js, Controls.js, main.js |
フェーズ1はそれぞれ独立した内容なので、全員が同時進行可。フェーズ2に入るとBが作ったgameLogic.jsの関数をCが呼び出す場面が出てくるので、フェーズ1が終わったタイミングで一度集まって仕様を確認する。
- 3×3のマス目(9マス分)を
<div>や<button>で構造化する - ターン表示・勝者表示用のテキストエリアを配置する
- リセットボタンを配置する
- スコア表示エリア(Xの勝利数・Oの勝利数)を配置する
- CSS・JSは一切書かない(構造のみ)
- ブラウザのデフォルトスタイルをリセットする(margin・padding・box-sizing)
- フォント・文字色など全体共通のスタイルを定義する
- ページ全体を中央寄せするレイアウトを定義する
- 盤面サイズ(= 3)を定数として定義する
- 勝利条件(縦・横・斜めの8パターンのインデックス組み合わせ)を定義する
- プレイヤー名(
'X'・'O')を定数として定義する
checkWinner(board):盤面を受け取り勝者またはnullを返すisDraw(board):盤面を受け取り引き分けか判定するgetNextPlayer(current):現在のプレイヤーを受け取り次のプレイヤーを返す- 上記3関数を
exportする
Square.js:1マスを表すDOM要素を生成して返す関数を作るBoard.js:Square.jsを9個並べて盤面全体のDOMを生成する関数を作る- ゲームロジックとは接続せず、固定のダミーデータで描画だけ動ければOK
board.css:3×3グリッドレイアウトで盤面を整形するboard.css:マス目のサイズ・枠線・ホバー時のスタイルを定義するcontrols.css:リセットボタン・ターン表示・勝者表示・スコアのスタイルを定義する
saveScore(winner):勝者を受け取りLocalStorageのスコアを更新するloadScore():LocalStorageから現在のスコアを読み込んで返すresetScore():LocalStorageのスコアをリセットする- 上記3関数を
exportする
- ターンまたは勝者のメッセージを画面に反映する関数を作る
- スコアを画面に反映する関数を作る
- リセットボタンのクリックイベントを受け付けるコールバックを外部から渡せる設計にする
gameLogic.js・Board.js・Controls.js・storage.jsをimportする- ゲームの初期状態を作り、盤面を描画する
- マス目クリック時に
checkWinner・isDraw・getNextPlayerを呼び出してゲームを進行する - 勝者・引き分け確定時に
saveScoreを呼び出してスコアを更新・表示する - リセット時にゲーム状態を初期化して再描画する
- 各自の担当部分を結合した状態で通しプレイを行う
- 勝利・引き分け・リセット・スコア保存が正しく動くことを確認する
- ビューポートに応じて盤面サイズが変わるようCSSを調整する
- スマートフォン幅(375px以下)でも崩れないことを確認する
- ランダムな空きマスに置く簡易CPUを実装する
getCpuMove(board):盤面を受け取りCPUが置くインデックスを返す- 更に余裕があれば難易度別CPUも実装する
- 「2人対戦 / CPU対戦」を切り替えるモード選択UIを追加する
- CPU対戦選択時はプレイヤーのクリック後に
getCpuMoveを呼び出して自動で打つ
tic-tac-toe/
├── .github/ # PRテンプレート
├── .vscode/ # VS Code共通設定(保存時自動整形)
├── src/
│ ├── styles/
│ │ ├── base.css # リセット・全体共通スタイル → A
│ │ ├── board.css # 盤面・マス目のスタイル → A
│ │ └── controls.css # ボタン・ステータス表示のスタイル → A
│ ├── components/
│ │ ├── Square.js # 1マスの描画・クリックイベント → C
│ │ ├── Board.js # 盤面全体の描画 → C
│ │ └── Controls.js # リセットボタン・勝者/ターン表示 → C
│ ├── core/
│ │ ├── gameLogic.js # 勝敗判定・ターン管理 → B
│ │ └── storage.js # スコアのLocalStorage読み書き → B
│ ├── constants/
│ │ └── config.js # 盤面サイズ・勝利条件などの定数 → B
│ └── main.js # 全体の初期化・コンポーネント接続 → C
├── index.html # エントリーポイント → A
├── .prettierrc # コードフォーマット設定
├── .gitignore # バージョン管理非対象の定義
├── .eslintrc.json # Lintルール設定
├── package-lock.json # 依存関係のバージョンロック
└── package.json # 依存関係の定義(ESLint・Prettier)
CはBが作ったgameLogic.jsの関数を呼び出す形になるため、Bの実装が終わる前からCが作業を進められるよう、関数の名前・引数・戻り値だけをあらかじめ決めておく。中身の実装はBに任せてOK。
【現在の勝者を返す関数】
関数名 : checkWinner
引数 : board(マス目9個の配列。例:['X','O',null,'X',null,null,null,'O',null])
戻り値 : 勝者がXなら 'X'、Oなら 'O'、まだ決まっていなければ null
【引き分けを判定する関数】
関数名 : isDraw
引数 : board(同上)
戻り値 : 引き分けなら true、そうでなければ false
【次のプレイヤーを返す関数】
関数名 : getNextPlayer
引数 : current(現在のプレイヤー。'X'か'O')
戻り値 : 次のプレイヤー('X'なら'O'、'O'なら'X')
-
リポジトリをクローンする
git clone <https://github.com/(リポジトリURL)> -
依存パッケージをインストールする(ESLint・Prettierが入る)
cd tic-tac-toe npm install -
VS Codeで開くと推奨拡張機能のインストールを促されるので、全てインストールする(Prettier・ESLint・Live Server)
-
Live Serverの「Go Live」ボタンで
index.htmlを開いて表示されれば準備完了です
mainへの直接pushは禁止。必ずブランチを切ってPRを出す- ブランチ名の基本フォーマットは
feature/#Issue番号-作業内容(例:feature/#1-html-markup) - PRは他のメンバー1名以上にレビューしてもらってからマージする
- ファイル保存時に自動整形されるため手動フォーマット不要
- 連絡・進捗共有はDiscordで行う
- ブラウザはChromeで統一する