ruby-go 0.0.2 → 1.0.0
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.
- checksums.yaml +5 -5
- data/README.md +30 -16
- data/bin/rubygo +56 -0
- data/lib/ruby-go.rb +9 -4
- data/lib/ruby-go/board.rb +66 -0
- data/lib/ruby-go/game.rb +135 -0
- data/lib/ruby-go/liberty.rb +11 -0
- data/lib/ruby-go/moves.rb +63 -0
- data/lib/ruby-go/printers/html.rb +76 -0
- data/lib/ruby-go/printers/text.rb +40 -0
- data/lib/ruby-go/stone.rb +50 -0
- data/lib/ruby-go/version.rb +3 -0
- data/test/go_test.rb +158 -136
- data/test/sgf_test.rb +43 -42
- data/test/test_helper.rb +2 -1
- metadata +15 -11
- data/bin/ruby-go.rb +0 -45
- data/lib/board.rb +0 -82
- data/lib/game.rb +0 -116
- data/lib/stone.rb +0 -101
@@ -0,0 +1,40 @@
|
|
1
|
+
module RubyGo
|
2
|
+
class TextPrinter
|
3
|
+
COLORS = { black: 'x', white: 'o', empty: '_' }.freeze
|
4
|
+
|
5
|
+
attr_reader :io
|
6
|
+
|
7
|
+
def initialize(io)
|
8
|
+
@io = io
|
9
|
+
end
|
10
|
+
|
11
|
+
def print_game(game)
|
12
|
+
print_board(game.board)
|
13
|
+
io.puts " " + "_"*(game.board.size * 2)
|
14
|
+
io.print " Prisoners || White: #{game.captures[:black]} |"
|
15
|
+
io.puts " Black: #{game.captures[:white]}"
|
16
|
+
io.puts " " + "-"*(game.board.size * 2)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def print_board(board)
|
22
|
+
if board.size < 11
|
23
|
+
io.puts " #{(0..board.size - 1).to_a.join(' ')}"
|
24
|
+
else
|
25
|
+
io.puts " #{(0..10).to_a.join(' ')}#{(11..board.size - 1).to_a.join('')}"
|
26
|
+
end
|
27
|
+
|
28
|
+
board.rows.each_with_index do |row, i|
|
29
|
+
i = " #{i}" if i < 10
|
30
|
+
io.print "#{i} "
|
31
|
+
row.each { |stn| print_stone(stn) }
|
32
|
+
io.puts
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def print_stone(stone)
|
37
|
+
io.print "#{COLORS[stone.color]} "
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RubyGo
|
2
|
+
class Stone
|
3
|
+
LETTERS = ('a'..'z').to_a
|
4
|
+
|
5
|
+
attr_reader :color, :x_coord, :y_coord
|
6
|
+
|
7
|
+
def initialize(x_coord, y_coord, color)
|
8
|
+
@x_coord = x_coord
|
9
|
+
@y_coord = y_coord
|
10
|
+
@color = color
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_sgf
|
14
|
+
x = LETTERS[x_coord]
|
15
|
+
y = LETTERS[y_coord]
|
16
|
+
";#{color.to_s[0].upcase}[#{x}#{y}]"
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty?
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_coord
|
24
|
+
[x_coord, y_coord]
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(other)
|
28
|
+
other.is_a?(Stone) &&
|
29
|
+
(color == other.color) &&
|
30
|
+
(to_coord == other.to_coord)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# This can be changed to a Pass object
|
35
|
+
class NullStone < Stone
|
36
|
+
def initialize(color = :empty)
|
37
|
+
@x_coord = nil
|
38
|
+
@y_coord = nil
|
39
|
+
@color = color
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_sgf
|
43
|
+
";#{color.to_s[0].upcase}[]"
|
44
|
+
end
|
45
|
+
|
46
|
+
def empty?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/test/go_test.rb
CHANGED
@@ -1,161 +1,183 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
require 'stringio'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
module RubyGo
|
5
|
+
class GoTest < Minitest::Test
|
6
|
+
def setup
|
7
|
+
@row_9_str = " 8 _ _ _ _ _ _ _ _ _ \n"
|
8
8
|
|
9
|
-
|
9
|
+
@game = Game.new(board: 9)
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
def test_game_has_blank_board_when_initialized
|
13
|
+
assert @game.board.empty?
|
14
|
+
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
def test_printer_can_print_the_game
|
17
|
+
out = StringIO.new
|
18
|
+
printer = RubyGo::TextPrinter.new(out)
|
19
|
+
printer.print_game(@game)
|
20
|
+
out.rewind
|
21
|
+
assert_includes out.read, @row_9_str
|
22
|
+
end
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
|
24
|
+
def test_game_can_place_a_stone
|
25
|
+
game = Game.new(board: 9)
|
26
|
+
game.place_black(2,2)
|
27
|
+
assert_equal :black, game.board.at(2,2).color
|
28
|
+
end
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
assert_includes out.read, @row_9_str
|
30
|
-
end
|
30
|
+
def test_cannot_place_a_stone_on_top_of_another_stone
|
31
|
+
game = Game.new(board: 9)
|
32
|
+
game.place_black(2,2)
|
33
|
+
assert_raises(Game::IllegalMove) do
|
34
|
+
game.place_white(2,2)
|
35
|
+
end
|
36
|
+
end
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
def test_can_capture_one_stone
|
39
|
+
game = Game.new(board: 9)
|
40
|
+
game.place_black(2,2)
|
41
|
+
game.place_white(2,1)
|
42
|
+
game.place_white(2,3)
|
43
|
+
game.place_white(1,2)
|
44
|
+
game.place_white(3,2)
|
45
|
+
assert_equal 1, game.captures[:black]
|
46
|
+
end
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
game.
|
48
|
+
def test_capture_removes_stone
|
49
|
+
game = Game.new(board: 9)
|
50
|
+
game.place_black(2,2)
|
51
|
+
game.place_white(2,1)
|
52
|
+
game.place_white(2,3)
|
53
|
+
game.place_white(1,2)
|
54
|
+
game.place_white(3,2)
|
55
|
+
assert game.board.at(2,2).empty?
|
43
56
|
end
|
44
|
-
end
|
45
57
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
58
|
+
def test_capture_three_stones
|
59
|
+
game = Game.new(board: 9)
|
60
|
+
game.place_black(2,1)
|
61
|
+
game.place_black(2,2)
|
62
|
+
game.place_black(2,3)
|
63
|
+
game.place_white(2,0)
|
64
|
+
game.place_white(2,4)
|
65
|
+
game.place_white(1,1)
|
66
|
+
game.place_white(1,2)
|
67
|
+
game.place_white(1,3)
|
68
|
+
game.place_white(3,1)
|
69
|
+
game.place_white(3,2)
|
70
|
+
game.place_white(3,3)
|
71
|
+
assert_equal 3, game.captures[:black]
|
72
|
+
end
|
55
73
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
74
|
+
def test_snap_back
|
75
|
+
game = Game.new(board: 9)
|
76
|
+
game.place_black(3,2)
|
77
|
+
game.place_black(2,3)
|
78
|
+
game.place_black(4,3)
|
79
|
+
game.place_black(2,4)
|
80
|
+
game.place_black(5,4)
|
81
|
+
game.place_black(3,5)
|
82
|
+
game.place_black(4,5)
|
83
|
+
game.place_white(4,2)
|
84
|
+
game.place_white(5,3)
|
85
|
+
game.place_white(3,4)
|
86
|
+
game.place_white(4,4)
|
87
|
+
game.place_white(3,3)
|
88
|
+
assert_equal 1, game.captures[:black]
|
89
|
+
assert_equal 0, game.captures[:white]
|
90
|
+
game.place_black(4,3)
|
91
|
+
assert_equal 1, game.captures[:black]
|
92
|
+
assert_equal 3, game.captures[:white]
|
93
|
+
end
|
65
94
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
game.white(1,1)
|
74
|
-
game.white(1,2)
|
75
|
-
game.white(1,3)
|
76
|
-
game.white(3,1)
|
77
|
-
game.white(3,2)
|
78
|
-
game.white(3,3)
|
79
|
-
assert_equal 3, game.captures[:black]
|
80
|
-
end
|
95
|
+
def test_can_undo
|
96
|
+
game = Game.new()
|
97
|
+
game.place_black(2,2)
|
98
|
+
game.place_white(2,3)
|
99
|
+
game.undo
|
100
|
+
assert game.board.at(2,3).empty?
|
101
|
+
end
|
81
102
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
103
|
+
def test_can_undo_pass
|
104
|
+
game = Game.new
|
105
|
+
game.place_black(2,2)
|
106
|
+
game.white_pass
|
107
|
+
game.undo
|
108
|
+
assert_equal 0, game.passes
|
109
|
+
end
|
89
110
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
111
|
+
def test_can_undo_until_beginning
|
112
|
+
game = Game.new()
|
113
|
+
game.place_black(2,2)
|
114
|
+
game.place_white(3,2)
|
115
|
+
game.place_black(3,3)
|
116
|
+
3.times {game.undo}
|
117
|
+
assert game.board.at(2,2).empty?
|
118
|
+
end
|
98
119
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
120
|
+
def test_can_pass
|
121
|
+
game = Game.new
|
122
|
+
game.black_pass
|
123
|
+
game.white_pass
|
124
|
+
assert_equal 2, game.passes
|
125
|
+
end
|
105
126
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
127
|
+
def test_cannot_play_a_suicide
|
128
|
+
game = Game.new
|
129
|
+
game.place_black(3,3)
|
130
|
+
game.place_white(2,3)
|
131
|
+
game.place_white(4,3)
|
132
|
+
game.place_white(3,4)
|
133
|
+
game.place_white(3,2)
|
134
|
+
assert_raises(Game::IllegalMove) do
|
135
|
+
game.place_black(3,3)
|
136
|
+
end
|
115
137
|
end
|
116
|
-
end
|
117
138
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
139
|
+
def test_cannot_play_a_ko
|
140
|
+
game = Game.new
|
141
|
+
game.place_black(3,3)
|
142
|
+
game.place_white(2,3)
|
143
|
+
game.place_white(4,3)
|
144
|
+
game.place_white(3,4)
|
145
|
+
game.place_white(3,2)
|
146
|
+
game.place_black(4,2)
|
147
|
+
game.place_black(4,4)
|
148
|
+
game.place_black(5,3)
|
149
|
+
game.place_black(3,3)
|
150
|
+
assert_raises(Game::IllegalMove) do
|
151
|
+
game.place_white(4,3)
|
152
|
+
end
|
131
153
|
end
|
132
|
-
end
|
133
154
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
155
|
+
def big_capture_area_game
|
156
|
+
game = Game.new
|
157
|
+
game.place_black(0,0)
|
158
|
+
game.place_white(1,1)
|
159
|
+
game.place_black(0,1)
|
160
|
+
game.place_white(0,2)
|
161
|
+
game.place_black(1,0)
|
162
|
+
game.place_white(2,0)
|
163
|
+
game
|
164
|
+
end
|
144
165
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
166
|
+
def test_can_play_in_previous_capture_area_that_is_not_a_ko
|
167
|
+
game = big_capture_area_game
|
168
|
+
assert game.board.at(0,0).empty?
|
169
|
+
game.place_black(0,0)
|
170
|
+
assert_equal :black, game.board.at(0,0).color
|
171
|
+
|
172
|
+
game = big_capture_area_game
|
173
|
+
assert game.board.at(0,1).empty?
|
174
|
+
game.place_black(0,1)
|
175
|
+
assert_equal :black, game.board.at(0,1).color
|
176
|
+
|
177
|
+
game = big_capture_area_game
|
178
|
+
assert game.board.at(1,0).empty?
|
179
|
+
game.place_black(1,0)
|
180
|
+
assert_equal :black, game.board.at(1,0).color
|
181
|
+
end
|
160
182
|
end
|
161
183
|
end
|
data/test/sgf_test.rb
CHANGED
@@ -1,51 +1,52 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
|
3
|
-
|
3
|
+
module RubyGo
|
4
|
+
class SGFTest < Minitest::Test
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
def test_game_converts_to_sgf
|
7
|
+
game = Game.new
|
8
|
+
assert_equal "(;GM[1]FF[4]CA[UTF-8]AP[jphager2]SZ[19]PW[White]PB[Black])",
|
9
|
+
game.to_sgf
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
def test_game_converts_to_sgf_with_moves
|
13
|
+
game = Game.new
|
14
|
+
game.place_black(15,3)
|
15
|
+
game.place_white(3,3)
|
16
|
+
game.place_black(3,15)
|
17
|
+
game.place_white(15,15)
|
18
|
+
assert_equal "(;GM[1]FF[4]CA[UTF-8]AP[jphager2]SZ[19]PW[White]PB[Black];B[pd];W[dd];B[dp];W[pp])",
|
19
|
+
game.to_sgf
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
22
|
+
def test_game_converts_correctly_with_capture
|
23
|
+
game = Game.new
|
24
|
+
game.place_black(15,3)
|
25
|
+
game.place_white(16,3)
|
26
|
+
game.place_black(16,2)
|
27
|
+
game.place_white(15,2)
|
28
|
+
game.place_black(14,2)
|
29
|
+
game.place_white(14,3)
|
30
|
+
game.place_black(17,3)
|
31
|
+
game.place_white(15,4)
|
32
|
+
game.place_black(16,4)
|
33
|
+
game.place_white(15,3)
|
33
34
|
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
assert_equal "(;GM[1]FF[4]CA[UTF-8]AP[jphager2]SZ[19]PW[White]PB[Black];B[pd];W[qd];B[qc];W[pc];B[oc];W[od];B[rd];W[pe];B[qe];W[pd])",
|
36
|
+
game.to_sgf
|
37
|
+
end
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
def test_saves_sgf
|
40
|
+
file = File.expand_path('../../my_go_game.sgf', __FILE__)
|
41
|
+
path = File.expand_path('../../*.sgf', __FILE__)
|
42
|
+
File.delete(file) if File.exist?(file)
|
43
|
+
game = Game.new
|
44
|
+
game.place_black(15,3)
|
45
|
+
game.place_white(16,3)
|
46
|
+
game.place_black(16,2)
|
47
|
+
game.place_white(15,2)
|
48
|
+
game.save
|
49
|
+
assert_includes Dir.glob(path), file
|
50
|
+
end
|
49
51
|
end
|
50
|
-
|
51
52
|
end
|