動物将棋 (10)
棋譜を記録するプログラムを作ったので、後回しにしてきた引き分けの判断と、待った、投了のルーチンを作りたいと思います。
千日手による引き分け
動物将棋では、同じ局面に3回なった時点で引き分けとなります。ということは、将棋開始から全ての局面を覚えておかなければなりません。さすがに、これは大変なので、棋譜を元に盤面を再生して、今の盤面と同じ局面が何回出てきたかを数えるプログラムにしました。
千日手のチェックは、doTouchAction()のループの勝負判断の後に入れました。winner=3のときに引き分けとします。
--勝負がついたか調べます winner = chkSyobuAri() --千日手をチェックします if( chkSenNichiTe( KifuN-1 )==1 )then winner = 3 end if( winner~=0 )then break end
chkSenNichiTe(KifuN-1)が千日手のチェックプログラムです。このプログラム自体が引数の手数まで、盤面を再現する作りになっています。また、KifuN=1が開始面としています。tejun=2としているのは、KifuN=1を開始面としたので、次のKifuN=2が先手の1手目になるからです。
下記が千日手チェックプログラムですが、棋譜を再現するので、ほぼ、doTouchAction()のループと同様な形になっています。
bnとが再生する盤面のデータが入る配列です。1手再生するたびに、千日手のチェックで、今の盤面と比較し、同じ盤面であれば、sennichiカウンタをカウントアップしています。
------------------------------------------ -- 棋譜を再生しながら千日手をチェックします ------------------------------------------ function chkSenNichiTe( n ) local i,j local bn = {} bn[1] = { 23,0,0,14 } bn[2] = { 25,21,11,15 } bn[3] = { 24,0,0,13 } local mchi = {} mchi[1] = {0,0,0,0,0,0} mchi[2] = {0,0,0,0,0,0} local hx,hy,hx1,hy1,koma,koma1 local tst local tejun = 2 local sennichi = 0 local x,y local chkflg = 0 if( n>=KifuN )then return end for i=1,n do if( Kifu[i]<6000 )then tst = tostring( Kifu[i] ) hx = tonumber(string.sub( tst, 1, 1 )) hy = tonumber(string.sub( tst, 2, 2 )) hx1 = tonumber(string.sub( tst, 3, 3 )) hy1 = tonumber(string.sub( tst, 4, 4 )) koma = bn[hx][hy] else tst = tostring( Kifu[i] ) koma = tonumber(string.sub( tst, 1, 2 )) - 50 hx = -1 hy = -1 hx1 = tonumber(string.sub( tst, 3, 3 )) hy1 = tonumber(string.sub( tst, 4, 4 )) end if( hy==-1 )then --持ち駒を打った for j=1,6 do if( mchi[tejun][j]==koma )then mchi[tejun][j] = 0 break end end bn[hx1][hy1] = koma else --盤上の駒を動かした koma = bn[hx][hy] if( koma==11 and hy1==1 )then --ひよこの場合 --最前列ではにわとりになる koma = 12 elseif( koma==21 and hy1==4 )then --ひよこの場合 --最前列ではにわとりになる koma = 22 end --Ban[hx1][hy1]に駒がある場合は、自分の持ち駒となる if( bn[hx1][hy1]~=0 )then --持ち駒に追加します koma1 = tejun*10 + (bn[hx1][hy1] % 10) --にわとりはひよこにする if( koma1==12 )then koma1 = 11 end if( koma1==22 )then koma1 = 21 end for j=1,6 do if( mchi[tejun][j]==0 )then mchi[tejun][j] = koma1 break end end end bn[hx][hy] = 0 bn[hx1][hy1] = koma end tejun = 3 - tejun --手順を入れ替える --千日手のチェック chkflg = 0 for x=1,3 do for y=1,4 do if( bn[x][y]~=Ban[x][y] )then chkflg = 1 x = 4 break end end end if( chkflg==0 )then sennichi = sennichi + 1 if( sennichi==3 )then return 1, bn, mchi end end end return 0, bn, mchi end
千日手のカウンタが3になると、1を返して、千日手であることを知らせます。また、この関数は引数で再現する手数を指定できます。そして、この関数内で生成したbnとmchi[]を返り値として渡せるように作りました。
このようにすると、この関数を呼び出すことにより、好きな手順の盤面を作ることができます。この方法で、「待った」処理を実装しました。
メインルーチンの修正
引き分けを作ったので、メインルーチンもあわせて修正しました。
while(true)do -- 将棋盤を描く drawSyogiBan() -- 将棋データを初期化します initKomadata() --盤上の駒と持ち駒を描画します drawAll() --どうぶつしょうぎスタート winner = doTouchAction() if( winner==3 )then --画面にテキストを表示 msgDisp( "ひきわけです" ) a = dialog( "せんにちてにより、ひきわけです", "もういちど、さしますか",2 ) else if( winner==1 )then wstr="せんて" elseif( winner==2 )then wstr="ごて" end --画面にテキストを表示 msgDisp( wstr.."のかち" ) a = dialog( wstr.."のかちです", "もういちど、さしますか",2 ) end if( a~=1 )then break end toast( "画面タッチではじまります", 0 ) touch(3) end
メニュの実装
「待った」と「投了」は、メニュから選択するようにしました。メニュを出すには、画面の「おやつ」エリアにタッチします。タッチによりメニュを出すようにするため、doTouchAction()のタッチ待ちループにメニュルーチンを入れました。下記のoyatsu()がそうです。引数はタッチ座標です。
while(true)do x,y = touch(1) --駒にタッチするまで待つ koma, hx, hy = getKomaNum() if( koma~=0 and koma~=-1 and getPlyer(koma)==tejun )then break end oyatsu( x, y ) --おやつ領域にタッチしたときはおやつ処理を行う if( Touryo==1 )then break end --投了した touch(2) --指が画面から離れるまで待つ end
メニュ処理は下記です。最初のif文はガード句です。タッチ範囲でなければ何もせずに返ります。メニュでは、リストダイアログで「待った」「参った」を選ぶようにしました。「待った」を選んだ場合は、二手戻します。今回は二手戻すことにしました。KifuNが既に+1されているので、二手戻すには、-3する必要があります。また、ここでchkSenNichiTe()を呼んでも、千日手と判断されることは無いはずです。
chkSenNichiTe()が生成した盤データと持ち駒データを、BanとMochiに代入します。棋譜再生時には、持ち駒をソートしていないので、読み込んだ後、持ち駒をソートしています。そして、実際のKifuNを-2します。
------------------------------------------ -- おやつ処理(メニュ処理のこと) ------------------------------------------ function oyatsu( x, y ) if( x<Msg.x1+4 or y<Msg.y0 or x>Center.x+127-19 or y>Msg.y1 )then return end local a local n local ret item.clear() item.add( "まった", 0 ) item.add( "まいりました", 0 ) a = item.list("えらんでね") if( a==1 )then --まったの処理 if( KifuN>3 )then n = KifuN - 3 ret, Ban, Mochi = chkSenNichiTe( n ) sortMochiKoma( 1 ) sortMochiKoma( 2 ) KifuN = KifuN - 2 drawAll() msgDisp( "まった・・・" ) end elseif( a==2 )then --投了しました Touryo = 1 end end
投了の処理は簡単です。グローバル変数にTouryoを用意しました。この値を1にするだけです。上のメニュで下記のプログラムが投了を判断してループを抜けています。
if( Touryo==1 )then break end --投了した
タッチのWhileループを抜けた後に、投了の処理を追加しました。
if( Touryo==1 )then --投了した msgDisp( "まいりました" ) winner = 3 - tejun break end
winnerを相手にセットしてdoTouchAction()のループを抜けて、メインルーチンに戻ります。
以上が、今回実装したプログラムです。
これで、動物将棋の将棋盤としては、ほぼ遊べるようになったと思います。後は、手順の読み上げを実装して、Luaridaによる動物将棋プログラムの完成としたいと思います。