Class: ChessData::Game
- Inherits:
-
Object
- Object
- ChessData::Game
- Defined in:
- lib/chess_data/game.rb
Overview
Represents a chess game, as read in from a PGN file.
Header information in a pgn is stored in a hash table, and method_missing used to provide an accessor-like mechanism for storing/retrieving header information. For example:
db = Database.new
db.add_games_from "test/data/fischer.pgn"
game = db[0]
puts game.event
puts game.white
puts game.black
Each of the ‘event’, ‘white’, ‘black’ values are retrieved from the PGN header.
New key values can be created and assigned to, to construct a header for a game. For example:
db = Database.new
game = Game.new
game.white = "Peter"
game.black = "Paul"
game.result = "1-0"
game << "e4"
db << game
db.to_file "mygames.pgn"
And the pgn file will contain:
[Result "1-0"]
[White "Peter"]
[Black "Paul"]
1. e4 1-0
Constant Summary collapse
- MatchHeader =
Regular expression used to match a PGN header line.
/\[(\w+) \"(.*)\"\]/
Instance Attribute Summary collapse
-
#moves ⇒ Object
Stores the sequence of moves in the game.
Class Method Summary collapse
-
.from_pgn(stream) ⇒ Object
Reads a single game from a given IO stream.
Instance Method Summary collapse
-
#<<(move) ⇒ Object
Append given move to list of moves.
-
#half_moves ⇒ Object
Return the number of half-moves in the game.
-
#initialize ⇒ Game
constructor
A new instance of Game.
-
#method_missing(iname, *args) ⇒ Object
method_missing is used for accessing key-value terms in the header.
-
#play_game {|board, result| ... } ⇒ Object
Step through the game from start position, one half-move at a time.
-
#search(&block) ⇒ Object
Test if game meets the position definition given in the block using Game#play_game to step through the game.
-
#start_position ⇒ Object
Return the start position for the game.
-
#to_pgn(stream) ⇒ Object
Write game in PGN format to given IO stream.
Constructor Details
#initialize ⇒ Game
Returns a new instance of Game.
44 45 46 47 |
# File 'lib/chess_data/game.rb', line 44 def initialize @header = {} @moves = [] end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(iname, *args) ⇒ Object
method_missing is used for accessing key-value terms in the header.
-
Any unknown method call is checked if it is the key of a header item and, if so, the value for that key is returned.
-
If the unknown method has an ‘=’ sign in it, a new key is created and assigned a value, which must be an argument to method.
54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/chess_data/game.rb', line 54 def method_missing iname, *args name = iname.to_s if @header.has_key? name @header[name] elsif name.include? "=" # assign, so create new key key = name.delete "=" @header[key] = args[0] else puts "Unknown key '#{name}' for header #{@header}" super end end |
Instance Attribute Details
#moves ⇒ Object
Stores the sequence of moves in the game.
42 43 44 |
# File 'lib/chess_data/game.rb', line 42 def moves @moves end |
Class Method Details
.from_pgn(stream) ⇒ Object
Reads a single game from a given IO stream. Returns nil if failed to read a game or its moves.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/chess_data/game.rb', line 143 def Game.from_pgn stream game = Game.new moves = [] # ignore blank lines begin line = stream.gets return nil if line.nil? # failed to read game/empty file end while line.strip.empty? # read the header while MatchHeader =~ line game.send "#{$1.downcase}=", $2 line = stream.gets.strip end # ignore blank lines begin line = stream.gets return nil if line.nil? # failed to read moves for game end while line.strip.empty? # read the moves begin next if line.start_with? "%" semi_index = line.index ";" # look for ; comment start line = line[0...semi_index] if semi_index # and strip it moves << line.strip line = stream.gets end until line.nil? || line.strip.empty? || MatchHeader =~ line # return an event line if it immediately follows the moves # so can be read for next game stream.ungetc line if MatchHeader =~ line # parse the moves and add to game move_str = moves.join(" ") if /{.*}/.match(move_str) # remove { } comments move_str = $` + " " + $' end move_str.split(" ").each do |token| case token when "1-0", "0-1", "1/2-1/2", "*" then game.result = token when /^\d+/ then ; # ignore the move number when /^$\d+/ then ; # ignore NAG when Moves::LegalMove then game << Moves.new_move(token) end end return game end |
Instance Method Details
#<<(move) ⇒ Object
Append given move to list of moves. Given move can be a valid move type or a string.
An InvalidMoveError is raised if the move is not valid.
72 73 74 75 76 77 78 79 80 |
# File 'lib/chess_data/game.rb', line 72 def << move if move.respond_to? :make_move @moves << move elsif move.kind_of? String @moves << Moves.new_move(move) else raise InvalidMoveError.new("Invalid type of move: #{move}") end end |
#half_moves ⇒ Object
Return the number of half-moves in the game.
83 84 85 |
# File 'lib/chess_data/game.rb', line 83 def half_moves @moves.size end |
#play_game {|board, result| ... } ⇒ Object
Step through the game from start position, one half-move at a time. Yields to a block the current board position and the next move. Yields final board position and result at end of game.
100 101 102 103 104 105 106 107 |
# File 'lib/chess_data/game.rb', line 100 def play_game board = start_position @moves.each do |move| yield board, move board = move.make_move board end yield board, result end |
#search(&block) ⇒ Object
Test if game meets the position definition given in the block using Game#play_game to step through the game.
111 112 113 114 115 116 117 |
# File 'lib/chess_data/game.rb', line 111 def search &block defn = ChessData::PositionDefinition.new(&block) play_game do |board| return true if defn.check board end return false end |
#start_position ⇒ Object
Return the start position for the game. PGN games may provide a start-position, if they do not begin from the start position.
89 90 91 92 93 94 95 |
# File 'lib/chess_data/game.rb', line 89 def start_position if @header.has_key? "fen" ChessData::Board.from_fen @header["fen"] else ChessData::Board.start_position end end |
#to_pgn(stream) ⇒ Object
Write game in PGN format to given IO stream. This method is usually called from Database#to_file but can also be called directly.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/chess_data/game.rb', line 123 def to_pgn stream @header.keys.each do |key| stream.puts "[#{key.capitalize} \"#{@header[key]}\"]" end stream.puts # blank separating line move_str = "" move_number = 1 @moves.each_slice(2) do |full_move| move_str += "#{move_number}. #{full_move[0]} #{full_move[1]} " move_number += 1 end move_str += result stream.puts WordWrap.ww(move_str, 80) end |