ボードの実装が出来たので、今日は人同士が実際にこのゲームを遊べるようにしてみる。
CUIだけどw
ボードの表示
まずは、ボードを表示するためのモジュールを実装。
module YWF module BoardViewer COL_INDEX = " 1 2 3 4 5 6 7 8 9" LINE = " +---+---+---+---+---+---+---+---+---+" def view(board) puts "[#{sprintf '%03d', board.move}] turn: #{mark(board.turn)}, token: #{mark(board.token)}" puts COL_INDEX puts LINE (Board::ROW_MIN..Board::ROW_MAX).each do |row| print " #{row} " (Board::COL_MIN..Board::COL_MAX).each do |col| color = board.color(row, col) print "| #{mark(color)} " end puts "|" puts LINE end end def mark(color) case color when Board::EMPTY " " when Board::BLACK "O" when Board::GRAY "-" when Board::WHITE "X" else raise "invalid color. [color: #{color}]" end end module_function :view, :mark end end
ここでは、黒をO
、白をX
、そして灰色を-
で出力するようにしてる。
ゲーム進行の管理
次に、ゲーム進行を管理するクラスを実装。
module YWF class Game def initialize(black_player, white_player) @black_player = black_player @white_player = white_player end def start board = Board.new done = loop do BoardViewer.view(board) if board.game_end? break true end if board.must_pass? puts "pass!" board = board.pass else if board.turn == Board::BLACK command, row, col = @black_player.select(board) else command, row, col = @white_player.select(board) end case command when :play board = board.play(row, col) when :change board = board.change(row, col) when :exit break false end end end puts "----------" if done puts "black: #{board.count(Board::BLACK)}, white: #{board.count(Board::WHITE)}" if board.win?(Board::BLACK) puts "#{BoardViewer.mark(Board::BLACK)} win." elsif board.win?(Board::WHITE) puts "#{BoardViewer.mark(Board::WHITE)} win." else puts "draw." end else puts "exit." end end end end
このクラスは、ゲームを進行させながら、手番のプレイヤーに手の選択を促すようにしていく。
そのインタフェースとなるのが、selectというメソッド。
このメソッドは、現在のボードを引数とし、実行するアクションとその位置を戻すことを要求する。
プレイヤーが人ならここで手の選択を促すし、AIなら思考ルーチンを走らせて手の選択をすることになる。
なお、途中でゲームをやめたくなる場合もあるので、実行するアクションとして:exitが返されたら、ゲームを終了するようにしてある。(Ctrl+Cでもいいんだけどね)
プレイヤー
残すは、プレイヤー。
#!/usr/bin/env ruby require_relative "board" require_relative "game" module YWF class Human def select(board) puts "playable : #{board.playable_places}" puts "changeable: #{board.changeable_places}" loop do $stdout.print "> " $stdout.flush command, row_s, col_s = $stdin.gets.split(/\s*[\s,]\s*/) row = row_s.to_i col = col_s.to_i begin case command[0] when 'p' if board.playable?(row, col) break [:play, row, col] else raise "cannot playable. [row: #{row}, col: #{col}]" end when 'c' if board.changeable?(row, col) break [:change, row, col] else raise "cannot changeable. [row: #{row}, col: #{col}]" end when 'e' break [:exit, 0, 0] else raise "invalid command. [command: #{command}]" end rescue => e puts e.message puts "input 'play row, col', 'change row, col', or 'exit'." end end end end end if __FILE__ == $PROGRAM_NAME include YWF game = Game.new(Human.new, Human.new) game.start end
YWF::Human#selectで行っているのは、置くことが可能な位置、灰色の石を自分の色に変えることが出来る位置をリストアップし、手の選択を促すこと。
ただし、入力のフォーマットが間違っていたり、合法手が指定されない可能性もあるので、例外が起きた場合はエラーメッセージを表示し、再度入力を促すようにしてある。
あとは、実際に遊べるように、ゲームを作って実行させるコードをちょろっと書いてある。
実行例
さて、これを実行すると、例えばこんな感じ。
[001] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | X | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | - | O | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 4], [3, 6], [3, 7], [4, 3], [4, 7], [5, 3], [6, 3], [6, 7], [7, 4], [7, 5], [7, 6]] changeable: [[4, 4], [6, 6]] > play 3, 7 [002] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | O | O | | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 4], [3, 5], [3, 6], [4, 7], [5, 7], [6, 3], [6, 7], [7, 4]] changeable: [] > play 5, 7 [003] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | - | - | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | X | - | | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 4], [4, 3], [4, 7], [6, 3], [6, 7], [7, 5]] changeable: [[4, 4], [4, 6], [5, 5], [6, 6]] > play 6, 7 [004] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | - | O | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | - | O | O | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 4], [3, 5], [3, 6], [7, 4], [7, 5], [7, 6], [7, 7]] changeable: [[5, 5]] > change 5, 5 [005] turn: O, token: O 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | X | - | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | - | O | O | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 3], [3, 4], [3, 6], [4, 3], [4, 7], [4, 8], [6, 3], [7, 5]] changeable: [[4, 4], [4, 6], [6, 5]] > play 3, 6 [006] turn: X, token: O 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | | O | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | O | O | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | X | O | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | - | O | O | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[2, 7], [2, 8], [3, 3], [3, 4], [3, 5], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7]] changeable: [] > play 3, 5 [007] turn: O, token: O 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | X | O | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | - | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | X | X | O | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | - | O | O | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 3], [3, 4], [4, 7], [4, 8], [5, 3], [5, 8], [6, 3], [7, 4]] changeable: [[4, 4], [4, 6]] > change 4, 4 [008] turn: X, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | X | O | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | O | - | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | | - | - | O | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | - | O | O | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 8], [5, 3], [7, 5], [7, 7]] changeable: [[5, 4], [5, 5], [6, 5]] > play 5, 3 [009] turn: O, token: - 1 2 3 4 5 6 7 8 9 +---+---+---+---+---+---+---+---+---+ 1 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 2 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 3 | | | | | X | O | O | | | +---+---+---+---+---+---+---+---+---+ 4 | | | | - | - | - | | | | +---+---+---+---+---+---+---+---+---+ 5 | | | X | X | X | - | X | | | +---+---+---+---+---+---+---+---+---+ 6 | | | | O | - | O | O | | | +---+---+---+---+---+---+---+---+---+ 7 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 8 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ 9 | | | | | | | | | | +---+---+---+---+---+---+---+---+---+ playable : [[3, 3], [3, 4], [4, 2], [4, 7], [4, 8], [6, 3]] changeable: [[4, 4], [4, 5], [4, 6], [5, 6]] > exit ---------- exit.
うん、いい感じ。
プレイ感は、割とオセロと変わらないけどw
今日はここまで!