二人が交互に取る

三山くずしゲームの前段階で、三個の山に積まれた石を二人が交互に取り除きます。
一個の山からであれば、幾らでも石を取り除くことが出来ます。

最初に miyama.txt を初期化してから AA でログインして下さい。
別のネット環境からホームページを立ち上げて BB でログインします。
(1台のパソコンで別のホームページを立ち上げてテストすることも出来ます)
BB の先手で始まります。自分の手番を確認してから石をクリックして下さい。
miyama.txtの初期化
id=AA で参加
id=BB で参加

プログラムの設計

  1. ネット対戦型三山くずしゲームでは、サーバーを介して交互に石を取り除く制御が面倒です。
    クライアント間で参照するデータは、サーバー上に置いて相互に参照します。
    更新された情報をクライアントが取得するには、サーバーに対して送信要求(Polling)を出します。
    miyama_click.php は mode, id を引数にして、5秒間隔で呼び出しを繰り返します。
    function update()
    {   var id= "<?php echo $id;?>";
        str= "miyama_click.php?mode=9&id=" + id;
        location.href = str;
    }
    setTimeout('update()',5000);
    
  2. miyama.txt には書き込み/読み込み属性を設定して下さい。
    内容は初期化されるので、何が書かれていても構いません。
  3. 今回のプログラムで記録する miyama.txt の形式です。
    1. $state; で進行状態を記録
    2. $ary[3]; で三個の山の石数を記録
  4. disp() 関数で三個の山に石を並べて表示します。
    画像をクリックすると、onClick で指定した関数 MouseClick() を呼び出します。
    クリックで石を取り除く方法は Image Click3 を参照して下さい。
    // 三つの山に石を並べる
    function disp($id, $ary)
    {   $w = "'" . $id . "'";
        for($i=0; $i<3; $i++)
        {   $n= $i*15;
            for($j=0; $j<$ary[$i]; $j++)
            {   print "<img src=\"img/jewel.gif\" onClick=\"MouseClick($w, $n)\">\n";
                $n++;
            }
            print "<br><br>\r\n";
        }
    }
    
  5. MouseClick() 関数では "miyama_click.php" を再帰的に呼び出します。
    num がクリックされた石(画像)の番号です。
    function MouseClick(id, num)
    {   str= "miyama_click.php?mode=1&id=" + id + "&num=" + num;
        window.alert(str);
        location.href = str;
    }
    

miyama_click.php の説明

  1. miyama_click.php のソースコードです。
    window.alert(str); で miyama_click.php を呼び出す文字列を確認しています。
    また error_log() で app.log にログ情報を書き出します。
    <html>
    <head>
      <meta http-equiv="content-type" content="text/html; charset=shit_jis">
      <title>Miyama Click</title>
    <script type="text/javascript">
    function MouseClick(id, num)
    {   str= "miyama_click.php?mode=1&id=" + id + "&num=" + num;
        window.alert(str);
        location.href = str;
    }
    </script>
    <?php
    // 三つの山に石を並べる
    function disp($id, $ary)
    {   $w = "'" . $id . "'";
        for($i=0; $i<3; $i++)
        {   $n= $i*15;
            for($j=0; $j<$ary[$i]; $j++)
            {   print "<img src=\"img/jewel.gif\" onClick=\"MouseClick($w, $n)\">\n";
                $n++;
            }
            print "<br><br>\r\n";
        }
    }
    // プレイする
    function play($id, $num)
    {   global $ary;
        if ($num<0)      return FALSE;
        $y= intval($num/15);
        $c= $num%15;
        if ($ary[$y]<=$c)    return FALSE;
        $ary[$y]= $c;
        return TRUE;
    }
    // miyama.txt に書き込む
    function  put()
    {   global $state, $ary;
        if (!($fp = fopen("miyama.txt","w")))
        {   die("Text File Not Found");  }
        flock($fp,LOCK_EX);
        fputs($fp,"$state\r\n");
        fputs($fp,"$ary[0]\r\n"); 
        fputs($fp,"$ary[1]\r\n"); 
        fputs($fp,"$ary[2]\r\n");
        flock($fp,LOCK_UN);
        fclose($fp);
    }
    // miyama.txt から入力
    function  get()
    {   global $state, $ary;
        if (!($fp = fopen("miyama.txt","r")))
        {   die("Text File Open Error");  }
        $state= intval(fgets($fp));
        $ary[0] = intval(fgets($fp)); 
        $ary[1] = intval(fgets($fp)); 
        $ary[2] = intval(fgets($fp)); 
        fclose($fp);
    }
    // $state に従ったメッセージを設定する
    function message()
    {   global $state;
        switch($state)
        {   case 1: return "プレイヤーAの手番です";
            case 2: return "プレイヤーBの手番です";
            case 9: return "** GAME OVER **";
            default:
                return "* state error *: $state";
        }
    }
    ?>
    </head>
    
    <body bgcolor=#f4f8ff>
    <h1>Miyama Click</h1>
    <script>
    history.forward();
    </script>
    <?php
        $state = 2;
        $ary= array(15, 15, 15);
        $id= $_GET['id'];       //ID
        if (!isset($id))
        {   put();
            print "ゲームをリセットしました<br>\r\n";
            exit();
        }
        $mode= $_GET['mode'];
        if (!isset($mode))  $mode= 0;
        $num= $_GET['num'];
        get();
        switch($mode)
        {   case 1:     //石を取る
                if ($state==1 && $id=='AA') //Aが石を取る
                {   if (play($id, $num)==TRUE)  $state = 2;
                }
                if ($state==2 && $id=='BB') //Bが石を取る
                {   if (play($id, $num)==TRUE)  $state = 1;
                }
                break;
            case 9:
                break;
        }
        $msg= message();
        put();              //現在の設定値を書き出す
    error_log("$id, $mode; $state, $ary[0], $ary[1], $ary[2]\n", 3, 'app.log');
        disp($id, $ary);
        print("<br><br>マウスのクリックで三つの山から石を取り除きます。");
        print("<br>\r\n$msg<br>\r\n");
    ?>
    
    <script type="text/javascript">
    function update()
    {   var id= "<?php echo $id;?>";
        str= "miyama_click.php?mode=9&id=" + id;
        location.href = str;
    }
    setTimeout('update()',5000);
    </script>
    </body>
    </html>
    
  2. 今回は miyama_class を使わずに miyama_click.php だけで作成しています。
    $state でゲームの進行を管理します。
    今回は[1:プレイヤーAの手番]と[2:プレイヤーBの手番]しか使いません。
  3. $mode でプレイヤの操作を管理します。
    今回は[0:ログイン]と[1:石を取り除く]と[9:ポーリング]しか使いません。
    最初に呼び出されたときは mode が設定されていないので山に石を表示します。($mode=0)
    クリックから呼び出されたときは mode=1 なので石を取り除きます。
  4. disp() 関数で三個の山に石を並べて表示します。
  5. play() 関数は実際に山から石を取り除く関数です。
  6. put() 関数で miyama.txt に書き出します。
    ファイルが二重に更新されないようにロックをかけています。
  7. get() 関数で miyama.txt から読み出します。
  8. message() 関数で $state を参照してメッセージを設定します。
  9. 戻るボタンでページが以前の状態に戻ることが無いようにします。
    <script>
    history.forward();
    </script>
    

[Next Chapter ↓] Miyama Class
[Previous Chapter ↑] 三山くずしゲームガイド

前田稔の超初心者のプログラム入門
PHP Program