Permalink
Browse files

Made '@board' an instance variable of player implementations and made…

… 'Pentago::Rules' module accessible only through *a* board instance.
  • Loading branch information...
etrepat committed Nov 25, 2011
1 parent fdc8e8d commit 0ffcfd21a41f78bb21b601d0c52ece9526199616
View
@@ -7,8 +7,8 @@
module Pentago; end
-require_relative 'pentago/board'
require_relative 'pentago/rules'
+require_relative 'pentago/board'
require_relative 'pentago/game'
require_relative 'pentago/version'
View
@@ -21,12 +21,31 @@ class Board
ROWS = COLS = 6
SIZE = ROWS * COLS
+ attr_accessor :squares
+
+ include Pentago::Rules
+
+ class << self
+ def restore(board)
+ restored = Board.new
+ restored.squares = case board
+ when Array
+ raise TypeError, "incompatible board array #{board.size}" if board.size != SIZE
+ board.dup
+ when Board
+ board.dup.to_a
+ else
+ raise TypeError, 'incompatible types'
+ end
+
+ restored
+ end
+ end
+
def initialize
clear
end
- attr_accessor :squares
-
def [](x, y)
raise IllegalPositionError, "illegal position [#{x}, #{y}]" unless valid_position?(x, y)
@squares[translate(x, y)]
@@ -116,8 +135,8 @@ def to_a
@squares
end
- def ==(board)
- @squares == board.squares
+ def ==(other)
+ @squares == other.squares
end
alias_method :eql?, :==
@@ -126,21 +145,6 @@ def dup
Board.restore(@squares)
end
- def self.restore(board)
- restored = Board.new
- restored.squares = case board
- when Array
- raise TypeError, "incompatible board array #{board.size}" if board.size != SIZE
- board.dup
- when Board
- board.dup.to_a
- else
- raise TypeError, 'incompatible types'
- end
-
- restored
- end
-
private
def translate(x,y)
@@ -1,10 +1,10 @@
module Pentago
class DummyPlayer < Player
- def compute_next_move(board)
+ def compute_next_move
# search for empty space w/ a highly precise algorithm :)
begin
x, y = rand(Board::COLS), rand(Board::ROWS)
- end while board[x,y]
+ end while @board[x,y]
# equally, search for a square to turn? into which direction
square = rand(Board::SQUARES.size)
View
@@ -1,16 +1,26 @@
module Pentago
class Game
DuplicatedPlayersError = Class.new(StandardError)
-
+
include Observable
- include Pentago::Rules
-
+
+ class << self
+ def create(options)
+ board = options.fetch(:board, Board.new)
+ player1 = Player.create(options.fetch(:player_engine_1), :board => board,
+ :marble => 1)
+ player2 = Player.create(options.fetch(:player_engine_2), :board => board,
+ :marble => 2)
+
+ Game.new(:board => board, :player1 => player1, :player2 => player2)
+ end
+ end
+
def initialize(params)
+ @board = params.fetch(:board)
@player1 = params.fetch(:player1)
@player2 = params.fetch(:player2)
raise DuplicatedPlayersError if @player1 == @player2
-
- @board = params.fetch(:board, Board.new)
end
attr_reader :player1, :player2, :board, :current_player
@@ -19,17 +29,17 @@ def play
reset
play_turn while !game_over?
end
-
+
def reset
@board.clear
@current_player = nil
@winner = nil
turn.rewind
end
-
+
def play_turn
@current_player = turn.next
- @current_player.play_turn(@board)
+ @current_player.play_turn
changed
notify_observers self
@@ -42,32 +52,32 @@ def turns_played
def players
@players ||= [player1, player2]
end
-
+
def winner
unless @winner
- who_won = find_winner(@board)
+ who_won = @board.find_winner
@winner = players.select { |p| p.marble == who_won }.first
end
-
+
@winner
end
-
+
def board_full?
@board.full?
end
-
+
def game_over?
- check_game_over(@board)
+ @board.game_over?
end
-
+
def tie_game?
- check_tie_game(@board)
+ @board.tie_game?
end
-
+
private
-
+
def turn
@turn ||= players.cycle
end
end
-end
+end
@@ -1,15 +1,15 @@
module Pentago
class HumanPlayer < Player
- def initialize(marble, name='', callback=nil)
- super(marble, name)
- @ask_for_move_callback = callback
+ def initialize(opts={})
+ super(opts)
+ @ask_for_move_callback = opts.fetch(:callback, nil)
end
attr_accessor :ask_for_move_callback
- def compute_next_move(board)
- raise 'how come should I ask a user for a move?' unless ask_for_move_callback
- @ask_for_move_callback.call(self, board)
+ def compute_next_move
+ raise 'how come should I ask a user for a move?' unless @ask_for_move_callback
+ @ask_for_move_callback.call(self, @board)
end
end
end
@@ -1,18 +1,19 @@
module Pentago
class NegamaxPlayer < Player
- def initialize(marble, name='', search_depth=1)
- super(marble, name)
- @search_depth = search_depth
+ def initialize(opts={})
+ super(opts)
+
+ @search_depth = opts.fetch(:search_depth, 1)
end
attr_accessor :search_depth
- def compute_next_move(board)
- available_moves(board).sort_by do |square, rotation|
+ def compute_next_move
+ available_moves(@board).sort_by do |square, rotation|
x, y = square
s, d = rotation
- board_copy = board.dup
+ board_copy = @board.dup
board_copy[x, y] = @marble
board_copy.rotate(s, d)
@@ -21,7 +22,7 @@ def compute_next_move(board)
end
def negamax(board, depth, player, alpha=-1, beta=1)
- return score(board, player) if depth == 0 || check_game_over(board)
+ return score(board, player) if depth == 0 || board.game_over?
available_moves(board).each do |square, rotation|
x, y = square
@@ -40,14 +41,14 @@ def negamax(board, depth, player, alpha=-1, beta=1)
# TODO: improve scoring functions
def score(board, player)
- winner = find_winner(board)
+ winner = board.find_winner
return 1000000 if winner && winner == player
return -1000000 if winner && winner == opponent(player)
score_for(board, player) - score_for(board, opponent(player))
end
def score_for(board, marble)
- (board.rows + board.columns + board.diagonals).inject(0) do |sum, run|
+ board.runs.inject(0) do |sum, run|
sum + run.count { |value| value.nil? || value == marble }
end
end
@@ -63,4 +64,3 @@ def available_moves(board)
end
end
end
-
View
@@ -1,41 +1,51 @@
module Pentago
class Player
- include Pentago::Rules
+ class << self
+ def create(engine_name, options={})
+ Pentago.const_get(engine_name).new(options)
+ rescue Exception => e
+ raise TypeError, "#{e.message}\n!! Invalid player engine (#{engine_name})!"
+ end
+ end
- def initialize(marble, name='')
- @marble = marble
- @name = name
+ def initialize(opts={})
+ @board = opts.fetch(:board)
+ @marble = opts.fetch(:marble)
+ @name = opts.fetch(:name, '')
@last_move = nil
end
+ attr_accessor :board
attr_reader :marble, :name, :last_move
- def play_turn(board)
- x, y, s, d = compute_next_move(board)
- execute_move(board, x, y, s, d)
+ def play_turn
+ x, y, s, d = compute_next_move
+
+ execute_move(x, y, s, d)
+
@last_move = [x, y, s, d]
end
- def compute_next_move(board)
+ def compute_next_move
# to be overriden by subclasses
raise 'compute_next_move should be overriden by a subclass'
end
- def execute_move(board, x, y, square, direction)
- board[x, y] = marble
- board.rotate(square, direction)
+ def execute_move(x, y, square, direction)
+ @board[x, y] = @marble
+ @board.rotate(square, direction)
end
-
- def ==(player)
- self.marble == player.marble
+
+ def ==(other)
+ @board == other.board && @marble == other.marble
end
-
+
alias_method :eql?, :==
-
+
def to_s
name.empty? ? marble.to_s : name
end
-
+
alias_method :to_str, :to_s
end
end
Oops, something went wrong.

0 comments on commit 0ffcfd2

Please sign in to comment.