この記事で扱っていること
- 電気イスゲームを作る方法
を紹介しています。
注意:すべてのエラーを確認しているわけではないので、記事の内容を実装する際には自己責任でお願いします。また、エラー配線は適当な部分があるので適宜修正してください。
某テレビ番組で、電気イスゲームというゲームが行われていました。
ルールはシンプルで、以下のようなものです。
(番組では攻撃側、守備側という表現はしていませんが、便宜上ゲームに参加する2人をこう区別することにしています)
- 最初12個のイスがあり、1から12まで番号が振られている
- 残っているイスのうち、攻撃側は1つ選び、電気を流す装置をONにする(このとき守備側はどれを選んだかを見てはいない)
- 守備側は残っているイスから1つ選んで座る(このとき攻撃側は守備側がどのイスを選ぶかを見てもOK)
- 攻撃側と守備側が合意したら、攻撃側が電気を流すためのボタンを押す。
- もし攻撃側が選んだイスに守備側が座っていたら電気が流れアウト扱いとなり、これまで守備側が獲得していたポイントが0になる
- もし攻撃側が選んだイスに守備側が座っていなかったらセーフ扱いとなり、守備側はそのイスについた番号をポイントとして獲得する。また、セーフになったイスは取り除かれ、以降そのイスには誰も座れなくなる
- 攻撃側が選んだイスに守備側が座ったかどうか関係なく攻守交替する
- 終了条件を満たすまで攻撃、守備を繰り返す
この終了条件とは、どちらかが3回電気をくらうか(くらった方の負け)、どちらかのポイントが40以上になるか(なった方の勝ち)、イスが残り1つになった時点でポイントが高い方の勝ちとなる、という条件になります。
番組ではお互いの性格や行動を読んでの心理戦が繰り広げられて見ごたえがありますが、これをLabVIEWプログラムで組むとどうなるかについて考えてみました。
当然(?)電気を流すような仕組みにはしていませんが(もちろんハードウェアと連携すれば近いことはできると思いますが)、ルールは上記に沿ったプログラムを組むことができます。
どんな結果になるか
フロントパネルには、プレイヤーAとプレイヤーBの得点およびアウトの数を表す表示器、そしてイスに見立てた12個のボタンを設けています。
また、ゲームをやり直す、攻撃が失敗したときに、守備側が「攻撃側はどのボタンを選んだのか」を確認するためのボタン、次のゲームへ進むためのボタンを配置しています。
ボタンは、1から12の番号が振られたものは全てスイッチ、それ以外はラッチ動作としています。
プログラムを実行すると、最初プレイヤーAの攻撃が始まります。
以降の操作もそうですが、今誰の操作の番かといった情報をステータスとして表示します。
プレイヤーAはボタンを一つ選ぶのですが、このとき、Enterキーを押すまでボタンを選びなおすことができます。
Enterキーを押したら、Aがあるボタン(以下の例では「5」を選択)を押したという状態(制御器がTrueの表示になっている状態)は見えなくなり(Falseとなり)、次にプレイヤーBがボタンを選ぶ番となります。
こちらも、Enterキーを押すまでボタンを選びなおせ、Aが選んでないと思われるボタンを選んでいきます。
見事Aが選んでいないボタンを選べたら、そのボタンの番号がBの得点になります。
例えば以下の図ではBが「10」を選んだ場合に、Aが選んだ「5」とは異なるので、Bに10点が加算されています。
また、10のボタンは取り除かれ、以降ゲームで選択できなくなります。
もしBが、Aが選んだボタンを選んだ場合にはBのアウトカウントが一つ点灯し、ポイントも0になります。
この攻撃、守備の操作が終わった後、攻守交替するには次へボタンを押しますが、次へボタンを押す前に確認ボタンを押すことで、Aの攻撃が成功したかどうかに関係なく、Aが選んだボタンを表示させることができます。
プログラムの構造
プログラムの動作を最適化するのであれば、このようなボタン操作をともなったプログラムはイベントストラクチャを使用したり、ゲーム状態の処理やUIの更新等の機能ごとに複数のループを作るほうがベターだと思います。
ですがそのようなプログラムは今回のゲームについては少しオーバースペックでありかつ作るのも面倒なので、単純なステートマシンで実装できるようにしました。
常にループが回り続けるようなプログラムとなっているので、待機関数でループの周期を調整してください(下図だと100ミリ秒としています)。
ケースの数は7個ありますが似たような処理をしていたりほとんど何もしないケースもあるので実際にはそこまで作るのは難しくないと思います。
なお、ステートマシンの中で扱うデータが複数あったため、一つのクラスタでまとめています。
クラスタの中身は以下を参考にしてください。
ブールのリファレンス配列は、数字のついたボタンすべてを含んでいます。
また、クラスタの中身にturnという列挙体がありますが、これには「A」という要素と「B」という要素の二つしか入っていません。
また、イベントストラクチャを使用していないのですが、いつでも最初からやり直したりゲームを終わらせることができるように、ステートマシンの各ステートごとの処理を記述するケースストラクチャの外で、これらのボタン操作への処理を行っています。
他にも、常にUI上の値を更新しうる、A pointやA out count、ステータスの表示についてもケースストラクチャの外に出しています。
このようにすることで、ローカル変数を使用せずにプログラムを書くことができます(全体を通して今回のプログラムではローカル変数は使用していません)。
ではそれぞれのステートの中身を確認していきます。
まずはinitializeステートです。
表示の初期化を行うために、ステートマシンのクラスタの中身の初期化を行っています。
次に、select out buttonステートです。
主にユーザーに操作を促すメッセージの値の更新と、特定のキーボードのキー(今回はEnterキー)が押されたら次のステートに進む、という処理を行っています。
今回はEnterキーが押されたら次のステートに進むようにしていますが、フロントパネルにボタンを配置してそれが押された時、だったり、はたまたハードウェアを用意してそのハードウェアからの特定の信号で反応させる(デジタル信号でTrueを受け取ったとき、など)ようにもできます。
クラスタのturnデータにより、今どちらのターンなのかを判断し、それによって表示するメッセージを変えています。
メッセージの種類は何でも構いません、わかりやすい表示にします。
キーボードの入力値を読み取ってそれがRETURN(Enterキー)だった場合には、どのボタンが押されたかを判断して、それをクラスタのout numberデータとします。
もし攻撃側がボタンを全く選ばずにEnterを押したり、2個以上選んでEnterを押した場合には警告を出すようにしておきます。
また、攻撃側がボタンを1個押してEnterした後は、そのボタンの表示をFalseにするのを忘れないでください。(RETURNの中のケースストラクチャのTrueケースの中でプロパティノードを使用して行っています)
なお、さりげなく待機関数に500と入力していますが、ここに待機関数がないと、次のステートの処理でもEnterキーの入力状態を判断する場面がありそちらでのEnterキー入力と被らないようにするために若干動作を遅めにするために設けています。
次にselect safe buttonステートです。
select out buttonステートとほとんどやっていることは変わりません。
こちらでも待機関数を使用していますが、これは、結果発表のドキドキ感を演出するためにEnterキーを押してからワンテンポ遅れて結果を表示するためです。
ケースストラクチャの中身も以下のようで、select out buttonをほぼ複製して作れてしまいます。
ステータスに表示するメッセージの内容は変更が必要です。
次にjudgeステートです。
攻撃側と守備側が選んだボタンが一致するかを判定し、ケースストラクチャで処理を変えています。
一致する場合、守備側のアウトカウントを増やし、また守備側のポイントを0にする必要があります。
ケースの中身は以下のようにしておきます。
もし攻撃側と守備側が選んだボタンが一致しない場合には、守備側が押したボタンが守備側のポイントに加算されます。
このとき、今回のプログラムの方法だと、selected number(守備側が選んだボタンの番号)に1を加えた値がボタンの番号と一致するようになるのでインクリメントを使用しています。
次はresultステートです。
終了条件を満たしているかどうかを判定していきます。
ルールを変更したい場合には、このステートの中身の処理を変更すればいい、ということになります。
複数の判定条件ごとにいちいちケースストラクチャを使用するのはブロックダイアグラムの幅を大きくとるので、複数の判定結果それぞれのブール値をまとめて数値にし、その値によって表示するメッセージを変えています。
2つめのケースストラクチャがTrueのときのメッセージもまた自由に編集してください。
イスが残り一つになった場合の終了時の状態としては、Aが勝つ、Bが勝つ以外に引き分けがあり得ます。
どの終了条件も満たさない場合に次のwait for commandステートに移ります。
wait for commandステートでは、ユーザーが「仕掛けられたボタンを確認する」か「次のゲームへ進む」を押すまで何も変化はありません。
2個目のケースストラクチャの中身がごちゃごやしているのは、今何ゲーム目か、どちらの攻撃の番かをわかりやすく表示するためのround messageの表示を適切に扱うために処理している部分になります。
round messageには、1-A、1-B、2-A、2-B、3-A、・・・などと表示されていくことになります。
最後はendステートです。
終了条件を満たすとこのケースに移ってプログラムが終了します。
規定回数で終わりにしたい場合
実はこのゲーム、番組では、攻撃、守備をそれぞれ8回ずつやったら終わり、というルールもあるのですが、実際には電気を3回くらったら終わりおよびイスが1個になったら終わりというルールもあるので9回以上続くことはありません。(それまでにどちらかの終了条件を満たします)
ただ、回数制限を設けると戦略性が変わると思うので、この点についてプログラムを改造するのであれば、回数を指定して終わらせるようにできます。
既に書いたように、終了条件についてはresultステートで記述するので、例えば6回戦までで終わらせる場合には、round messageが「6-B」であるかどうかを判定させるようにします。
N回戦までであれば「N-B」になる、ということですね。
今回のプログラムはイベントストラクチャを使用せず、ユーザーの入力を待つようにしています。
そのため、ステートマシンのWhileループは常に回り続けています。
この性質を利用して、例えば攻撃側、守備側に時間制限をつけるようにするのも面白いと思うので、チャレンジしてみてください。
本記事では、電気イスゲームをLabVIEWで作成する方法を紹介しました。
たまにはこのようなゲームをLabVIEWで作るのも面白いと思いますし、プログラム作りの腕試しにもなると思うので、興味があれば作ってみてください。
ここまで読んでいただきありがとうございました。
コメント