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
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-go
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jphager2
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: SgfParser
|
@@ -16,29 +16,34 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.0.0
|
27
27
|
description: A gem to play go and save games as sgf
|
28
28
|
email: jphager2@gmail.com
|
29
29
|
executables:
|
30
|
-
-
|
30
|
+
- rubygo
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
34
|
- LICENSE
|
35
35
|
- README.md
|
36
36
|
- Rakefile
|
37
|
-
- bin/
|
38
|
-
- lib/board.rb
|
39
|
-
- lib/game.rb
|
37
|
+
- bin/rubygo
|
40
38
|
- lib/ruby-go.rb
|
41
|
-
- lib/
|
39
|
+
- lib/ruby-go/board.rb
|
40
|
+
- lib/ruby-go/game.rb
|
41
|
+
- lib/ruby-go/liberty.rb
|
42
|
+
- lib/ruby-go/moves.rb
|
43
|
+
- lib/ruby-go/printers/html.rb
|
44
|
+
- lib/ruby-go/printers/text.rb
|
45
|
+
- lib/ruby-go/stone.rb
|
46
|
+
- lib/ruby-go/version.rb
|
42
47
|
- test/go_test.rb
|
43
48
|
- test/sgf_test.rb
|
44
49
|
- test/test_helper.rb
|
@@ -61,8 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
66
|
- !ruby/object:Gem::Version
|
62
67
|
version: '0'
|
63
68
|
requirements: []
|
64
|
-
|
65
|
-
rubygems_version: 2.4.6
|
69
|
+
rubygems_version: 3.1.2
|
66
70
|
signing_key:
|
67
71
|
specification_version: 4
|
68
72
|
summary: The game of Go, writen in Ruby
|
data/bin/ruby-go.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require_relative '../lib/ruby-go'
|
4
|
-
|
5
|
-
game = Game.new
|
6
|
-
|
7
|
-
def game.prompt_for_turn
|
8
|
-
print "Pass or enter coordinates x, y for move (e.g. 4, 4): "
|
9
|
-
ans = gets.chomp.downcase.gsub(/\s/, '')
|
10
|
-
|
11
|
-
if ans =~ /pass/
|
12
|
-
self.pass
|
13
|
-
nil
|
14
|
-
else
|
15
|
-
ans.split(',').collect {|c| Integer(c)}
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
system "clear"
|
20
|
-
game.view
|
21
|
-
turn = 0
|
22
|
-
until game.passes >= 2
|
23
|
-
begin
|
24
|
-
if turn % 2 == 0
|
25
|
-
#black's turn
|
26
|
-
puts "Black's turn"
|
27
|
-
ans = game.prompt_for_turn
|
28
|
-
game.black(*ans) if ans
|
29
|
-
else
|
30
|
-
#white's turn
|
31
|
-
puts "white's turn"
|
32
|
-
ans = game.prompt_for_turn
|
33
|
-
game.white(*ans) if ans
|
34
|
-
end
|
35
|
-
|
36
|
-
system "clear"
|
37
|
-
game.view
|
38
|
-
|
39
|
-
turn += 1
|
40
|
-
rescue Game::IllegalMove
|
41
|
-
next
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
system "clear"
|
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
|