Class: Board
- Inherits:
-
Object
- Object
- Board
- Defined in:
- lib/gorb/board.rb
Instance Attribute Summary collapse
-
#black ⇒ Object
readonly
Returns the value of attribute black.
-
#dead_groups ⇒ Object
readonly
Returns the value of attribute dead_groups.
-
#groups ⇒ Object
Returns the value of attribute groups.
-
#handicap ⇒ Object
readonly
Returns the value of attribute handicap.
-
#komi ⇒ Object
readonly
Returns the value of attribute komi.
-
#letters ⇒ Object
readonly
Returns the value of attribute letters.
-
#size ⇒ Object
readonly
Returns the value of attribute size.
-
#turn ⇒ Object
Returns the value of attribute turn.
-
#white ⇒ Object
readonly
Returns the value of attribute white.
Instance Method Summary collapse
-
#add_stone(point, color = nil) ⇒ Object
Add a Stone to the board if the move is legal.
-
#generate_hash ⇒ Object
Generate a hash of a board situation.
-
#initialize(black = nil, white = nil, handicap = 0, komi = 6.5, size = "19x19") ⇒ Board
constructor
Initialize a new Board instance.
- #legal?(point, color) ⇒ Boolean
-
#mark_dead_group(stone) ⇒ Object
Mark dead groups after the game has ended to ease the scoring.
-
#neighbors(point) ⇒ Object
Return the neighboring points of the point.
-
#read(diagram) ⇒ Object
Read a board situation from a (possibly incomplete) diagram.
- #remove_stone(point) ⇒ Object
-
#resolve!(added_stone) ⇒ Object
Recalculate all liberties.
-
#scoring ⇒ Object
Count the score.
-
#search(points) ⇒ Object
Search the Board for stones in given points.
- #stone_at?(point) ⇒ Boolean
- #stones_at?(points) ⇒ Boolean
-
#turn_over ⇒ Object
Pass the turn and generate a hash of the board situation for checking ko.
Constructor Details
#initialize(black = nil, white = nil, handicap = 0, komi = 6.5, size = "19x19") ⇒ Board
Initialize a new Board instance. Requires two Player objects, a handicap, a komi and a size as arguments. The handicap should be an integer from 0 to 9. Komi can be negative. Size should be either 9x9, 13x13 or 19x19.
11 12 13 14 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 |
# File 'lib/gorb/board.rb', line 11 def initialize(black=nil, white=nil, handicap=0, komi=6.5, size="19x19") @black = black ||= Player.new("Black") @white = white ||= Player.new("White") @komi = komi @size = size @groups = [] @hashes = [] @turn = black @dead_groups = [] raise ArgumentError, "Incorrect handicap" if handicap < 0 or handicap > 9 @handicap = handicap @komi = 0.5 if handicap > 0 @turn = white if handicap > 1 if size == "9x9" @letters = %w{A B C D E F G H J} handicap_stones = %w{G7 C3 G3 C7 E5 C5 G5 E7 E3} elsif size == "13x13" @letters = %w{A B C D E F G H J K L M N} handicap_stones = %w{K10 D4 K4 D10 G7 D7 K7 G10 G4} elsif size == "19x19" @letters = %w{A B C D E F G H J K L M N O P Q R S T} handicap_stones = %w{Q16 D4 Q4 D16 K10 D10 Q10 K16 K4} else raise ArgumentError, "Incorrect board size" end case @handicap when 2..5, 7, 9 handicap_stones[0..(@handicap-1)].each {|s| self.add_stone(s, :black)} when 6, 8 handicap_stones[0..@handicap].each {|s| self.add_stone(s, :black)} self.remove_stone(handicap_stones[4]) # Middle stone end end |
Instance Attribute Details
#black ⇒ Object (readonly)
Returns the value of attribute black.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def black @black end |
#dead_groups ⇒ Object (readonly)
Returns the value of attribute dead_groups.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def dead_groups @dead_groups end |
#groups ⇒ Object
Returns the value of attribute groups.
5 6 7 |
# File 'lib/gorb/board.rb', line 5 def groups @groups end |
#handicap ⇒ Object (readonly)
Returns the value of attribute handicap.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def handicap @handicap end |
#komi ⇒ Object (readonly)
Returns the value of attribute komi.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def komi @komi end |
#letters ⇒ Object (readonly)
Returns the value of attribute letters.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def letters @letters end |
#size ⇒ Object (readonly)
Returns the value of attribute size.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def size @size end |
#turn ⇒ Object
Returns the value of attribute turn.
5 6 7 |
# File 'lib/gorb/board.rb', line 5 def turn @turn end |
#white ⇒ Object (readonly)
Returns the value of attribute white.
6 7 8 |
# File 'lib/gorb/board.rb', line 6 def white @white end |
Instance Method Details
#add_stone(point, color = nil) ⇒ Object
Add a Stone to the board if the move is legal. This function will also keep track of the turn. You can force the color with additional color argument – in this case the turn is not changing.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/gorb/board.rb', line 51 def add_stone(point, color=nil) # Guess the color based on turn, unless color was forced. unless color if @turn == black color = :black else color = :white end advance = true end # Check the legality of the move and play it if legal. raise ArgumentError, "Illegal move" unless legal?(point, color) stone = Stone.new(self, point, color) resolve!(stone) # If the color was not explicitly set, advance the turn. turn_over if advance return stone end |
#generate_hash ⇒ Object
Generate a hash of a board situation. Used to enforce ko rule.
164 165 166 |
# File 'lib/gorb/board.rb', line 164 def generate_hash @groups.flatten.inject([]) {|hash, stone| hash << stone.to_s}.sort.hash end |
#legal?(point, color) ⇒ Boolean
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 |
# File 'lib/gorb/board.rb', line 79 def legal?(point, color) # Check if the point if already occupied. return false if self.stone_at? point # The method for checking legality requires placing a test stone to the # point and seeing what happens. This is done by doing a deep copy of the # board and playing the move there. dummy_board = Marshal.load(Marshal.dump(self)) # Check for suicide. stone = Stone.new(dummy_board, point, color) legal = true if stone.group.liberties == 0 # Normally suicide is not ok... legal = false # ...but killing with 'suicide' (filling dame) is ok. opposing = dummy_board.search(stone.neighbors) opposing.each do |opp_stone| if opp_stone.color != color and opp_stone.group.liberties == 0 legal = true end end end # Check for ko. dummy_board.resolve!(stone) legal = false if @hashes.include? dummy_board.generate_hash return legal end |
#mark_dead_group(stone) ⇒ Object
Mark dead groups after the game has ended to ease the scoring.
179 180 181 182 |
# File 'lib/gorb/board.rb', line 179 def mark_dead_group(stone) group = self.search(stone).first.group @dead_groups << group end |
#neighbors(point) ⇒ Object
Return the neighboring points of the point.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/gorb/board.rb', line 145 def neighbors(point) x, y = @letters.index(point[0]), point[1, 2].to_i neighbors = [] unless y == 1 neighbors << @letters[x] + (y - 1).to_s end unless y == self.size.split('x')[0].to_i neighbors << @letters[x] + (y + 1).to_s end unless @letters[x] == @letters.first neighbors << @letters[x-1] + y.to_s end unless @letters[x] == @letters.last neighbors << @letters[x+1] + y.to_s end return neighbors end |
#read(diagram) ⇒ Object
Read a board situation from a (possibly incomplete) diagram.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/gorb/board.rb', line 257 def read(diagram) # Try to read captured pieces information from gnugo output. black_captured = /Black \(X\) has captured (\d) pieces/.match(diagram) white_captured = /White \(O\) has captured (\d) pieces/.match(diagram) if black_captured self.black.captured += black_captured.captures.first.to_i end if white_captured self.white.captured += white_captured.captures.first.to_i end diagram.gsub!(/Black.*/, '') diagram.gsub!(/White.*/, '') diagram.gsub!(/N O P/, '') diagram.gsub!(/[A-NP-WYZa-z0-9]/, '') diagram.gsub!(/[-| ():]/, '') diagram.strip().split("\n").each_with_index do |line, i| line.split("").each_with_index do |char, j| coords = @letters[j] + (self.size.split('x')[0].to_i-i).to_s if char == "X" self.add_stone(coords, :black) elsif char == "O" self.add_stone(coords, :white) end end end end |
#remove_stone(point) ⇒ Object
72 73 74 75 76 77 |
# File 'lib/gorb/board.rb', line 72 def remove_stone(point) stone = self.search(point).first raise ArgumentError, "No such stone" unless stone stone.group.delete(stone) stone.board.groups.delete(stone.group) if stone.group.size == 0 end |
#resolve!(added_stone) ⇒ Object
Recalculate all liberties. Removes dead groups from the table.
110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/gorb/board.rb', line 110 def resolve!(added_stone) @groups.each do |group| if not group.include? added_stone libs = group.liberties! self.send(added_stone.color).captured += group.size if libs == 0 end end # The group of last added stone is checked after others to make kills by # 'suicide' (filling dame) work. added_stone.group.liberties! end |
#scoring ⇒ Object
Count the score.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/gorb/board.rb', line 185 def scoring white, black = 0, 0 # Remove dead groups from board (or its clone). score_board = Marshal.load(Marshal.dump(self)) score_board.dead_groups.each do |group| score_board.groups.delete(group) if group.first.color == :white black += group.size elsif group.first.color == :black white += group.size end end # Collect all empty points into a list. empty_points = [] side = self.size.split('x')[0].to_i for i in (0..side-1) for j in (0..side-1) coords = @letters[i] + (side - j).to_s if not score_board.stone_at?(coords) empty_points << coords end end end # Flood fill and remove from list of empty points. areas = [] until empty_points.empty? current_area = [] first_point = empty_points.first remove_from_empty_points = Proc.new do |point| if empty_points.include? point current_area << empty_points.delete(point) for neighbor in self.neighbors(point) remove_from_empty_points.call(neighbor) end end end remove_from_empty_points.call(first_point) areas << current_area end # Check bordering stones or groups: if uniform, award points. areas.each do |area| colors = [] area.each do |empty_point| self.neighbors(empty_point).each do |neighbor| stone = score_board.search(neighbor).first if stone colors << stone.color unless colors.include? stone.color end end end if colors == [:white] white += area.size elsif colors == [:black] black += area.size end end # Add captured stones to the total. white += score_board.white.captured black += score_board.black.captured # Add komi. white += self.komi {:white => white, :black => black} end |
#search(points) ⇒ Object
Search the Board for stones in given points.
123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/gorb/board.rb', line 123 def search(points) if points.is_a?(String) points = [points] end stones = [] @groups.each do |group| group.each do |stone| stones << stone if points.include? stone.point end end return stones end |
#stone_at?(point) ⇒ Boolean
136 137 138 |
# File 'lib/gorb/board.rb', line 136 def stone_at?(point) @groups.any? {|group| group.include? point} end |
#stones_at?(points) ⇒ Boolean
140 141 142 |
# File 'lib/gorb/board.rb', line 140 def stones_at?(points) points.all? {|point| self.stone_at? point} end |
#turn_over ⇒ Object
Pass the turn and generate a hash of the board situation for checking ko.
169 170 171 172 173 174 175 176 |
# File 'lib/gorb/board.rb', line 169 def turn_over if @turn == @black @turn = @white else @turn = @black end @hashes << generate_hash end |