【講座?】CoRを触ってみる21~三目並べを作る8~【自分用メモ?】

投稿者:    mini 光楼(114) 投稿日:2017/06/17 12:19

【講座?】CoRを触ってみる20~三目並べを作る7~【自分用メモ?】←前回

#このブログの情報は2017年6月11日現在のものです。今後変更になる可能性があります。

今回は敵のAIを作っていきます。

いつものようにgame.rbを開いてね!

AIの処理の流れですが、
勝てる手を探す → 負けない手を探す
という流れにしようと思います。

  • 勝てる手を探す
×が3つ揃う位置を探します。

方法は全ての空のマスに順番に×を入れて、勝敗判定の処理をさせます。
判定が2(プレイヤーの敗北)となれば、そのマスに×を入れるとAIが勝てるということになります。

では早速やってみましょう!
#前略
  update do
    comparison_board_data = board_data.dup #配列コピー

    if decision % 2 == 0 #勝敗判定実行判断
      ####プレーヤーor敵ターン####

      if turn % 2 == 0
        ####プレーヤーのターン####
  
        #クリックまたはタップしたか
        if pointer.down?
          column = ((pointer.x - board_left_position) / board_size).floor #列を求める
          paragraph = ((pointer.y - board_up_position) / board_size).floor #段を求める
    
          #列が0~2、段も0~2の範囲か
          if (0..2) === column && (0..2) === paragraph
            block = column + paragraph * 3 #列・段からマス番号を求める
            if board_data[block] == 0 #選択したマスが空白か
              board_data[block] = 1
  
              turn += 1 #行動したらターン交代
              decision += 1
            end
          end
        end
  
      else
        ####敵のターン####
        wait_time(500) #0.5秒待機

        ##### ↓追加分↓  #####
        result = nil #×を入れるマス番号
        temporary_board = nil #操作用盤面

        #勝てる場所探索
        9.times do |i| #9回ループ
          if board_data[i] == 0 #i番のマスは空か
            temporary_board = board_data.dup #操作用盤面に盤面データをコピー
            
            temporary_board[i] = -1 #×を入れてみる
            if win_or_loss(temporary_board) == 2 #プレイヤーが負けるか
              result = i #×配置場所確定
              break #ループ脱出
            end
          end
        end
        ##### ↑追加分↑  #####

        ##### ↓変更分↓  #####
        #ランダム選択
        if result == nil
          while(board_data[result = rand(9)] != 0) do
            wait_time(100) #無限ループ対策
          end
        end
        ##### ↑変更分↑  #####
  
        board_data[result] = -1 #盤面情報書き換え
        turn += 1 #ターン交代
        decision += 1
      end
#後略

勝てる場所を見つけた後にランダム選択を行われると困るので、配置場所が決まってない時だけ行うようif文を追加しました。

さて続いては……
  • 負けない手を探す
これは『勝てる手を探す』の逆です。
〇を置いてみてプレイヤーが勝ってしまう場所を探すんです。

なのでさっきのを
#勝てる場所探索
        9.times do |i| #9回ループ
          if board_data[i] == 0 #i番のマスは空か
            temporary_board = board_data.dup #操作用盤面に盤面データをコピー
            
            temporary_board[i] = 1 #〇を入れてみる
            if win_or_loss(temporary_board) == 1 #プレイヤーが勝つか
              result = i #×配置場所確定
              break #ループ脱出
            end
          end
        end

とすればいいのですが、ほとんど同じ処理を2度書くのはあまり賢くないやり方なので、ループと変数使ってもう一度処理を行わせます。
  update do
    comparison_board_data = board_data.dup #配列コピー

    if decision % 2 == 0 #勝敗判定実行判断
      ####プレーヤーor敵ターン####

      if turn % 2 == 0
        ####プレーヤーのターン####
  
        #クリックまたはタップしたか
        if pointer.down?
          column = ((pointer.x - board_left_position) / board_size).floor #列を求める
          paragraph = ((pointer.y - board_up_position) / board_size).floor #段を求める
    
          #列が0~2、段も0~2の範囲か
          if (0..2) === column && (0..2) === paragraph
            block = column + paragraph * 3 #列・段からマス番号を求める
            if board_data[block] == 0 #選択したマスが空白か
              board_data[block] = 1
  
              turn += 1 #行動したらターン交代
              decision += 1
            end
          end
        end
  
      else
        ####敵のターン####
        wait_time(500) #0.5秒待機

        result = nil #×を入れるマス番号
        temporary_board = nil #操作用盤面
        input_mark = -1 #配置するマーク
        required_decision = 2 #欲しい判定結果

        ##### ↓変更分↓  #####
        #勝てる場所・負けを防ぐ場所探索
        j = 0
        while 1
          9.times do |i| #9回ループ
            if board_data[i] == 0 #i番のマスは空か
              temporary_board = board_data.dup #操作用盤面に盤面データをコピー
              
              temporary_board[i] = input_mark #×・〇を入れてみる
              if win_or_loss(temporary_board) == required_decision #欲しい判定が出るか
                result = i #×配置場所確定
                break #ループ脱出
              end
            end
          end

          if j == 0 && result == nil #負けを防ぐ処理へ
            j = 1
            input_mark = 1 #配置するマークを〇に
            required_decision = 1 #欲しい判定を1に
          else
            break #ループ脱出
          end
        end
        ##### ↑変更分↑  #####

        #ランダム選択
        if result == nil
          while(board_data[result = rand(9)] != 0) do
            wait_time(100) #無限ループ対策
          end
        end
  
        board_data[result] = -1 #盤面情報書き換え
        turn += 1 #ターン交代
        decision += 1
      end

ループ回数を変数jで管理しています。
勝てる場所が見つかったならループを脱出・見つからなかったら負けを防ぐ処理へ移行します。

AIの負けを防げる位置が見つかった場合はループを脱出、見つからなかった場合もループ回数の条件に引っ掛かりループを脱出します。
変数resultnilのままなのでランダム処理を行います。

AIはこれで完成です。
お疲れさまでした!

次回→まだ

コメントする

コメントするには、ログインする必要があります。

コメント一覧

            mini mosmoss(投稿日:2017/06/20 00:13, 履歴)
三目並べでこれだとオセロやチェスのaiを思うと気が遠くなります
CoRならではの書き方の色々、参考にさせてもらいます
    mini 光楼(114)(投稿日:2017/06/21 18:55, 履歴)
ルールが複雑なほどAIの処理も大変ですよね
Cdv30200 aoi icon mini aoihikawa(投稿日:2017/06/18 18:12, 履歴)
特定の処理を
ループで纏めたり、
変数の呼び出しで統一したり、

こういった部分を考えて、
じっくり組み上げていくところも
プログラムの面白いところですね
    mini 光楼(114)(投稿日:2017/06/19 20:32, 履歴)
そうですね。慣れるまでが大変ですが(;^ω^)