第47回 1手読み(ランダム打ち)のリバーシを作成しました

1手読み(ランダム打ち)のリバーシを作成

1手読み(ランダム打ち)のリバーシを作成したので、ソースを上げます。

GitHub - binagit/reversi_random


githubに上げたけど、ソースを掲載

githubに上げていますが、こちらにもソースを載せます。

README

[プログラムの説明]
 リバーシプログラムである。
 コンピュータは1手読みでランダムな座標に打ってくる。


[起動方法]
  $ ruby main_console.rb


[ログについて]
  ログはlogディレクトリの下に作られていく。
  ログレベルは、Conf.rbにDEBUGを指定している。
 プログラムのなかでもすべてDEBUGレベルによる
 出力を行なっているので、気に入らなければレベルを
 他のレベルに変えればよい。


[操作方法]
  BまたはWで黒または白を決定後、 a,5 のようにカンマ区切りで
  座標を指定する事で石を置いていく。


[終了方法]
  最後(石を置くおところが無くなった状況)までゲーム行なった場合、
 自動的に処理は終了するが途中で止めたい場合は、Ctr + c でプログ
  ラムを止める


[ディレクトリ構造]
 reversi_random トップディレクトリ
  +-- README          リードミー
  +-- main_console.rb プログラム起動ファイル
  +-- log
  |    `-- reversi_random.log  ログファイル
  +-- class
  |    |-- Com_console.rb    コンピュータを表すクラス
   |    |-- Conf.rb           設定ファイルを表すクラス
   |    |-- Human_console.rb  人を表すクラス
   |    |-- Point.rb          座標を表すクラス
   |    `-- System_console.rb システムを表すクラス
  `-- module
        |-- com     Com_console.rbにのみ取り込まれるモジュールを入れたディレクトリ
        |    `-- AI_random.rb  ランダム打ちに必要な処理を集めたモジュール
        |-- common  複数のクラスに取り込まれるモジュールを入れたディレクトリ
        |    |-- Check.rb   石が置けるかどうかチェックする関数群を集めたモジュール
        |    `-- Update.rb  ボードを更新するための関数を集めたモジュール
        |-- human   Human_console.rbにのみ取り込まれるモジュールを入れたディレクトリ
        |    `-- H_Console.rb  Human_consoleクラスの中から、コンソール出力が必要な処理の部分を集めたモジュール
        `-- system  System_console.rbにのみ取り込まれるモジュールを入れたディレクトリ
             `-- S_Console.rb System_consoleクラスの中から、コンソール出力が必要な処理の部分を集めたモジュール


[ソース説明]
 人を表すクラスはHuman_consoleクラスであるが、コンソール出力に必要な処理は、
 H_Consoleモジュールに抜き出してある。
 同様に、システムを表すクラスからもコンソール出力に必要な処理は、S_Console.rb
 に抜き出されている。

 クラスは、多くの機能をモジュールによって追加される方針で作られている。
 これは複数のクラスで同じ処理をする関数がたくさん作られるからである。
 これら共通の関数群を抜き出してモジュールにしている。

 以上は、共通する項目についてモジュールにした例であるが、今回のプログラ
 ムにのみ起因する部分もモジュールとして抜き出している。
  例えば、コンソール出力に必要な関数を抜きだしたモジュールなどがそれである。
 将来、ブラウザで遊べるようにしたいとなった場合、このコンソールに必要なモ
  ジュールをインクールドするのをやめ、新しく作ったモジュールをインクルードし
 なおすだけで、それを可能にしようという発想からである。

 また、コンピュータの思考ルーチンに関する部分もモジュールとして抜き出してある。
 これも後から別の思考ルーチンを使いたいとなったばあいに、今使っているモジュール
 をインクルードするのをやめ、新しいモジュールをインクルードするだけで思考ルーチ
 ンが変わるようにするためである。


[クラス説明]
  Com_consoleクラス
    1手読み(ランダム打ち)を行なうコンピュータを表すクラス
    実際に石が置けるかどうかチェックするのはCheckモジュール   
    実際にボードの更新を行なうのはUpdateモジュール

  Human_consoleクラス
    人を表すクラス
    石を置く場所を入力してもらう処理を行なう
    実際には、H_ConsoleモジュールがCheckモジュールのチェックメソッドを使い、
    入力される値を求める

  System_consoleクラス
    システムを表すクラス
      ボードの更新
      ボードの表示
      人の先手、後手を決める
      ゲームの終了判定
       を行なう
 

  *以上の大まかなクラスがほとんどの処理を行なう。
 *おおざっぱな流れとしては、人の手番では人クラスがが座標を決め、
  *その座標をシステムクラスに渡し、システムクラスがボードを裏返
  *す(更新する)。 コンピュータの手番ではコンピュータクラスが
  *座標を決め、その座標をシステムクラスに渡しシステムクラスが
 *ボードを裏返す(更新する)という手順で行なわれる。


[インクルードしているモジュール]
  Com_consoleクラス
    - Checkモジュール
    - Updateモジュール    *今回は深読みしないので実はUpdateモジュールは使われていない
    - AI_randomモジュール


  Human_consoleクラス
    - Checkモジュール
    - H_Consoleモジュール


  System_consoleクラス
    - Checkモジュール
    - Updateモジュール
    - S_Consoleモジュール

main_console.rb

require 'class/System_console.rb'
require 'class/Human_console.rb'
require 'class/Com_console.rb'
require 'class/Point.rb'
require 'class/Conf.rb'
require 'logger'
require 'pp'


conf             = Conf.new
logger           = Logger.new(conf.log_file, conf.log_rotate)
logger.level     = conf.log_level
logger.progname  = __FILE__ 
logger.debug{ 'main_console start' }

first_board = [
               ['S','S','S','S','S','S','S','S','S','S'],
               ['S','E','E','E','E','E','E','E','E','S'],
               ['S','E','E','E','E','E','E','E','E','S'],
               ['S','E','E','E','E','E','E','E','E','S'],
               ['S','E','E','E','W','B','E','E','E','S'],
               ['S','E','E','E','B','W','E','E','E','S'],
               ['S','E','E','E','E','E','E','E','E','S'],
               ['S','E','E','E','E','E','E','E','E','S'],
               ['S','E','E','E','E','E','E','E','E','S'],
               ['S','S','S','S','S','S','S','S','S','S']
              ]

sys = System_console.new(first_board, 'B', 0)

#---------------------------------------------------------------------------
# 先手、後手を決める
#---------------------------------------------------------------------------
sys.commit_player

# ボードを表示
sys.print_board

while !sys.is_game_over
  logger.debug{ '手数: ' + sys.turn.to_s }
  logger.debug{ 'プレイヤー: ' + "\n" + sys.get_player.pretty_inspect }

  if !sys.is_pass then
    logger.debug{ 'パスでない' }
    #---------------------------------------------------------------------------
    # パスでない場合
    #---------------------------------------------------------------------------
    # 石を打つ座標を取得
    point_info = sys.get_player.select_point_info
    logger.debug{ '置く場所: ' + point_info.pretty_inspect }

    # 打った座標を登録
    sys.point = point_info[:point]
    # ボードを更新
    sys.update(point_info)
    # 手数を更新
    sys.turn = sys.turn + 1
    # ボードを表示
    sys.print_board
  else
    logger.debug{ 'パス' }
    #---------------------------------------------------------------------------
    # パスの場合
    #---------------------------------------------------------------------------
    # パスであることを表示
    sys.print_pass
  end

  # 次の手番のプレイヤーに交代
  sys.change_player
 
  # 次の手番の色に交代
  sys.change_color
end

logger.debug{ 'main_console end' }

Com_console.rb

require 'module/common/Check.rb'
require 'module/common/Update.rb'
require 'module/com/AI_random'
require 'logger'
require 'pp'

#//////////////////////////////////////////////////////////////////////////////////
# Com_consoleクラス
# ------------------------   インスタンス変数   -----------------------------------
#   @conf       設定ファイル 
#   @logger     ロガー
#   @board      ボードの情報(コピーしたものを持つ)
#   @color      コンピュータの色
#   @e_color    コンピュータの敵の色
# --------------------  インクルードするモジュール -------------------------------
#    Checkモジュール      石が置けるかどうかのメソッド群
#    Updateモジュール    盤を更新する(石をひっくり返す)メソッド群
#    AI_randomモジュール  ランダム打ちAIを搭載するためのメソッド群
# ---------------------------- 内容説明 ------------------------------------------
#    1手読み(ランダム打ち)を行なうコンピュータを表すクラス
#    実際に石が置けるかどうかチェックするのはCheckモジュール   
#    実際にボードの更新を行なうのはUpdateモジュール
#//////////////////////////////////////////////////////////////////////////////////

class Com_console
  #################################################################################
  # 処理内容:
  #  コンストラクタ
  #################################################################################
  def initialize(board, color)
    @conf             = Conf.new
    @logger           = Logger.new(@conf.log_file, @conf.log_rotate)
    @logger.level     = @conf.log_level
    @logger.progname  = __FILE__ 
    @logger.debug{ 'Com_console initialize start' }

    @board   = board.dup
    @color   = color
    @e_color = @color == 'W' ? 'B' : 'W' 

    @logger.debug{ 'Com_console initialize end' }
  end

  #################################################################################
  # インクルード
  #################################################################################
  include Check
  include Update
  include AI_random

end

Conf.rb

require 'logger'

#//////////////////////////////////////////////////////////////////////////////////
# Confクラス
# ------------------------   インスタンス変数   -----------------------------------
#   @log_file      ログファイルの場所
#   @log_rotate    ログローテーとの数
#   @log_level     ログのレベル
# ---------------------------- 内容説明 ------------------------------------------
#   設定ファイルの役目をするクラス
#   インスタンス変数の内容が設定内容である    
#//////////////////////////////////////////////////////////////////////////////////

class Conf
  def initialize
    @log_file      = 'log/reversi_random.log'
    @log_rotate    = 5
    @log_level     = Logger::DEBUG
  end

  attr_accessor :log_file, :log_rotate, :log_level
end

Human_console.rb

require 'module/common/Check.rb'
require 'module/human/H_Console.rb'
require 'pp'

#//////////////////////////////////////////////////////////////////////////////////
# Human_consoleクラス
# ------------------------   インスタンス変数   -----------------------------------
#   @conf       設定ファイル 
#   @logger     ロガー
#   @board      ボードの情報(コピーしたものを持つ)
#   @color      人の色
#   @e_color    人の敵の色
# --------------------  インクルードするモジュール -------------------------------
#     Checkモジュール      石が置けるかどうかのメソッド群
#     H_Consoleモジュール  コンソール出力が必要なメソッド群
# ---------------------------- 内容説明 ------------------------------------------
#   人を表すクラス
#   石を置く場所を入力してもらう処理を行なう
#   実際には、H_ConsoleモジュールがCheckモジュールのチェックメソッドを使い、
#   入力される値を求める
#//////////////////////////////////////////////////////////////////////////////////
class Human_console
  #################################################################################
  # 処理内容:
  #  コンストラクタ
  #################################################################################
  def initialize(board, color)
    @conf             = Conf.new
    @logger           = Logger.new(@conf.log_file, @conf.log_rotate)
    @logger.level     = @conf.log_level
    @logger.progname  = __FILE__ 
    @logger.debug{ 'Human_console initialize start' }

    @board = board.dup
    @color = color
    @e_color = @color == 'W' ? 'B' : 'W'

    @logger.debug{ 'Human_console initialize end' }
  end

  include Check
  include H_Console

end

Point.rb

#//////////////////////////////////////////////////////////////////////////////////
# Pointクラス
# -----------------------   インスタンス変数   -----------------------------------
#   @y  y座標を保持
#   @x  x座標を保持
# --------------------  インクルードするモジュール -------------------------------
#    なし
# ---------------------------- 内容説明 ------------------------------------------
#   boardの座標を保持するクラス
#   石が置ける場所の情報のやりとりに使われる
#//////////////////////////////////////////////////////////////////////////////////
class Point
  def initialize(y, x)
    @y = y
    @x = x
  end

  attr_accessor :y, :x
end

System_console.rb

require 'module/common/Check.rb'
require 'module/common/Update.rb'
require 'module/system/S_Console.rb'
require 'class/Human_console.rb'
require 'class/Com_console.rb'
require 'class/Conf.rb'
require 'logger'
require 'pp'

#//////////////////////////////////////////////////////////////////////////////////
# System_consoleクラス
# ------------------------   インスタンス変数   -----------------------------------
#     @board    思考対象時のボード
#     @color    思考対象時の色
#     @e_color  思考対象時の的の色
#     @turn     思考対象時の手数
#     @point    今回プレイヤーが打った座標
#     @player   [
#                思考対象時の手数のときのプレイヤーが0要素目,
#                思考対象時の手数のときにプレイヤーでないものが1要素目
#               ]
# --------------------  インクルードするモジュール -------------------------------
#     S_Consoleモジュール  コンソール出力が必要なメソッド群
#     Checkモジュール      石が置けるかどうかのメソッド群
#     Updateモジュール     ボードを更新するメソッド群
# ---------------------------- 内容説明 ------------------------------------------
#  システムを表すクラス
#    ボードの更新
#    ボードの表示
#    人の先手、後手を決める
#    ゲームの終了判定
#      を行なう
#//////////////////////////////////////////////////////////////////////////////////
class System_console
  #################################################################################
  # 処理内容:
  #  コンストラクタ
  #
  # 引数:
  #  board  思考対象のボード  
  #  color  思考対象時の打ち手の色
  #  turn   思考対象時の手数
  #################################################################################
  def initialize(board, color, turn)
    @conf             = Conf.new
    @logger           = Logger.new(@conf.log_file, @conf.log_rotate)
    @logger.level     = @conf.log_level
    @logger.progname  = __FILE__ 
    @logger.debug{ 'System_console initialize start' }

    @board   = board
    @color   = color
    @e_color = @color == 'W' ? 'B' : 'W' 
    @turn    = turn
    @point   = Point.new(0,0)
    @player  = []

    @logger.debug{ 'System_console initialize end' }
  end

  attr_accessor :board, :color, :e_color, :turn, :point

  include Check
  include Update
  include S_Console

  #################################################################################
  # 処理内容:
  #  今回パスかどうか判断する
  # 戻り値:
  #   false  パスでない場合
  #   true   パスである場合
  #################################################################################
  def is_pass
    @logger.debug{ 'is_pass start' }

    if self.get_mobility_info.length == 0 then
      @logger.debug{ 'is_pass end : return true' }
      return true
    else
      @logger.debug{ 'is_pass end : return false' }
      return false
    end
  end

  #################################################################################
  # 処理内容:
  #  ゲーム終了判定
  # 戻り値:
  #   false  終了でない場合
  #   true   終了である場合
  #################################################################################
  def is_game_over
    @logger.debug{ 'is_game_over start' }
    #----------------------------------------------------------------------------#
    # 現在の手で置ける場所があるか調べる
    #----------------------------------------------------------------------------#
    if self.is_pass then
      #----------------------------------------------------------------------------#
      # 次の手番でも置ける場所があるか調べる
      #----------------------------------------------------------------------------#
      # 次の手に色を変える
      self.change_color

      if self.is_pass then
        # 次の手番でも置ける場所がなかったのでゲームオーバー
        @logger.debug{ 'is_game_over end : return true' }
        return true
      end

      # 元の手に色を戻す
      self.change_color
    end

    @logger.debug{ 'is_game_over end : return false' }
    return false
  end

  #################################################################################
  # 処理内容:
  #  現在手番のプレイヤーを登録
  #################################################################################
  def commit_player
    @logger.debug{ 'commit_player start' }
    if self.select_human_turn == 'B' then
      @player[0] = Human_console.new(@board, 'B')
      @player[1] = Com_console.new(@board, 'W')
    else
      @player[0] = Com_console.new(@board, 'B')
      @player[1] = Human_console.new(@board, 'W')
    end
    @logger.debug{ 'commit_player end' }
  end

  #################################################################################
  # 処理内容:
  #  次の手番用にプレイヤーを交代
  #################################################################################
  def change_player
    @logger.debug{ 'change_player start' }
    @player.reverse!
    @logger.debug{ 'change_player end' }
  end

  #################################################################################
  # 処理内容:
  #  次の手番用に色を交代
  #################################################################################
  def change_color
    @logger.debug{ 'change_color start' }
    @color, @e_color = @e_color, @color
    @logger.debug{ 'change_color end' }
  end

  #################################################################################
  # 処理内容:
  #  現在の手番のプレイヤーを返す
  #################################################################################
  def get_player
    @player[0]
  end

end

AI_random.rb

#//////////////////////////////////////////////////////////////////////////////////
# AI_randomモジュール
# --------------------- インクルードされるクラス -----------------------------------
#   Com_randomクラス
# ---------------------------- 内容説明 ------------------------------------------
#   1手読み(ランダム打ち)に必要な関数を集めたモジュール
#//////////////////////////////////////////////////////////////////////////////////

module AI_random
  #################################################################################
  # 処理内容:
  #  石が置ける座標の中からランダムでひとつ返す
  # 戻り値:
  #  石が置ける座標の情報
  #  { point => 石が置ける座標, dir => 石が置ける向き }
  #################################################################################
  def select_point_info
    @logger.debug{ 'select_point_info start' }

    mobility_info = self.get_mobility_info

    r_info = mobility_info[rand(mobility_info.length)]
    @logger.debug{ 'select_point_info end : return ' + "\n" + r_info.pretty_inspect }
    return r_info
  end
end

Check.rb

#//////////////////////////////////////////////////////////////////////////////////
# Checkモジュール
# --------------------- インクルードするクラス -----------------------------------
#   Com_*クラス
#   Human_*クラス
# ---------------------------- 内容説明 ------------------------------------------
#   石が置けるかどうかチェックする関数群を集めたモジュールである
#   石が置ける座標を列挙する関数も持つ
#//////////////////////////////////////////////////////////////////////////////////

module Check
  #################################################################################
  # 処理内容:
  #  座標が'E'でなあるかチェック
  #  (つまりすでに石が置かれていないかチェック)
  # 戻り値:
  #   true   座標が'E'であるとき
  #   false  座標が'E'でないとき
  #################################################################################
  def is_empty(point)
    @board[point.y][point.x] == 'E' ? true : false
  end

  #################################################################################
  # 処理内容:
  #  上向きにひっくり返せるか探索
  # 戻り値:
  #  'up'  上にひっくり返せるとき
  #  ''    上にひっくり返せないとき
  #################################################################################
  def check_up(point)
    if @board[point.y-1][point.x] == @e_color then
      check_y, check_x = point.y-2, point.x
      while @board[check_y][check_x] == @e_color do check_y = check_y - 1 end
      return 'up' if @board[check_y][check_x] == @color
    end

    return ''
  end

  #################################################################################
  # 処理内容:
  #  下向きにひっくり返せるか探索
  # 戻り値:
  #  'down'  下にひっくり返せるとき
  #  ''      下にひっくり返せないとき
  #################################################################################
  def check_down(point)
    if @board[point.y+1][point.x] == @e_color then
      check_y, check_x = point.y+2, point.x
      while @board[check_y][check_x] == @e_color do check_y = check_y + 1 end
      return 'down' if @board[check_y][check_x] == @color
    end

    return ''
  end

  #################################################################################
  # 処理内容:
  #  左向きにひっくり返せるか探索
  # 戻り値:
  #  'left'  左にひっくり返せるとき
  #  ''      左にひっくり返せないとき
  #################################################################################
  def check_left(point)
    if @board[point.y][point.x-1] == @e_color then
      check_y, check_x = point.y, point.x-2
      while @board[check_y][check_x] == @e_color do check_x = check_x - 1 end
      return 'left' if @board[check_y][check_x] == @color
    end

    return ''
  end

  #################################################################################
  # 処理内容:
  #  右向きにひっくり返せるか探索
  # 戻り値:
  #  'right'  右にひっくり返せるとき
  #  ''       右にひっくり返せないとき
  #################################################################################
  def check_right(point)
    if @board[point.y][point.x+1] == @e_color then
      check_y, check_x = point.y, point.x+2
      while @board[check_y][check_x] == @e_color do check_x = check_x + 1 end
      return 'right' if @board[check_y][check_x] == @color
    end

    return ''
  end
    
  #################################################################################
  # 処理内容:
  #  右上向きにひっくり返せるか探索
  # 戻り値:
  #  'right_up'  右上にひっくり返せるとき
  #  ''          右にひっくり返せないとき
  #################################################################################
  def check_right_up(point)
    if @board[point.y-1][point.x+1] == @e_color then
      check_y, check_x = point.y-2, point.x+2
      while @board[check_y][check_x] == @e_color do check_x = check_x + 1; check_y = check_y - 1; end
      return 'right_up' if @board[check_y][check_x] == @color
    end

    return ''
  end
    
  #################################################################################
  # 処理内容:
  #  左上向きにひっくり返せるか探索
  # 戻り値:
  #  'left_up'  左上にひっくり返せるとき
  #  ''         左上にひっくり返せないとき
  #################################################################################
  def check_left_up(point)
    if @board[point.y-1][point.x-1] == @e_color then
      check_y, check_x = point.y-2, point.x-2
      while @board[check_y][check_x] == @e_color do check_x = check_x - 1; check_y = check_y - 1; end
      return 'left_up' if @board[check_y][check_x] == @color
    end

    return ''
  end

  #################################################################################
  # 処理内容:
  #  左下向きにひっくり返せるか探索
  # 戻り値:
  #  'left_down'  左下にひっくり返せるとき
  #  ''           左上にひっくり返せないとき
  #################################################################################
  def check_left_down(point)
    if @board[point.y+1][point.x-1] == @e_color then
      check_y, check_x = point.y+2, point.x-2
      while @board[check_y][check_x] == @e_color do check_x = check_x - 1; check_y = check_y + 1; end
      return 'left_down' if @board[check_y][check_x] == @color
    end

    return ''
  end

  #################################################################################
  # 処理内容:
  #  右下向きにひっくり返せるか探索
  # 戻り値:
  #  'right_down'  右下にひっくり返せるとき
  #  ''            右上にひっくり返せないとき
  #################################################################################
  def check_right_down(point)
    if @board[point.y+1][point.x+1] == @e_color then
      check_y, check_x = point.y+2, point.x+2
      while @board[check_y][check_x] == @e_color do check_x = check_x + 1; check_y = check_y + 1; end
      return 'right_down' if @board[check_y][check_x] == @color
    end

    return ''
  end

  #################################################################################
  # 処理内容:
  #  石を置ける座標を列挙する
  # 返り値:
  #  置ける座標のPointと置ける向きの情報を持った配列
  #  [
  #   { :point => Point.new(座標), :dir => [置ける向き1, 置ける向き2, .. ] },
  #   ...
  #  ]
  #################################################################################
  def get_mobility_info
    @logger.debug{ 'get_mobility_info start' }

    mobility_info = []
    for check_y in (1..@board.length-2).to_a 
      for check_x in (1..@board.length-2).to_a 
        point = Point.new(check_y, check_x)
        #=======================================================================#
        # 'E'である場所を探す
        #=======================================================================#
        if self.is_empty(point) then
          checks = [
                    self.check_up(point), self.check_down(point), self.check_left(point), self.check_right(point),
                    self.check_right_up(point),  self.check_left_up(point),
                    self.check_left_down(point), self.check_right_down(point)
                   ]
          #=======================================================================#
          # 全部 '' でないものがあった場合、 その座標を登録
          #=======================================================================#
          if ! checks.all?{|v| v == ''} then
            mobility_info.push( {:point => point, :dir => checks.select{|v| v != ''}} )
          end
        end
      end 
    end

    @logger.debug{ 'get_mobility_info end : return ' + "\n" + mobility_info.pretty_inspect }
    return mobility_info
  end
end

Update.rb

#//////////////////////////////////////////////////////////////////////////////////
# Updateモジュール
# --------------------- インクルードされるクラス -----------------------------------
#   Com_*クラス
#   System_*クラス
# ---------------------------- 内容説明 ------------------------------------------
#   ボードを更新するための関数を集めたモジュール
#//////////////////////////////////////////////////////////////////////////////////
module Update
  #################################################################################
  # 処理内容:
  #  受け取ったPointと方向を元に石をひっくり返す
  #################################################################################
  def update(mobility_info)
    @logger.debug{ 'update start' }
    @logger.debug{ 'mobility_info : ' + "\n" + mobility_info.pretty_inspect }
    @board[mobility_info[:point].y][mobility_info[:point].x] = @color

    mobility_info[:dir].each{|dir|
      @logger.debug{ 'call : ' + "\n" + dir.pretty_inspect } 
      self.method( 'update_' + dir ).call(mobility_info[:point])
    }
    @logger.debug{ 'update end' }
  end

  #################################################################################
  # 処理内容:
  #  上向きにひっくり返す
  #################################################################################
  def update_up(point)
    update_y, update_x = point.y-1, point.x
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_y                    = update_y - 1
    end
  end

  #################################################################################
  # 処理内容:
  #  下向きにひっくり返す
  #################################################################################
  def update_down(point)
    update_y, update_x = point.y+1, point.x
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_y                    = update_y + 1
    end
  end

  #################################################################################
  # 処理内容:
  #  左向きにひっくり返す
  #################################################################################
  def update_left(point)
    update_y, update_x = point.y, point.x-1
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_x                    = update_x - 1
    end
  end

  #################################################################################
  # 処理内容:
  #  右向きにひっくり返す
  #################################################################################
  def update_right(point)
    update_y, update_x = point.y, point.x+1
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_x                    = update_x + 1
    end
  end

  #################################################################################
  # 処理内容:
  #  右上向きにひっくり返す
  #################################################################################
  def update_right_up(point)
    update_y, update_x = point.y-1, point.x+1
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_y                    = update_y - 1
      update_x                    = update_x + 1
    end
  end

  #################################################################################
  # 処理内容:
  #  左上向きにひっくり返す
  #################################################################################
  def update_left_up(point)
    update_y, update_x = point.y-1, point.x-1
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_y                    = update_y - 1
      update_x                    = update_x - 1
    end
  end

  #################################################################################
  # 処理内容:
  #  左下向きにひっくり返す
  #################################################################################
  def update_left_down(point)
    update_y, update_x = point.y+1, point.x-1
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_y                    = update_y + 1
      update_x                    = update_x - 1
    end
  end

  #################################################################################
  # 処理内容:
  #  右下向きにひっくり返す
  #################################################################################
  def update_right_down(point)
    update_y, update_x = point.y+1, point.x+1
    while @board[update_y][update_x] != @color
      @board[update_y][update_x]  = @color
      update_y                    = update_y + 1
      update_x                    = update_x + 1
    end
  end
end

H_Console.rb

#//////////////////////////////////////////////////////////////////////////////////
# H_Consoleモジュール
# --------------------- インクルードされるクラス -----------------------------------
#   Human_consoleクラス
# ---------------------------- 内容説明 ------------------------------------------
#   Human_consoleクラスの中から、コンソール出力が必要な処理の部分を集めた関数群
#//////////////////////////////////////////////////////////////////////////////////
module H_Console
  #################################################################################
  # 処理内容:
  #  人がどこの座標に置くか決める
  # 返り値:
  #  人が置く座標の情報
  #  { :point => 置ける座標,  :dir => ひっくり返せる向き }
  #################################################################################
  def select_point_info
    #---------------------------------------------------------------------------------------
    # 石が置ける座標を求める
    #---------------------------------------------------------------------------------------
    mobility_info = self.get_mobility_info

    #---------------------------------------------------------------------------------------
    # 入力された値をチェック用に一時保存する変数
    #---------------------------------------------------------------------------------------
    point = []

    #---------------------------------------------------------------------------------------
    # 正しい入力があるまで繰り返し
    #---------------------------------------------------------------------------------------
    puts '以下の箇所に置くことができます'
    mobility_info.each{|v| print 'x軸: ' + ['','a','b','c','d','e','f','g','h'][v[:point].x] + ', ' + 'y軸' + v[:point].y.to_s + "\n" }
    print ' どこに打ちますか? (x軸,y軸) ==> '
    while 1
      point = gets.chomp
      point = point.split(/,/)

      #---------------------------------------------------------------------------------------
      # カンマ区切りで かつ 合計3文字の入力かチェック
      #---------------------------------------------------------------------------------------
      if point.length != 2 then
        puts '========================================='
        puts '入力はカンマ区切りで1文字づつです'
        puts 'もう一度入力してください。 入力例:  a,3'
        puts '========================================='
        print ' どこに打ちますか? (x軸,y軸) ==> '
        next
      end

      #---------------------------------------------------------------------------------------
      # a~hの値が入力されているかチェック
      #---------------------------------------------------------------------------------------
      if !(/[abcdefgh]/ =~ point[0]) then
        puts '========================================='
        puts '1つ目はa~hです'
        puts 'もう一度入力してください。 入力例:  a,3'
        puts '========================================='
        print ' どこに打ちますか? (x軸,y軸) ==> '
        next
      end

      #---------------------------------------------------------------------------------------
      # 1~8の値が入力されているかチェック
      #---------------------------------------------------------------------------------------
      if !(/[12345678]/ =~ point[1]) then
        puts '========================================='
        puts '2つ目は1~8です'
        puts 'もう一度入力してください。 入力例:  a,3'
        puts '========================================='
        print ' どこに打ちますか? (x軸,y軸) ==> '
        next
      end

      #---------------------------------------------------------------------------------------
      # a~hをboardの配列の位置に変換
      #---------------------------------------------------------------------------------------
      point[0] = {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>4, 'e'=>5, 'f'=>6, 'g'=>7, 'h'=>8}[point[0]]
      point[1] = point[1].to_i

      #---------------------------------------------------------------------------------------
      # 石が置ける座標かどうか判断
      #---------------------------------------------------------------------------------------
      if mobility_info.map{|v| v[:point] }.find_all{|po| (po.x == point[0]) && (po.y == point[1]) } == [] then
        puts '========================================='
        puts 'そこには石が置けません                   '
        puts '========================================='
        print ' どこに打ちますか? (x軸,y軸) ==> '
        next
      end

      #---------------------------------------------------------------------------------------
      # すべてのチェックをくぐり抜けているとループを抜ける(正しい値を得た)
      #---------------------------------------------------------------------------------------
      break
    end

    return mobility_info.map.find{|v| v[:point].x == point[0] && v[:point].y == point[1] }
  end
end

S_Console.rb

#//////////////////////////////////////////////////////////////////////////////////
# S_Consoleモジュール
# --------------------- インクルードされるクラス -----------------------------------
#   System_consoleクラス
# ---------------------------- 内容説明 ------------------------------------------
#   System_consoleクラスの中から、コンソール出力が必要な処理の部分を集めた関数群
#     人の先手、後手を決める処理
#     ボードの表示
#//////////////////////////////////////////////////////////////////////////////////

module S_Console
  #################################################################################
  # 処理内容:
  #  人の先手、後手を決める
  #################################################################################
  def select_human_turn
    puts '**************************************'
    puts '  先手、後手 どちらで開始しますか?  '
    puts '======================================'
    puts '         Black  ○ 先手:  B           '
    puts '         White  ●  後手:  W           '
    puts '======================================'
    print '==> '

    while 1
      turn = gets.chomp
      if (turn == 'B') || (turn == 'W') then
        break
      else
        print 'BかWで入力してください ==> '
      end
    end

    return turn
  end

  #################################################################################
  # 処理内容:
  #  パスである旨を表示し、リターンキーで処理を進めさせる
  #################################################################################
  def print_pass
    puts '=================================================='
    puts self.get_player.instance_of?(Human_console) ? '人は' : 'コンピュータは'
    puts ' パスです リターンキーを押して次に進んでください '
    puts '=================================================='
    print '==> '

    while 1
      message = gets
      if message == "\n" then
        break
      else
        print 'リターンキーを押してください ==> '
      end
    end
  end

  #################################################################################
  # 処理内容:
  #  @boardを出力する
  #################################################################################
  def print_board
    puts '****** ' '現在の手数: ' + self.turn.to_s + ' ******'  
    if @turn != 0 then
      print '******↓ '
      print self.get_player.instance_of?(Human_console) ? '' : 'コンピュータ'
      print '(' + ['','a','b','c','d','e','f','g','h'][@point.x] + ', ' + @point.y.to_s + ')'
      print ' ↓******' + "\n"
    end
    puts '  a b c d e f g h '
    puts ' ┌─┬─┬─┬─┬─┬─┬─┬─┐'

    i = 1
    p_board = @board[1..8]

    p_board.each{|x_line|
      print i.to_s + '|'
      i = i + 1
      x_line.each{|v|
        print '' + '|' if v == 'B'
        print '' + '|' if v == 'W'
        print ' ' + '|' if v == 'E'
      }
      print "\n"
    }

    puts ' └─┴─┴─┴─┴─┴─┴─┴─┘'
  end
end