ruby-go 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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