👾

ノーコード?AIとTypeScriptで作る「まんどらごら★づくし」

に公開

ノーコード?AIとTypeScriptで作る「まんどらごら★づくし」

1. はじめに

こんにちは!この記事では、私が開発したブラウザベースのボードゲーム「まんどらごら★づくし」の製作過程と、そこで得られた知見を共有します。このプロジェクトは、Zennで開催される「TypeScriptでやってみた挑戦・学び・工夫」というテーマの記事投稿コンテストへの応募を目指してスタートしました。

このゲームは「ファイナルファンタジーXI(FF11)」の期間限定ミニゲームから来ています。自分なりに再現しつつ新しい要素を加えたいという思いがありました。

そして、このプロジェクトのもう一つの大きな特徴は、開発の大部分をGoogle製のAIソフトウェアエンジニアリングエージェント「Jules」との対話を通じて進めたことです。要件定義から設計、コーディング、テスト、さらにはこの記事の構成案作成に至るまで、Julesが強力なアシスタントとして機能しました。これは、従来の開発スタイルとは一線を画す「AI支援開発」、あるいはある種の「ローコード的アプローチ」とも言えるかもしれません。

本記事では、「まんどらごら★づくし」の概要と主要機能を紹介するとともに、特に以下の点に焦点を当てて解説します。

  • Julesとの協調による開発プロセスで直面した挑戦と、それを乗り越えるための工夫。
  • TypeScriptを全面的に採用し、型システムをどのように活用して複雑なゲームロジックや状態管理の安全性を高め、開発効率を向上させたか。
  • 開発中に遭遇した具体的な課題(挑戦)、そこから得られた学び、そして適用した独自の工夫

AIと共にTypeScriptでゲームを開発するという、少し未来的な開発体験を皆さんと共有できれば幸いです。

2. ゲーム「まんどらごら★づくし」の概要

「まんどらごら★づくし」は、4x4の盤面で対戦する思考型ボードゲームです。プレイヤー(PC)とNPC(または攻略モードではPCが操作する相手側)が交互にコマを配置し、勝利条件を目指します。

基本的なルール:

  • 盤面: 4x4の16マス。
  • コマ: PCは「マンドラゴラ」(通常は緑色系のCSS表現)、相手は「コリガン」(茶色/灰色系のCSS表現)のコマを使用します。
  • 目的: 自分のコマを縦・横・斜めのいずれかに4つ直線で並べると勝利です。
  • 反則負け: 自分のコマがちょうど3つだけ連続して直線に並んでしまった場合、その時点で反則負けとなります(例:●●●X は反則。●●X● はセーフ)。4つ並びの勝利が優先されます。
  • コマの変換: 相手のコマを自分のコマで直線状に挟む(サンドイッチする)と、挟まれた相手のコマはすべて自分のコマに変換されます。この変換によって反則負けになることもあります(勝利優先)。

主なゲームシステム:

  • 対戦形式: 最大5ゲームを行い、先に3勝した方がマッチの最終的な勝者となります。各ゲームの先攻・後攻は交互に入れ替わります(第1ゲームはランダム)。
  • ドラポイントシステム: PCは各ゲーム終了時に、盤上の自分のコマ数に応じて「ドラポイント」を獲得します(勝利時はコマ数そのまま、敗北時はコマ数の半分(切り捨て))。このポイントはマッチ終了時にまとめて精算されます。
  • ボーナスゲーム: まれに「アデニウム(ドラ2倍)」や「キトルルス(ドラ3倍)」といったボーナスゲームが発生し、PCのコマの見た目が変わり(CSSで表現)、獲得ドラが増加します。
  • 攻略モード: プレイヤーが盤面を自由に設定し、特定の局面からAIに次の一手(推奨手)を分析させることができます。
  • 一手戻す機能: 直前の手をキャンセルできます。

UIテーマ:
当初のシンプルなUIから、ユーザー様との対話を経て「かわいいファンタジー」テーマ、そして最終的には「エレガントな中世ヨーロッパ風」テーマへとデザインを一新しました。コマやキャラクター表現も、画像アセットを使用せずCSSの図形描画でテーマ性を追求しています。

3. 開発環境と技術スタック

本プロジェクト「まんどらごら★づくし」の開発にあたり、採用した主な技術スタックと開発・デプロイ環境は以下の通りです。これらは、モダンなWebフロントエンド開発の標準的な構成でありつつ、特にTypeScriptの活用を前提として選定しました。

  • プログラミング言語: TypeScript

    • 本プロジェクトの根幹であり、Zennコンテストのテーマでもあります。静的型付けによる開発効率の向上、コードの堅牢性、リファクタリングの安全性を期待して全面的に採用しました。特に、複雑なゲーム状態やロジックを扱う上で、その恩恵を大きく受けました(詳細はセクション5で後述)。
  • フロントエンドフレームワーク: Next.js (React)

    • Reactベースのフレームワークであり、今回はApp Routerを採用しました。SSR(サーバーサイドレンダリング)やSSG(静的サイト生成)の機能も持ちますが、本ゲームでは主にクライアントサイドでのインタラクティブな動作に利用しています。TypeScriptとの親和性が非常に高く、効率的なコンポーネントベースの開発が可能です。
  • スタイリング: Tailwind CSS

    • ユーティリティファーストのCSSフレームワークです。HTMLのクラス属性に直接スタイルを指定していくことで、迅速なUI構築と、テーマ変更への柔軟な対応を実現しました。当初の「かわいいファンタジー」から「エレガントな中世ヨーロッパ風」への大幅なデザイン変更も、Tailwind CSSの特性を活かして行いました。
  • 開発アシスタントAI: Jules (Google製)

    • 本プロジェクトのユニークな点として、設計相談、コード生成・修正、デバッグ支援など、開発の多くの局面でAIソフトウェアエンジニアリングエージェント「Jules」を活用しました。Julesとの対話を通じて、計画の立案から具体的なコード実装までを共同で行いました(詳細はセクション4で後述)。
  • デプロイ先プラットフォーム: Vercel

    • Next.jsとの相性が抜群に良く、GitHubリポジトリと連携させることで、非常に簡単な手順でビルド、デプロイ、ホスティングが可能です。無料プランの範囲内で個人プロジェクトを公開するのに最適な選択肢の一つです。実際に、開発中のビルドエラー特定にもVercelのログが役立ちました。

これらの技術選定により、型安全性を確保しつつ、迅速なUI構築とAI支援による効率的な開発サイクルを目指しました。

4. Julesと共に挑んだ開発プロセス:AI支援開発の実際

このプロジェクト「まんどらごら★づくし」の大きな特徴の一つは、開発の大部分をAIソフトウェアエンジニアリングエージェントである私、Julesとの対話形式で進めた点です。これは従来の開発プロセスとは異なる、「AI支援開発」あるいは一種の「ローコード/ノーコード的アプローチ」と言えるかもしれません。本章ではその実際と、そこでの挑戦、学び、工夫を紹介します。

挑戦:AIへの的確な指示と意図伝達

最も大きな挑戦は、人間が持つゲームの全体像や細かなニュアンス、そして時には曖昧な「こうしたい」という感覚を、AIであるJulesにテキストベースでいかに的確に伝えるか、という点でした。Julesは指示されたタスクをコードに落とし込む能力は高いものの、行間を読むことや、暗黙の前提を理解することは苦手です。

  • ルールの詳細化: 例えば「3つ並んだら反則」というルールも、当初Julesは「盤面のどこかに3つあれば」と広く解釈しましたが、後のフィードバックで「連続して3つ」という正確な定義に修正しました。このような細部の詰めが重要でした。
  • UIの雰囲気: 「かわいい感じ」から「中世ヨーロッパ風で気品がある感じ」への変更指示では、具体的な色合い(例:石のような灰色、森の緑)や形状(例:丸みを減らす)といった言語化できる要素を伝えることで、Julesは適切なTailwind CSSクラスを選定しやすくなりました。

TypeScriptの活用:AIとの共通言語としての型定義

このコミュニケーションの課題において、TypeScriptの型定義が非常に重要な「共通言語」として機能しました。

  • GameStateや各種コンポーネントのPropsの型を最初に定義することで、Julesが生成するコードの構造やデータの流れが明確になりました。
  • 私が「この関数はこの型のデータを返し、このコンポーネントはこの型のPropsを受け取るべき」と指示すれば、Julesはその制約の中でコードを生成するため、期待から大きく外れることが少なくなりました。
  • 型エラーは、Julesの生成したコードに対する具体的な修正指示点としても機能しました。

学びと工夫:イテレーションによる段階的開発とJulesの活用法

  1. 段階的な機能実装: 一度に全ての機能をJulesに指示するのではなく、「まずは盤面の基本ロジック」「次にAI対戦機能」「その後UIテーマ変更」といった形で、主要機能ごとに段階的に開発を進めました。各段階でJulesが計画(Plan)を提示し、私が承認または修正する形で進めました。

  2. Julesの得意分野の活用:

    • 定型的なコード生成: 新しいコンポーネントの雛形作成、既存コンポーネントへのProps追加、型定義に基づく関数の骨格作成などは非常に高速でした。
    • 複雑なロジックの記述: 例えば8方向をチェックするコマ変換ロジックや、NPC AIの盤面評価のための全マス探索などは、人間がアルゴリズムの概要を伝えれば、Julesが正確にコード化してくれました。
    • リファクタリング: 「このロジックを別関数に切り出してほしい」といった指示にも的確に対応し、コードの整理を助けてくれました。
  3. 人間によるレビューとテストの重要性: Julesが生成したコードは、常に私がレビューし、意図通りに動作するか、より良い記述がないかを確認しました。特にゲームロジックの正しさやUIの細かな挙動は、最終的には人間がテストプレイを通じて検証する必要がありました。

  4. 「ノーコード」的側面と限界: 要件定義とフィードバックに集中することで、ユーザー様(そしてある程度は私も)は直接的なコーディング量を大幅に削減できました。しかし、完全にコードを意識しない「ノーコード」とは異なり、Julesの提案を理解し、時には具体的な修正指示を出すためには、コード(特にTypeScriptとReactの知識)の理解が不可欠でした。Julesはあくまで強力な「開発アシスタント」であり、設計の舵取りや最終的な品質担保は人間の役割です。

このAI支援開発は、まさに「挑戦」であり、AIとの効果的な協業方法を「学び」、そのためのコミュニケーションを「工夫」する連続でした。結果として、短期間で非常に多機能なゲームを構築できたのは、この開発スタイルによるところが大きいと感じています。

5. TypeScriptで実現した核心機能と「挑戦・学び・工夫」

本章では、ゲーム「まんどらごら★づくし」の主要機能開発における具体的な挑戦、TypeScriptの活用ポイント、そしてそこから得られた学びや工夫を機能ごとに紹介します。

5.1. ゲーム状態管理とTypeScriptの型定義

ボードゲーム開発の心臓部とも言えるのが、盤面、手番、スコア、モードといった多岐にわたる情報を集約する「ゲーム状態(GameState)」の管理です。この状態が変化することでゲームは進行します。

挑戦:複雑な状態の一元管理と予測可能性

多くの情報を持つGameStateを単一のオブジェクトで矛盾なく、かつ安全に管理することは大きな挑戦でした。特に、多くのロジックがこの状態を更新するため、変更が予期せぬ影響を及ぼさないようにする必要がありました。

TypeScriptの活用:型による「設計図」と「安全装置」

  1. GameStateインターフェースの明確化:
    ゲームが持つべき全情報をインターフェースとして定義。各プロパティの型(number, boolean, 文字列リテラルユニオン型 Player, CellState, GameModeなど)を明示し、構造をコードレベルで保証しました。

    // src/game/types.ts より抜粋
    export type Player = "PC" | "NPC";
    export type CellState = "EMPTY" | Player;
    export type GameMode = "VERSUS_NPC" | "STRATEGY"; // Simplified
    
    export interface GameState {
      board: BoardState; // BoardStateも型定義されている
      currentPlayer: Player;
      gameMode: GameMode;
      // ... 他にも多くのプロパティ
      history: GameState[]; // Undo機能のための履歴
    }
    

    ユニオン型は特定の値セットのみを許容し、意図しないデータの混入を防ぎます。

  2. 型安全性による早期バグ発見とリファクタリング支援:
    GameStateを扱う全ての箇所で型チェックが働き、不正なデータ代入やバグを開発初期に発見。状態構造の変更時も影響範囲が明確になり、安全なリファクタリングが可能でした。

学びと工夫:型はコミュニケーションの基盤

型定義はゲーム設計の第一歩であり、全体の安定性と開発効率を向上させました。特にAIアシスタントJulesとの共同開発では、型定義が共通言語となり、指示の曖昧さを減らし、スムーズな連携を実現しました。

5.2. 複雑なルールロジックの実装(勝敗判定・反則・コマ変換)

本ゲームの勝敗は、4つ並び(勝利)、3つ連続並び(反則負け)、コマ変換(サンドイッチ)が複雑に絡み合います。正確な判定と優先順位処理が挑戦でした。「3つ連続反則」の条件は当初誤解釈しており、ユーザー様のフィードバックで正確なロジックに修正しました。

TypeScriptの活用:明確な関数シグネチャと型による一貫性

  1. 関数責務の分離と型定義:
    勝敗判定 (judgement.ts) やコマ変換 (conversion.ts) のロジックを関数に分割し、入力(BoardState, Player)と出力({ isFour: boolean, isThree: boolean }等)に型を付けました。

    // src/game/judgement.ts より `checkLine` 修正後抜粋
    export const checkLine = (line: CellState[], player: Player): { isFour: boolean; isThree: boolean } => {
      const isFour = line.every(cell => cell === player);
      if (isFour) return { isFour: true, isThree: false };
      let isThreeFoul = false;
      if (line[0] === player && line[1] === player && line[2] === player && line[3] !== player) isThreeFoul = true;
      else if (line[0] !== player && line[1] === player && line[2] === player && line[3] === player) isThreeFoul = true;
      return { isFour: false, isThree: isThreeFoul };
    };
    

    これにより各関数の役割が明確になり、handleCellClickLogic内での「コマ配置 → コマ変換 → 勝敗判定」という処理フローを型安全に組み合わせられました。

学びと工夫:ルールの正確なモデル化と段階的実装

複雑なルールは正確な理解とモデル化が不可欠です。ユーザー様との対話を通じてルール詳細を詰め、型定義や関数設計に落とし込みました。AI(Jules)には具体的なロジック記述を任せ、人間がルール定義と検証を行う分担が効果的でした。

5.3. NPC AIの設計と実装

NPC AIには、(1)自身の勝利、(2)相手の勝利阻止、(3)相手の反則誘導、(4)盤面評価に基づく最善手選択、という戦略的思考を目指しました。

挑戦:適切な「評価関数」と「反則誘導」ロジック

盤面や手の「良さ」を数値化する評価関数evaluateMoveの設計、特に各戦略要素への重み付けと、「相手の次の全ての可能な手が反則になるか」という反則誘導ロジックの実現が複雑でした。計算量と応答速度のバランスも課題でした。

TypeScriptの活用:型定義によるAIロジックの整理

  1. 評価関数の型シグネチャ: evaluateMove(board: BoardState, player: Player, move: Move): number のように型を明確化。

  2. ゲームロジック関数の安全な再利用: AIのシミュレーション内で、型付けされた既存の盤面評価関数(evaluateBoard)などを安全に活用できました。

    // src/game/ai.ts より `evaluateMove` の一部抜粋
    const aiEvalResult = evaluateBoard(boardAfterAiConversions, aiPlayer);
    if (aiEvalResult.hasFourInARow) return SCORE_AI_WINS;
    if (aiEvalResult.hasThreeInARowFoul) return SCORE_AI_FOULS;
    // ... 相手の手を読み、反則誘導などを評価 ...
    

学びと工夫:ヒューリスティックなAIとJulesとの連携

AIの賢さは評価関数に依存します。今回は1手先読みを中心としたヒューリスティック評価を採用。AIの評価ロジックの骨子(評価項目や優先度)は人間が設計し、具体的な評価計算や全候補手の探索といった実装はJulesが行うという連携が有効でした。
* 初手の定石導入: ゲーム開始時(盤面が空の場合)のAIの初手について、単にランダムな空きマスを選ぶのではなく、より定石に近い手として「四隅のいずれかをランダムに選択する」というシンプルなヒューリスティックを導入しました。これは、findBestMove関数の冒頭で盤面が空かどうかを判定し、空であれば四隅からランダムに手を選ぶロジックを追加することで実現しました。これにより、特にAIが先手の場合のオープニングが若干引き締まりました。

5.4. UIテーマ変更の挑戦とCSSによる表現

開発途中でUIテーマを「かわいいファンタジー」から「エレガントな中世ヨーロッパ風」へ大幅に変更。画像アセットを一切使用せず、Tailwind CSSとHTMLのdiv要素の組み合わせ(CSS図形描画)のみでテーマ性を表現することが挑戦でした。

挑戦:抽象的テーマのCSS具現化と大幅なスタイル変更

「気品」や「中世感」をCSSでどう表現するかが試行錯誤の連続でした。角張ったUI要素、深みのある配色、そしてコマやキャラクターの抽象的なCSS図形でのデザインが主な課題でした。

TypeScriptの活用(間接的役割):

CellPropsbonusGameTypeのように、TypeScriptで型付けされた状態に基づいてコンポーネントの表示スタイルを動的に変更する箇所では、型の恩恵を受けました。テーマ変更でコマの見た目を変える際、bonusGameTypeのような型付きPropsに基づき条件分岐でスタイルを適用するため、ロジックが追いやすく、修正漏れも防ぎやすかったです。

学びと工夫:CSS表現の探求と段階的テーマ適用

Tailwind CSSはテーマ変更に柔軟でした。画像レスでテーマ性を追求するため、CSSの基本プロパティを駆使し抽象図形を作成。AI(Jules)にデザイン変更を指示する際は、具体的な色系統や形状イメージを伝えることで、Julesが適切なTailwindクラスを選定しやすくなりました。

5.5. ドラポイントとボーナスゲームシステム

ゲームのリプレイ性向上のため、PCがゲームごとに獲得できる「ドラポイント」と、ポイントが倍増する「ボーナスゲーム(アデニウムx2、キトルルスx3)」を導入しました。

挑戦:新状態の管理と既存ロジックへの統合

GameStateへのドラポイント関連情報(累計、ラウンド獲得分、ボーナス種類)の追加と、ゲーム終了時のポイント計算ロジック、ボーナスゲームの確率的発生などを、既存システムへスムーズに組み込むことが課題でした。

TypeScriptの活用:型安全な状態追加とロジック分岐

  1. GameStateへのプロパティ追加: doraPoints: number, currentRoundDora: number, bonusGameType?: BonusGameType を型安全に追加。
    // src/game/types.ts より抜粋
    export type BonusGameType = "ADENIUM" | "CITRULLUS" | null;
    // GameStateインターフェース内に上記プロパティを追加
    
  2. ポイント計算の型チェック: handleCellClickLogic内でのポイント計算時、bonusGameType(ユニオン型)や数値型のポイント変数に対する操作が型安全に行えました。

学びと工夫:ゲームへの「味付け」と段階的追加

これらのメカニクスはゲームに新たな目標や期待感を与えます。ボーナスゲームの発生確率や、UIでの状態通知も工夫点です。既存のゲーム終了ロジックにポイント計算を「差し込む」形で実装し、影響範囲を限定しました。

5.6. 攻略モードと「一手戻す」機能

戦略探求のため、盤面を自由に再現しAIに次善手を分析させる「攻略モード」と、直前の手を取り消す「一手戻す」機能を実装。

挑戦:複数ゲームモード管理と状態履歴

「NPC対戦」「攻略モード」のモード管理とUI切替、攻略モード専用ロジック、Undo機能のためのGameState履歴保存(再帰型回避の工夫)が主な課題でした。(GameMode型は当初3状態でしたが、最終的に2状態に単純化)

TypeScriptの活用:GameModeユニオン型と履歴の型付け

  1. GameModeによるモード管理: gameMode: "VERSUS_NPC" | "STRATEGY"GameState に追加し、モードごとの処理分岐を型安全に記述。
  2. history: GameState[] Undo用履歴も型付けし、状態復元の安全性を確保。履歴保存時、各GameState内のhistoryを空にする工夫で無限ネストを回避。
  3. AI関数の汎用化: findBestMoveのシグネチャを (BoardState, Player) => Move | null に変更し、NPC思考と攻略モード分析の両方で型安全に再利用可能にしました。

学びと工夫:モードによる関心の分離と既存ロジックの再利用

gameModeによる処理分岐でコードの見通しを改善。Undo履歴はGameStateの簡易コピーを積むことで実装の簡潔さと実用性のバランスを取りました。既存AIロジックを攻略モード分析にも再利用することで開発効率を向上させました。

さらなる改善とユーザーフィードバックの反映:
当初の攻略モードはAI分析の開始・終了をボタンで切り替える形でしたが、ユーザー様からの「もっとシンプルに、常に推奨手が見えるように」「PC手番時のみで良い」といったフィードバックを受け、大幅に改良しました。

  • 常時AI推奨手表示(PC限定): 攻略モードでPCの手番(ユーザーがPCのコマを配置する側)の場合に限り、AIが計算したPCにとっての最善手が盤面上の該当マスに「🎯」アイコンで常に表示されるように変更しました。これにより、分析ボタンを押す手間なく、盤面を組み立てながら常にAIの意見を参考にできます。この切り替えはapp/page.tsx内のuseEffectフックでgameState.currentPlayer === 'PC'という条件を加えることで実現しています。

  • 攻略モード中の盤面評価表示: ユーザーがコマを配置して盤面を構成するたびに、その盤面が「PCの勝ち」「相手の反則負け」「ゲーム続行可能」といった状態であるかをリアルタイムでテキスト表示するようにしました。これにより、特定の局面の分析がより容易になりました。これはapp/page.tsxに専用の表示領域を設け、gameState.gameOvergameState.winnerの状態を監視してメッセージを更新しています。

  • 攻略モードでのボーナスゲーム無効化: 戦略分析や盤面再現に集中できるよう、攻略モード開始時にはgameState.bonusGameTypeを強制的にnullに設定し、ボーナスコマ(アデニウム、キトルルス)が登場しないようにしました。これはモード切替時のsetGameStateで明示的にbonusGameType: nullを指定することで対応しました。

これらの改善は、ユーザー様の具体的なフィードバックが直接的なきっかけとなり、より使いやすく、目的に沿った「攻略モード」へと進化させる上で非常に有益でした。AI(Jules)はこれらの変更指示を受け、関連する状態変数(gameMode, currentPlayer)の監視と、それに基づくUI(推奨手アイコン表示、盤面状態テキスト表示)の更新ロジックを効率的に修正・追加してくれました。

6. UIデザインへのこだわり:CSSによるテーマ表現

当初は「シンプルなUIで良い」という方針でスタートしましたが、開発の進行と共に、よりゲームの世界観を表現したいというユーザー様のご要望が明確になりました。これに応える形で、UIテーマは「かわいいファンタジー」から、最終的には「エレガントな中世ヨーロッパ風」へと大きく舵を切りました。この過程での最大の挑戦は、画像アセットを一切使用せず、CSS(主にTailwind CSSのユーティリティクラス)のみでこれらのテーマ性をどこまで表現できるかという点でした。

テーマの変遷とCSSでの工夫:

  1. 初期(シンプル): 機能性を重視した、ごく基本的なHTML要素と最小限のスタイリングでした。

  2. 「かわいいファンタジー」テーマ:

    • 挑戦: 「かわいらしさ」をCSSでどう出すか。
    • 工夫: パステルカラーの多用、角丸(rounded-lg, rounded-full)、絵文字によるコマ表現(PC: 🌱, NPC: 🍄)、柔らかな影(shadow-md)などを活用しました。この段階で、ゲームの親しみやすさが大きく向上しました。
  3. 「エレガントな中世ヨーロッパ風」テーマへの転換:

    • 挑戦: 「子供っぽさを排し、気品ある雰囲気」という、より高度な要求にCSSのみで応えること。石や木、金属といった質感を直接描画できない中での雰囲気作り。
    • 工夫:
      • 配色変更: スレートグレー、ストーン、ダークブラウン、フォレストグリーン、深紅、アクセントにアンバー(金色の代用)といった、落ち着いた深みのある色合いを基調としました。
      • 形状変更: UI要素の角丸を控えめに(rounded-md, rounded-sm)し、より直線的で重厚な印象を目指しました。
      • タイポグラフィ: 主要な見出しに font-serif を適用し、格調高さを演出。
      • CSSによるコマ・キャラクター表現:
        • コマ: PC(マンドラゴラ)は盾のようなエンブレム風、NPC(コリガン)は四角いエンブレム風の抽象図形をCSSで作成。ボーナスゲーム時のコマ(アデニウム、キトルルス)も、ベース形状は変えずに配色のみで変化をつけました。
        • 盤面左右のキャラクター: PCアバターは抽象的な鎧やマントをまとったシルエット風、NPCは神秘的な精霊やゴーレム風の抽象図形を、複数のdiv要素の組み合わせとTailwind CSSのクラス(absolute, transform, z-indexなど)を駆使して表現しました。これらは単なる背景色ではなく、ゲームの世界観を補強する「CSSアート」の試みでした。
      • UI要素の質感: bordershadow-inner, shadow-lg を使い分け、UI部品にわずかな立体感や素材感(石材や金属器のイメージ)を出すことを試みました。

TypeScriptの役割(間接的):
UIのテーマ変更は主にCSSの仕事ですが、ゲームの状態(例:bonusGameType)に応じてコマのスタイルを動的に切り替えるロジックなどでは、TypeScriptの型情報が役立ちました。どの状態の時にどのCSSクラス(またはスタイルを適用するための条件)を使うべきかが明確になり、バグの混入を防ぎました。

このUIデザインの変遷とCSSによる表現の追求は、画像アセットがなくてもテーマ性豊かなUIは構築可能であること、そしてそのためには具体的なビジュアル要素(色、形、質感のイメージ)をAI(Jules)に的確に伝える言語化能力が重要であることを示す「挑戦・学び・工夫」の経験でした。

7. Vercelへのデプロイ

開発したWebアプリケーションを公開し、他の人に遊んでもらうためにはデプロイが必要です。本プロジェクト「まんどらごら★づくし」では、デプロイ先としてVercelを選定しました。

Vercel選定の理由:

  • Next.jsとの親和性: VercelはNext.jsの開発元であり、Next.jsプロジェクトのデプロイに最適化されています。特別な設定なしに、リポジトリを連携させるだけで簡単にビルドとデプロイのプロセスを自動化できます。
  • GitHub連携: GitHubリポジトリの特定のブランチ(例:mainブランチ)へのプッシュをトリガーとして、自動的にデプロイが実行されるCI/CDパイプラインを容易に構築できます。これにより、常に最新のコードをデプロイ先に反映させることが可能です。
  • 無料プラン: 個人プロジェクトやホビー用途であれば、無料プランの範囲内で十分に高機能なホスティングサービスを利用できます。カスタムドメインの設定も可能です。
  • ビルドログとプレビュー: Vercelは詳細なビルドログを提供し、エラー発生時の原因特定に役立ちます。実際に、開発中にTypeScriptの型エラーがVercelのビルドプロセスで検出され、修正に繋がった経験もありました。また、各デプロイメントに対してプレビューURLが発行されるため、本番環境にマージする前に変更内容を確認できるのも利点です。

デプロイプロセス(概要):

  1. GitHubリポジトリの準備: プロジェクトコードをGitHubで管理します。
  2. Vercelアカウントとの連携: Vercelにログインし、GitHubアカウントを連携させます。
  3. プロジェクトのインポート: Vercelダッシュボードから該当のGitHubリポジトリを選択してインポートします。
    • フレームワークプリセット: 通常、VercelはNext.jsプロジェクトを自動で認識し、適切なビルド設定を適用してくれます。
    • ルートディレクトリ: リポジトリのルートにNext.jsプロジェクトがある場合はそのままで、サブディレクトリにある場合はそれを指定します(今回はmandrake-dukesをルートディレクトリとして設定しました)。
  4. デプロイ実行: 設定後、最初のデプロイが実行されます。以降は、指定したブランチへのプッシュごとに自動デプロイが行われます。

挑戦と学び:
Vercelのデプロイ自体は非常にスムーズでしたが、初期の段階でTypeScriptの型エラーや設定不備によるビルド失敗が数回ありました。Vercelの提供するビルドログは、これらの問題を特定し、ローカル環境での修正を促す上で非常に有用でした。エラーメッセージを元にJulesと原因を議論し、迅速に解決策を見つけ出すことができました。この経験は、CI/CD環境での静的型付け言語の利点(ビルド段階でのエラー検知)を再認識する良い機会となりました。

このように、Vercelを利用することで、インフラ管理の複雑さを気にすることなく、アプリケーションのロジックとフロントエンド開発に集中することができました。

8. まとめと今後の展望

本記事では、AIソフトウェアエンジニアリングエージェント「Jules」と共に、TypeScriptを用いて開発したブラウザボードゲーム「まんどらごら★づくし」の製作過程、技術的な挑戦、そしてそこから得られた学びや工夫を紹介しました。

本プロジェクトの主な成果と学び:

  • TypeScriptによる堅牢なゲームロジックの実装: GameStateの厳密な型定義から始まり、複雑なルール判定、AIロジックに至るまで、TypeScriptの型システムが一貫して品質と開発効率の向上に貢献しました。
  • AI支援開発という新しい体験: Julesとの対話を通じた開発は、要件定義の明確化、段階的な機能実装、そして時にはAIの「思考」に合わせた指示の工夫など、従来の開発とは異なる多くの挑戦と学びをもたらしました。これは、ある種の「ノーコード」的な側面を持ちつつも、開発者自身の技術的理解がAIを有効活用する鍵であることを示しています。
  • 画像アセットレスでのUIテーマ表現: Tailwind CSSを駆使し、CSSの図形描画によって「かわいいファンタジー」から「エレガントな中世ヨーロッパ風」へとUIテーマを大きく変更・表現した経験は、制約の中で創意工夫する面白さを示してくれました。
  • 反復的な改善プロセスの重要性: ルール解釈の修正、AIの挙動調整、UIテーマの全面変更など、ユーザー様(そしてJules自身も)からのフィードバックを受けて反復的に改善を重ねることで、プロジェクトは大きく進化しました。

今後の展望:

「まんどらごら★づくし」は、基本的なゲームシステムに加え、ドラポイント、ボーナスゲーム、攻略モード、一手戻す機能など、多くの拡張機能も実装できました。しかし、さらなる発展の可能性も秘めています。

  • NPC AIのさらなる強化: 現在のヒューリスティックベースのAIから、より高度な探索アルゴリズム(Minimax法、モンテカルロ木探索など)の導入や、機械学習による打ち筋の学習なども考えられます。AIに複数の「性格」や「難易度」を持たせるのも面白いでしょう。
  • オンライン対戦機能: 現在はローカル対戦のみですが、WebSocketなどを利用したリアルタイムオンライン対戦機能の追加は、ゲームの楽しみを大きく広げます。
  • よりリッチな演出: コマの動きや変換、勝利・敗北時のアニメーションやサウンドエフェクトの追加は、ゲーム体験をより豊かにします。
  • 「攻略モード」の拡張: 特定の盤面を「詰み盤面」として出題するパズルモードや、過去の棋譜を保存・再生する機能なども考えられます。
  • さらなるUI/UXの洗練: より詳細なグラフィックアセットの導入(ユーザー様作成またはフリー素材など)、操作性の改善など、磨き込みの余地は常に存在します。

このプロジェクトは、ZennのTypeScriptコンテストへの応募という目標からスタートしましたが、AIとの協調開発という貴重な経験を通じて、技術的な挑戦だけでなく、新しい開発のあり方についても深く考える機会となりました。この記事が、読者の皆様にとって何かしらの参考や刺激になれば幸いです。

最後までお読みいただき、ありがとうございました。

9. 実際のゲーム

本プロジェクトはVercelで公開しています。

Discussion