VB.netでオセロ その2ひっくり返す処理

 こんにちは

前回の記事の続きをしていきます


kpen1214.hatenablog.com


 

前回は画面作成と、ボタンを押したらそこに●が表示される仕組みを作りました

今回は挟んだ石をひっくり返す処理を実装します
書いてるコードはみやすさの問題で実際とは違う部分もいくつかあります。

全体の流れ

おしたボタンの座標によってひっくり返すかチェックする方向の決定

おいた石と隣接する石の色と比べ返すかどうかの判断

ひっくり返すべきと判断したらひっくり返す

また違う方向で同じことを行う

ボタンを押したときの処理の命令の前回からの変更点

    Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click…Button64.Click 
        'テキストが入ってないときのみ動かす
        If sender.Text = "" Then
            stnPlace(sender)
            'ボタンの番号をbtnNmbへ抜き出す処理
            Dim btnName As String = sender.Name
            btnName = btnName.Replace("Button", "")
            Dim btnNmb As Integer = Integer.Parse(btnName)
            revChk(btnNmb) '石をひっくり返す処理
            trnChange()
        End If
    End Sub

新しく関数revChkを追加した。この関数でひっくり返す処理を行う。
また、その関数に使う数字としてボタンの座標が必要なのでボタンのデフォルト名「Button○」から数字を抜き取る作業を行っている


座標によってひっくり返す方向の決定

    '場所に応じて処理をする方向を決める。
    Private Function revChk(ByVal btnNumber As Integer)
        Dim chkDir() As Integer = {-8, -1, 1, 8, -9, -7, 7, 9} '確認する方向の関数 -8=上 -1=左 1=右 8=下 -9=左上 -7=右上 7=左下 9=右下
        Dim i As Integer
        '座標ごとにチェックする方向を除外
        If btnNumber >= 1 And btnNumber <= 16 Then
            Array.Clear(chkDir, 0, 1) '上のチェックを除外
            Array.Clear(chkDir, 4, 1) '左上のチェックを除外
            Array.Clear(chkDir, 5, 1) '右上のチェックを除外
        End If
        If {1, 2, 9, 10, 17, 18, 25, 26, 33, 34, 41, 42, 49, 50, 57, 58}.Contains(btnNumber) Then
            Array.Clear(chkDir, 1, 1) '左のチェックを除外
            Array.Clear(chkDir, 4, 1) '左上のチェックを除外
            Array.Clear(chkDir, 6, 1) '左下のチェックを除外
        End If
        If {7, 8, 15, 16, 23, 24, 31, 32, 39, 40, 47, 48, 55, 56, 63, 64}.Contains(btnNumber) Then
            Array.Clear(chkDir, 2, 1) '右のチェックを除外
            Array.Clear(chkDir, 5, 1) '右上のチェックを除外
            Array.Clear(chkDir, 7, 1) '右下のチェックを除外
        End If
        If btnNumber >= 49 And btnNumber <= 64 Then
            Array.Clear(chkDir, 3, 1) '下のチェックを除外
            Array.Clear(chkDir, 7, 1) '右下のチェックを除外
            Array.Clear(chkDir, 6, 1) '左下のチェックを除外
        End If

        'それぞれの方向で盤面チェック
        For Each i In chkDir
            Select Case i
                Case -8
                    upChk(btnNumber)
                Case -1
                    lChk(btnNumber)
                Case 1
                    rChk(btnNumber)
                Case 8
                    unChk(btnNumber)
                Case -9
                    lupChk(btnNumber)
                Case -7
                    rupChk(btnNumber)
                Case 7
                    lunChk(btnNumber)
                Case 9
                    runChk(btnNumber)
            End Select

            'Console.WriteLine(i)
        Next
    End Function

流れとしては

ひっくり返すかどうかのチェックする方向の配列を決定(上下左右斜めの8方向)

座標によってチェックする必要のない方向を除外

方向chk関数を使いひっくり返すか判断&ひっくり返す

それぞれの方向で繰り返す

チェックする方向を進める方法

基準とする座標から方向と対応した数字を減らしていくようにした
f:id:koji007:20210427133123p:plain
青が座標で赤が方向の数字を表している

例えば座標10の場所から左上が黒かどうかをチェックしたいなら10-9=1の座標の色ともとの座標の色の比較をする、そこで同じ色ならさらに左上をチェックして違う色のところまで同じことを繰り返す…という感じ
なんか伝われ!!

チェックする必要のない方向の除外
        '座標ごとにチェックする方向を除外
        If btnNumber >= 1 And btnNumber <= 16 Then
            Array.Clear(chkDir, 0, 1) '上のチェックを除外
            Array.Clear(chkDir, 4, 1) '左上のチェックを除外
            Array.Clear(chkDir, 5, 1) '右上のチェックを除外
f:id:koji007:20210427140346p:plain
イメージ図

上端二行なら上の石をひっくり返すことはないのでその方向は除外
同じように8方向で座標によって除外する方向を設定。


盤面チェックをすると決めた方向でチェックを繰り返す
        'それぞれの方向で盤面チェック
        For Each i In chkDir
            Select Case i
                Case -8
                    upChk(btnNumber)
                Case -1
                    lChk(btnNumber)
                Case 1
                    rChk(btnNumber)
                Case 8
                    unChk(btnNumber)
                Case -9
                    lupChk(btnNumber)
                Case -7
                    rupChk(btnNumber)
                Case 7
                    lunChk(btnNumber)
                Case 9
                    runChk(btnNumber)
            End Select

            'Console.WriteLine(i)
        Next

for文を使い、chkDirに格納された方向の配列を繰り返し、場合によってupChk(上をひっくり返すかどうか判断し実際にひっくり返す関数)などを実行する
伝わってる気がしない、もうダメだ

ひっくり返すべきか判断して実際にひっくり返す処理

    '上をひっくり返す処理
    Private Function upChk(ByVal btnNmb0 As Integer)
        Dim btn000 As Button 'チェック対象のボタンの変数
        Dim n As Integer = 0 'ボタンチェック回数
        Dim i As Integer 'ループ変数
'①
        Do
            n += 1
            btnNmb0 += -8
            btn000 = chgNmbtoBtn(btnNmb0)
            'ボタンに●が入っていないなら中止
            If btn000.Text <> "●" Then
                Exit Function
            End If
            '同じ色の端の石来たら終了
            If btnNmb0 >= 1 And btnNmb0 <= 8 And btn000.ForeColor <> turnColor() Then
                Exit Function
            End If
        Loop While btn000.ForeColor <> turnColor()

        'n-1回盤面をひっくり返す②
        For i = 2 To n
            btnNmb0 += 8
            btn000 = chgNmbtoBtn(btnNmb0)
            btn000.ForeColor = turnColor()
        Next i

ここの流れについては



繰り返した回数(nとする)の記録

元の座標の取得(例:座標25)

上方向の座標(座標Yとする)の取得(例:25-8=17)

chgNmbtoBtn関数(自前の関数)を使い数字を対応するButtonオブジェクトに変換し、btn000変数に格納
(例:chgNmbtoBto(17)=Button17 btn000はButton17を表している)

座標Yに異常がないかの確認、同色かつ端の石ならその場で終了、石がおいてなくても終了

座標Yの石の色とそのターンの色(先行なら黒など)が違うかの確認 →同じなら→ ①(もう一回同じ操作)

違うなら



座標Yの下の石をひっくり返す、これをn-1回繰り返す

終了


こんな感じです、伝わってる気も誰かが読んでるような気もしない


実際の動作

f:id:koji007:20210427142940g:plain

こんな感じになった!!案外なんとか動きました


まとめ

今回やってみて関数で動作をまとめることとコメント、バージョン管理の大事さなどアルゴリズムを考えることよりも重要なことがよくわかった
作ったプログラムももっと関数使ったりしたら可読性がよくてメンテナンスしやすいプログラムにできると思う

多分アルゴリズムの方ももっとスマートでシンプルにできるような気がする、結構やってる判断アナログだし…

次回は石をおける場所を制限する機能をつけたいと思う、全然やりかた思いつかんけどなんとかなるやろ