プログラムリスト
1 VAC :FOR B=1 TO 5:PRINT B:FOR H=-27 TO 4:I(ABS H)=1:NEXT H:FOR C=1 TO 25
2 D=.1*INT (62+RAN#*4)+INT (RAN#*(8+B:FOR E=0 TO 24:IF D=L(E) THEN 2
3 NEXT E:$=" ♠♥♦♣A23456789XJQK*123Hs4fRS":GOTO 7
4 PRINT "[";MID(FRAC G*9+1,1);MID(G,1);"]";
5 F=F(H*J+I):G=F(H*K+I):RETURN
6 H=1:FOR I=5 TO 25 STEP 5:GOTO 10
7 G=D:GOSUB 4:PRINT I;K;:GOSUB 4:INPUT F:IF ABS (F-3<3;I=K:K=F
8 IF F+G≠1 THEN 7
9 F(5*K+I)=D:NEXT C:FOR I=1 TO 5
10 C=1:D=9:E=2:FOR J=1 TO 5:FOR K=1 TO 5:GOSUB 5:IF INT F≠INT G;D=D-.5
11 IF F>INT G*2.5;E=SGN E:GOTO 13
12 IF INT F-G>4;E=0
13 NEXT K:IF FRAC F≠FRAC G;C=0
14 NEXT J:C(C)=6+E:PRINT :IF 0>D*E;D=4
15 A=A+INT (D*9*FRAC (SQR 27*D↑4:PRINT MID(D+20,1);A;:NEXT I:GOTO I
30 STOP :IF A≧B*90;NEXT B:PRINT "CLEAR!!"
15行に続くのが30行なのに注意しましょう.それについての解説は技術情報で.
変数の増設
DEFM 10
EXE
実行の前に変数を10個増設します.(8ステップx 10個 = 80ステップ)
プログラム自体は464ステップで、変数増設分の80ステップとあわせてちょうど544ステップになります.
既知の問題
PB-100 初期出荷バージョンの VAC
に絡む異常動作
1行目の VAC
に FOR
文が続いているため、初期出荷バージョンの VAC に絡むエラーに遭遇します. 該当する機種では VAC
を外した上で、ゲーム開始の前にマニュアルで実行してください.
技術情報
変数表
変数 | カード配置時の内容 | 役判定時の内容 |
---|---|---|
A | 得点 | |
B | ステージ | |
C | FOR~NEXT | 0:フラッシュ不成立, 1:成立 |
D | 新カード | ペア系判定、役 |
E | FOR~NEXT | 0:ストレート不成立, 1:X, J, Q, K, A と並ぶロイヤルストレート, 2:ストレート |
F | 座標入力 | 判定カード1 |
G | カード表示 | 判定カード2 |
H | =5 | 5→1 |
I | X 座標 | FOR~NEXT |
J | =1 | FOR~NEXT |
K | Y 座標 | FOR~NEXT |
L(0)~L(24) | カード |
行番号マップ
行 | 処理 |
---|---|
1 | 初期設定他 |
2 | 新しいカード、チェック |
3 | キャラクタセット他 |
4 | トランプ表示 |
5 | カード読み出し |
6 | 判定ループ後半 |
7 | 配置画面表示、キー入力、座標変更 |
8 | カード配置用判定 |
9 | 新しいカードを置く、判定ループ前半他 |
10 | 役判定初期設定、ペア系判定他 |
11 | X, J, Q, K, A と並ぶロイヤルストレートの判定 |
12 | ストレート判定 |
13 | フラッシュ判定 |
14 | フラッシュ、ストレートフラッシュ、ロイヤルストレートフラッシュの決定.ストレート(ロイヤルストレートを含む)の決定. |
15 | 得点計算、役と合計点表示 |
30 | クリア判定、エンディング表示他 |
初期化
FOR H=-27 TO 4:I(ABS H)=1:NEXT H
1行目ではカードの初期化(L(0)~L(24)=1)と同時にカード配置用変数の初期化(H=5, I~K=1)も行っています.
小数部に格納したマーク情報の取り出し
2行目の頭で乱数から新しいカードがつくられます.RAN#
が2つありますが、これは1つの変数で整数部にはトランプの数字を、小数部にはマークを格納しているのです.
この式の中の62とは何でしょう?
D の値の最小は6.2となり、これは ♠ の A を表します.
ここで併せて4行目のカードの表示を見て下さい.マークを表示するため小数を抜き出すときに FRAC G*10
としてしまいがちですが、 小数部の最小値を0.2にしてあるため FRAC G*9
とすることができます.
さらに整数部の最小値を6にしカード未配置の値を1にしたことで、数字の表示は MID(G,1)
とダイレクトに行っています.
ポーカーの役判定アルゴリズム ペア系
10行以降、いよいよ役の判定アルゴリズムを見ていきましょう.
ポーカープログラムを組んだことがあるという方ならご承知のように、ペア系の判定には有効な方法が知られています.私はアスキー社の書籍『応用 BASIC』(桜田幸嗣, 1987年)で学びました.
まず、正五角形の5つの頂点に判定する5枚のカードを置いていきます.そして計10本の対角線について、 数字が同じものを数えます.1本ならワンペア、2本ならツーペア、3本ならスリーペア、4本ならフルハウス、フォーカードは6本です.
本作に固有の事情として、10行目 FOR J=1 TO 5:FOR K=1 TO 5
とあるように、2枚のカードの比較を25回しています. これは省メモリの為の処置で、同一のカードを比較する、という不毛な処理をしています.
一般的なプログラミングでは、対角線の数と同じ10回のループで行うのが良いでしょう.例えば、FOR J=1 TO 4:FOR K=J+1 TO 5
が判定ルーチンを囲むループ文になるでしょう.
ポーカーの役判定アルゴリズム ストレート系
このようにペア系が簡潔に判定できるのに対し、ストレートの判定には「数字の大きさ順に並び替えたのち、比較する」という方法を検討するかもしれません.
square では 5つの数字の差が4以内であるかを調べます(12行).その上で、ペアが無い(0本)ならストレート成立という判定をしています. この4以内の判定は、ペア系判定と同じループの中で実施できる点も良いですね.(14行 IF O>D*E;D=4
)
さらに付け加えると、14行の頭では5つの数字の差が4以内で、その上フラッシュの場合、 ペア系のチェックを経ずにストレートフラッシュかロイヤルストレートフラッシュと判定しています(C(C)=E+6
).
また、11行目の IF F>INT G*2.5;
では、ロイヤルストレートの判定を行っています. F が X(15.2~15.5) 以上、G が A(6.2~6.5) の場合にだけ、ロイヤルストレートのフラグが立つことになります.
本作に固有の事情として、ペア系の判定で触れた25回のループの効能で、差(絶対値)の比較ではなく、値の比較をしています.(12行 IF INT F-G>4;
)つまり絶対値を求める処理(ABS(INT F-INT G)
)を省けています.
一般的なプログラミングでは、10回のループの中で絶対値を求めて判定するのが良いでしょう.
役の得点を求める
D*9*FRAC(SQR 27*D^4
こうして役の判定結果が D に格納されましたが、困ったことに役の強さと D の数値が必ずしも一致しないのです.
ブタは D=-1、ワンペアは D=0、ツーペアは D=1、 スリーカードは D=2、ストレート(ロイヤルストレートを含む)は D=4、フラッシュは D=6、 フルハウスは D=3、フォーカードは D=5、ストレートフラッシュは D=8、 ロイヤルストレートフラッシュは D=7 という具合です.
既にお気づきのように3行の $ の内容も役の強さ順ではありません.役の表示はこうして解決できるとして、問題は得点です.
一般的なプログラミングでは、D と得点の対応表を用意することを検討するかもしれません.しかし PB-100 の限られたメモリではそうもいきません.
そこで15行頭では得点を数式から求めています.ゲームで利用するデータの数式からの生成は、広大なダンジョンを実現した『トロネコの大冒険4』では核心的テクニックです.本作のソレはトロネコに比べれば地味ですが、この式無しには544ステップに収まりませんでした.
D*9*FRAC(SQR 27*D^4
は square を完成に漕ぎつけるために PB-100 を数時間走らせて見つけた式なのです!!
あと10ステップも余裕があればこのように頑張った開発にはならなかったのでありました.
変則的なジャンプ命令
15行の最後では NEXT I:GOTO I
と見なれないことをしてます.
1回目にここを通過するときは I=6 で6行に飛びます.2回目には I=30 で30行へ.
この変則的な方法で、縦の5組のチェックから横の5組のチェックへの切り替えを行います.
コラム
行番号に絡むリストの圧縮
GOTO 0
行
一般に BASIC において行番号は、のちのちの追加がしやすいように10おきに、などと入門書には書かれています. しかし Pocket BASIC では、そんな悠長なことをしてられないことがしばしば、私の作品にいたってはそれは必然であります.
PB-100 では GOTO, GOSUB
の後にくる行番号はその字数分だけステップを使うため、また一行に62文字までの制限のために、 おいそれと巨大な行番(Version 1 BASIC では100行~999行)を使うわけには行きません.
とくに1行~9行は、そこにジャンプするのに( :GOTO 1
など)3ステップで可能な点も有効利用したいところです. こうなると僕は0行もありにして欲しかったです.
行番号が一定の数列でなくてもよい
それに加えて square では、必ずしも行番号が一定の数列(等差数列)でなくてもよいことを利用してリストを最適化しています.
ご覧のように15行の後に30行が続くのがそれで、変数の変化を把握して :GOTO I
のみで分岐させるように工夫した結果、行番号が離れることになりました.
PB-100 では GOTO N*5+9
などと飛び先を変数を含む式で指定して分岐させる方法が多用されますので、以上を頭の隅に置いておけば式部分を圧縮できる場合もでてくるでしょう.
また PB-400 等の Version 2.0以降の BASIC では ON GOTO, ON GOSUB
文を備えていますが、Version 1機との互換性のために使用は控えたいところです.