Class: PGN::Board
- Inherits:
-
Object
- Object
- PGN::Board
- Defined in:
- lib/pgn/board.rb
Overview
Board represents the squares of a chess board and the pieces on each square. It is responsible for translating between a human readable format (white queen's rook on the bottom left) and the obvious internal representation (white queen's rook is position [0,0]). It takes care of converting square names (e4) to actual locations, and can convert to unicode chess pieces for display purposes.
Constant Summary collapse
- START =
The starting, internal representation of a chess board
[ ['R', 'P', nil, nil, nil, nil, 'p', 'r'], ['N', 'P', nil, nil, nil, nil, 'p', 'n'], ['B', 'P', nil, nil, nil, nil, 'p', 'b'], ['Q', 'P', nil, nil, nil, nil, 'p', 'q'], ['K', 'P', nil, nil, nil, nil, 'p', 'k'], ['B', 'P', nil, nil, nil, nil, 'p', 'b'], ['N', 'P', nil, nil, nil, nil, 'p', 'n'], ['R', 'P', nil, nil, nil, nil, 'p', 'r'] ].freeze
- FILE_TO_INDEX =
('a'..'h').each_with_index.to_h
- INDEX_TO_FILE =
FILE_TO_INDEX.map(&:reverse).to_h
- RANK_TO_INDEX =
('1'..'8').each_with_index.to_h
- INDEX_TO_RANK =
RANK_TO_INDEX.map(&:reverse).to_h
- UNICODE_PIECES =
algebraic to unicode piece lookup
{ 'k' => "\u{265A}", 'q' => "\u{265B}", 'r' => "\u{265C}", 'b' => "\u{265D}", 'n' => "\u{265E}", 'p' => "\u{265F}", 'K' => "\u{2654}", 'Q' => "\u{2655}", 'R' => "\u{2656}", 'B' => "\u{2657}", 'N' => "\u{2658}", 'P' => "\u{2659}", nil => '_' }.freeze
Instance Attribute Summary collapse
-
#squares ⇒ Array<Array<String>>
The pieces on the board.
Class Method Summary collapse
-
.start ⇒ PGN::Board
A board in the starting position.
Instance Method Summary collapse
-
#at(arg0, arg1 = nil) ⇒ String?
String squares are parsed with getbyte arithmetic (a=0x61, '1'=0x31) so the common string lookup allocates nothing.
- #change!(changes) ⇒ self
-
#coordinates_for(position) ⇒ Array<Integer>
The coordinates of the square.
-
#dup ⇒ PGN::Board
8 column arrays are shared and cloned lazily by #update on first mutation (copy-on-write).
-
#initialize(squares) ⇒ Board
constructor
A new instance of Board.
-
#inspect ⇒ String
The board in human readable format with unicode pieces.
-
#position_for(coordinates) ⇒ String
The square in algebraic notation.
-
#update(square, piece) ⇒ self
Copy-on-write: clone only the column being mutated, and only once per instance, so unchanged columns stay shared with any board this one was duped from.
Constructor Details
#initialize(squares) ⇒ Board
Returns a new instance of Board.
76 77 78 79 |
# File 'lib/pgn/board.rb', line 76 def initialize(squares) self.squares = squares @owned = Array.new(8, false) end |
Instance Attribute Details
#squares ⇒ Array<Array<String>>
Returns the pieces on the board.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 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 |
# File 'lib/pgn/board.rb', line 15 class Board # The starting, internal representation of a chess board # START = [ ['R', 'P', nil, nil, nil, nil, 'p', 'r'], ['N', 'P', nil, nil, nil, nil, 'p', 'n'], ['B', 'P', nil, nil, nil, nil, 'p', 'b'], ['Q', 'P', nil, nil, nil, nil, 'p', 'q'], ['K', 'P', nil, nil, nil, nil, 'p', 'k'], ['B', 'P', nil, nil, nil, nil, 'p', 'b'], ['N', 'P', nil, nil, nil, nil, 'p', 'n'], ['R', 'P', nil, nil, nil, nil, 'p', 'r'] ].freeze FILE_TO_INDEX = ('a'..'h').each_with_index.to_h INDEX_TO_FILE = FILE_TO_INDEX.map(&:reverse).to_h RANK_TO_INDEX = ('1'..'8').each_with_index.to_h INDEX_TO_RANK = RANK_TO_INDEX.map(&:reverse).to_h # algebraic to unicode piece lookup # UNICODE_PIECES = { 'k' => "\u{265A}", 'q' => "\u{265B}", 'r' => "\u{265C}", 'b' => "\u{265D}", 'n' => "\u{265E}", 'p' => "\u{265F}", 'K' => "\u{2654}", 'Q' => "\u{2655}", 'R' => "\u{2656}", 'B' => "\u{2657}", 'N' => "\u{2658}", 'P' => "\u{2659}", nil => '_' }.freeze attr_accessor :squares # @return [PGN::Board] a board in the starting position # def self.start PGN::Board.new(START) end # @param squares [<Array<Array<String>>>] the squares of the board # @example # PGN::Board.new( # [ # ["R", "P", nil, nil, nil, nil, "p", "r"], # ["N", "P", nil, nil, nil, nil, "p", "n"], # ["B", "P", nil, nil, nil, nil, "p", "b"], # ["Q", "P", nil, nil, nil, nil, "p", "q"], # ["K", "P", nil, nil, nil, nil, "p", "k"], # ["B", "P", nil, nil, nil, nil, "p", "b"], # ["N", "P", nil, nil, nil, nil, "p", "n"], # ["R", "P", nil, nil, nil, nil, "p", "r"], # ] # ) # def initialize(squares) self.squares = squares @owned = Array.new(8, false) end # @overload at(str) # Looks up a piece based on the string representation of a square (e4) # @param str [String] the square in algebraic notation # @overload at(file, rank) # Looks up a piece based on zero-indexed coordinates (4, 3) # @param file [Integer] the file the piece is on # @param rank [Integer] the rank the piece is on # @return [String, nil] the piece on the square, or nil if it is # empty # @example # board.at(4,3) #=> "P" # board.at("e4") #=> "P" # # String squares are parsed with getbyte arithmetic (a=0x61, '1'=0x31) # so the common string lookup allocates nothing. def at(arg0, arg1 = nil) return squares[arg0][arg1] unless arg1.nil? squares[file_of(arg0)][rank_of(arg0)] end # @param changes [Hash<String, <String, nil>>] changes to make to the board # @return [self] # @example # board.change!({"e2" => nil, "e4" => "P"}) # def change!(changes) changes.each do |square, piece| update(square, piece) end self end # @param square [String] the square in algebraic notation # @param piece [String, nil] the piece to put on the square # @return [self] # @example # board.update("e4", "P") # # Copy-on-write: clone only the column being mutated, and only once per # instance, so unchanged columns stay shared with any board this one was # duped from. def update(square, piece) file = file_of(square) unless @owned[file] squares[file] = squares[file].dup @owned[file] = true end squares[file][rank_of(square)] = piece self end # @param position [String] the square in algebraic notation # @return [Array<Integer>] the coordinates of the square # @example # board.coordinates_for("e4") #=> [4, 3] # def coordinates_for(position) [file_of(position), rank_of(position)] end # @param coordinates [Array<Integer>] the coordinates of the square # @return [String] the square in algebraic notation # @example # board.position_for([4, 3]) #=> "e4" # def position_for(coordinates) file, rank = coordinates file_chr = INDEX_TO_FILE[file] rank_chr = INDEX_TO_RANK[rank] [file_chr, rank_chr].join('') end # @return [String] the board in human readable format with unicode # pieces # def inspect squares.transpose.reverse.map do |row| row.map { |chr| UNICODE_PIECES[chr] }.join(' ') end.join("\n") end # @return [PGN::Board] a copy of self. The outer array is copied; the # 8 column arrays are shared and cloned lazily by #update on first # mutation (copy-on-write). # def dup PGN::Board.new(squares.dup) end private def file_of(square) square.getbyte(0) - 97 end def rank_of(square) square.getbyte(1) - 49 end end |
Class Method Details
.start ⇒ PGN::Board
Returns a board in the starting position.
57 58 59 |
# File 'lib/pgn/board.rb', line 57 def self.start PGN::Board.new(START) end |
Instance Method Details
#at(str) ⇒ String? #at(file, rank) ⇒ String?
String squares are parsed with getbyte arithmetic (a=0x61, '1'=0x31) so the common string lookup allocates nothing.
96 97 98 99 100 |
# File 'lib/pgn/board.rb', line 96 def at(arg0, arg1 = nil) return squares[arg0][arg1] unless arg1.nil? squares[file_of(arg0)][rank_of(arg0)] end |
#change!(changes) ⇒ self
107 108 109 110 111 112 |
# File 'lib/pgn/board.rb', line 107 def change!(changes) changes.each do |square, piece| update(square, piece) end self end |
#coordinates_for(position) ⇒ Array<Integer>
Returns the coordinates of the square.
138 139 140 |
# File 'lib/pgn/board.rb', line 138 def coordinates_for(position) [file_of(position), rank_of(position)] end |
#dup ⇒ PGN::Board
8 column arrays are shared and cloned lazily by #update on first mutation (copy-on-write).
167 168 169 |
# File 'lib/pgn/board.rb', line 167 def dup PGN::Board.new(squares.dup) end |
#inspect ⇒ String
Returns the board in human readable format with unicode pieces.
157 158 159 160 161 |
# File 'lib/pgn/board.rb', line 157 def inspect squares.transpose.reverse.map do |row| row.map { |chr| UNICODE_PIECES[chr] }.join(' ') end.join("\n") end |
#position_for(coordinates) ⇒ String
Returns the square in algebraic notation.
147 148 149 150 151 152 |
# File 'lib/pgn/board.rb', line 147 def position_for(coordinates) file, rank = coordinates file_chr = INDEX_TO_FILE[file] rank_chr = INDEX_TO_RANK[rank] [file_chr, rank_chr].join('') end |
#update(square, piece) ⇒ self
Copy-on-write: clone only the column being mutated, and only once per instance, so unchanged columns stay shared with any board this one was duped from.
123 124 125 126 127 128 129 130 131 |
# File 'lib/pgn/board.rb', line 123 def update(square, piece) file = file_of(square) unless @owned[file] squares[file] = squares[file].dup @owned[file] = true end squares[file][rank_of(square)] = piece self end |