オストルのゲームAIを作る　GUI編

オストルのGUIをJavaScriptで作成しました。
連続した駒をオス処理は再帰を使って記述しました。

[こちら](http://komon117.holy.jp/ostle.html)で遊べます。
千日手ルールは未実装です。遊ぶ人が気を付けてください。

```ostle.html
<html><head><meta charset="Shift_JIS">
<title>Ostle</title><script>
<!--
var canvas;  //オブジェクト
var ctx;     //コンテキスト
var nametbl=[
 "■","□","●"
];
var x1=[ 0, -1, 0, 1];    //移動量x
var y1=[-1, 0, 1, 0];    //移動量y
var movtbl=[
    [ 1, 1, 1, 1 ], //0:■
    [ 1, 1, 1, 1 ], //1:□
    [ 1, 1, 1, 1 ], //2:●
];
var setup=[
    [  1,  1,  1,  1,  1],
    [ -1, -1, -1, -1, -1],
    [ -1, -1,  2, -1, -1],
    [ -1, -1, -1, -1, -1],
    [  0,  0,  0,  0,  0],
];
var fontcolor=["black","maroon"];    //文字の色
var boxcolor=["LimeGreen","gray" ,"blue" ,"red"];        //線の色
var fillcolor=["LimeGreen","Khaki","white","LightPink"];  //盤面の色
var psize=32;           //マスのピクセル数
var board=[];           //盤バッファ
var bw=7,bh=7;      //盤バッファのサイズ
var ofsx=1,ofsy=1;    //盤のオフセット座標
var turn=0;             //0=先手/1=後手
var startx=-1,starty=-1;   //選択した座標
var username=["ipad","iphone","android"];    //端末の名前
var playtbl=["先手","後手"];
var connect=0;
//盤の情報
function piece() {
    this.id=-1;         //駒の種類(-1=駒がない)
    this.player=0;      //駒の向き(0=先手/1=後手/2=穴)
    this.movable=false; //移動可能フラグ
}
//初期化処理
function init(){
    canvas=document.getElementById("world");
    canvas.width=psize*bw;
    canvas.height=psize*bh;
    ctx=canvas.getContext('2d');
    ctx.font="24px 'ＭＳ Ｐゴシック'";
    user=window.navigator.userAgent.toLowerCase();
    for(i=0; i<username.length; i++){
        if(user.indexOf(username[i]) > 0)break;
    }
    if(i < username.length){
        document.addEventListener("touchstart", touchstart);
    }else{
        document.addEventListener("mousedown", mousedown);
    }
    board=new Array(bh);
    for(y=0; y<bh; y++){
        board[y]=new Array(bw);
        for(x=0; x<bw; x++){
            board[y][x]=new piece();   //盤バッファの初期化
        }
    }
    for(x=0; x<5; x++){
        for(y=0; y<5; y++){
            board[ofsy+y][ofsx+x].id=setup[y][x];
            board[ofsy+y][ofsx+x].player=setup[y][x];
        }
    }
    redraw();    //画面全体を再描画
}
//スマホ/タブレット用のタッチイベント
function touchstart(e){
    if (e.targetTouches.length==1){
        touch=e.targetTouches[0];
        touchpiece(touch.pageX ,touch.pageY);
    }
}
//PC用のクリックイベント
function mousedown(e){
    touchpiece(e.clientX ,e.clientY);
}
//駒の移動処理
function touchpiece(tx,ty){
    cx=Math.floor((tx-8)/psize);
    cy=Math.floor((ty-8)/psize);
    if(isinside(cx,cy,0,0,bw,bh)==false)return;
    if(startx==-1){
        movestart(cx,cy);    //移動開始
    }else{
        moveend(cx,cy);      //移動終了
        startx=-1;
        redraw();    //画面全体を再描画
    }
}
//移動開始
function movestart(cx,cy){
    id=board[cy][cx].id;
    if(id==-1)return;    //駒が存在しないと無効
    player=board[cy][cx].player;
    if(!(player==turn || player==2))return;    //自分の駒と穴のみ動かせる
    startx=cx;
    starty=cy;
    drawpiece(startx,starty,id,player,2);    //駒を白色に
    if(isinside(startx,starty,ofsx,ofsy,5,5)==false){
        for(x=ofsx; x<(ofsx+5); x++){  //配置可能マスを描画
            for(y=ofsx; y<(ofsx+5); y++){  //配置可能マスを描画
                while(isinside(x,y,ofsx,ofsy,5,5)==true){
                    drawpiece(x,y,-1,0,3);
                    board[y][x].movable=true;
                }
            }
        }
    }
    for(dir=0; dir<4; dir++){    //移動可能マスを描画
        x=startx;
        y=starty;
        while(movtbl[id][dir] > 0){
            x +=x1[dir];
            y +=y1[dir];
            id2=board[y][x].id;
            player2=board[y][x].player;
            if(isinside(x,y,ofsx,ofsy,5,5)==false) break; //盤の外へ
            if((id==2) && (id2==0)) break;
            if((id==2) && (id2==1)) break;
            drawpiece(x,y,id2,player2,3);    //盤面を赤色に
            board[y][x].movable=true;
            if(movtbl[id][dir]==1) break;
        }
    }
}
//移動終了
function moveend(endx,endy){
    if(board[endy][endx].movable==false)return;    //移動不可能
    dx=endx - startx;
    dy=endy - starty;
    if(board[endy][endx].id==-1){    // 移動先が空白マスの場合
        board[endy][endx].id=board[starty][startx].id;
        board[endy][endx].player=board[starty][startx].player;
        board[starty][startx].id=-1;
        board[starty][startx].player=-1;
    }else if(board[endy][endx].id==2){    // 移動先が穴の場合
        board[starty][startx].id=-1;
        board[starty][startx].player=-1;
    }else if(isinside(endx,endy,ofsx,ofsy,5,5)==false){    // 移動先が盤外の場合
        board[starty][startx].id=-1;
        board[starty][startx].player=-1;
    }else if((board[endy][endx].id==0) || (board[endy][endx].id==1)){    // 移動先に駒がある場合
        // 1マス隣の駒を再帰処理する
        moveend(endx+dx,endy+dy)
    	// 押した駒を1マス隣にずらす
        board[endy][endx].id = board[starty][startx].id;
        board[endy][endx].player = board[starty][startx].player;
        // 押した駒があった場所を空白マスにする
        board[starty][startx].id = -1;
        board[starty][startx].player = -1;
    }
    turn ^=1;    // プレイヤーの切り替え
}
//駒を描画
function drawpiece(x,y,id,player,color){
    px=x*psize;
    py=y*psize;
    ctx.fillStyle=boxcolor[color];
    ctx.fillRect(px, py, psize, psize);
    ctx.fillStyle=fillcolor[color];
    ctx.fillRect(px+1, py+1, psize-2, psize-2);
    if(id==-1)return;
    ctx.fillStyle=fontcolor[(id >> 3)& 1];
    if(player){
        px=-px-psize;
        py=-py-psize;
        ctx.rotate(Math.PI);
    }
    ctx.fillText(nametbl[id],px+4,py+24,300);
    if(player)ctx.rotate(Math.PI);
}
//駒が範囲内に存在するか判定
function isinside(x,y,ax,ay,w,h){
    if((x<ax)||(x>=(ax+w))||(y<ay)||(y>=(ay+h)))return(false);
    return(true);
}
//画面全体を再描画
function redraw(){
    for(y=0; y<bh; y++){
        for(x=0; x<bw; x++){
            if(isinside(x,y,ofsx,ofsy,5,5))c=1; else c=0;
            id=board[y][x].id;
            player=board[y][x].player;
            drawpiece(x,y,id,player,c);    //駒を描画
            board[y][x].movable=false;
        }
    }
    ctx.fillStyle=fontcolor[0];
    ctx.fillText(playtbl[turn],80,(1-turn)*196+24,300);
}
//-->
</script></head><body onload="init()">
<canvas id="world"></canvas></body></html>

```