0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

jsでオセロゲーム作成

Last updated at Posted at 2020-11-25

##二次元配列

// 初期画面起動時
var turn = 0 //ターン目 1:黒、‐1:白
var ban_ar = new Array(8) //ここで要素数が8の配列にする
for (var x = 0; x < ban_ar.length; x++){
    ban_ar[x]=new Array(8)
}

盤面の状況を二次元配列で定義する。
まずはArrayオブジェクトでbar_arの要素を8つ作る。
それからfor文でban_ar[0~7]の要素に8つの要素を作ることで8×8の64個の要素を作る事が出来る。

これをconsoleで出力すると
スクリーンショット 2020-11-21 133601.png
となる

解説参考
https://prokatsu.com/javascript_array/
Arryaオブジェクトについて
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/Array

##テーブルを取得

var ban =document.getElementById("field")

htmlでテーブルタグに```id="field"とつけたのを取得する

###なぜここで盤面作成と初期化の関数を呼び出すのか

//取得したテーブルに盤面を作成
ban_new()

//盤面の初期化
ban_init()

##クリック時のイベント

for (var x = 0; x < 8; x++){
    for (var y = 0; y < 8; y++)
    var select_cell = ban.rows[x].cells[y];
    select_cell.onclick = function () {
        if (ban_ar[this.parentNode.rowIndex, this.cellIndex] == 0) {
            if (check_reverse(this.parentNode.rowIndex, this.cellIndex) > 0) {
                ban_set()
                cheng_turn()
            }
        }
    }
}

これはfor文の中にfor文が入ってるので
xが0の間にyが0~7をfor文を回し、次にxが1の間にyが0~7までfor文を回すという事。

その中は
var select_cell =どの配列.どこ
を定義するもの。
その後に、そのcellがクリックされたときのイベントについて(無名関数)の記述。
日本語に訳すと

if(ban_ar[ban_arのxの位置、banのyの位置]置く前どっちも0の時=まだ何も置かれていなくて新たに置ける時{
if(石を置いてひっくり返せるか確認する関数(親ノード.rowのxのセル、ban_arのyのセル)が0より大きい時=石が置ける時){
石を置く
ターンを変わる
    }
   }
  }
}

クリックされた時の処理は
クリックされた場所に石がないことを確認

その場所に置く側の石が置けるかを確認

置ける場合は盤面を更新して相手にターンを渡す

≻parentnodeがなぜ必要かがわからない
二次元配列だから?

##テーブルで盤面を作成する

function ban_new() {
    for (var x = 0; x < 8; x++){
        var tr = document.createElement("tr")
        ban.appendChild(tr)
        for (var y = 0; y < 8; y++){
            var td = document.createElement("td")
            tr.appendChild(td)
        }
    }
}

今回はtrで横一列を8つ作りそれをthで8のセルに分けた。
createElementで要素を作りappendChildでそれを追加しています。
2重のfor文はさっきと同じ
もしここでx<4にしたら上半分しか盤面がなくなる(笑)

trタグ
http://www.htmq.com/html/tr.shtml
tdタグ
http://www.htmq.com/html/td.shtml
createElement
https://itsakura.com/js-createelement
appendChild
https://developer.mozilla.org/ja/docs/Web/API/Node/appendChild

ここまで書いたら盤面が表示されていると思うから確認

##全削除ボタン

function ban_init() {
    //すべてクリア
    for (var x = 0; x < 8; x++) {
        for (var y = 0; y < 8; y++){
            ban_ar[x][y] = 0
        }
    }

すべてのセルを0にして空欄にする

その後に
##黒白2枚ずつの初期状態

    ban_ar[3][3] = -1
    ban_ar[4][3] = 1
    ban_ar[3][4] = 1
    ban_ar[4][4] = -1
    ban_set()

で黒白を2枚ずつ配置することが出来る。
ちなみに

    ban_ar[0][7] = 1
    ban_ar[7][0] = 1
    ban_ar[7][7] = -1```
とすれば最初から四つの角を埋めた状態になる(しかしゲームはルール上一生始まらない笑)

##ターンのリセット
```js
  turn = 0
  cheng_turn()

でターンをゼロ指定をしてターンを元に戻す関数を実行する

##盤面の状況を画面に反映させる処理

function ban_set() {
    var stone = ""//基本は何も置いていないから空欄
    for (var x = 0; x < 8; x++){
        for (var y = 0; y < 8; y++){
            switch (ban_ar[x][y]) {
                case 0:
                    stone = ""
                    break;
                case -1:
                    stone = ""
                    break;
                case 1:
                    stone = ""
                    break;
            }
            ban.rows[x].cells[y].innerText = stone;
        }
    }
    return true
}

ここのメインはswitch文でもし
2つのfor文ですべてマスを確認する任意のマスが
0なら空白
-1なら白
1なら黒
として反映させる
このswitch文によって各セルの状況がわかったのでinnerTextでレンダリング(表示)する

≻switch文
https://www.javadrive.jp/javascript/if/index4.html
innerText
https://techacademy.jp/magazine/15332

ban_ar[x][y]=盤面の白黒空白の判断するのはどこだ?
ここでのreturn trueの意味は?(なくても作動するが、、、)

##ターン変更時の処理

function cheng_turn() {
    var tarn_msg = document.getElementById("view_tarn")
    if (turn == 0) {
        turn = 1
        turn_msg.textContent = "黒の番です"
        return
    }
    //ターンを変更
    turn = turn * -1
    //ターンを交代して、置けるところがあるかを確認する
    //現状の配置をバックアップ
    var ban_bak = new Array(8)
    var check_reverse_cnt = 0
    for (var i = 0; i < ban_ar.length; i++){
        ban_bak[i]=new Array(8)
    }
    for (var x = 0; x < 8; x++){
        for (vary = 0; y < 8; y++){
            ban_bak[x][y]=ban_ar[x][y]
        }
    }
    var white_cnt = 0
    var black_cnt = 0
    for (var x=0; x < 8; x++) {
        for (var y = 0; y < 8; y++){
            //空白マスのみ置けるのでチェック
            //それ以外は石の数の計算
            switch(ban_ar[x][y]){
                case 0:
                check_reverse_cnt = check_reverse_cnt + check_reverse(x, y)
                //バックアップから元に戻す
                for (var i = 0; i < 8; ii++){
                    for (var ii = 0; ii < 8; ii++){
                        ban_ar[i][ii]=ban_bak[i][ii]
                    }
                }
                break;
                case -1:
                white_cnt++
                break
                case 1:
                black_cnt++
                break
            }
        }
    }
    //白と黒の合計が8*8=64の場合は終了
    if (white_cnt + black_cnt == 64 || white_cnt == 0 || black_cnt == 0) {
        if (white_cnt == black_cnt) {
            alert("引き分けです")
        } else if (white_cnt > black_cnt) {
            alert("勝負は黒:"+black_cnt+"対、白:"+white_cnt+"で白の勝ち")
        } else {
            alert("勝負は黒:"+black_cnt+"対、白:"+white_cnt+"で黒の勝ちです")
        }
    } else {
        //置ける場所がない場合はターンを相手に戻す
        if (check_reverse_cnt == 0) {
            switch (turn) {
                    case -1:
                    alert("白のおける場所がありません。続けて黒の番になります")
                    turn = turn * -1
                    break;
                    case 1:
                    alert("黒のおける場所がありません。続けて白の番となります。")
                    turn = turn * -1
                    break;
            }
        }
    }
    //ターンを表示
    switch (turn) {
        case -1:
            tarn_msg.textContent = "白の番です";
            break;
        case 1:
            tarn_msg.textContent = "黒の番です";
            break;
    }
}

ここはかなり文量が多いですが1つずつ分解していきます

まずtarn_msgにhtmlで定義したview_tarnを与えます
次にif文でゲーム開始時はturnが0でどちらのターンにもならないのでturnを1にして黒のターンにしてあげます。

それからturn = turn * -1でターンを変更する

バックアップを取るために64個のマスと同じように2次元配列で64個の配列を作る。
それからban_bak[x][y]にban_bar[x][y]を入れる事でバックアップを同期する

white_cntとblack_cntは盤上に各色の石の枚数を定義する

その後switch文でセルが空欄の時check_reverseが空欄だからcheck_cntは変わらない
もし-1なら白をカウントしてる数に+1をする
もし1なら黒のカウントに+1をする

###試合が続行できない場合
試合が続行できない状況は
・すべてのマスが埋まる
・どちらかが0になる
・白もしくわ黒のどちらかが置けるマスがない時
それらをif文で処理してきます。
上の2つの場合はどちらも勝敗が決まるのでif文のor演算子を使ってまとめて書きます。

2つ目に置ける場所がない時は先使ったswitch文のように白の場合は黒のターンにして、黒の場合は白のターンにします。

or演算子「|」
https://udemy.benesse.co.jp/development/system/javascript-if-else.html

なぜバックアップをとるのか?
check_reverse_cntがよくわからない
check_reverse_cnt = check_reverse_cnt + check_reverse(x, y)が謎

###ターン表示
こちらも白黒に応じてテキスト内容を変えるだけなのでswitch文を使います。

##指定したセルにターン側の石が置けるかを確認
check_reverse関数の引数にはセルの縦横軸の数値を入れるようにして

 なぜx=x+1が成り立つのか?「=」は左から右に代入する代入演算子だから!!

//指定したセルから指定した方向へのreverseを行う
function line_reverse(row_index, cell_index, add_x, add_y) {
    //最初に今の盤状況を対比する
    var ban_bak = new Array(8)
    for (var i = 0; i < ban_ar.length; i++){
        ban_bak[i]=new Array(8)
    }
    for (var x = 0; x < 8; x++){
        for (var y = 0; y < 8; y++){
            ban_bak[x][y]=ban_ar[x][y]
        }
    }
    var line_reverse_cnt = 0
    var turn_flg = 0
    var xx = row_index
    var yy = cell_index
    //指定したセルから指定された方向へ移動
    //完了条件になるまで石を返す
    while (true){
        xx = xx + add_x
        yy = yy + add_y
        //盤の端」に到達したら抜ける
        if (xx < 0 || xx > 7 || yy < 0 || yy > 7) {
            break;
        }
        //移動先のセルに石がなかったら抜ける
        if (ban_ar[xx][yy] == 0) {
            break
        }
        //移動先のセルが自分自身であれば、石があった事を判断し抜ける
        if (ban_ar[xx][yy] = turn) {
            turn_flg = 1
            break;
        }
        //上記以外は相手の石であるので、裏返して裏返した件数の加算
        ban_ar[xx][yy] = ban_ar[xx][yy] * -1
        line_reverse_cnt++
    }
    //裏返しを行ったが移動先に自分の石がなかった場合は元に戻す
    if (line_reverse_cnt > 0) {
        if (turn_flg == 0) {
            for (var i = 0; i < 8; i++){
                for (var ii = 0; ii < 8; ii++) {
                    ban_ar[i][ii] = ban_bak[i][ii]
                    line_reverse_cnt=0
                }
            }
        } else {
            //ちゃんと裏返しが出来たら置いた所に自分の石を置く
            ban_ar[row_index][cell_index]=turn
        }
    }
//最後に裏返しを行った件数を戻す
    return line =line_reverse_cnt
}

##指定したセルから指定した方向へのリバースを行う
バックアップを取った時と同じ形で対比をする
while文でいくつひっくり返せるかを計算する。
しかし8方向中すべて返せるかはわからないのでここで処理する
(石をひっくり返すには相手の石がないといけないから)
そこで残った方向にひっくり返せえるという事だから、そこでいくつひっくり返せるかを計算したら、そこに石を置いて自分のスコアをreturnで返す。

なぜ対比するのか?

≻このゲームを通じて「x=x+1」が成り立つ理由にずっと悩んでいたがjsでの「=」は数学的な意味での「=」とは違う
jsでは代入演算子と呼び、右側のデータなどを左の数値やプロパティなどに代入することである。
数学的な意味の「イコール」を示したい場合は「==」の等価や「===」の厳密等価を使う。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?