SHIROのIchigoJam日記

マイコン「IchigoJam」(イチゴジャム)の電子工作とプログラミングをメインに

三目並べ

子どもの頃に地面やアスファルトに書いて遊んだ人が多いと思う「三目並べ」を作ってみました。
f:id:shiro0922:20200930082917p:plain

遊び方

  • プログラムを動かすと、3×3のます目が表示されます。自分(YOU)かコンピュータ(CPU)か、ランダムにどちらかが先手になります。
  • 自分(YOU)の手の時は、上下左右の矢印キーでカーソルを移動して、スペースキーで打ってください。自分の手は「○」(丸)で表示されます。
  • コンピュータ(CPU)の手の時は、プログラムが自動で考えて手を打ってきます。コンピュータの手は「×」で表示されます。
  • どちらかの手が、縦・横・斜めのどこかで三目そろうと、そのプレイヤーの勝利(WIN)です。どちらもそろわずに9手が終わった場合は引き分け(DRAW)です。

f:id:shiro0922:20200930083451p:plain

f:id:shiro0922:20200930083503p:plain

プログラム

1 '*3MOKU
2 CLV:VIDEO 5:CLS
3 LET [10],7,3,11,3,15,3,11,3,7
4 FOR N=1 TO 3:?"...":NEXT
5 IF RND(2) GOTO 14
6 LC 0,3:?"YOU"
7 X=X-BTN(28)*(X>0)+BTN(29)*(X<2)
8 Y=Y-BTN(30)*(Y>0)+BTN(31)*(Y<2)
9 LC X,Y,1:Z=X+Y*3:WAIT 5
10 IF !BTN(32) OR [Z]!=0 GOTO 7
11 [Z]=1:LC X,Y:?"O":S=S+1:BEEP
12 T=1:GSB 30
13 IF E=3 OR S=9 GOTO 26
14 LC 0,3:?"CPU"
15 G=0:H=0:T=-1
16 FOR Y=0 TO 2
17 FOR X=0 TO 2
18 GSB 30
19 W=[Z+10]+E*E*20+F*F*10
20 IF W>H && [Z]=0 G=Z:H=W
21 NEXT:NEXT
22 Z=G:X=Z%3:Y=Z/3
23 [Z]=-1:LC X,Y:?"X":S=S+1:BEEP 30
24 T=-1:GSB 30
25 IF E!=3 && S!=9 GOTO 6
26 LC 4,3
27 IF E=3 ?"WIN!";:BEEP 10,30
28 IF S=9 ?"DRAW";:BEEP 30,30
29 CLK:END
30 Z=X+Y*3:L=[Z+10]:E=0:F=0
31 FOR M=0 TO 3
32 C=0:D=0
33 IF L&(1<<M) GSB LINE()+4+M*3
34 IF C>E E=C
35 IF D>F F=D
36 NEXT:RTN
37 FOR U=0 TO 2
38 A=[U+Y*3]:GSB 49
39 NEXT:RTN
40 FOR V=0 TO 2
41 A=[X+V*3]:GSB 49
42 NEXT:RTN
43 FOR U=0 TO 2
44 A=[U*4]:GSB 49
45 NEXT:RTN
46 FOR U=0 TO 2
47 A=[2+U*2]:GSB 49
48 NEXT:RTN
49 C=C+(A=T):D=D+(A=-T):RTN

ネットを検索して調べると、「三目並べ」はプログラミングの課題として有名なようで、再帰呼び出しで全ての手(パターン)を調べてコストを計算して…とCPUの最善手を考えるようです。
このプログラムのCPUは、人間と同様に「縦・横・斜めの列の可能性が多い所へ打つ」「自分が3個そろいそうな所へ打つ」「相手が3個そろいそうだったら止める」という思考で、何とか1kバイトに収めました。
CPUもかなり完璧に考えるので、人間が真剣にプレイすると全てのゲームがドローになります。そうか、それでつまらないから、IchigoJamの三目並べのプログラムが今まで無かったんだな…(^_^;)

プログラム説明追記

IJUtilitiesで行番号追加・ラベル削除する前のリストを掲載します。
思考系ゲームを作る方の参考になれば嬉しいです。

'*3MOKU
CLV:VIDEO 5:CLS
LET [10],7,3,11,3,15,3,11,3,7
FOR N=1 TO 3:?"...":NEXT
IF RND(2) GOTO @CPU
@YOU
LC 0,3:?"YOU"
@YLOOP
X=X-BTN(28)*(X>0)+BTN(29)*(X<2)
Y=Y-BTN(30)*(Y>0)+BTN(31)*(Y<2)
LC X,Y,1:Z=X+Y*3:WAIT 5
IF !BTN(32) OR [Z]!=0 GOTO @YLOOP
[Z]=1:LC X,Y:?"O":S=S+1:BEEP
T=1:GSB @CHK
IF E=3 OR S=9 GOTO @GEND
@CPU
LC 0,3:?"CPU"
G=0:H=0:T=-1
FOR Y=0 TO 2
FOR X=0 TO 2
GSB @CHK
W=[Z+10]+E*E*20+F*F*10
IF W>H && [Z]=0 G=Z:H=W
NEXT:NEXT
Z=G:X=Z%3:Y=Z/3
[Z]=-1:LC X,Y:?"X":S=S+1:BEEP 30
T=-1:GSB @CHK
IF E!=3 && S!=9 GOTO @YOU
@GEND
LC 4,3
IF E=3 ?"WIN!";:BEEP 10,30
IF S=9 ?"DRAW";:BEEP 30,30
CLK:END
@CHK
Z=X+Y*3:L=[Z+10]:E=0:F=0
FOR M=0 TO 3
C=0:D=0
IF L&(1<<M) GSB LINE()+4+M*3
IF C>E E=C
IF D>F F=D
NEXT:RTN
@CHKX
FOR U=0 TO 2
A=[U+Y*3]:GSB @CHKT
NEXT:RTN
@CHKY
FOR V=0 TO 2
A=[X+V*3]:GSB @CHKT
NEXT:RTN
@CHKXY
FOR U=0 TO 2
A=[U*4]:GSB @CHKT
NEXT:RTN
@CHKYX
FOR U=0 TO 2
A=[2+U*2]:GSB @CHKT
NEXT:RTN
@CHKT
C=C+(A=T):D=D+(A=-T):RTN

配列変数[0]~[8]で、9個のマスの値を記憶します。(0=打たれていない、1=YOU、-1=CPU)
012
345
678
3行目のLETでの配列値の設定は、各マス目(0~8、添字は+10)の横(X)・縦(Y)・斜め(XY・YX)の列判定方向を表します。それぞれビット0・1・2・3です。
@CPUからの10数行がCPU思考部分です。9マスそれぞれで@CHKからの重み付け計算ルーチンを呼び出し、計算した重みに列判定配列の値も加算。最終的に重みが一番大きいマスへ打ちます。
@CHKからのマス目重み付け計算ルーチンでは、上記の列判定配列値を元に、@CHKX(横)、@CHKY(縦)、@CHKXY・@CHKYX(斜め)のサブルーチンを呼び出し、それぞれの列でYOUの石とCPUの石をカウントして、その最大値を返します。
この@CHK以下のルーチンは、両者の手を打った後の勝利判定にも共用していて、それで何とか1kバイトに収めています。