gorb 0.0.1 → 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/gorb/board.rb +176 -37
- data/lib/gorb/group.rb +4 -0
- data/lib/gorb/player.rb +4 -0
- data/lib/gorb/stone.rb +2 -24
- metadata +2 -3
data/lib/gorb/board.rb
CHANGED
@@ -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
|
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
|
data/lib/gorb/group.rb
CHANGED
data/lib/gorb/player.rb
CHANGED
data/lib/gorb/stone.rb
CHANGED
@@ -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
|
-
|
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.
|
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-
|
16
|
+
date: 2010-09-13 00:00:00 +03:00
|
18
17
|
default_executable:
|
19
18
|
dependencies: []
|
20
19
|
|