gorb 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,10 +3,11 @@ require 'gorb/stone'
3
3
  class Board
4
4
 
5
5
  attr_accessor :groups, :turn
6
- attr_reader :black, :white, :handicap, :komi, :size
6
+ attr_reader :black, :white, :handicap, :komi, :size, :letters, :dead_groups
7
7
 
8
- # Initialize a new Board instance. Requires two Player objects and a
9
- # handicap as arguments. The handicap should be an integer from 0 to 9.
8
+ # Initialize a new Board instance. Requires two Player objects, a handicap,
9
+ # a komi and a size as arguments. The handicap should be an integer from
10
+ # 0 to 9. Komi can be negative. Size should be either 9x9, 13x13 or 19x19.
10
11
  def initialize(black=nil, white=nil, handicap=0, komi=6.5, size="19x19")
11
12
  @black = black ||= Player.new("Black")
12
13
  @white = white ||= Player.new("White")
@@ -15,6 +16,7 @@ class Board
15
16
  @groups = []
16
17
  @hashes = []
17
18
  @turn = black
19
+ @dead_groups = []
18
20
 
19
21
  raise ArgumentError, "Incorrect handicap" if handicap < 0 or handicap > 9
20
22
  @handicap = handicap
@@ -22,10 +24,13 @@ class Board
22
24
  @turn = white if handicap > 1
23
25
 
24
26
  if size == "9x9"
27
+ @letters = %w{A B C D E F G H J}
25
28
  handicap_stones = %w{G7 C3 G3 C7 E5 C5 G5 E7 E3}
26
29
  elsif size == "13x13"
30
+ @letters = %w{A B C D E F G H J K L M N}
27
31
  handicap_stones = %w{K10 D4 K4 D10 G7 D7 K7 G10 G4}
28
32
  elsif size == "19x19"
33
+ @letters = %w{A B C D E F G H J K L M N O P Q R S T}
29
34
  handicap_stones = %w{Q16 D4 Q4 D16 K10 D10 Q10 K16 K4}
30
35
  else
31
36
  raise ArgumentError, "Incorrect board size"
@@ -71,40 +76,6 @@ class Board
71
76
  stone.board.groups.delete(stone.group) if stone.group.size == 0
72
77
  end
73
78
 
74
- # Search the Board for stones in given points.
75
- def search(points)
76
- stones = []
77
- @groups.each do |group|
78
- group.each do |stone|
79
- stones << stone if points.include? stone.point
80
- end
81
- end
82
- return stones
83
- end
84
-
85
- def stone_at?(point)
86
- @groups.any? {|group| group.include? point}
87
- end
88
-
89
- def stones_at?(points)
90
- points.all? {|point| self.stone_at? point}
91
- end
92
-
93
- # Recalculate all liberties. Removes dead groups from the table.
94
- def resolve!(added_stone)
95
- @groups.each do |group|
96
- group.liberties! if not group.include? added_stone
97
- end
98
- # The group of last added stone is checked after others to make kills by
99
- # 'suicide' (filling dame) work.
100
- added_stone.group.liberties!
101
- end
102
-
103
- # Generate a hash of a board situation. Used to enforce ko rule.
104
- def generate_hash
105
- @groups.flatten.inject([]) {|hash, stone| hash << stone.to_s}.sort.hash
106
- end
107
-
108
79
  def legal?(point, color)
109
80
  # Check if the point if already occupied.
110
81
  return false if self.stone_at? point
@@ -135,6 +106,66 @@ class Board
135
106
  return legal
136
107
  end
137
108
 
109
+ # Recalculate all liberties. Removes dead groups from the table.
110
+ def resolve!(added_stone)
111
+ @groups.each do |group|
112
+ if not group.include? added_stone
113
+ libs = group.liberties!
114
+ self.send(added_stone.color).captured += group.size if libs == 0
115
+ end
116
+ end
117
+ # The group of last added stone is checked after others to make kills by
118
+ # 'suicide' (filling dame) work.
119
+ added_stone.group.liberties!
120
+ end
121
+
122
+ # Search the Board for stones in given points.
123
+ def search(points)
124
+ if points.is_a?(String)
125
+ points = [points]
126
+ end
127
+ stones = []
128
+ @groups.each do |group|
129
+ group.each do |stone|
130
+ stones << stone if points.include? stone.point
131
+ end
132
+ end
133
+ return stones
134
+ end
135
+
136
+ def stone_at?(point)
137
+ @groups.any? {|group| group.include? point}
138
+ end
139
+
140
+ def stones_at?(points)
141
+ points.all? {|point| self.stone_at? point}
142
+ end
143
+
144
+ # Return the neighboring points of the point.
145
+ def neighbors(point)
146
+ x, y = @letters.index(point[0]), point[1, 2].to_i
147
+ neighbors = []
148
+ unless y == 1
149
+ neighbors << @letters[x] + (y - 1).to_s
150
+ end
151
+ unless y == self.size.split('x')[0].to_i
152
+ neighbors << @letters[x] + (y + 1).to_s
153
+ end
154
+ unless @letters[x] == @letters.first
155
+ neighbors << @letters[x-1] + y.to_s
156
+ end
157
+ unless @letters[x] == @letters.last
158
+ neighbors << @letters[x+1] + y.to_s
159
+ end
160
+ return neighbors
161
+ end
162
+
163
+ # Generate a hash of a board situation. Used to enforce ko rule.
164
+ def generate_hash
165
+ @groups.flatten.inject([]) {|hash, stone| hash << stone.to_s}.sort.hash
166
+ end
167
+
168
+ # Pass the turn and generate a hash of the board situation for checking ko.
138
169
  def turn_over
139
170
  if @turn == @black
140
171
  @turn = @white
@@ -144,4 +175,112 @@ class Board
144
175
  @hashes << generate_hash
145
176
  end
146
177
 
178
+ # Mark dead groups after the game has ended to ease the scoring.
179
+ def mark_dead_group(stone)
180
+ group = self.search(stone).first.group
181
+ @dead_groups << group
182
+ end
183
+
184
+ # Count the score.
185
+ def scoring
186
+ white, black = 0, 0
187
+
188
+ # Remove dead groups from board (or its clone).
189
+ score_board = Marshal.load(Marshal.dump(self))
190
+ score_board.dead_groups.each do |group|
191
+ score_board.groups.delete(group)
192
+ if group.first.color == :white
193
+ black += group.size
194
+ elsif group.first.color == :black
195
+ white += group.size
196
+ end
197
+ end
198
+
199
+ # Collect all empty points into a list.
200
+ empty_points = []
201
+ side = self.size.split('x')[0].to_i
202
+ for i in (0..side-1)
203
+ for j in (0..side-1)
204
+ coords = @letters[i] + (side - j).to_s
205
+ if not score_board.stone_at?(coords)
206
+ empty_points << coords
207
+ end
208
+ end
209
+ end
210
+
211
+ # Flood fill and remove from list of empty points.
212
+ areas = []
213
+ until empty_points.empty?
214
+ current_area = []
215
+ first_point = empty_points.first
216
+ remove_from_empty_points = Proc.new do |point|
217
+ if empty_points.include? point
218
+ current_area << empty_points.delete(point)
219
+ for neighbor in self.neighbors(point)
220
+ remove_from_empty_points.call(neighbor)
221
+ end
222
+ end
223
+ end
224
+ remove_from_empty_points.call(first_point)
225
+ areas << current_area
226
+ end
227
+
228
+ # Check bordering stones or groups: if uniform, award points.
229
+ areas.each do |area|
230
+ colors = []
231
+ area.each do |empty_point|
232
+ self.neighbors(empty_point).each do |neighbor|
233
+ stone = score_board.search(neighbor).first
234
+ if stone
235
+ colors << stone.color unless colors.include? stone.color
236
+ end
237
+ end
238
+ end
239
+ if colors == [:white]
240
+ white += area.size
241
+ elsif colors == [:black]
242
+ black += area.size
243
+ end
244
+ end
245
+
246
+ # Add captured stones to the total.
247
+ white += score_board.white.captured
248
+ black += score_board.black.captured
249
+
250
+ # Add komi.
251
+ white += self.komi
252
+
253
+ {:white => white, :black => black}
254
+ end
255
+
256
+ # Read a board situation from a (possibly incomplete) diagram.
257
+ def read(diagram)
258
+ # Try to read captured pieces information from gnugo output.
259
+ black_captured = /Black \(X\) has captured (\d) pieces/.match(diagram)
260
+ white_captured = /White \(O\) has captured (\d) pieces/.match(diagram)
261
+
262
+ if black_captured
263
+ self.black.captured += black_captured.captures.first.to_i
264
+ end
265
+ if white_captured
266
+ self.white.captured += white_captured.captures.first.to_i
267
+ end
268
+
269
+ diagram.gsub!(/Black.*/, '')
270
+ diagram.gsub!(/White.*/, '')
271
+ diagram.gsub!(/N O P/, '')
272
+ diagram.gsub!(/[A-NP-WYZa-z0-9]/, '')
273
+ diagram.gsub!(/[-| ():]/, '')
274
+ diagram.strip().split("\n").each_with_index do |line, i|
275
+ line.split("").each_with_index do |char, j|
276
+ coords = @letters[j] + (self.size.split('x')[0].to_i-i).to_s
277
+ if char == "X"
278
+ self.add_stone(coords, :black)
279
+ elsif char == "O"
280
+ self.add_stone(coords, :white)
281
+ end
282
+ end
283
+ end
284
+ end
285
+
147
286
  end
@@ -11,6 +11,10 @@ class Group < Array
11
11
  group.each {|stone| self << stone}
12
12
  @board.groups.delete group
13
13
  end
14
+ # Group references need to be updated for each stone after a merge.
15
+ self.each do |stone|
16
+ stone.group = self
17
+ end
14
18
  end
15
19
 
16
20
  def include?(point)
@@ -1,8 +1,12 @@
1
1
  class Player
2
2
 
3
+ attr_accessor :captured
4
+ attr_reader :name, :rank
5
+
3
6
  def initialize(name="Anonymous", rank=nil)
4
7
  @name = name
5
8
  @rank = rank
9
+ @captured = 0
6
10
  end
7
11
 
8
12
  def to_str
@@ -10,16 +10,8 @@ class Stone
10
10
  @point = point
11
11
  @color = color
12
12
 
13
- if @board.size == "9x9"
14
- @letters = %w{A B C D E F G H J}
15
- elsif @board.size == "13x13"
16
- @letters = %w{A B C D E F G H J K L M N}
17
- elsif @board.size == "19x19"
18
- @letters = %w{A B C D E F G H J K L M N O P Q R S T}
19
- end
20
-
21
13
  if (@point[1, 2].to_i > @board.size.split('x')[0].to_i or
22
- not @letters.index(@point[0]))
14
+ not @board.letters.index(@point[0]))
23
15
  raise ArgumentError, "Invalid point"
24
16
  end
25
17
 
@@ -28,21 +20,7 @@ class Stone
28
20
 
29
21
  # Return the neighboring points of the Stone.
30
22
  def neighbors
31
- x, y = @letters.index(@point[0]), @point[1, 2].to_i
32
- neighbors = []
33
- unless y == 1
34
- neighbors << @letters[x] + (y - 1).to_s
35
- end
36
- unless y == @board.size.split('x')[0].to_i
37
- neighbors << @letters[x] + (y + 1).to_s
38
- end
39
- unless @letters[x] == @letters.first
40
- neighbors << @letters[x-1] + y.to_s
41
- end
42
- unless @letters[x] == @letters.last
43
- neighbors << @letters[x+1] + y.to_s
44
- end
45
- return neighbors
23
+ @board.neighbors(@point)
46
24
  end
47
25
 
48
26
  # Return the liberties of the Stone.
metadata CHANGED
@@ -4,9 +4,8 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 0
8
7
  - 1
9
- version: 0.0.1
8
+ version: "0.1"
10
9
  platform: ruby
11
10
  authors:
12
11
  - Aku Kotkavuo
@@ -14,7 +13,7 @@ autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
15
 
17
- date: 2010-09-06 00:00:00 +03:00
16
+ date: 2010-09-13 00:00:00 +03:00
18
17
  default_executable:
19
18
  dependencies: []
20
19