5.オセロ

JavaScript製のオセロです、そこそこ強いです。

[実行結果]

別ウインドウで開きます →
Ver1.0より強くなっていますが、勝とうと思えば勝てます。

別ウインドウで開きます →
角を取りやすいので、遊び相手にはちょうどいいです。Ver1.0 ソース

遊び方

先手、後手と白、黒から始めるからを選びStartを押して始めてください。

あとは普通のオセロと同じです。

[プログラム]

<body onload="AllReset()">

<script type="text/javascript" charset="Shift_JIS">
/////////////////////////////////////
/* オセロ Ver1.3                   */
/* Programing by 月見              */
/* http://www.bc.wakwak.com/~cosa/ */
/////////////////////////////////////

//////////////////
/* フォーム作成 */
//////////////////
document.write("<form name='Othello' id='Othello'>");   //フォームの作成
   Pcounter=0;
   document.write("<div id='Xnum'>1 2 3 4 5 6 7 8</div>");
      for(i=1;i<=8;i++){
         document.write("<br />"+i+" ");
         for(j=1;j<=8;j++){
            document.write('<input type="text" size="1" name="P'+i+","+j+'" onclick="ReverCheck('+Pcounter+')" />');
            Pcounter++;
         }
      }
document.write("</form>");


////////////////////////
/* グローバル変数宣言 */
////////////////////////
var flag1;   //先手=1 後手=0
var flag2;   //ループを抜けるためのフラグ
var flag3;   //0ならばひっくり返す数値を返す
var flag4;   //ひっくり返せる場所があるなら1
var flag5;   //黒=1 白=0
var flag6;   //スタートしたか?
var flag8;   //割り込み禁止

var ChaCounter; //ひっくり返す枚数

var WriCheck;   //駒の保存
var ComCheck;   //COMの思考場所

var FAdd;   //フルアドレス

var Max;    //返す枚数が多い場所
var Xmax;   //返す場所のX座標
var Ymax;   //返す場所のY座標
var FMax;   //返す場所のフルアドレス

var NowX;   //置いた場所のX座標
var NowY;   //置いた場所のY座標

var NoReverCount //両方とも返せる?

////////////////
/* 処理関数群 */
////////////////
function Start_Reset()   //ゲーム開始
{
   if(!flag6){
      flag6=1;
      document.Information.Msg.value="";
      document.Setting.SorR.value="Reset";

      WBNumber()

      if(document.Setting.Color[0].checked){
         flag5=1;
         document.Information.Which.value="●の番です";
      }else{
         flag5=0;
         document.Information.Which.value="○の番です";
      }

      if(document.Setting.Either[0].checked){
         flag1=1;
      }else{
         flag1=0;
         ComLogic();
         ReverCheck(FMax);
      }
   }else{
      flag6=0;
      AllReset();
      document.Setting.SorR.value="Start";
   }
}


function AllReset()   //すべて初期化 初期値代入
{
   flag2=1;
   flag3=1;
   flag4=1;
   flag6=0;
   flag8=1;

   ChaCounter=0;

   WriCheck=new Array(64);
   ComCheck=new Array(64);

   Max=0;
   Xmax=0;
   Ymax=0;
   FMax=0;

   for(i=0;i<64;i++) WriCheck[i]="";

   WriCheck[27]="○";
   WriCheck[28]="●";
   WriCheck[36]="○";
   WriCheck[35]="●";

   for(i=0;i<64;i++) document.Othello.elements[i].value=WriCheck[i];

   document.Information.Com.value="";
   document.Information.Which.value="";
   document.Information.Msg.value="";
   document.Information.White.value="";
   document.Information.Black.value="";
}


function PlayerChage(FAdd)   //プレイヤー交替とチェック
{
   if(ChaCounter != 0){
      WriCheck[FAdd]=NowPiece;

      if(flag1 && flag4){
         flag1=0;
         WhichColor();
      }else{
         flag1=1;
         WhichColor();
      }

      document.Information.Msg.value="";
   }else{
      document.Information.Msg.value="そこには置けない";
   }

   WBNumber();
   for(i=0;i<64;i++) document.Othello.elements[i].value=WriCheck[i];
}


function WhichColor()   //次はどっち?
{
   if(flag1){
      if(flag5)
         document.Information.Which.value="●の番です";
      else
         document.Information.Which.value="○の番です";
   }else{
      if(flag5)
         document.Information.Which.value="○の番です";
      else
         document.Information.Which.value="●の番です";
   }
}


function WorB()   //白と黒を切り替え、flag5がtrueなら先手は黒
{
   if(flag1){
      if(flag5)
      {
         NowPiece="●";
         NexPiece="○";
      }else{
         NowPiece="○";
         NexPiece="●";
      }
   }else{
      if(flag5)
      {
         NowPiece="○";
         NexPiece="●";
      }else{
         NowPiece="●";
         NexPiece="○";
      }
   }
}


function NoReverCheck()   //ひっくり返す場所があるかチェック
{
   var RMax=0;

   NoReverCount=0;
   ChaCounter=0;
   flag4=1;

   WorB();

   flag3=0;
   for(j=0;j<64;j++){
      if(WriCheck[j] == ""){
         ChaCounter=0;

         ReverNumber(j);

         if(ChaCounter>RMax)   RMax=ChaCounter;
      }
   }
   flag3=1;

   if(RMax == 0){   //ひっくり返す場所がない
      document.Information.Msg.value="置く所がないのでパス";

      flag4=0;
      NoReverCount++;

      if(flag1){
         flag1=0;
         WhichColor();
      }else{
         flag1=1;
         WhichColor();
      }
   }
}


function ComTime()   //時間差プログラム
{
   clearTimeout(Com);
   ReverNumber(FMax);
   PlayerChage(FMax);
   document.Information.Com.value="Comは("+Xmax+","+Ymax+")におきました";
}


function ReverCheck(PointAdd)   //フォームクリックしたら動く
{
   if(flag6 && flag8){
      flag8=0;
      FAdd=PointAdd;   //グローバル変数に

      if(WriCheck[FAdd] == ""){
         NoReverCount=0;

         NoReverCheck();
         if(flag1 && flag6){
            ReverNumber(FAdd);
            PlayerChage(FAdd);
         }

         NoReverCheck();
         if(!flag1 && flag6){
            document.Information.Com.value="Com思考中";
            ComLogic();
            Com=setTimeout("ComTime()",800);
         }

         if(NoReverCount == 2)WBNumber();
      }
      flag8=1;
   }else{
      document.Information.Msg.value="Startを押してください";
   }

   WBNumber();
}


function ReverNumber(FAdd)   //返す場所をチェック
{
   NowX=FAdd_x(FAdd);
   NowY=FAdd_y(FAdd);

   ReverChange(1,FAdd);    //→     右
   ReverChange(-1,FAdd);   //←     左

   ReverChange(8,FAdd);    //↓     下
   ReverChange(-8,FAdd);   //↑     上

   ReverChange(7,FAdd);    //←↓ 左下
   ReverChange(-7,FAdd);   //←↑ 左上

   ReverChange(9,FAdd);    //→↓ 右下
   ReverChange(-9,FAdd);   //→↑ 右上
}


function ComLogic()   //コンピューター思考
{
   Max=1;
   Xmax=0;
   Ymax=0;
   FMax=-1;

   WorB();

//   var RevPoint=new Array(30); //ランダム機能追加時 2行とも//をはずす
//   for(j in RevPoint) RevPoint[j]="";

   for(j in WriCheck) ComCheck[j]=WriCheck[j];

   RevCounter=0;
   flag7=1;
   flag3=0;
   flag8=1;

   ComLoop();

   function ComLoop(){
      for(j=0;j<64;j++){
         if(ComCheck[j] == ""){
            ChaCounter=0;

            ReverNumber(j);

            if(String(j).match(/^0$|^7$|56|63/) && ChaCounter != 0){
               flag8=0;
               FMax=j;
               Xmax=FAdd_x(j);
               Ymax=FAdd_y(j);
               break;
            }

            if(flag7 && String(j).match(/^1$|^6$|^8$|^9$|14|15|48|49|54|55|57|62/))continue;

      //ランダム機能追加時 ここから下を5行削除
            if(ChaCounter>=Max){
               Max=ChaCounter;
               FMax=j;
               Xmax=FAdd_x(j);
               Ymax=FAdd_y(j);
            }
           //ここまで

   /* 置く場所をランダムに。 処理速度が極端に落ちるので保留 (追加時 /* */をはずす)
            if(ChaCounter>=Max){
                  Max=ChaCounter;
                  RevPoint[RevCounter]=j;

                  for(j=0;j<RevCounter;j++){
                     ran=Math.floor(Math.random()*(RevCounter+1))
                     Back=RevPoint[j];
                     RevPoint[j]=RevPoint[ran];
                     RevPoint[ran]=Back;
                  }
                  RevCounter++;
            }
   */
         }
      }
   }

   if(FMax == -1){
      flag7=0;
      ComLoop();
   }

   flag7=1;
   flag3=1;

// ランダム機能追加時 ここから下5行//をはずす
//   if(flag8){
//      FMax=RevPoint[0];
//      Xmax=FAdd_x(RevPoint[0]);
//      Ymax=FAdd_y(RevPoint[0]);
//   }
}


function WBNumber()   //白と黒の数をカウント
{
      var WhiCounter=0;   //白が何枚
      var BlaCounter=0;   //黒が何枚
      for(i=0;i<64;i++) if(WriCheck[i].indexOf("○") != -1) WhiCounter++;
      for(i=0;i<64;i++) if(WriCheck[i].indexOf("●") != -1) BlaCounter++;
      document.Information.White.value=WhiCounter;
      document.Information.Black.value=BlaCounter;

      if(WhiCounter+BlaCounter == 64 || WhiCounter == 0 || BlaCounter == 0 || NoReverCount == 2){
         flag6=1;

         document.Information.Which.value="";
         document.Information.Com.value="";

         if(WhiCounter > BlaCounter)
            document.Information.Msg.value="白の勝ち";
         else if(WhiCounter < BlaCounter)
            document.Information.Msg.value="黒の勝ち";
         else if(NoReverCount == 2)
            document.Information.Msg.value="引き分け";
      }
}


function ReverChange(FulPos,FAdd)   //ひっくり返す(flag3がfalseならば返す枚数だけを返す)
{
   flag2=0;
   var RevCounter=FulPos;   //フルアドレスをコピー
   var OldY=NowY;           //ひとつ前のY座標

   while(0 <= FAdd+RevCounter && FAdd+RevCounter < 64){
      if(WriCheck[FAdd+RevCounter] == "") break;

      NexX=FAdd_x(FAdd+RevCounter);
      NexY=FAdd_y(FAdd+RevCounter)

      if(FulPos == 1 || FulPos == -1){
         if(NowY != NexY) break;
      }else{
         if(OldY == NexY) break;
      }

      if(FulPos == 7 || FulPos == -9)if(NowX < NexX) break;
      if(FulPos == 9 || FulPos == -7)if(NowX > NexX) break;

      if(WriCheck[FAdd+RevCounter] == NowPiece){
         flag2=1;
         break;
      }

      OldY=FAdd_y(FAdd+RevCounter);
      RevCounter+=FulPos;
   }

   if(flag2){
      if(FulPos<0){
         for(i=FulPos;i>RevCounter;i+=FulPos){
            if(flag3)WriCheck[FAdd+i]=NowPiece;
            ChaCounter++;
         }
      }else{
         for(i=FulPos;i<RevCounter;i+=FulPos){
            if(flag3)WriCheck[FAdd+i]=NowPiece;
            ChaCounter++;
         }
      }
   }

   return(ChaCounter);
}


function FAdd_x(FulAdd)   // FAdd → x 変換
{
   y=(FulAdd%8 == 0)? 1:(FulAdd%8)+1;
   return(y);
}


function FAdd_y(FulAdd)   // FAdd → y 変換
{
   x=Math.floor(FulAdd/8)+1;
   return(x);
}

function xy_FAdd(xP,yP)      //x,y → FAdd 変換
{
   FulAdd=(((yP-1)*8)+xP)-1;
   return(FulAdd);
}
</script>

[裏話]

意外に苦戦しましたよ。ルールが単純明快なんですぐできるとタカをくくっていたのが間違い。 ひっくり返すのをやるアルゴリズムをつくるのに苦戦...

今までの反省を活かして、プログラム作成中にメモをところどころ入れています。 多少はいままでよりプログラムがみやすくなっています。


© 2000-2003 Tsukimi / HobbySpace