0
0

More than 3 years have passed since last update.

オセロゲーム作成の独自機能

Last updated at Posted at 2020-11-25

前回作ったオセロゲームに独自機能を追加していく

特殊マップ(黒白とランダムで空欄)
・石が少ない方が勝ち
・途中経過を表示
・石を挟んでなくても置ける(挟んでもok)
・縦横の挟みでしかひっくり返せない or 斜めでしかひっくり返せない
・3人以上が1つのゲームにでてくる
・ゲーム終了時にあるマスを取っていれば高得点
・1ターンに石を2つ置く
・置くと 勝ち/負け になるマス

https://github.com/syuilo/misskey/issues/1200

途中経過の確認

リセットボタンの下に表示するのと途中経過ボタンを押したらポップアップに表示させるのを作る

そのために白・黒・空欄を数えるためのswtich文を再度書く
htmlに表示させるのはターンと同じ仕組みにするためid=progressを記述しjsではvar progress = document.getElementById("progress")としてtextContentで表示させる

var progress = document.getElementById("progress")
progress.textContent="" + white_cnt+ ""+black_cnt

これでクリックするたびに連動して変わるようになった
しかし最初からは表示されない。
ただ上の記述をグローバルスコープにしても反映されない。
なぜならwhite_cntblack_cntが関数スコープ内にあるからだ。

だから関数スコープに加えてグローバルスコープで定義してみる
そしたら最初から表示されるようになった。

クリックでポップアップに表示する場合

htmlでonclickを記述したボタンを作成してjsのグローバルスコープにあるアラートを書いた関数を紐づけた
アラートの中身は("白" + white_cnt+ "黒"+black_cnt)
けどいつ発火させても0対0になってしまう。

先と同じ関数スコープに書いても「値が読み取られることがない」となってしまいアラートにも反映されない。

だからhtmlではidだけを指定してjsでクリックされた時の処理を書いた

document.getElementById("btn_progress").onclick = function() {
 alert("" + white_cnt+ ""+black_cnt)
};

そしたら無事に発火させることが出来、スタート時と随時結果を反省するためにグローバルスコープと関数スコープの両方に書く。

https://www.sejuku.net/blog/29821
後はリセットボタンを押したときに連動して0にするため
function ban_initにこれまでの記述をすべて書き込む

function ban_init () {
---通常のban_initの記述は省略---

 // 追加実装 途中経過
    var white_cnt = 0
    var black_cnt = 0
    var progress = document.getElementById("progress")
    progress.textContent="" + white_cnt+ ""+black_cnt
    document.getElementById("btn_progress").onclick = function() {
    alert("" + white_cnt + "" + black_cnt)
    };
    // 追加実装 途中経過 end
}

※先に白と黒を定義しておかないと発火させたときundifinedになる

リバースを出来なくても置ける

オセロの概念を壊すルールを実装していく
具体的にはひっくり返せなくても置けるようにするため、
ひっくり返せるかを確認する機能を省くor確認した時の基準を緩くする
、と実装できると見ている

まず緩くしてみたら

if (check_reverse(this.parentNode.rowIndex, this.cellIndex) >= 0)
//元々は≻0だった

ターンは変わるようになったが石を置くことが出来なかった。

cheng_turnは作動したがban_setが正常に作動しなかったと考えられる。
と思ったがban_setはあくまでも各マスの0,1,-1を判断して白黒の石を置くだけなのでcheng_turnに問題があるはず(ban_setは正常のはず)
(石が置いてある場所をクリックしてもターンは変わらなかったので石がちゃんと置ければ正常にゲームが出来るはず)

ここ以外の所でも制限がかかってる可能性アリ
line_reverse関数をいじるとひっくり返す判定が変わってしまうので今回は別の場所で考える

縦横の挟みでしかひっくり返せない or 斜めでしかひっくり返せない

各方向へリバースできるか確認するところで絞ればいい

    reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, -1, 0)
    // reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, -1, 1)
    reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, 0, 1)
    // reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, 1, 1)
    reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, 1, 0)
    // reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, 1, -1)
    reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, 0, -1)
    // reverse_cnt = reverse_cnt + line_reverse(row_index, cell_index, -1, -1)

3人以上が1つのゲームにでてくる

ターンを3の倍数、3の倍数から1引いた、3の倍数から2引いた、を当てる

通常はturn = turn *-1の所をturn + 1にして動くと思ったが3の倍数の時のあまりの数で分岐させることが出来ずに4ターン目を実行することが出来なかった
ターンでどうするか悩んだが3の倍数を割った時のあまりで判断することにした。最後に+1したのは0だと空欄と混乱しそうだったから。

 turn = (turn + 1) % 3+1

これで4ターン目も回すことが出来た。
次の問題は挟んで石をひっくり返す所である。

これまではターンと同様に―1をかけることで石をひっくり返していたが3種類のためそれでは対応できなくなってしまった。

しかしこれも上と同じように書いてみる

ban_ar[xx][yy] = (ban_ar[xx][yy] + 1) % 3+1

これでもダメだった
どうしても1つずれてしまう。
(★で黒と白を挟まむと●は★になるが●は●になってしまう)

ここでまた悩んだ
けど、元のオセロゲームの時に書いた

//移動先のセルが自分自身であれば、石があった事を判断し抜ける
        if (ban_ar[xx][yy] == turn) {
            turn_flg = 1
            break;
        }

によって気づいた。

この上のコードは石を置いたときに9方向のマスを確認する時、自分の石であるかを確認するコードだ。(置いたマスの隣が自分の石であったらひっくり返せないから裏返しを実行しないようにするためのコードである。)

それを今回は活用したいと思う。
ひっくり返すものを=turnとすれば置いた石に割り振った数字が代入されるのではないかと考えた

ban_ar[xx][yy] = turn

これで見事に3人でゲームをする事が可能になった

残りは途中経過などを変更するのと、決着の判定を変える必要がある。

途中経過は各箇所にstar_cntを追加するだけなので難しくない。

問題は勝負の判定である。とりあえず3つのうち、どれかが0になったらその石が負けという事で書いてみる。(もしくわ1つが0になった時点で多い方が勝ち)
※2つになってから続行するのも可能だとは思うがとても大変になりそうなので今は見送る
(1つが負けたらターンの仕組みを変えるor負けた石のターンが回ってきたら「負けました」というポップアップを表示して次の石にターンを回す)というのを考えた。

後は石が置けない時の処理を変更する必要がある。
けど、switch文に星用の個所を追加し置けなかった時のターンの処理を3色の通常時と同じにすれば動くはず

ゲーム終了時にあるマスを取っていれば高得点

最初に置こうが、その後取り返そうが最後に取ってる人が勝ち
けど、随時途中経過には反映されている形にする

任意のマスを座標指定して得点アップを考えた
しかし色の判断をどうやるかが問題である。
画面を眺めてても答えはわからないので色々弄ってみる。
まず最初の4マスだけ100ポイントにしようと思ったが全く反映されなかった。
だから通常は1マス1ポイントだが2ポイントに試したところ1つ目の石を置いたら引き分け表示をされた。
元々はインクリメントだったが+1にしたら+2と同じように引き分け表示をされた。
インクリメントについて調べてみる。
そしたらインクリメントをインクリメンを2つずつ追加する記事を見つけた。
それを参考に

switch(ban_ar[3][4]){
                case -1://
                white_cnt = white_cnt+2
//最初は white+2としたから反映されなかった
                break
                case 1:
                    black_cnt = black_cnt+2
                break
}

と記述したら無事に1マスあたり2ポイントに設定することが出来た。

それを利用して3.4のマスを100点にしようとしたら

switch(ban_ar[3][4]){
                case 0:
                    check_reverse_cnt = check_reverse_cnt + check_reverse(x, y)
                    //バックアップから元に戻す
                    // console.log(check_reverse_cnt)
                    for (var i = 0; i < 8; i++){
                        for (var ii = 0; ii < 8; ii++){
                            ban_ar[i][ii]=ban_bak[i][ii]
                        }
                    }
                break;
                case -1://マスに白石があるという事
                    white_cnt = white_cnt + 100;
                break
                case 1:
                    black_cnt = black_cnt + 100;
                break
}

と記述してゲームをスタートしたらまさかの1対6404という悲惨な数字が出力されてしまった。
そのまま続けていっても64~~と変わらなかったので100ポイント追加しようとしたら6400ポイント追加されるような処理がかかっているらしい。6400の64は2次元配列が影響してるのではないかと思う。
そんな感じでコードを確認していたら、上のswitch文がバリバリ2次元配列の中に入っていたので速攻で犯人が確定。
その2次元配列から出してあげたら正常に作動した。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Increment
https://techacademy.jp/magazine/37376

置くと 勝ち/負け になるマス

色を判別しておかれた瞬間に勝負を決する
勝ち負け引き分けを分けるところに組み込むつもりでやっていく
元の勝敗を決するif文がターンを変更する時の関数にあるので同じ関数内にif文で書いていく。
しかしif文だと該当のマスに石を押してないのに勝利判定が出てしまうので上で使ったswitch文をアレンジした

switch(ban_ar[2][2]){
                case 0:
                    check_reverse_cnt = check_reverse_cnt + check_reverse(x, y)
                    //バックアップから元に戻す
                    // console.log(check_reverse_cnt)
                    for (var i = 0; i < 8; i++){
                        // for (var ii = 0; ii < 8; ii++){
                        //     ban_ar[i][ii]=ban_bak[i][ii]
                        // }
                    }
                break;
                case -1://マスに白石があるという事
                    alert("独自ルールにより白勝ち")
                break
                case 1:
                    alert("独自ルールにより黒勝ち")
                break
    }

これにより無事実装完了
しかしswitch文で出来てif文では出来ない事はないと思うのでif文の書き方などを再度おさらいして書いてみる。
恐らくif文の条件式の書き方に問題がある。

if (ban_ar[2][2] = -1) {
        alert("")
    } else if(ban_ar[2][2] = 1) {
   alert("")
    }

悩んで3分で気づいた(3分もかかった)
条件式内の等価演算子だ。
数学の=が抜けない悪い癖がまた出た。
ここを修正したら無事に実装することが出来た。

1ターンに石を2つ置く

単純に1ターンのうちにクリックを2回出来るようにすればいい
1回目はターン変更なし、2回目はターン変更ありの記述にして組み込めばいけるのではないか?ちょっと厳しそう、、、

全く見えてこないので現状のゲームシステムについて振り返る
今はturn0から始まり、石を置けるマスをクリックするとcheng_turn関数に当たる
cheng_turnではturn=0の時に+1をして黒に回すがそれ以外の時は―1をかける事で1、-1、1、-1,1という流れでターンが切り替わっていく

だからその1ターンの中にクリックを2回出来るようにしようと思う。
まずこちらを試した

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()
                }
            }
              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()
                }
            }
        }
        }
    }
}

これだとターンが交代されなかった。
(2マスずつではゲームを続行できないので3マスずつ置いて始める事にした。)

そもそもここはマスがクリックされた時の処理を判断するところであるためターンを変更することは出来ない気がする、、、
要は白・白・黒・黒・白・白をどうやって定義するかである

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