REALbasic かってに連載します(第21回)


 Dialogの種類をFrameで「7-Global Floating Window」を選択すると、このダイアログは常に全てのアプリケーションより優先してアクティブになります。アラーム時計などに応用できます。

 pushbuttonのActionイベントで、Canvasに表示された白色部分を透明にしたピクチュア(transparent=1)を別のピクチュアに切り替える場合、もとのピクチュアに上書きされ、残っているもとのピクチュアが見えてしまいます。Canvasのリフレシュには、Timerも使えますがうまい方法があります。
pushbuttonのActionイベントの最後に、window1.backdrop=「PICTイメージファイルの名前」、として初めのwindow1.backdropを再度読み込んでやると、全部のCanvasのリフレシュが出来ます。
 第21回 - 人工知能に挑戦 -

 人工知能マックA対マックBによる対戦をオセロゲームでやってみることにします。さてどうなることやら........。

 今回の目標は、コンピュータ同志の対戦を端から見て楽しむことです。
 とりあえず、ゲームは、単純なオセロゲームとしました。


 まずは、ベースとなるオセロゲームをプログラムしましょう。
    おおまかな流れです。

  1. マス目の数 64 だけ Canvas の配列(0 から 63 まで)を作り配置します。
  2. 白と黒のコマの図を、pict形式で用意する。(私はクラリスを使いました)
  3. 縦 n 行、横 m 列の配列変数 obw (m,n) に白、黒、コマなし、の情報を入れる。
  4. マウスクリック時に、コマを表示させるルーチンを作る。
  5. はさんだコマの判定と再描画を行う。
  6. プレイヤーを交代させる。


 まず、Canvasを1つツールボックスからウィンドウにドラッグします。properties欄のLeft,Top,Width,Height を30,30,40,40 とします。このCanvaseをもう1つ複製します。Command+D ですね。すると、Name の欄がCanvas2となりますので、これを、Canvas1と書き換えます。すると、「Do you want to create a control arrey?」と表示が出ますので、yes と答えます。この複製したCanvasを、Left,Topを75,30にします。次に、両方のCanvasを選択して、再び複製します。Left,Topを調整して、4つのCanvasを選択して、再び複製します。このように、次々に作業すればそれほど手間はかかりません。全部で8*8の64個のCanvaseを作ります。配列のインデックスは0からですから63が最後です。

 次に、コマの絵を用意します。私は、クラリスを使いました。ドローの文書で、だ円を重ねただけです。大きさは適当でかまいません。後から、プログラムで調整できます。保存するときに、Pict形式を選んで、ファイル名には「.pict」を加えておきます。白と黒2種類「white.pict」「black.pict」としました。
 このpict形式の2つのファイルを、いま作っているプロジェクトファイルと同じフォルダーに移しておきます。RunやBuildするときに必ず必要です。
さて、この絵を、プログラム内で使うためには、プロジェクトファイルを開き、Window1やMenuのあるProjectウィンドウにこれら2つのpictファイルをドラッグするだけです。今回はフォルダーを作って、すっきりさせました。ドラッグした後のファイル名には「.pict」が自動的に除かれます。これら2つの、propertiesウィンドウのTranseparent の欄をWhite-1 に変更して下さい。背景の白色を透明にします。

 マウスクリック時に、コマを表示させるルーチンは、Canvaeを配列にしましたので、非常にコンパクトに表現できます。
「obw(m,n)」は m 行 n 列目の状態をコマなしを0、白色を1、黒色を2で判別する変数です。全てのCanvasで同じような処理を行う場合は、ここ1箇所に書き込めばよいので楽ですが、それぞれのCanvasで違うことを行う場合は、「select case index ........」で場合分けが必要になります。「player」は現在の打ち手で白が1、黒が2です。
Window1.Canvas1.MouseDown:
Function MouseDown(Index As Integer, X As Integer, Y As Integer) As Boolean
  dim m,n as integer
  //canvasの1次元配列をます目の2次元配列に変換
  m=(index mod 8)+1
  n=(index) \ 8+1
  //コマが無ければ打つ。
  if obw(m,n)=0 then
    obw(m,n)=player
    redraw
  end if
  return true
End Function

ここでのサブルーチン「redraw」は、先程登録したコマの絵を表示させるものです。
「c1.graphics.drawpicture white,0,0,c1.width,c1.height,0,0,bw,bh」部分は、「c1」のCanvasに、「white」という名前のpictを表示する書式です。whiteに続く「0,0,c1.width,c1.height」はCanvasのどの部分に表示するかの領域を示し、さらに続く「0,0,bw,bh」はpictのどの部分を表示させるかの領域を示します。今回は、pictの方がCanvasより大きいので、pict全体がCanvas全体に縮小されて表示されます。

Window1.redraw:
Sub redraw()
  //obw=0 コマなし
  //obw=2 黒
  //obw=1 白
  dim n,m,bw,bh,ww,wh as integer
  dim c1 as canvas
  bw=black.width
  bh=black.height
  ww=white.width
  wh=white.height
  for n=1 to 8
    for m=1 to 8
      c1=canvas1(m+8*(n-1)-1)
      select case obw(m,n)
      case 0
        c1.graphics.forecolor=backcolor
        c1.graphics.fillrect 0,0,c1.width,c1.height
      case 1
        c1.graphics.drawpicture white,0,0,c1.width,c1.height,0,0,bw,bh
      case 2
        c1.graphics.drawpicture black,0,0,c1.width,c1.height,0,0,ww,wh
      end select
    next
  next
  
End Sub

 ここまでの、プログラムの確認用に「Othero)test」を用意しました。コマを置くだけのルーチンしかありませんので、ゲームにはなりません。でも、基本部分だけをひとまず、まとめておくことは大切だと思います。ここから、いくらでも発展させられます。

 さて、次はいよいよ、はさんだコマの判定ルーチンです。Canvas1( ):MouseDownにサブルーチン「onesearch」を用いた、裏返しのプログラムを書きます。サブルーチン「onesearch」はコマを打つ位置(m0、n0)を引数として、裏返すことのできる位置(m、n)を「、」で区切られた、文字列で返すものです。
 この判別ルーチンは、作る人の個性というか、感性と言うか、ものの見方によっては、全く違うアプローチがあるものと思われます。もっとエレガントな方法があるはずですが、思い付きませんでした。
 結局ここでは、置いたコマから、8方向を、1方向ずつしらみつぶしに調べていく方法を使いました。右、右上、上、左上、左、左下、下、右下。それぞれの方向ではさむことのできるコマがある場合はその位置を、文字列にして加えていきます。もし、このサブルーチンからの返り値が、””なら、はさめるコマが無いことになるので、打つことが可能か不可能かの判別にも利用できます。
 オセロゲーム単体では、このルーチンが心臓部分です。この部分は直接サンプルを読んで下さい。
 以下はこのルーチン「onesearch」の利用法です。

Window1.Canvas1.MouseDown:
Function MouseDown(Index As Integer, X As Integer, Y As Integer) As Boolean
  dim m,n,k,ndata,p as integer
  dim revall,revone as string
  //canvasの1次元配列をます目の2次元配列に変換
  m=(index mod 8)+1
  n=(index) \ 8+1
  //はさんで裏返すことのできる位置の全てが「onesearch(m,n)」
  revall=""
  revone=""
  //
  if obw(m,n)=0 then
    revall=onesearch(m,n)
    if revall<>"" then
      obw(m,n)=player
      //読み込んだ「,」で区切られたデータの総数「ndata」を調べる。
      ndata=CountFields(revall,",")   
      for p=1 to ndata-1
        //区切り文字「,」ごとにデータをばらす
        //データの最後に「,」があるので「ndata-1」
        revone=NthField(revall,",",p)
        obw((val(revone) mod 8)+1,(val(revone)) \ 8+1)=player
      next
      redraw
      change
      disp
      //-----------------------------------
      //thinkmac
      //-----------------------------------
    else
      msgBox "そこには打てません"
      disp
    end if
  end if
  return true
End Function

 ここまでの、まとめを、サンプル「Othero)manual」としました。一応、勝ち負けの判定も出来ますので、二人いれば、ゲームが出来ます。

 さて、いよいよ、マックの出番です、白が打った後、マックに打たせてみます。さきほどの、Window1.Canvas1.MouseDown:内の
//-----------------------------------
//thinkmac
//-----------------------------------
この部分を、使います。
以下が、サブルーチン「thinkmac」です。「allpossible」は打てる全ての場所、「onepossible」は思考ルーチンが返して来る打つ位置です。
Window1.thinkmac:
Sub thinkmac()
  dim allpossible,onepossible,allrev,onerev as string
  dim m,n,p,npossible,nrev,space as integer
  allpossible=""
  allrev=""
  onepossible=""
  onerev=""
  space=64
  w1=1
  b2=0
  for n=1 to 8
    for m=1 to 8
      if obw(m,n)<>0 then
        space=space-1
      end if
      if onesearch(m,n)<>"" then
        allpossible=allpossible+str(m+8*(n-1)-1)+","
      end if
    next
  next
  if space<>0 then
    if allpossible<>"" then
      //-----------------------
      //思考ルーチン
      //「mac2」が頭脳です
      //onepossible=mac2(allpossible)
      //-----------------------
      //思考ルーチン
      //「mac1」が頭脳です
      onepossible=mac1(allpossible)
      //----------------------
      //裏返すことが可能な全ての位置
      allrev=onesearch((val(onepossible) mod 8)+1,(val(onepossible)) \ 8+1)
      //コマを打つ
      obw((val(onepossible) mod 8)+1,(val(onepossible)) \ 8+1)=player
      nrev=CountFields(allrev,",")
      for p=1 to nrev-1
        onerev=""
        onerev=NthField(allrev,",",p)
        //はさんだコマを裏返す
        obw((val(onerev) mod 8)+1,(val(onerev)) \ 8+1)=player
      next
      b2=1
      redraw
      change
      disp
    else
      msgbox "黒の打てるところがありません"
      b2=0
      change
      disp
    end if
  else
    score
  end if
End Sub
 思考ルーチン「mac1」は、一応考えます。思考ルーチン「mac2」は、ほとんど考えませんので、無茶苦茶に速いです。サンプル「Othero)mac」はあなたと「mac1」の戦いです。もう一息で、マック対マックとなります。が、ここで問題が.....。
 この思考ルーチン、オセロゲームなどほとんどやったことがない私が考えたもので、私が相手では、ちょうどよいのですが(私より強い)、これを息子にやらせると、息子の方が強いのです。従って、このまま私が思考ルーチンを作っても、これでは、相当低レベルの対戦しか出来そうにありません。
 「mac1」対「mac2」の戦いは、あと一歩で実現できそうですが、どうも気力が失せてしまいました。皆さんで、「mac3」や「mac4」の思考ルーチンを作り、お互いに戦わせることもできそうです。どなたか、続きをやってみませんか。
 今回はここまでにしておきます。

 それでは、お楽しみください。
「阪神」と「iMac」にはまっている方々のためのサンプルも用意しました。こんな感じで遊んでください。


 今回の「Othero)test」、「Othero)manual」、「Othero)mac」、「iMac)GoTigers」の ソースプログラムです。
  
  プロジェクトファイルのみです。REALbasicの最新バージョンが必要です。(pictファイルのせいで少し大きくなってしまいました)145 kバイト  

  ソースコードについての質問やご意見がありましたら以下のアドレスまでご連絡下さい。
          koko-@mx2.tiki.ne.jp   

このホームページのホストは です。 無料ホームページをどうぞ!