見出し画像

この道はいつか来た道~サブルーチンについて

Pi STARTER(ラズベリーパイ用BASIC言語パッケージ)版
ひとりで遊べる人狼ゲームを公開中(要ラズパイ&Pi STARTER)

http://www.ne.jp/asahi/chitose/net/download/jinrou/

プログラミング超ド素人の七転八倒ドキュメントを記してきた本連載ですが、今回はそのハイライト的な内容を記載します。そういやハイライトっていま450円もするんですってね。改正健康増進法のもと全国で受動喫煙防止条例出てるし、愛煙家は大変だなー。ほらまた話がそれた。

画像1

サブルーチン。今日はこの言葉をぜひ覚えていってください。これの威力はすごいです。一般人がこれを覚えるとスーパーサイヤ人くらいになれます。レベル1の冒険者がいきなり勇者の剣を持って俺様最強の無双モードです。
…というのはアオリにしても言い過ぎですが、いやでもマジすごいっすよサブルーチン。サブちゃんのルーチンじゃないっすよ。私がプログラムミングを投げ出さず、いまも趣味として続けていられるのはこれを身に着けて覚醒したからにほかなりません。そんだけ重要なのに最近のプログラミング解説書ではあまり丁寧に解説されていないように感じます。こういう機能あるから覚えといてねみたいな感じで。どちらかというと「FOR~NEXT」にあたる一定回数繰り返し処理の方が丁寧に解説されているような気がします(私はあれあんましありがたみを感じないんですが…まぁ私のやりたいことと相性が合わないんでしょうねきっと)。

さて、「人狼ゲーム」では「誰かを選ぶ」場面がたびたび登場します。
ざっと例をあげると、

・議論時に誰かを名指しするとき

画像2

・議論時に自身の役職を宣言し、占った対象の名前を言うとき

画像3

・投票時

画像4

・ボディーガードが誰を守るか選ぶとき

画像5

・人狼が誰を襲うか決めるとき

画像6

・予言者が占うとき

画像7

こういう処理をプログラミングで作るのが難しいかっていうと、そんなことはありません。条件分岐(IF~THEN)と変数(A=○)と入力(INPUT)の概念を理解していれば、あとは労力だけの問題です。

そう「労力」ですよ。

私が最初にそれを意識したのは、議論時のプレイヤー発言処理を作っているときのことでした。
(このときはまだCPUプレイヤーの発言はランダムだった)
このゲームは性質上、誰かしらを上げたり落としたりする発言がつきもの。私のプログラムでは、初回で次の4通りの発言を考えていました。

画像8

でまぁ、とりあえず指名処理作ってみたですよ。
言葉で書いていくと、こんなプログラムになります。

・その時点の対象者だけを表示し、番号を入力させて選ばせる(INPUT)。
(例えば投票時などは、死んだり追放されたりしている者は変数を見てIF~THENではじいて表示しないようにする)

・間違った番号が入力されたらメッセージを出す。
(これも入力された番号をIF~THENで判定し、有効な数字以外はメッセージを表示して指名処理の最初に戻す)。

・番号が妥当なものであれば先に進む。

画像9

確かにこれ難しくはないのですが、やらなきゃいけないことがたくさんあってちょっとしんどい処理内容です。
しかも「その時点」ってのが曲者で、たとえばこれ議論時に予言者(偽物の場合も含めて)が占いの結果を言うときなどは死んだり追放されたりしている者も対象にしないといけないわけですよ。さらに言うと人狼は襲撃シーンで仲間の人狼を外さなきゃならないし、ボディーガードは前回指名した者を外さなきゃならないし、投票時の決選投票では残った者以外を外さなきゃならないし…。

うわあぁぁぁぁぁぁぁぁ!!!!!!!!!
無理無理無理無理無理無理無理無理!!!!!!!!!!

指名処理が発生するたびにこれ書くの?
正気?
これで終わりじゃないんだよ?
これから先何度も何度も何度も何度も?
しかもケースごとに表示する条件も変えて?

私はこのときはじめて「もうやめよう」と思いました。
いまならまだ、すべてなかったことにして日常に戻れる。
しんどかったけどいい経験した。
それでいいじゃないか。
気分はセリヌンティウスを置き去りにした走れメロス。

画像10

でも、と思ったのですよ。
こういうことを、世間でプログラミングしている人はどう解決しているんだろうかと。
たぶんこういう問題って私が初めてブチ当たってるわけじゃないはず。
何かあるんだ、これを突破する方法が。
BASICでそういや何かあった、何だろう?確か昔読んだ気がするんだけど。
ググッてみたらと言うけれど、何てググればいいかも分からない。

こういうときは、先人の力を借りる!
すなわち、昔の人が書いた書物にすがるッ!

画像11

高橋はるみさんを昔の人っていうの何か失礼じゃないか?と思いつつ、昔にかえってページを開く。この方は私が中学生くらいのころマイコン少年の間でスタープログラマー的な地位を確立していた方で、写真のような著作を何冊も記されたプログラミングのお師匠様でした。
その書き方の特徴は「制作するにあたって考えた過程」をぜんぶ記すことに徹したドキュメントになっていること。うまくいったこともそうでないことも、回り道したことも、試行錯誤の過程をぜんぶ書いたうえで最終的に完成したプログラムを提示するというのが基本スタイル。何であれ悩む人ってだいたい同じようなところで詰まるので、この方の本のように
「私は何で悩んで何を試したけど何がダメでこうしたらうまくいった」

みたいな実例を書いてくれているのが一番参考になるのですよ。

画像12

(写真撮ってるのは著作権に抵触していると思いますが、もうほぼ手に入らない書物ですし、一部引用ということでご容赦いただけると幸いです)

で、見つけましたよ!

「GOSUB~RETURN」
(GOSUBで指定した場所に飛ばし、RETURNでGOSUBの直後に戻ってくる)

これを使うと、例えば指名処理だけをプログラムの流れとは全然別の場所に置いておいて、必要な時だけそこに飛ばし(GOSUB)、用が済んだらまたプログラムの流れの中に戻ってきて(RETURN)、また必要になったらそこに飛ばす…という使い方ができるってわけ。

そしてこのとき飛ぶ「使いまわし可能な処理の流れ」を「サブルーチン」というのだそうです。
「サブルーチン」ってそういうことか!
言葉だけは耳にタコができるくらい聞いてきたけど、自分で使ってみるまでどんな代物か分からんかったわ!
…そういう言葉ってありません?

「『サブルーチン』って言うんだがね…
 自動的に指名処理を実行してくれているんだ…
 ラベル…『@WHOの中』でね…
 おまえが誰かの名前をそいつにしゃべったら
 自動的に RETURN が出て
 ……『戻ってきた』
 誰を指名した? ン?
 教えてくれよ…誰を指名して来た?」

画像13

ともあれこれさえ分かれば繰り返し出てくる指名処理もこわくありません。すべての役職などによる例外的処理もサブルーチンの中でまとめてしまえば良いからです。

それとともにこれまで抱えてきた不安すべてがこれによって吹き飛び、当初の目標だった「デタラメでも最初から最後まで止まらずに動く人狼ゲーム」が一応完成するに至ったので、その貢献度たるや産業革命クラスです。プログラミングをやる人から見たら「お前そんなバカなとこで詰まってたの?」と言われるかもしれませんが、ド素人から見たら解説書にひとこと「サブルーチンを実行します」とか書かれてても何のことやらさっぱりてなもんですよ。とはいえ昔さんざんベーマガ等で打ち込んでいた命令ではあるので、あのころもちょっと丁寧に勉強していたら、いまごろ違う人生を歩んでいたんだろうなぁと少し反省もしています。

画像14

さて、最後に「指名処理」のサブルーチンのリスト。
実際の完成物では、予言者(本物)が人間と占った者を緑色で表記したり、人狼と分かっている者を赤色で表記するなど工夫しています。
でもこれ、もちょっと短くできるかもなぁ…。

'プレイヤー が ほか の メンバー を えらぶ とき'
@WHO
COLOR RGB(255,255,255) '← 文字色を白に'
PRINT
PRINT "だれ を えらびますか?"
PRINT
@WHO_B '← 選択肢にプレイヤーBを表示する/しないの処理'
IF R<2 AND B[6]>0 THEN @WHO_C '← Bがいない場合表示しない(予言者時除く)'
IF R!=8 AND B[6]>0 THEN @WHO_C '← Bがいない場合表示しない(予言者宣言ではないとき)'
IF R==8 AND A[32]>0 THEN @WHO_C '← 予言者宣言時、一度Bが選択されている場合は表示しない'
IF R==6 AND O>0 AND B[5]<10*O THEN @WHO_C '← 決選投票時、Bが選ばれていないときは表示しない'
IF A[2]==1 AND B[8]==1 THEN COLOR RGB(0,255,0) '← 予言者のとき人間と判明したものを緑色で表示'
IF A[2]==1 AND B[8]==5 THEN COLOR RGB(255,0,0) '← 予言者のとき人狼と判明したものを赤色で表示'
IF A[2]==5 AND B[2]==5 THEN COLOR RGB(255,0,0) '← 人狼のとき、仲間の人狼は赤色で表示'
IF R==5 AND B[2]==5 THEN COLOR RGB(255,0,0) '← 人狼のとき、仲間の人狼は赤色で表示(夜の襲撃時)'
IF R==2 AND B[7]==1 THEN COLOR RGB(0,128,255) '← ボディーガードのとき、前回指名したものを薄青色で表示'
PRINT "1.";B$ '← 番号とプレイヤーBの名前を表示'
COLOR RGB(255,255,255) '← 文字色を白に'

'(~ ここはBからJまで同じプログラムが書いてあるので間を飛ばしてBとJだけ掲載)'

@WHO_J '← 選択肢にプレイヤーJを表示する/しないの処理'
IF R<2 AND J[6]>0 THEN @INPUT_WHO
IF R!=8 AND J[6]>0 THEN @INPUT_WHO
IF R==8 AND A[40]>0 THEN @INPUT_WHO
IF R==6 AND O>0 AND J[5]<10*O THEN @INPUT_WHO
IF A[2]==1 AND J[8]==1 THEN COLOR RGB(0,255,0)
IF A[2]==1 AND J[8]==5 THEN COLOR RGB(255,0,0)
IF A[2]==5 AND J[2]==5 THEN COLOR RGB(255,0,0)
IF R==5 AND J[2]==5 THEN COLOR RGB(255,0,0)
IF R==2 AND J[7]==1 THEN COLOR RGB(0,128,255)
PRINT "9.";J$
COLOR RGB(255,255,255)
@INPUT_WHO
PRINT
PRINT "ばんごう で せんたく"
INPUT Y '← 番号を入力させる'
@INPUT_B
IF Y==1 THEN
IF R==1 AND B[6]==1 THEN @INPUT_BANISH '← 追放されています'
IF R!=8 AND B[6]==1 THEN @INPUT_BANISH '← 追放されています'
IF R==1 AND B[6]>1 THEN @INPUT_DEAD '← 死んでいます'
IF R!=8 AND B[6]>1 THEN @INPUT_DEAD '← 死んでいます'
IF R==8 AND A[32]>0 THEN @INPUT_YOGEN '← 既に選んだよ'
IF R==5 AND B[2]==5 THEN @INPUT_JINROU '← 共食いはやめてね'
IF R==2 AND B[7]==1 THEN @INPUT_GUARD '← さっき守ったよ'
IF R==6 AND O>0 AND B[5]<10*O THEN @INPUT_FAILED '← その数字は選べないよ'
P=2
ENDIF

'(~ ここもBからJまで同じプログラムが書いてあるので間を飛ばしてBとJだけ掲載)'

@INPUT_J
IF Y==9 THEN
IF R==1 AND J[6]==1 THEN @INPUT_BANISH
IF R!=8 AND J[6]==1 THEN @INPUT_BANISH
IF R==1 AND J[6]>1 THEN @INPUT_DEAD
IF R!=8 AND J[6]>1 THEN @INPUT_DEAD
IF R==8 AND A[40]>0 THEN @INPUT_YOGEN
IF R==5 AND J[2]==5 THEN @INPUT_JINROU
IF R==2 AND J[7]==1 THEN @INPUT_GUARD
IF R==6 AND O>0 AND J[5]<10*O THEN @INPUT_FAILED
P=10
ENDIF
IF Y<1 OR Y>9 THEN
COLOR RGB(255,255,0)
PRINT
PRINT "ただしい すうじ を"
PRINT "にゅうりょく して ください"
BEEP 16
' PRINT
' LINPUT N$
GOTO @WHO
ENDIF
PRINT
RETURN '← 正しく数字が入力された場合は、ここに飛ばしたGOSUBのところに戻れ'
@INPUT_BANISH
COLOR RGB(255,255,0)
PRINT
PRINT "そのもの は ついほう されています!"
BEEP 16
'PRINT
'LINPUT N$
GOTO @WHO
@INPUT_DEAD
COLOR RGB(255,255,0)
PRINT
PRINT "そのもの は しんで います!"
BEEP 16
'PRINT
'LINPUT N$
GOTO @WHO
@INPUT_FAILED
COLOR RGB(255,255,0)
PRINT
PRINT "そのもの は せんたく できません"
BEEP 16
'PRINT
'LINPUT N$
GOTO @WHO
@INPUT_JINROU
COLOR RGB(255,255,0)
PRINT
PRINT "おいおい おれ は なかま だぜ?"
PRINT "ともぐい は やめろよ"
BEEP 16
'PRINT
'LINPUT N$
GOTO @WHO
@INPUT_GUARD
COLOR RGB(255,255,0)
PRINT
PRINT "れんぞく で おなじ ひと を"
PRINT "まもる こと は できません"
PRINT "えらびなおして ください"
BEEP 16
'PRINT
'LINPUT N$
GOTO @WHO
@INPUT_YOGEN
COLOR RGB(255,255,0)
PRINT
PRINT "そのもの は すでに えらびました"
PRINT "えらびなおして ください"
BEEP 16
'PRINT
'LINPUT N$
GOTO @WHO

いったんプログラムとして「最低限動くレベル」では完成したものの、人に遊ばせるにはもうちょっとチューンナップが必要です。ということで、ここから「各プレイヤーの感情(信用度)」の要素やら、2日目以降の役職ごとの発言の違い、嘘吐きの存在が判明したときの状況の変化などをプログラムの中に入れていきます。

ということで、次回は各ブレイヤーの信用度をめぐる話。
あぁだんだんプログラミングの沼に入っていく…。

この記事が気に入ったらサポートをしてみませんか?