AIと作ったマインスイーパー:初手で必ず負けない盤の作り方

子どもでも遊べるボードゲームのアプリを、AIに手伝ってもらいながら作っています。3つ目のゲームとしてマインスイーパーを足したかったのですが、最初の1手で地雷を踏んで終わるゲームは、たぶん二度と遊んでもらえません。この記事では、その初手の安全をどう作ったか、旗の操作で迷って何を選んだか、AIのレビューで既存の作りの不備が出てきたところまでを書きます。

作りたかったもの

1つのアプリを開くと、遊ぶゲームを選べる形にしています。すでに2本入っていて、その3つ目としてマインスイーパーを足すのが今回の作業でした。

AIに頼む前に、できあがりの条件を自分の言葉で並べました。ゲームを選んで遊べること、地雷が配置されること、最初の1手で地雷を踏まない仕組み、旗、連鎖して開く処理、勝敗、難易度が動くこと。そして、テストが通っていること。ここを先に決めておくと、あとで「できた」と言えるかどうかを自分で判断できます。

難易度は2種類にしました。「はじめて」が5×5で地雷3個、「なれてきた」が6×6で地雷5個です。

盤をこれ以上大きくしなかったのには理由があります。縦向きのiPhoneだと、6列なら1マスが約54ポイント(画面の大きさの単位)ですが、7列にすると約46ポイントまで縮みます。Appleは、指で正確に押せるように、押せる部分を44×44ポイント以上にすることを勧めています。46ポイントはその目安すぐ上で、余裕がありません。指の太さは大人も子どもも変えられないので、列を増やすのではなく6列までで止めました。

AIへの頼み方

上の条件を箇条書きのまま渡して、実装をAIに頼みました。実際に打ち込んだ指示の文は残していないので、ここには載せません。やったことは「条件を並べて渡し、作ってもらう」だけです。

もう1つ、先に渡したことがあります。既存の2本とどこまで部品を共有するか、です。共有するのはボタンの見た目(PrimaryButtonStyle)、読みやすい幅で並べる処理(readableColumn)、音を鳴らす処理(SoundPlaying)の3つだけにしました。逆に、対戦ゲームのために作った Player・Piece・Position・BoardView・CellView・CPU・GameError は流用していません。

できあがったものは、そのまま信じずに ChatGPT にも見てもらいました。あとで書くとおり、ここで今回の変更とは別のところに残っていた不備が1つ出てきました。

つまずいたところ

一番迷ったのは、最初の1手です。ふつうに作ると、先に地雷を置いてから盤を見せることになります。すると、運が悪ければ1手目で地雷を踏んで終わります。難易度を「はじめて」と名付けている盤で、最初のタップで終わるのは、遊ぶ人にとってはただの理不尽です。

2つ目は旗の置き方です。マインスイーパーでは、地雷がありそうなマスに旗を立てます。多くのアプリは長押しで旗を立てますが、長押しは画面のどこにも書かれていないので、初めて触る子どもには見つけられません。ふつうのタップとの区別もあいまいで、開けたいだけなのに旗が立つ、ということが起きます。

3つ目はテストが書けない形になっていたことです。難易度を enum(あらかじめ決めた選択肢だけを持てる型)にしていたので、「地雷が多すぎる盤」という設定をそもそも表現できませんでした。表現できないのに、コードの中には地雷が多すぎる場合に止めるための確認が入っています。この確認は絶対に通らない道になり、テストで確かめられません。

4つ目は、ChatGPTの指摘で見つかった既存の不備です。タイトル画面(TitleView)が、1人で遊ぶゲームにも GameStart(mode: .twoPlayers)、つまり「2人対戦で始める」という使わない情報を渡していました。動いてはいますが、意味のない値です。

どう判断したか

最初の1手については、地雷を置く時期を後ろにずらしました。盤を見せる時点では地雷を決めず、最初にタップされたマスと、その周囲8マスを避けて地雷を置きます。こうすると、どこを押しても1手目で負けません。

この流れを図にします。地雷の配置が、最初のタップより後ろに来ているところが要点です。

flowchart TD
  A["盤を表示(地雷はまだ決めない)"] --> B["最初のタップ"]
  B --> C["そのマスと周囲8マスを避けて地雷を置く"]
  C --> D["0のマスは連鎖して開く"]
  D --> E["2手目以降はふつうに進む"]

旗から始めた人のことも考えました。旗を立てただけでは地雷を置かないようにしたので、先に旗を立ててから開いた場合も、初手の安全はそのまま保たれます。

旗の操作は、長押しをやめました。盤の下に「あける」「はた」の切替を常に出しておき、押して切り替える形です。いま選ばれている側は、色と枠の両方で示します。色だけで示すと、色の見え方が違う人には伝わらないためです。「もういっかい」を押したときは、必ず「あける」に戻します。前回の続きで旗モードのまま始まると、最初のタップの意味が変わってしまうからです。

開く処理は、0のマスから連鎖して開きます。旗が立っているマスは連鎖でも開きません。地雷以外を全部開けば成功で、旗を立てきる必要はありません。

テストが書けなかった問題は、盤の設定を難易度から切り離して解きました。マスの数と地雷の数を MinesweeperConfiguration という別のものにして、難易度はその設定を選ぶだけにしました。設定を自由に作れるようになったので、「地雷が多すぎる盤」をテストの中で作れます。到達できない確認ではなくなりました。

タイトル画面は、渡す情報の形を作り直しました。GameStart.Kind は対戦ゲームだけを表すようにして、マインスイーパーは MinesweeperStart から直接ゲーム画面へ進みます。使わない対戦モードを渡す場所が無くなりました。

分かれ道を図にします。1人で遊ぶゲームが、対戦用の入口を通らなくなったところが変わった点です。

flowchart LR
  T["タイトル画面"] --> G["GameStart(対戦ゲーム)"]
  T --> M["MinesweeperStart"]
  G --> G1["対戦ゲームの画面"]
  M --> M1["マインスイーパーの画面"]

時間の計測と、難易度ごとのベスト記録も入れました。保存先は端末の中だけで、外には送りません。iOSの UserDefaults(アプリの小さな設定を端末に保存する仕組み)を使うので、プライバシーマニフェスト(PrivacyInfo.xcprivacy)というファイルを足し、利用理由 CA92.1、収集するデータなし、トラッキングなしを宣言しました。CA92.1 は、アプリ自身しか読めない情報の読み書きに使う理由の番号です。

書式が崩れていないかは、コマンドで確かめられます。

plutil -lint PrivacyInfo.xcprivacy

できたもの

iPhone 17 Pro のシミュレータでテストを実行し、11スイート・103テストが全部通りました。既存が70、新しく書いたものが33です。既存のテストは1件も緩めていません。ビルドの警告も0件でした。

初手の安全は、押す場所を5通り(角・辺・中央)、乱数を30通り変えて確かめました。最初のマスとその周囲8マスに地雷が来ないこと、必ず連鎖して複数のマスが開くことを見ています。さらに画面側でも、実際の乱数を使って200回繰り返し、最初のタップで負けないことを確認しました。

地雷が多すぎる設定も確かめました。5×5に17個を渡すと tooManyMines として拒否され、ちょうど置ける上限の16個は通ります。テストが書ける形にした効き目がここに出ています。

旗のまわりは、境界だけを並べてテストしました。最初に旗を立てても地雷が置かれないこと、連鎖が旗のマスを開かないこと、旗のマスを開こうとすると拒否されること、開いているマスに旗を立てられないこと、勝敗が決まったあとは開くことも旗も拒否されること。そして「もういっかい」で状態が消え、操作が「あける」に戻ることです。

勝てることも確かめました。両方の難易度で、乱数を20通り変えて、地雷以外を順に開くと必ず成功で終わります。時間と記録は、テスト用の時計で時間を進めて確認しました。最初のマスを開いてから数え始めること、クリア時に確定してベスト記録に残ること、前より遅ければ更新されないこと、失敗時は記録しないこと、記録が難易度ごとに分かれることです。

プライバシーマニフェストは、書式の確認と、ビルド後のアプリに入っていることをファイルの存在で確かめました。ゲーム選択画面に3つのゲームが並ぶことは、シミュレータの画面を撮って確認しています。

1つ、数えて安心した数字があります。最初の1手で盤が全部開いて即クリアになる割合を、それぞれ20000回試して測りました。「はじめて」で3.9%、「なれてきた」で0.3%でした。まれな当たりの範囲なので、設計は変えていません。

まだ手が付いていないこともあります。実際に盤をタップする操作の確認、時間表示が1秒ごとに進むこと、iPadでの表示、VoiceOver(画面を読み上げる機能)での読み上げです。これは次のタスクに回しました。

まとめ

  • 理不尽をコードの都合で残さない。「最初の1手で負ける」は、地雷を置く時期をタップより後ろにずらすだけで無くなりました。AIに頼むときも、こういう条件は先に言葉にしておくと伝わります。
  • 隠れた操作は初めての人には無いのと同じです。長押しをやめて、画面に出ている切替にしました。色だけでなく枠でも示す、という細かいところも先に決めました。
  • テストできない形は、先に形を直す。難易度と盤の設定を分けただけで、「地雷が多すぎる」を試せるようになりました。AIのレビューで出た既存の不備も、動いていても直しておくと後が楽です。

次は、実際に盤をタップする操作の確認と、iPadやVoiceOverでの見え方・読み上げを片付けます。

参考にした資料

この記事は、筆者の開発記録をもとにAI(Claude)が下書きし、筆者が確認して公開しています。