{*********************************************************************
 *  *** 三目並べ ***                                                 *
 *                                                                   *
 *        HAPPyのサンプルプログラム                                  *
 *          (作者  浅野比富美 Public Domain Software)                *
 *********************************************************************}

program TicTacToe(input,output)             ;   { 英語で tic-tac-toeという.  }
  const o          =  1                     ;   { 先手 ○  または 先手の勝ち.}
        n{othing}  =  0                     ;   { 空き     または 引き分け.  }
        x          = -o                     ;   { 後手 ×  または 後手の勝ち.}
  type  status     = x..o                   ;   { 盤の状態 または ｹﾞｰﾑ評価値.}
        placeRange = 0..9                   ;   { 場所の範囲   (1〜9がok).   }
        tableType  = array[placeRange] of status;{ ゲーム盤の型.             }
  var   table      : tableType              ;   { ゲーム盤.                  }
        ox,you,com : status                 ;   { ○または×.                }
        time       : integer                ;   { 1手目､2手目,3手目･･･ .     }

(****************************)
(*       盤の表示処理       *)
(****************************)
  procedure print      ;
    var p : placeRange ;
  begin
    for p:=1 to 9 do
      begin  case table[p] of
               o : write(' ○') ;
               n : write( p:3 ) ;             { 置いていない場所は番号を表示.}
               x : write(' ×')
             end ;
             if p mod 3 = 0 then writeln      { 3 つごとに改行する.          }
      end ;
    writeln
  end {print} ;

(****************************)
(*       初期設定処理       *)
(****************************)
  procedure init              ;
    var p        : placeRange ;
        ordinary : integer    ;
  begin
    writeln('コンピュータと３目並べをしよう！') ;
    for p:=1 to 9 do table[p] := n  ;
    write('先手○ 後手× を選んでネ(先手･･･1 後手･･･1以外) ? ') ;
    read(ordinary)                  ;
    if ordinary = 1 then you := o             { 先手は　○                   }
                    else you := x   ;         { 後手は　× .                 }
    com := -you                     ;         { コンピュータはあなたの逆.    }
    print                                     { 置く場所を示すために盤を表示.}
  end {init} ;

(****************************)
(*   　３つ並び判定関数　   *) (*table に ox が 3つ並んでいれば真を返す *)
(****************************)                                    {   table  }
  function complete(table : tableType ; ox : status) : Boolean ;  {  1  2  3 }
    var oooORxxx : -3..+3 ;                                       {  4  5  6 }
  begin                                                           {  7  8  9 }
    oooORxxx := ox * 3    ;
    complete := (table[1]+table[2]+table[3] = oooORxxx) or        { １行目.  }
                (table[4]+table[5]+table[6] = oooORxxx) or        { ２行目.  }
                (table[7]+table[8]+table[9] = oooORxxx) or        { ３行目.  }
                (table[1]+table[4]+table[7] = oooORxxx) or        { １列目.  }
                (table[2]+table[5]+table[8] = oooORxxx) or        { ２列目.  }
                (table[3]+table[6]+table[9] = oooORxxx) or        { ３列目.  }
                (table[1]+table[5]+table[9] = oooORxxx) or        { 右下がり.}
                (table[3]+table[5]+table[7] = oooORxxx)           { 左下がり.}
  end {complete} ;

(****************************)
(*      最善の手を探す      *)
(****************************)
(***** time手目のtableについて ox を 置ける 最善のplace を 調べる｡
   関数値:(ox･･勝ち  n･･引き分け  -ox･･負け) 負けの時  place は 無意味 *****)
  function bestSelect(table : tableType ; ox : status ; time : integer ;
                      var place : placeRange) : status ;
    var p : placeRange ;

  (****************************)
  (*      思考ルーチン        *)
  (****************************)
    function think : status ;
      label 9                       ;                 { 関数出口.            }
      var   p,pp       : placeRange ;                 { 置く場所 ppはダミー. }
            memoplace  : placeRange ;                 { ワーク               }
            eval       : status     ;                 { 評価値.              }
    begin
      (* まずその局面で勝てる場所を探す *)
      for p := 1 to 9 do
        begin
          if table[p] = n then
            begin
              table[p] := ox ;
              if complete(table,ox) then              {   ３つ揃えば         }
                begin
                  place := p ;
                  eval := ox ;                        {     勝ちが           }
                  goto 9 {return}                     {     決定.            }
                end ;
              table[p] := n
            end
        end ;
      (* この局面だけでは勝てないので先読みする *)
      memoPlace := 0 ;                                { ﾙｰﾌﾟ後も0なら負け.   }
      for p := 1 to 9 do
        begin
          if table[p] = n then
            begin
              table[p] := ox ;                        {   そこに置いてみる.  }
              eval := bestSelect(table,-ox,time+1,pp);{ 相手の最善手を調べる.}
              if      eval = ox then
                begin
                  place := p ;
                  goto 9 {return}
                end
              else if eval = n  then memoPlace :=p ; { 引き分けにできる場所.}
              table[p] := n                          { 置いたのを取り消す.  }
            end
        end ;
      if memoPlace = 0 then eval := -ox              { どこに置いても負け.  }
                       else eval := n     ;          { 引き分けにできる.    }
      place := memoPlace                  ;          { メモした場所を 返却. }
9:    think := eval
    end {think} ;

(****** bestSelect 開始 *****)
  begin {bestSelect}                                              {  1  2  3 }
    case time of                                                  {  4  5  6 }
                1 : begin                                         {  7  8  9 }
                      place      := 5 ;        {  1手目は真ん中              }
                      bestSelect := n          {  勝負は未決                 }
                    end ;
                2 : begin
                      p := 1 ;
                      while table[p] = n do p := p + 1 ;
                      if p=5 then place := 1   { 真ん中に置かれたのなら1     }
                             else place := 5 ; { 真ん中以外なら真ん中を取る  }
                      bestSelect := n          { 勝負は未決                  }
                    end ;
      3,4,5,6,7,8 : bestSelect := think ;      { 3〜8手目は手を考える        }
                9 : begin                      { 9手目(最後の手)は 残った所  }
                      place := 1 ;
                      while table[place] <> n do place := place + 1 ;
                      bestSelect := n          { 勝負は引き分け              }
                    end
    end
  end {bestSelect} ;

(****************************)
(*     あなたの手の処理     *)
(****************************)
  procedure yourSelect  ;
    var p  : integer    ;
        ok : Boolean    ;
        pp : placeRange ;
  begin
    repeat
      if you = o then write('○')
                 else write('×') ;
      write('をどこに置くかい (0:ｱﾄﾞﾊﾞｲｽ  1〜9:場所) ? ') ;
      read(p) ;
      if p = 0 then
        begin
          if bestSelect(table,you,time,pp) = com
            then writeln('既に君の負けは決定しているよ')
            else writeln(pp:2,'に置いたらどうかなあ') ;
          ok := false
        end
      else if (1<=p) and (p<=9) then ok := table[p] = n
                                else ok := false
    until ok ;                                    { 置ける場所が選ばれるまで.}
    table[p] := you                               { 選んだ場所に置く.        }
  end {yourSelect} ;

(****************************)
(*  コンピュータの番の処理  *)
(****************************)
  procedure comSelect ;
    var place : placeRange               ;      { 置く場所.                  }
        d     : status                   ;      { 無意味だが処理上必要なもの.}
  begin
    write('コンピュータは')              ;
    d := bestSelect(table,ox,time,place) ;      {  最善の手を選び            }
    writeln(place:2,' に置こう')         ;
    table[place] := com                         {     そこに 置く.           }
  end {comSelect} ;

(****************************)
(*      メイン処理          *)
(****************************)
begin {main}
  init      ;                                         { 初期設定をする.      }
  ox   := x ;                                         { ○を先手とするﾃｸﾆｯｸ. }
  time := 0 ;                                         { 手数をクリアする.    }
  repeat                                              { 決着か8手目までやる. }
    time := time + 1 ;                                {   手数を進める.      }
    ox   := -ox      ;                                {   ○×を反転する.    }
    if ox = you then yourSelect                       {   あなたの番.        }
                else  comSelect  ;                    {   コンピュータの番   }
    print                                             {   盤を表示する.      }
  until complete(table,ox) or (time = 8{手目})  ;     { 9手目はしない.       }
  if complete(table,ox) then                          { ３つ揃った時         }
    if ox = you then writeln('コンピュータ故障！')    {   あなたは勝てない.  }
                else writeln('コンピュータの勝ち')    {   ｺﾝﾋﾟｭｰﾀが揃った.   }
  else               writeln('引き分けだネ'      )    { 8手目未決は引き分け. }
end {main}.
