require File.join(File.dirname(__FILE__), '../../ext/chess') module Chess # This class rappresents a chess game. class Game < CGame # Create a new game. If an array of moves is provided, the moves will be performed. # May be raise an IllegalMoveError or BadNotationError. def initialize(moves = []) moves.each { |m| move(m) } end # Creates a new game from a file in PGN format. # May be raise an InvalidPgnFormatError or IllegalMoveError or BadNotationError. def self.load_pgn(file) pgn = Chess::Pgn.new(file) game = Chess::Game.new pgn.moves.each { |m| game.move(m) } return game end # Creates a new game from a FEN string. # *Warning*: this game do not have history before the FEN placement. # May be raise an InvalidFenFormatError. def self.load_fen(fen) if fen =~ /^((?:[PRNBQKprnbqk1-8]{1,8}\/){7}[RNBQKPrnbqkp1-8]{1,8})\s(w|b)\s(K?Q?k?q?|\-)\s([a-h][1-8]|\-)\s(\d+)\s(\d+)$/ game = Chess::Game.new game.set_fen!(fen) return game else raise InvalidFenFormatError.new(fen) end end # Make a move. This add a new Board in the Storyboard. # The parameter +m+ represents the short algebraic chess notation string of the move. # +m+ can be from_square plus to_square ('e2e4', ..., 'b1c3'). # This method returns a string that represents the short algebraic chess notation of the move. # Raise an IllegalMoveError if the move is illegal. # Raise an BadNotationError if the short algebraic chess notation is malformed. def move(m) begin expand = expand_move(m) if expand[:from] move2(expand[:from], expand[:to], expand[:promotion]) else super(expand[:name], expand[:dis], expand[:to], expand[:promotion]) end rescue IllegalMoveError => e raise IllegalMoveError.new("Illegal move '#{m}'\n#{self.to_s}") end end alias :move= :move alias :<< :move # Make the array of moves. def moves=(moves) moves.each { |m| move(m) } end # Returns +:white+ if the active player is the white player, +:black+ otherwise. def active_player self.board.active_color ? :black : :white end # Returns +:white+ if the inactive player is the white player, +:black+ otherwise. def inactive_player self.board.active_color ? :white : :black end # Returns the status of the game. # Possible states are: # * +in_progress+:: the game is in progress. # * +white_wins+:: white player checkmate the black king. # * +black_wins+:: black player checkmate the white king. # * +stalemate+:: draw for stalemate # * +insufficient_material+:: draw for insufficient material to checkmate. # * +unknown+:: something went wrong. def status case self.result when '*' return :in_progress when '1-0' return :white_wins when '0-1' return :black_wins when '1/2-1/2' if self.board.stalemate? return :stalemate elsif self.board.insufficient_material? return :insufficient_material end end return :unknown end # Returns +true+ if the game is over def over? return self.result != '*' end # Returns the PGN rappresenting the game. def pgn pgn = Chess::Pgn.new pgn.moves = self.moves pgn.result = self.result return pgn end private # Expand the short algebraic chess notation string +m+ in a hash like this: # Ngxe2 ==> { :name => 'N', :dis => 'g', :from => nil, :to => 'e2', :promotion => '' } def expand_move(m) if match = m.match(/^(R|N|B|Q|K)?([a-h]?[1-8]?)(?:x)?([a-h][1-8])(?:=?(R|N|B|Q))?(?:ep)?(?:\+|\#)?$/) expand = { :name => match[1] || 'P', # Piece name (P|R|N|B|Q|K) :dis => match[2], # Disambiguating move :to => match[3], # Move to :promotion => match[4].to_s, # Promote with } expand[:from] = match[2] if match[2] && match[2].size == 2 return expand elsif m =~ /^(0|O)-(0|O)(\+|\#)?$/ if self.board.active_color # black king short castling return { :name => 'K', :dis => '', :from => 'e8', :to => 'g8', :promotion => '' } else # white king short castling return { :name => 'K', :dis => '', :from => 'e1', :to => 'g1', :promotion => '' } end elsif m =~ /^(0|O)-(0|O)-(0|O)(\+|\#)?$/ if self.board.active_color # black king long castling return { :name => 'K', :dis => '', :from => 'e8', :to => 'c8', :promotion => '' } else # white king long castling return { :name => 'K', :dis => '', :from => 'e1', :to => 'c1', :promotion => '' } end end raise BadNotationError.new(m) end end end