この記事で扱っていること
- ピクロスを作る方法
を紹介しています。
注意:すべてのエラーを確認しているわけではないので、記事の内容を実装する際には自己責任でお願いします。また、エラー配線は適当な部分があるので適宜修正してください。
ピクロスというのは、ピクチャークロスワードを略したゲームで、ルールは簡単なのに奥深いパズルゲームです。
プレイヤーが目指すのは、与えられた盤面のうちセーフのマスをすべてを空けることで、そのためのヒントが縦と横に書かれています。
縦と横には数字がそれぞれの列、行に対して複数書いてあり、これは「セーフのマスが何個連続で続いているか」を表します。
例えば、ある行に対して数字が「2 3」となっている場合、その行の中のセーフマスは2個連続した部分と3個連続した部分がある、ということになります。
2個連続した部分と3個連続した部分の間は当然1個以上空いてないといけない(そうでないと、「5個連続した部分」となってしまうため)ということ、およびこれらの情報が全ての行、列に対して指定されていることをヒントに、ゲームを進めていきます。
今回はそんなピクロスをLabVIEWで作る場合の例を考えてみました。
どんな結果になるか
フロントパネルにはピクチャ表示器があり、開始するとゲーム画面が表れます。

問題を選択してからプログラムを実行すると、ピクロスの盤面がピクチャ上に表示されます。
以下の図で15×15で表されている白いマス目に対し、セーフだと思うマスを左クリックで選択します。

また、右クリックにも対応させており、右クリックをすると、バツ印のマーク(目印)を付けることができます。
マークをつけたマスは左クリックしても空けることができなくなります。
最終的に全てのマスを空けるとゲームクリア、その絵が何なのかの答えが出ます。
指定回数以上間違える(セーフでないマスをクリックしてしまう)とゲームオーバーです。

プログラムの構造
ピクチャで数字の配置なりマス目なりを書くのは結構めんどくさいのですが、いくつかのサブVIを使用して表示させるべき情報をピクチャ上に表現する必要があります。
まず、プログラムの全体像は以下の通りです。
大きく分けて、問題の盤面を作る前半部分、ユーザーからのクリック操作を受けて盤面の状態を更新する後半部分に分けられます。

前半部分では、問題の情報を載せたファイルから読み取ったデータをもとに問題をピクチャ上に表現していきます。
問題を表現するのに必要なのは、行、列方向それぞれの数字の情報です。
盤面自体はboard create.viで作成していきます。

まずファイルから問題を読み取るためのサブVI、problem file read.viですが、これは問題を定義したファイルから必要なデータを取り出すためのサブVIです。
問題の情報をどのようにファイルに残しておくかは好みの問題なので下記の図はあくまで一例ですが、記事後半で紹介している、作問するプログラムで保存されるデータの中身に合わせた形で読み取りを行っています。
なお、テキストファイルから読み取るの関数は、右クリックして「ライン読み取り」にチェックを入れて、この関数が一回実行されるたびに一行だけ読み取るという状態にしています。

ファイルから読み取られた問題のデータを、後のプログラムで扱えるように文字列に変換しているプログラムがpicross_tonumber.viです。

読み取られた情報を基に盤面を作成するのがboard create.viです。

board create.viの中では、ファイルから読み取った行、列方向に表示するべき文字列を表示しながら盤面を作っていきます。
各行、各列にただ数字を並べても見にくいので、隣り合う行(列)同士で背景の色を変えるための処理をcell row label.viやcell column label.viで行い、マス自体はwrite cells.viで書いていきます。

cell row label.viの中身は以下の通りです。
sizeという入力は、表示領域のサイズのことで、入力値から2を引いているのは表示する数字の見栄えのためです(何も引かない状態とそこまで大差ないですが)。

ほとんど似ていますが、cell column label.viも以下のように構成します。

write cells.viではマスの四角形を一つずつ書いています。

盤面を作り終えたら、プログラムの後半部分、ユーザーの操作に応じて盤面の状態を変化させる処理に移ります。
ここは、イベントストラクチャでイベントを受けて、キューで処理ループ(下側のWhileループ)を動かす形としています。

イベントストラクチャでは3つのイベントを定義しています。
停止ボタンの値変更はプログラムを任意のタイミングで止めるためで、ユーザイベントを設けているのはプログラムが強制的に止まる条件(ユーザーがゲームをクリアするもしくはゲームオーバーになる)が満たされた場合にイベントストラクチャを止めるためにあります。
あとは、ユーザーが盤面をクリックしたときに反応するマウスダウン?イベントです。
このイベントで扱えるイベントデータである「ボタン」は、左クリックか右クリックかを判別できるので、このボタンの値によって次の処理の内容が決まります。

下側のWhileループではワイヤがいくつかありややこしいのでまずは各ワイヤの意味を整理しておきます。
下側のWhileループでは、各マスの状態を列挙体で表しており、この列挙体の値によって処理を分ける部分があるのですが、マスは縦、横にあるのでこれは2次元配列としています。
また、ファイルから読み取った、各マスがセーフか否かを表すのはブール値で表現できるため、この情報は各マスの状態とは別に2次元のブール配列として保持しています。
また、最終的なクリアの判断に使用するために、セーフのマスが合計いくつかあるかという情報を保持したワイヤもあります。

そんな下側のWhileループでは、イベントストラクチャから受け取ったデータをデキューし、まずは座標の情報からどのマスがクリックされたかをselected cell.viというサブVIで判断、および押されたボタンの種類に応じて処理を変えています。
処理の結果、セーフマスがすべてクリックされたらSuccess、指定回数(下のプログラムでは3)分だけ間違えたらfailという文字列をユーザーイベントに送りプログラムを終了します。

selected cell.viでは、イベントストラクチャで取得した「座標」の情報から、ユーザーが盤面のどの行、どの列をクリックしたかを調べるために使用します。
出力される情報はセルの位置、ということですね。

クリックされたマスの行、列の位置がわかってから、いくつかの処理のパターンがあり得ます。
そもそも、このプログラムにおいて、あるマスに対しては「なんの状態でもない」「マークがついている」「セーフ」「アウト」の4つの状態が考えられます。
「マークがついている」とは、「なんの状態でもない」状態から右クリックされたことで盤面上ではそのマスにバツ印が点いている状態であり、このときは再度右クリックされない限り左クリックは受け付けません。
もし「マークがついている」状態からもう一度そのマスに対し右クリックされたら「なんの状態でもない」に戻ります。
「セーフ」は「なんの状態でもない」状態から移ることができる状態で、空けてもいいマスが空いた状態を表します。
「アウト」も同じく「なんの状態でもない」状態から移ることができる状態で、空けてはいけないマスを空けた状態を表します。
これらのシナリオを、「左クリックされた」か「右クリックされた」かでまず場合分けしその中でさらに今現在4つのうちどの状態かで処理を分けたのが、下のWhileループのケースストラクチャの中身です。
下記の図で、外側のケースストラクチャの「1」は左クリックされた状態を、「2」は右クリックされた状態を表し、内側のケースストラクチャの「untouched」は「なんの状態でもない」、「mark」は「マークがついている」状態を表しています。
どのマスがどの状態であるかという情報は、列挙体の2次元配列が持っています。

左クリックされたマスが「なんの状態でもない」ときには、そのマスがセーフかアウトかを判定します。
ただ、盤面の表現の都合上、最初にcell reset.viでマスの表示状態をリセットします。

その後、そのマスを「掘って」、セーフかアウトかでセルの色を変えます。

一方、右クリックされた場合に、そのマスが「なんの状態でもない」のであればバツ印のマークをつけていくことになりますが、そのマークをつけるための処理をcell make mark.viで行っています。

それぞれの状態ごとに処理を変えていくことを繰り返し、ユーザーがゲームをクリアするかあるいか一定回数間違えるまでプログラムは続きます。
作問する場合
さて、ゲームを自分で作る場合の醍醐味の一つは、問題も自分で作れる、という点にあると思います。
このピクロスも自由に作問できるので、その作問のためのプログラムの例も紹介していきます。

それこそ盤面の行、列の数も自由に設定できるわけですが、そんな作問のためのプログラムは専用のviを用意し以下のフロントパネルとしています。
作問のための盤面はブールの2次元配列でいいですが、今回はクラシックスタイルのクラシックチェックボックスの2次元配列を使いました。
ファイルパス制御器は、問題を保存するためのcsvファイルのパスを指定します。
作品名と書かれた文字列制御器は、ゲームクリア時に、この絵は何でした、という答えを表示するのに使用します。

プログラムはイベントストラクチャを使用していますが、ファイルに問題データを保存する以外はなるべくシンプルにしました。

問題を作る時には、完成図を作り、この完成図の状態から、各行および列に対する「連続したマスの数」を表す数字に変換していきます。

このプログラムの形式で保存した問題であれば、上で紹介したプログラムのproblem file read.viおよびこれ以降のプログラムで正しく動作します。
本記事では、ピクロスを作る方法を紹介しました。
LabVIEWでピクチャを使用すると色々な表現ができるものの慣れないと座標の指定の仕方にてこずったりするので、慣れるための例として参考になればうれしいです。
ここまで読んでいただきありがとうございました。
コメント