ruby-go 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 74efecc082ec565b2ffa7c416f8a28497706c895
4
- data.tar.gz: 5f702299cf817f5fc56d70910b225ecc92879fd8
3
+ metadata.gz: 99c7a4ed50ed990d7029979e84ff6c18338577f2
4
+ data.tar.gz: 12c6da43266d2267faae29bfe7b121589e5c85d7
5
5
  SHA512:
6
- metadata.gz: 1531c446d300958152b41c69c0b05a8f503f90f0bdc35cd041e52f15db2370000971f1b3f2a83acc5a3097a789a4a324a2a3a344979ef1e9a9a0785db7e2a779
7
- data.tar.gz: 50b2b336a5f64132537ad193a0efe1c1a17867757539a730272fbbe6b4231f87f2eab036fb38ac67531bfcbf20289c8acee094458e84a43c0e5599878b9548eb
6
+ metadata.gz: 7453628a08d3b1f1cf25e39fa2494eaeb085af2e1f986974467f2e51040c58ddd5a351e93fc363a4011cb0e3c9001aa0c48d918afc3a70ae52f299bcfb02dc0b
7
+ data.tar.gz: 79eaf22114887997f545b5877ab3dcd19bc801b9fc8dd4b9e96d039d83e81400959a86b10ba1fec87b259f4a1b48d02d71f2bd21a4713114520addf51223993f
@@ -0,0 +1,83 @@
1
+ module RubyGo
2
+ class Board
3
+ Colors = {black: 'x', white: 'o', empty: '_'}
4
+
5
+ attr_accessor :board
6
+ def initialize(size)
7
+ @board = []
8
+ size -= 1
9
+
10
+ 0.upto(size) do |y|
11
+ row = []
12
+ 0.upto(size) do |x|
13
+ row << Liberty.new(x, y)
14
+ end
15
+ @board << row
16
+ end
17
+ end
18
+
19
+ def size
20
+ @board.length
21
+ end
22
+
23
+ def empty?
24
+ !@board.flatten.any? do |s|
25
+ !s.empty?
26
+ end
27
+ end
28
+
29
+ def at(x, y)
30
+ @board[y][x]
31
+ end
32
+
33
+ def around(x, y = :y_not_given)
34
+ x, y = x.to_coord if x.kind_of?(Stone)
35
+ stones = []
36
+ stones << at(x-1, y) unless x == 0
37
+ stones << at(x+1, y) unless x == (@board.length - 1)
38
+ stones << at(x, y-1) unless y == 0
39
+ stones << at(x, y+1) unless y == (@board.length - 1)
40
+ stones
41
+ end
42
+
43
+ def remove(stone)
44
+ stone.remove_from_board(self)
45
+ end
46
+
47
+ def place(stone)
48
+ stone.place_on_board(self)
49
+ end
50
+
51
+ def liberties(stone)
52
+ group_of(stone).inject(0) do |libs, stn|
53
+ libs + stn.liberties(self).count
54
+ end
55
+ end
56
+
57
+ def group_of(stone)
58
+ stone.group(self)
59
+ end
60
+
61
+ def to_str
62
+ out = ""
63
+ if @board.length < 11
64
+ out << "\s\s\s#{(0..@board.length - 1).to_a.join(' ')}\n"
65
+ else
66
+ out <<
67
+ "\s\s\s#{(0..10).to_a.join(' ')}" <<
68
+ "#{(11..@board.length - 1).to_a.join('')}\n"
69
+ end
70
+
71
+ @board.each_with_index do |row, i|
72
+ i = "\s#{i}" if i < 10
73
+ out << "#{i}\s#{(row.collect {|stn| stn.to_s}).join(' ')}\n"
74
+ end
75
+
76
+ out
77
+ end
78
+
79
+ def to_s
80
+ to_str
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,126 @@
1
+ module RubyGo
2
+ class Game
3
+
4
+ LETTERS = ('a'..'z').to_a
5
+
6
+ attr_reader :board
7
+ def initialize(board: 19)
8
+ @board = Board.new(board)
9
+ @moves = []
10
+ end
11
+
12
+ def save(name="my_go_game")
13
+ tree = SGF::Parser.new.parse(to_sgf)
14
+ tree.save(name + '.sgf')
15
+ end
16
+
17
+ def to_sgf
18
+ sgf = "(;GM[1]FF[4]CA[UTF-8]AP[jphager2]SZ[#{board.size}]PW[White]PB[Black]"
19
+
20
+ @moves.each do |move|
21
+ sgf << move[:stone].to_sgf
22
+ end
23
+
24
+ sgf << ')'
25
+ end
26
+
27
+ def view
28
+ puts @board.to_s
29
+ puts " " + "_"*(@board.size * 2)
30
+ print " Prisoners || White: #{captures[:black]} |"
31
+ puts " Black: #{captures[:white]}"
32
+ puts " " + "-"*(@board.size * 2)
33
+ end
34
+
35
+ def black(x, y)
36
+ play(BlackStone.new(x,y))
37
+ end
38
+
39
+ def white(x, y)
40
+ play(WhiteStone.new(x,y))
41
+ end
42
+
43
+ def black_pass
44
+ pass(:black)
45
+ end
46
+
47
+ def white_pass
48
+ pass(:white)
49
+ end
50
+
51
+ def pass(color)
52
+ @moves << {stone: NullStone.new(color), captures: [], pass: true}
53
+ end
54
+
55
+ def undo
56
+ move = @moves.pop
57
+ @board.remove(move[:stone])
58
+ move[:captures].each {|stone| @board.place(stone)}
59
+ end
60
+
61
+ def passes
62
+ @moves.inject(0) {|total, move| move[:pass] ? total + 1 : 0}
63
+ end
64
+
65
+ def captures
66
+ @moves.each_with_object({black: 0, white: 0}) do |move, total|
67
+ move[:captures].each do |capture|
68
+ total[capture.color] += 1
69
+ end
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def play(stone)
76
+ @board.place(stone)
77
+ @moves << {stone: stone, captures: [], pass: false}
78
+
79
+ capture; suicide; ko
80
+ end
81
+
82
+ def ko
83
+ return if @moves.length < 2 or !@moves[-2][:captures]
84
+
85
+ captures = @moves[-2][:captures]
86
+ stone = @moves.last[:stone]
87
+
88
+ if captures == [stone]
89
+ undo
90
+ raise IllegalMove,
91
+ "You cannot capture the ko, play a ko threat first"
92
+ end
93
+ end
94
+
95
+ def suicide
96
+ stone = @moves.last[:stone]
97
+ unless @board.liberties(stone) > 0
98
+ undo
99
+ raise IllegalMove, "You cannot play a suicide."
100
+ end
101
+ end
102
+
103
+ def capture
104
+ stone = @moves.last[:stone]
105
+ stones_around = @board.around(stone)
106
+
107
+ captures = stones_around.select {|stn| @board.liberties(stn) == 0}
108
+
109
+ captures.each {|stone| capture_group(stone)}
110
+ end
111
+
112
+ def capture_group(stone)
113
+ @board.group_of(stone).each {|stone| capture_stone(stone)}
114
+ end
115
+
116
+ def capture_stone(stone)
117
+ @moves.last[:captures] << stone
118
+ @board.remove(stone)
119
+ end
120
+
121
+ public
122
+
123
+ class IllegalMove < Exception
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,104 @@
1
+ module RubyGo
2
+ class Stone
3
+
4
+ attr_reader :color
5
+ def initialize(x, y)
6
+ @x, @y = x, y
7
+ @color = :none
8
+ end
9
+
10
+ def to_sgf
11
+ x = Game::LETTERS[@x]
12
+ y = Game::LETTERS[@y]
13
+ ";#{color.to_s[0].upcase}[#{x+y}]"
14
+ end
15
+
16
+ def empty?
17
+ @color == :empty
18
+ end
19
+
20
+ def place_on_board(board)
21
+ unless board.at(@x, @y).empty?
22
+ raise Game::IllegalMove,
23
+ "You cannot place a stone on top of another stone."
24
+ end
25
+ board.board[@y][@x] = self
26
+ end
27
+
28
+ def remove_from_board(board)
29
+ board.board[@y][@x] = Liberty.new(*self.to_coord)
30
+ end
31
+
32
+ def liberties(board)
33
+ board.around(self).select {|stone| stone.empty?}
34
+ end
35
+
36
+ def group(board, stones = [])
37
+ return stones if stones.any? {|stone| stone.eql?(self)}
38
+ stones << self
39
+
40
+ board.around(self).each do |stone|
41
+ if stone.color == @color
42
+ stone.group(board, stones)
43
+ end
44
+ end
45
+
46
+ stones
47
+ end
48
+
49
+ def to_coord
50
+ [@x, @y]
51
+ end
52
+
53
+ def to_str
54
+ Board::Colors[@color]
55
+ end
56
+
57
+ def to_s
58
+ to_str
59
+ end
60
+
61
+ def ==(other)
62
+ (self.color == other.color) and (self.to_coord == other.to_coord)
63
+ end
64
+ end
65
+
66
+ class Liberty < Stone
67
+ def initialize(x, y)
68
+ super
69
+ @color = :empty
70
+ end
71
+
72
+ def liberties(board)
73
+ [self]
74
+ end
75
+ end
76
+
77
+ class BlackStone < Stone
78
+ def initialize(x, y)
79
+ super
80
+ @color = :black
81
+ end
82
+ end
83
+
84
+ class WhiteStone < Stone
85
+ def initialize(x, y)
86
+ super
87
+ @color = :white
88
+ end
89
+ end
90
+
91
+ class NullStone < Stone
92
+ def initialize(color = :empty)
93
+ @x, @y = nil, nil
94
+ @color = color
95
+ end
96
+
97
+ def remove_from_board(board)
98
+ end
99
+
100
+ def to_sgf
101
+ ";#{color.to_s[0].upcase}[]"
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,3 @@
1
+ module RubyGo
2
+ VERSION = "0.2.0"
3
+ end
data/lib/ruby-go.rb CHANGED
@@ -1,5 +1,8 @@
1
- require_relative 'board'
2
- require_relative 'game'
3
- require_relative 'stone'
4
-
5
1
  require 'sgf'
2
+
3
+ require_relative 'ruby-go/board'
4
+ require_relative 'ruby-go/game'
5
+ require_relative 'ruby-go/stone'
6
+
7
+ module RubyGo
8
+ end
data/test/go_test.rb CHANGED
@@ -1,161 +1,165 @@
1
1
  require_relative 'test_helper'
2
2
  require 'stringio'
3
3
 
4
- class GoTest < Minitest::Test
5
- def setup
6
- b,w = Board::Colors[:black], Board::Colors[:white]
7
- e = Board::Colors[:empty]
4
+ module RubyGo
5
+ class GoTest < Minitest::Test
6
+ def setup
7
+ b,w = Board::Colors[:black], Board::Colors[:white]
8
+ e = Board::Colors[:empty]
8
9
 
9
- @row_9_str = "_ _ _ _ _ _ _ _ _\n"
10
+ @row_9_str = "_ _ _ _ _ _ _ _ _\n"
10
11
 
11
- @game = Game.new(board: 9)
12
- end
12
+ @game = Game.new(board: 9)
13
+ end
13
14
 
14
- def test_game_has_blank_board_when_initialized
15
- assert @game.board.empty?
16
- end
15
+ def test_game_has_blank_board_when_initialized
16
+ assert @game.board.empty?
17
+ end
17
18
 
18
- def test_empty_board_looks_like_empty_board
19
- assert_includes @game.board.to_s, @row_9_str
20
- end
19
+ def test_empty_board_looks_like_empty_board
20
+ assert_includes @game.board.to_s, @row_9_str
21
+ end
21
22
 
22
- def test_game_can_print_the_board
23
- out = StringIO.new
24
- temp = $stdout
25
- $stdout = out
26
- @game.view
27
- $stdout = temp
28
- out.rewind
29
- assert_includes out.read, @row_9_str
30
- end
23
+ def test_game_can_print_the_board
24
+ out = StringIO.new
25
+ temp = $stdout
26
+ $stdout = out
27
+ @game.view
28
+ $stdout = temp
29
+ out.rewind
30
+ assert_includes out.read, @row_9_str
31
+ end
31
32
 
32
- def test_game_can_place_a_stone
33
- game = Game.new(board: 9)
34
- game.black(2,2)
35
- assert_equal BlackStone.new(2,2), game.board.at(2,2)
36
- end
33
+ def test_game_can_place_a_stone
34
+ game = Game.new(board: 9)
35
+ game.black(2,2)
36
+ assert_equal BlackStone.new(2,2), game.board.at(2,2)
37
+ end
37
38
 
38
- def test_cannot_place_a_stone_on_top_of_another_stone
39
- game = Game.new(board: 9)
40
- game.black(2,2)
41
- assert_raises(Game::IllegalMove) do
42
- game.white(2,2)
39
+ def test_cannot_place_a_stone_on_top_of_another_stone
40
+ game = Game.new(board: 9)
41
+ game.black(2,2)
42
+ assert_raises(Game::IllegalMove) do
43
+ game.white(2,2)
44
+ end
43
45
  end
44
- end
45
46
 
46
- def test_can_capture_one_stone
47
- game = Game.new(board: 9)
48
- game.black(2,2)
49
- game.white(2,1)
50
- game.white(2,3)
51
- game.white(1,2)
52
- game.white(3,2)
53
- assert_equal 1, game.captures[:black]
54
- end
47
+ def test_can_capture_one_stone
48
+ game = Game.new(board: 9)
49
+ game.black(2,2)
50
+ game.white(2,1)
51
+ game.white(2,3)
52
+ game.white(1,2)
53
+ game.white(3,2)
54
+ assert_equal 1, game.captures[:black]
55
+ end
55
56
 
56
- def test_capture_removes_stone
57
- game = Game.new(board: 9)
58
- game.black(2,2)
59
- game.white(2,1)
60
- game.white(2,3)
61
- game.white(1,2)
62
- game.white(3,2)
63
- assert_equal Liberty.new(2,2), game.board.at(2,2)
64
- end
57
+ def test_capture_removes_stone
58
+ game = Game.new(board: 9)
59
+ game.black(2,2)
60
+ game.white(2,1)
61
+ game.white(2,3)
62
+ game.white(1,2)
63
+ game.white(3,2)
64
+ assert_equal Liberty.new(2,2), game.board.at(2,2)
65
+ end
65
66
 
66
- def test_capture_three_stones
67
- game = Game.new(board: 9)
68
- game.black(2,1)
69
- game.black(2,2)
70
- game.black(2,3)
71
- game.white(2,0)
72
- game.white(2,4)
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
67
+ def test_capture_three_stones
68
+ game = Game.new(board: 9)
69
+ game.black(2,1)
70
+ game.black(2,2)
71
+ game.black(2,3)
72
+ game.white(2,0)
73
+ game.white(2,4)
74
+ game.white(1,1)
75
+ game.white(1,2)
76
+ game.white(1,3)
77
+ game.white(3,1)
78
+ game.white(3,2)
79
+ game.white(3,3)
80
+ assert_equal 3, game.captures[:black]
81
+ end
81
82
 
82
- def test_can_undo
83
- game = Game.new()
84
- game.black(2,2)
85
- game.white(2,3)
86
- game.undo
87
- assert_equal Liberty.new(2,3), game.board.at(2,3)
88
- end
83
+ def test_can_undo
84
+ game = Game.new()
85
+ game.black(2,2)
86
+ game.white(2,3)
87
+ game.undo
88
+ assert_equal Liberty.new(2,3), game.board.at(2,3)
89
+ end
89
90
 
90
- def test_can_undo_until_beginning
91
- game = Game.new()
92
- game.black(2,2)
93
- game.white(3,2)
94
- game.black(3,3)
95
- 3.times {game.undo}
96
- assert_equal Liberty.new(2,2), game.board.at(2,2)
97
- end
91
+ def test_can_undo_until_beginning
92
+ game = Game.new()
93
+ game.black(2,2)
94
+ game.white(3,2)
95
+ game.black(3,3)
96
+ 3.times {game.undo}
97
+ assert_equal Liberty.new(2,2), game.board.at(2,2)
98
+ end
98
99
 
99
- def test_can_pass
100
- game = Game.new
101
- game.pass
102
- game.pass
103
- assert_equal 2, game.passes
104
- end
100
+ def test_can_pass
101
+ game = Game.new
102
+ game.pass(:black)
103
+ game.pass(:white)
104
+ game.black_pass
105
+ game.white_pass
106
+ assert_equal 4, game.passes
107
+ end
105
108
 
106
- def test_cannot_play_a_suicide
107
- game = Game.new
108
- game.black(3,3)
109
- game.white(2,3)
110
- game.white(4,3)
111
- game.white(3,4)
112
- game.white(3,2)
113
- assert_raises(Game::IllegalMove) do
109
+ def test_cannot_play_a_suicide
110
+ game = Game.new
114
111
  game.black(3,3)
112
+ game.white(2,3)
113
+ game.white(4,3)
114
+ game.white(3,4)
115
+ game.white(3,2)
116
+ assert_raises(Game::IllegalMove) do
117
+ game.black(3,3)
118
+ end
115
119
  end
116
- end
117
120
 
118
- def test_cannot_play_a_ko
119
- game = Game.new
120
- game.black(3,3)
121
- game.white(2,3)
122
- game.white(4,3)
123
- game.white(3,4)
124
- game.white(3,2)
125
- game.black(4,2)
126
- game.black(4,4)
127
- game.black(5,3)
128
- game.black(3,3)
129
- assert_raises(Game::IllegalMove) do
121
+ def test_cannot_play_a_ko
122
+ game = Game.new
123
+ game.black(3,3)
124
+ game.white(2,3)
130
125
  game.white(4,3)
126
+ game.white(3,4)
127
+ game.white(3,2)
128
+ game.black(4,2)
129
+ game.black(4,4)
130
+ game.black(5,3)
131
+ game.black(3,3)
132
+ assert_raises(Game::IllegalMove) do
133
+ game.white(4,3)
134
+ end
131
135
  end
132
- end
133
136
 
134
- def big_capture_area_game
135
- game = Game.new
136
- game.black(0,0)
137
- game.white(1,1)
138
- game.black(0,1)
139
- game.white(0,2)
140
- game.black(1,0)
141
- game.white(2,0)
142
- game
143
- end
137
+ def big_capture_area_game
138
+ game = Game.new
139
+ game.black(0,0)
140
+ game.white(1,1)
141
+ game.black(0,1)
142
+ game.white(0,2)
143
+ game.black(1,0)
144
+ game.white(2,0)
145
+ game
146
+ end
144
147
 
145
- def test_can_play_in_previous_capture_area_that_is_not_a_ko
146
- game = big_capture_area_game
147
- assert_equal Liberty.new(0,0), game.board.at(0,0)
148
- game.black(0,0)
149
- assert_equal BlackStone.new(0,0), game.board.at(0,0)
150
-
151
- game = big_capture_area_game
152
- assert_equal Liberty.new(0,1), game.board.at(0,1)
153
- game.black(0,1)
154
- assert_equal BlackStone.new(0,1), game.board.at(0,1)
155
-
156
- game = big_capture_area_game
157
- assert_equal Liberty.new(1,0), game.board.at(1,0)
158
- game.black(1,0)
159
- assert_equal BlackStone.new(1,0), game.board.at(1,0)
148
+ def test_can_play_in_previous_capture_area_that_is_not_a_ko
149
+ game = big_capture_area_game
150
+ assert_equal Liberty.new(0,0), game.board.at(0,0)
151
+ game.black(0,0)
152
+ assert_equal BlackStone.new(0,0), game.board.at(0,0)
153
+
154
+ game = big_capture_area_game
155
+ assert_equal Liberty.new(0,1), game.board.at(0,1)
156
+ game.black(0,1)
157
+ assert_equal BlackStone.new(0,1), game.board.at(0,1)
158
+
159
+ game = big_capture_area_game
160
+ assert_equal Liberty.new(1,0), game.board.at(1,0)
161
+ game.black(1,0)
162
+ assert_equal BlackStone.new(1,0), game.board.at(1,0)
163
+ end
160
164
  end
161
165
  end
data/test/sgf_test.rb CHANGED
@@ -1,51 +1,52 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- class SGFTest < Minitest::Test
3
+ module RubyGo
4
+ class SGFTest < Minitest::Test
4
5
 
5
- def test_game_converts_to_sgf
6
- game = Game.new
7
- assert_equal "(;GM[1]FF[4]CA[UTF-8]AP[jphager2]SZ[19]PW[White]PB[Black])",
8
- game.to_sgf
9
- end
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
- def test_game_converts_to_sgf_with_moves
12
- game = Game.new
13
- game.black(15,3)
14
- game.white(3,3)
15
- game.black(3,15)
16
- game.white(15,15)
17
- 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])",
18
- game.to_sgf
19
- end
12
+ def test_game_converts_to_sgf_with_moves
13
+ game = Game.new
14
+ game.black(15,3)
15
+ game.white(3,3)
16
+ game.black(3,15)
17
+ game.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
- def test_game_converts_correctly_with_capture
22
- game = Game.new
23
- game.black(15,3)
24
- game.white(16,3)
25
- game.black(16,2)
26
- game.white(15,2)
27
- game.black(14,2)
28
- game.white(14,3)
29
- game.black(17,3)
30
- game.white(15,4)
31
- game.black(16,4)
32
- game.white(15,3)
22
+ def test_game_converts_correctly_with_capture
23
+ game = Game.new
24
+ game.black(15,3)
25
+ game.white(16,3)
26
+ game.black(16,2)
27
+ game.white(15,2)
28
+ game.black(14,2)
29
+ game.white(14,3)
30
+ game.black(17,3)
31
+ game.white(15,4)
32
+ game.black(16,4)
33
+ game.white(15,3)
33
34
 
34
- 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])",
35
- game.to_sgf
36
- end
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
- def test_saves_sgf
39
- file = File.expand_path('../../my_go_game.sgf', __FILE__)
40
- path = File.expand_path('../../*.sgf', __FILE__)
41
- File.delete(file) if File.exist?(file)
42
- game = Game.new
43
- game.black(15,3)
44
- game.white(16,3)
45
- game.black(16,2)
46
- game.white(15,2)
47
- game.save
48
- assert_includes Dir.glob(path), file
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.black(15,3)
45
+ game.white(16,3)
46
+ game.black(16,2)
47
+ game.white(15,2)
48
+ game.save
49
+ assert_includes Dir.glob(path), file
50
+ end
49
51
  end
50
-
51
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-go
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - jphager2
@@ -35,10 +35,11 @@ files:
35
35
  - README.md
36
36
  - Rakefile
37
37
  - bin/ruby-go.rb
38
- - lib/board.rb
39
- - lib/game.rb
40
38
  - lib/ruby-go.rb
41
- - lib/stone.rb
39
+ - lib/ruby-go/board.rb
40
+ - lib/ruby-go/game.rb
41
+ - lib/ruby-go/stone.rb
42
+ - lib/ruby-go/version.rb
42
43
  - test/go_test.rb
43
44
  - test/sgf_test.rb
44
45
  - test/test_helper.rb
data/lib/board.rb DELETED
@@ -1,82 +0,0 @@
1
- class Board
2
- Colors = {black: 'x', white: 'o', empty: '_'}
3
-
4
- attr_accessor :board
5
- def initialize(size)
6
- @board = []
7
- size -= 1
8
-
9
- 0.upto(size) do |y|
10
- row = []
11
- 0.upto(size) do |x|
12
- row << Liberty.new(x, y)
13
- end
14
- @board << row
15
- end
16
- end
17
-
18
- def size
19
- @board.length
20
- end
21
-
22
- def empty?
23
- !@board.flatten.any? do |s|
24
- !s.empty?
25
- end
26
- end
27
-
28
- def at(x, y)
29
- @board[y][x]
30
- end
31
-
32
- def around(x, y = :y_not_given)
33
- x, y = x.to_coord if x.kind_of?(Stone)
34
- stones = []
35
- stones << at(x-1, y) unless x == 0
36
- stones << at(x+1, y) unless x == (@board.length - 1)
37
- stones << at(x, y-1) unless y == 0
38
- stones << at(x, y+1) unless y == (@board.length - 1)
39
- stones
40
- end
41
-
42
- def remove(stone)
43
- stone.remove_from_board(self)
44
- end
45
-
46
- def place(stone)
47
- stone.place_on_board(self)
48
- end
49
-
50
- def liberties(stone)
51
- group_of(stone).inject(0) do |libs, stn|
52
- libs + stn.liberties(self).count
53
- end
54
- end
55
-
56
- def group_of(stone)
57
- stone.group(self)
58
- end
59
-
60
- def to_str
61
- out = ""
62
- if @board.length < 11
63
- out << "\s\s\s#{(0..@board.length - 1).to_a.join(' ')}\n"
64
- else
65
- out <<
66
- "\s\s\s#{(0..10).to_a.join(' ')}" <<
67
- "#{(11..@board.length - 1).to_a.join('')}\n"
68
- end
69
-
70
- @board.each_with_index do |row, i|
71
- i = "\s#{i}" if i < 10
72
- out << "#{i}\s#{(row.collect {|stn| stn.to_s}).join(' ')}\n"
73
- end
74
-
75
- out
76
- end
77
-
78
- def to_s
79
- to_str
80
- end
81
- end
82
-
data/lib/game.rb DELETED
@@ -1,116 +0,0 @@
1
- class Game
2
-
3
- LETTERS = ('a'..'z').to_a
4
-
5
- attr_reader :board
6
- def initialize(board: 19)
7
- @board = Board.new(board)
8
- @moves = []
9
- end
10
-
11
- def save(name="my_go_game")
12
- tree = SGF::Parser.new.parse(to_sgf)
13
- tree.save(name + '.sgf')
14
- end
15
-
16
- def to_sgf
17
- sgf = "(;GM[1]FF[4]CA[UTF-8]AP[jphager2]SZ[19]PW[White]PB[Black]"
18
-
19
- @moves.each do |move|
20
- sgf << move[:stone].to_sgf
21
- end
22
-
23
- sgf << ')'
24
- end
25
-
26
- def view
27
- puts @board.to_s
28
- puts " " + "_"*(@board.size * 2)
29
- print " Prisoners || White: #{captures[:black]} |"
30
- puts " Black: #{captures[:white]}"
31
- puts " " + "-"*(@board.size * 2)
32
- end
33
-
34
- def black(x, y)
35
- play(BlackStone.new(x,y))
36
- end
37
-
38
- def white(x, y)
39
- play(WhiteStone.new(x,y))
40
- end
41
-
42
- def pass
43
- @moves << {stone: NullStone.new(), captures: [], pass: true}
44
- end
45
-
46
- def undo
47
- move = @moves.pop
48
- @board.remove(move[:stone])
49
- move[:captures].each {|stone| @board.place(stone)}
50
- end
51
-
52
- def passes
53
- @moves.inject(0) {|total, move| move[:pass] ? total + 1 : 0}
54
- end
55
-
56
- def captures
57
- @moves.each_with_object({black: 0, white: 0}) do |move, total|
58
- move[:captures].each do |capture|
59
- total[capture.color] += 1
60
- end
61
- end
62
- end
63
-
64
- private
65
- def play(stone)
66
- @board.place(stone)
67
- @moves << {stone: stone, captures: [], pass: false}
68
-
69
- capture; suicide; ko
70
- end
71
-
72
- def ko
73
- return if @moves.length < 2 or !@moves[-2][:captures]
74
-
75
- captures = @moves[-2][:captures]
76
- stone = @moves.last[:stone]
77
-
78
- if captures == [stone]
79
- undo
80
- raise IllegalMove,
81
- "You cannot capture the ko, play a ko threat first"
82
- end
83
- end
84
-
85
- def suicide
86
- stone = @moves.last[:stone]
87
- unless @board.liberties(stone) > 0
88
- undo
89
- raise IllegalMove, "You cannot play a suicide."
90
- end
91
- end
92
-
93
- def capture
94
- stone = @moves.last[:stone]
95
- stones_around = @board.around(stone)
96
-
97
- captures = stones_around.select {|stn| @board.liberties(stn) == 0}
98
-
99
- captures.each {|stone| capture_group(stone)}
100
- end
101
-
102
- def capture_group(stone)
103
- @board.group_of(stone).each {|stone| capture_stone(stone)}
104
- end
105
-
106
- def capture_stone(stone)
107
- @moves.last[:captures] << stone
108
- @board.remove(stone)
109
- end
110
-
111
- public
112
-
113
- class IllegalMove < Exception
114
- end
115
- end
116
-
data/lib/stone.rb DELETED
@@ -1,101 +0,0 @@
1
- class Stone
2
-
3
- attr_reader :color
4
- def initialize(x, y)
5
- @x, @y = x, y
6
- @color = :none
7
- end
8
-
9
- def to_sgf
10
- x = Game::LETTERS[@x]
11
- y = Game::LETTERS[@y]
12
- ";#{color.to_s[0].upcase}[#{x+y}]"
13
- end
14
-
15
- def empty?
16
- @color == :empty
17
- end
18
-
19
- def place_on_board(board)
20
- unless board.at(@x, @y).empty?
21
- raise Game::IllegalMove,
22
- "You cannot place a stone on top of another stone."
23
- end
24
- board.board[@y][@x] = self
25
- end
26
-
27
- def remove_from_board(board)
28
- board.board[@y][@x] = Liberty.new(*self.to_coord)
29
- end
30
-
31
- def liberties(board)
32
- board.around(self).select {|stone| stone.empty?}
33
- end
34
-
35
- def group(board, stones = [])
36
- return stones if stones.any? {|stone| stone.eql?(self)}
37
- stones << self
38
-
39
- board.around(self).each do |stone|
40
- if stone.color == @color
41
- stone.group(board, stones)
42
- end
43
- end
44
-
45
- stones
46
- end
47
-
48
- def to_coord
49
- [@x, @y]
50
- end
51
-
52
- def to_str
53
- Board::Colors[@color]
54
- end
55
-
56
- def to_s
57
- to_str
58
- end
59
-
60
- def ==(other)
61
- (self.color == other.color) and (self.to_coord == other.to_coord)
62
- end
63
- end
64
-
65
- class Liberty < Stone
66
- def initialize(x, y)
67
- super
68
- @color = :empty
69
- end
70
-
71
- def liberties(board)
72
- [self]
73
- end
74
- end
75
-
76
- class BlackStone < Stone
77
- def initialize(x, y)
78
- super
79
- @color = :black
80
- end
81
- end
82
-
83
- class WhiteStone < Stone
84
- def initialize(x, y)
85
- super
86
- @color = :white
87
- end
88
- end
89
-
90
- class NullStone < Stone
91
- def initialize(*args)
92
- @x, @y = nil, nil
93
- end
94
-
95
- def remove_from_board(board)
96
- end
97
-
98
- def to_sfg
99
- ""
100
- end
101
- end