コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

動物将棋 (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による動物将棋プログラムの完成としたいと思います。