生成AIが登場して、プログラマーが嬉しかったことの一つが、プログラミングで使う素材をAIが作ってくれるようになったという点があります。「こんな絵が欲しい」と思ったとき、絵のイメージさえ伝えれば思ったような絵が出ます。今日は、AIに絵を描いてもらって、それを自作パズルにして楽しむ方法を紹介します。
プログラマーらしい方法で生成AIで絵を描こう
生成AIが登場して、まだそれほど経っていないというのに、生成AIを使ったことがないという人は、ほとんどいないでしょう。特に、本連載を読んでいる読者の皆さんであれば、普段から生成AIを活用しているという人も多いことでしょう。
そのため、ヒマワリの画像が欲しいときには、以下のように生成AIに指示すれば良いことを皆さんご存じでしょう。
ヒマワリを描いて。ゴッホっぽい画風で。
しかし、本連載は、プログラミングを学ぶ連載です。せっかくなので、プログラマー的な視点で、生成AIに絵を依頼する面白いテクニックを一つ紹介します。それは、絵を描いてもらうとき、JSON形式で指示を与えるという方法です。
生成AIは、データフォーマットのJSONを正確に理解できるので、人間からしたら奇妙に見える次のような指示を理解して処理してくれます。
{
"ゴール": "ヒマワリの絵を描いて",
"画風": ["ゴッホ", "印象派", "油絵風"],
"サイズ": "正方形",
"用途": "パズルゲームのために必要",
"配置に関して": {
"背景": "海が見える綺麗な丘にあるヒマワリ畑",
"前景": ["一本大きなヒマワリ", "小さく農夫の男性"]
}
}
実際に、ChatGPTにJSONを与えてみた結果が以下のようになります。見比べてみると分かりますが、JSONで指定した方は、こちらの意図をしっかりと汲み取ったものになっているのが分かるでしょう。
そのため、筆者は、生成AIに絵を描かせるとき、上記のようなJSONで指示を与えることも少なくありません。と言うのも、JSONで指示を与える場合のメリットがいくつかあります。
まず、JSONフォーマットの分かりやすさです。JSONのオブジェクトは、いろいろな要求項目をプロパティとして与えるのに便利ですし、JSONの配列は、複数の項目を列挙するのに適しています。そのため、生成AIに的確な指示を与えるのにピッタリなんです。
そして、私たち人間側のメリットですが、何を描いて欲しいのかを明確にできるという点があります。生成AIは、私たちの指示通りに絵を描くように訓練されているので、明確に生成AIに何が必要なのかを与えることができるかどうかがカギになります。その点で、JSONで、一つ一つ項目を列挙して、情報を記述することで、思い通りの絵を描いてもらうことができます。
なお、先ほどは、生成AIの代表格の一つであるChatGPTに、JSONで指示を出しましたが、GoogleのGeminiでも、JSONで絵を描かせることができます。
画像をパズルで使おう
さて、生成AIに絵を描いてもらったら、次にパズルを作りましょう。今回は、Webブラウザ上で動くパズルを作りましょう。なでしこでブラウザ上で動くゲームを作る場合に便利なのが、こちらの「なでしこ3貯蔵庫」です。これは、ブラウザ上でプログラムを開発して、そのまま公開できるサービスです。
この貯蔵庫サービスですが、先日、大々的にリニューアルしました。AIを使って、Googleアカウントでのログインを実現し、コメント機能を追加し、デザインも使いやすいものに改良しました。
なお、リニューアルの様子をこちらの姉妹コラム(既存WebサービスをAIエージェントで改良してみよう)に書いていますので、興味があればご覧ください。
それで、貯蔵庫にログインしたら、画面上方にある「私の素材」というリンクをクリックします。そして、「ファイルをアップロード」ボタンを押します。すると、画像ファイルのアップロードが可能です。
画像ファイルをアップロードすると、プログラムから利用できるURLが生成されます。このURLを使うことで、プログラム中で好きな画像が使えるという訳です。今回、次のURLが生成されました。このURLを覚えておきます。
なでしこで画像の表示方法
それでは、プログラムで、AIに描いてもらった画像を描画してみましょう。なでしこのプログラムで、画像を表示するには、下記のように書きます。
URL=「https://n3s.nadesi.com/image.php?f=744.png」
URLの画像作成して、F画像に代入。
実行すると次のように表示されます。なでしこ3では、「画像部品」が用意されていて、「**の画像作成」と書くことで、画像を表示するためのオブジェクトが生成され、ゲームなどで活用できます。そして、パズルを作る際は、画像の一部分をコピーして描画する必要がありますが、「画像描画」命令を使うことで、画像の一部をキャンバスに描画できます。
パズルを完成させよう
それでは、パズルを完成させましょう。パズルと言っても、穴の空いたピースを交換する15パズルや、縦方向・横方向に全体をスライドするパズルなどいろいろあります。本連載でも、以前、こちらで、15パズルを作っています。
今回は、バラバラになったパズルのピースを入れ替えることで絵を完成させるタイプのパズルを作ってみましょう。貯蔵庫の画面上部にある「新規」ボタンをクリックして、以下のプログラムを書き込みましょう。そして、キャンバスの幅と高さを500x500に変更します。なお、こちらにもプログラムをアップロードしました。
#-------------------------------------------------------
# ピース入れ替えパズル
#-------------------------------------------------------
# 盤面の初期化 --- (※1)
盤面分割数=3 # ←難易度を指定(3x3)
画面幅=500 # ゲームキャンバスのサイズ
盤面幅=画面幅÷盤面分割数
画像幅=0
ピース幅=0
盤面=0から(盤面分割数*盤面分割数-1)の配列連番作成。
盤面クリア状態=盤面を「:」で配列結合
盤面を配列シャッフル。
CX=-1
CY=-1
「rgba(0,0,255,.5)」に塗り色設定。
# 画像の読み込み --- (※2)
URL=「https://n3s.nadesi.com/image.php?t=495faa9e9ccf1f56&f=741.png」
URLの画像作成して、F画像に代入。
F画像の読み込んだ時には:
画像幅=F画像$width
F画像$style$display="none"
ピース幅=INT(画像幅÷盤面分割数)
盤面表示。
# ラベル作成
「ピースを入れ替えて絵を完成させましょう」のラベル作成して、Fラベルに代入。改行作成
# パズルの盤面を描画する処理 --- (※3)
●盤面表示とは:
Yを0から(盤面分割数-1)まで繰り返し:
Xを0から(盤面分割数-1)まで繰り返し:
I=Y×盤面分割数+X
J=盤面[I]
TX,TY = [J%盤面分割数, INT(J÷盤面分割数)]
IX=TX×ピース幅
IY=TY×ピース幅
XX=X×盤面幅
YY=Y×盤面幅
F画像を[IX,IY,ピース幅,ピース幅, XX,YY,盤面幅,盤面幅]へ画像描画。
# カーソルを描画
もし、(CX=X)かつ(CY=Y)ならば:
[XX, YY, 盤面幅, 盤面幅]へ四角描画。
# クリア判定 --- (※4)
S=盤面を「:」で配列結合
もし、S=盤面クリア状態ならば:
Fラベル$テキスト=「完成!クリア!」
# 盤面のピースを入れ替える処理 --- (※5)
●ピース入替処理とは:
# クリックされた座標を取得
TX=INT(マウスX÷盤面幅)
TY=INT(マウスY÷盤面幅)
# 既に選択中だった場合
もし、CX≧0かつCY≧0ならば:
# 交換
TI=TY*盤面分割数+TX
CI=CY*盤面分割数+CX
T=盤面[CI]
盤面[CI] = 盤面[TI]
盤面[TI] = T
CX = -1
CY = -1
Fラベル$テキスト=「移動しました」
違えば:
CX = TX
CY = TY
# Fラベルに「移動先を選んで」をテキスト設定
Fラベル$テキスト=「移動先を選んで」
盤面表示。
描画中キャンバスをマウス押した時には:
ピース入替処理。
プログラムを実行すると、ピースの入れ替えパズルゲームを遊べます。
プログラムを確認しましょう。
(※1)では、パズルで使用する盤面の初期設定を行っています。「盤面分割数」は、画像を縦横それぞれ何個に分けるかを表します。ここでは3なので、3×3の合計9ピースになります。この数を増やすとピースが細かくなり、難易度が上がります。
「画面幅」は、パズルを表示するキャンバスの幅です。「盤面幅」は画面幅を分割数で割って求めているため、キャンバス上での1ピースの大きさを表します。画面幅が500で分割数が3の場合、1ピースは約166.7ピクセルになります。
「盤面」には、0から8までの数字が入ります。それぞれの数字は、元画像のどの部分を表示するかを示す番号です。たとえば0は左上の画像、1はその右隣、8は右下の画像を表します。盤面をシャッフルする前に、正しい順番を「盤面クリア状態」として文字列に変換して保存しています。完成時には現在の盤面を同じ形式の文字列に変換し、この保存済みの文字列と比較します。その後で盤面をシャッフルするため、ゲーム開始時には画像のピースがランダムな順番になります。
「CX」と「CY」は、現在選択されているピースの横位置と縦位置を記録する変数です。初期値のマイナス1は、まだ何も選択されていないことを表しています。最後に設定している半透明の青色は、選択中のピースを示すカーソルを描くための色です。
(※2)では、パズルに使用する画像をインターネット上のURLから読み込んでいます。画像の読み込みには時間がかかる場合があるため、画像が利用可能になる前に描画を始めることはできません。そこで、「画像を読み込んだ時」の処理の中で、画像サイズの取得や盤面の描画を行っています。画像を読み込むと、まず画像本来の幅を取得します。その後、画像そのものはHTML画面上に直接表示する必要がないため、表示状態を「none」にしています。画像は見えなくなりますが、描画用のデータとしては利用できます。
「ピース幅」は、元画像の幅を盤面分割数で割った値です。これは元画像から1ピース分を切り出すときの大きさとして使われます。ここでは「盤面幅」と「ピース幅」が別々に用意されています。「ピース幅」は元画像上で切り出す範囲の大きさであり、「盤面幅」はキャンバス上に表示するときの大きさです。そのため、元画像のサイズとキャンバスのサイズが異なっていても、キャンバスに合わせて拡大または縮小して表示できます。すべての準備が完了したところで「盤面表示」を呼び出し、最初のパズル画面を描きます。
(※3)では、現在の盤面配列をもとに、画像の各ピースをキャンバスへ描画しています。外側の繰り返しが縦方向、内側の繰り返しが横方向を担当しています。これにより、左上から右下まで、すべてのマスを順番に処理できます。
変数「I」は、縦横の座標を盤面配列の番号へ変換したものです。たとえば3×3の盤面でXが1、Yが2の場合は、2×3+1で7になります。これにより、二次元のマス目を一次元の配列として扱えます。ピースの描画後には、現在のXとYが選択中のCXとCYに一致するか確認しています。一致した場合は、そのピースの上に半透明の青い四角形を描きます。これにより、プレイヤーはどのピースを選んだのかを確認できます。
(※4)では、現在の盤面が完成状態になっているかを判定しています。現在の盤面配列を「:」でつないだ文字列に変換し、ゲーム開始前に保存した「盤面クリア状態」と比較します。たとえば正しい並びなら、盤面は「0:1:2:3:4:5:6:7:8」のような文字列になります。現在の並びがこの文字列と完全に一致すれば、すべてのピースが正しい位置にあると判断できます。完成していた場合は、ラベルの表示を「完成!クリア!」へ変更します。この判定は盤面を描画するたびに行われるため、ピースを交換した直後にクリアしたかどうかが分かります。
(※5)では、マウスで選ばれた2つのピースを入れ替えています。最初に、マウスの座標を盤面幅で割り、その整数部分を取得しています。これによって、マウスがどのマスの中で押されたのかを求められます。変数「TI」は今回クリックされたピースの配列番号で、変数「CI」は先に選択されていたピースの配列番号です。一時変数の「T」に片方の値を保存しながら代入することで、2つの配列要素を入れ替えています。一時変数を使わずに直接代入すると、片方の値が上書きされて失われてしまうため、この手順が必要になります。
まとめ
以上、今回は、AIで画像を生成して、それを利用したパズルゲームを作ってみました。ゲーム作りは、ちょっとしたアイデアで、さらに面白くなります。
今回は、画像を3×3に分割しただけなので簡単なパズルになりましたが、プログラム冒頭の変数「盤面分割数」を5に設定すると、5×5に分割するので、もう少し難しくなります。数値を変えて試してみてください。
もちろん、画像を入れ替えるだけでも、難易度や面白さが変わりますので、好きな雰囲気の画像を作って画像を入れ替えてみてください。プログラミング上達のコツは、プログラムの一部を書き換えてみて、その動作の違いを理解することです。試してみてください。
自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。





