gorb 0.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/LICENSE +0 -0
- data/README +6 -0
- data/Rakefile +11 -0
- data/lib/gorb.rb +4 -0
- data/lib/gorb/board.rb +147 -0
- data/lib/gorb/group.rb +42 -0
- data/lib/gorb/player.rb +12 -0
- data/lib/gorb/stone.rb +80 -0
- metadata +71 -0
data/LICENSE
ADDED
File without changes
|
data/README
ADDED
data/Rakefile
ADDED
data/lib/gorb.rb
ADDED
data/lib/gorb/board.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'gorb/stone'
|
2
|
+
|
3
|
+
class Board
|
4
|
+
|
5
|
+
attr_accessor :groups, :turn
|
6
|
+
attr_reader :black, :white, :handicap, :komi, :size
|
7
|
+
|
8
|
+
# Initialize a new Board instance. Requires two Player objects and a
|
9
|
+
# handicap as arguments. The handicap should be an integer from 0 to 9.
|
10
|
+
def initialize(black=nil, white=nil, handicap=0, komi=6.5, size="19x19")
|
11
|
+
@black = black ||= Player.new("Black")
|
12
|
+
@white = white ||= Player.new("White")
|
13
|
+
@komi = komi
|
14
|
+
@size = size
|
15
|
+
@groups = []
|
16
|
+
@hashes = []
|
17
|
+
@turn = black
|
18
|
+
|
19
|
+
raise ArgumentError, "Incorrect handicap" if handicap < 0 or handicap > 9
|
20
|
+
@handicap = handicap
|
21
|
+
@komi = 0.5 if handicap > 0
|
22
|
+
@turn = white if handicap > 1
|
23
|
+
|
24
|
+
if size == "9x9"
|
25
|
+
handicap_stones = %w{G7 C3 G3 C7 E5 C5 G5 E7 E3}
|
26
|
+
elsif size == "13x13"
|
27
|
+
handicap_stones = %w{K10 D4 K4 D10 G7 D7 K7 G10 G4}
|
28
|
+
elsif size == "19x19"
|
29
|
+
handicap_stones = %w{Q16 D4 Q4 D16 K10 D10 Q10 K16 K4}
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Incorrect board size"
|
32
|
+
end
|
33
|
+
|
34
|
+
case @handicap
|
35
|
+
when 2..5, 7, 9
|
36
|
+
handicap_stones[0..(@handicap-1)].each {|s| self.add_stone(s, :black)}
|
37
|
+
when 6, 8
|
38
|
+
handicap_stones[0..@handicap].each {|s| self.add_stone(s, :black)}
|
39
|
+
self.remove_stone(handicap_stones[4]) # Middle stone
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Add a Stone to the board if the move is legal. This function will also
|
44
|
+
# keep track of the turn. You can force the color with additional color
|
45
|
+
# argument -- in this case the turn is not changing.
|
46
|
+
def add_stone(point, color=nil)
|
47
|
+
# Guess the color based on turn, unless color was forced.
|
48
|
+
unless color
|
49
|
+
if @turn == black
|
50
|
+
color = :black
|
51
|
+
else
|
52
|
+
color = :white
|
53
|
+
end
|
54
|
+
advance = true
|
55
|
+
end
|
56
|
+
|
57
|
+
# Check the legality of the move and play it if legal.
|
58
|
+
raise ArgumentError, "Illegal move" unless legal?(point, color)
|
59
|
+
stone = Stone.new(self, point, color)
|
60
|
+
resolve!(stone)
|
61
|
+
|
62
|
+
# If the color was not explicitly set, advance the turn.
|
63
|
+
turn_over if advance
|
64
|
+
return stone
|
65
|
+
end
|
66
|
+
|
67
|
+
def remove_stone(point)
|
68
|
+
stone = self.search(point).first
|
69
|
+
raise ArgumentError, "No such stone" unless stone
|
70
|
+
stone.group.delete(stone)
|
71
|
+
stone.board.groups.delete(stone.group) if stone.group.size == 0
|
72
|
+
end
|
73
|
+
|
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
|
+
def legal?(point, color)
|
109
|
+
# Check if the point if already occupied.
|
110
|
+
return false if self.stone_at? point
|
111
|
+
|
112
|
+
# The method for checking legality requires placing a test stone to the
|
113
|
+
# point and seeing what happens. This is done by doing a deep copy of the
|
114
|
+
# board and playing the move there.
|
115
|
+
dummy_board = Marshal.load(Marshal.dump(self))
|
116
|
+
|
117
|
+
# Check for suicide.
|
118
|
+
stone = Stone.new(dummy_board, point, color)
|
119
|
+
legal = true
|
120
|
+
if stone.group.liberties == 0
|
121
|
+
# Normally suicide is not ok...
|
122
|
+
legal = false
|
123
|
+
# ...but killing with 'suicide' (filling dame) is ok.
|
124
|
+
opposing = dummy_board.search(stone.neighbors)
|
125
|
+
opposing.each do |opp_stone|
|
126
|
+
if opp_stone.color != color and opp_stone.group.liberties == 0
|
127
|
+
legal = true
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Check for ko.
|
133
|
+
dummy_board.resolve!(stone)
|
134
|
+
legal = false if @hashes.include? dummy_board.generate_hash
|
135
|
+
return legal
|
136
|
+
end
|
137
|
+
|
138
|
+
def turn_over
|
139
|
+
if @turn == @black
|
140
|
+
@turn = @white
|
141
|
+
else
|
142
|
+
@turn = @black
|
143
|
+
end
|
144
|
+
@hashes << generate_hash
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
data/lib/gorb/group.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Group < Array
|
2
|
+
|
3
|
+
def initialize(board, stone)
|
4
|
+
@board = board
|
5
|
+
@board.groups << self
|
6
|
+
self << stone
|
7
|
+
end
|
8
|
+
|
9
|
+
def merge(groups)
|
10
|
+
groups.each do |group|
|
11
|
+
group.each {|stone| self << stone}
|
12
|
+
@board.groups.delete group
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def include?(point)
|
17
|
+
self.any? {|stone| stone.point == point}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Check the liberties of the Group.
|
21
|
+
def liberties
|
22
|
+
libs = []
|
23
|
+
self.each do |stone|
|
24
|
+
stone.liberties.each do |liberty|
|
25
|
+
libs << liberty
|
26
|
+
end
|
27
|
+
end
|
28
|
+
libs.uniq!
|
29
|
+
return libs.size
|
30
|
+
end
|
31
|
+
|
32
|
+
# Destructive version of the former. Note that this will remove the Group
|
33
|
+
# from the board if it has no liberties, so all the existing groups should
|
34
|
+
# have their liberties checked before checking the liberties of a new group
|
35
|
+
# in order to allow kills by filling dame.
|
36
|
+
def liberties!
|
37
|
+
libs = self.liberties
|
38
|
+
@board.groups.delete(self) if libs == 0
|
39
|
+
return libs
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/gorb/player.rb
ADDED
data/lib/gorb/stone.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'gorb/group'
|
2
|
+
|
3
|
+
class Stone
|
4
|
+
|
5
|
+
attr_accessor :group
|
6
|
+
attr_reader :board, :point, :color
|
7
|
+
|
8
|
+
def initialize(board, point, color)
|
9
|
+
@board = board
|
10
|
+
@point = point
|
11
|
+
@color = color
|
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
|
+
if (@point[1, 2].to_i > @board.size.split('x')[0].to_i or
|
22
|
+
not @letters.index(@point[0]))
|
23
|
+
raise ArgumentError, "Invalid point"
|
24
|
+
end
|
25
|
+
|
26
|
+
@group = self.find_group
|
27
|
+
end
|
28
|
+
|
29
|
+
# Return the neighboring points of the Stone.
|
30
|
+
def neighbors
|
31
|
+
x, y = @letters.index(@point[0]), @point[1, 2].to_i
|
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
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the liberties of the Stone.
|
49
|
+
def liberties
|
50
|
+
liberties = neighbors
|
51
|
+
stones = @board.search(neighbors)
|
52
|
+
stones.each {|stone| liberties.delete(stone.point)}
|
53
|
+
return liberties
|
54
|
+
end
|
55
|
+
|
56
|
+
# Find the Group of the Stone or create a new one. If this Stone connects
|
57
|
+
# one or more groups, merge them together to a single Group.
|
58
|
+
def find_group
|
59
|
+
groups = []
|
60
|
+
stones = @board.search(neighbors)
|
61
|
+
stones.each do |stone|
|
62
|
+
if stone.color == @color and not groups.include? stone.group
|
63
|
+
groups << stone.group
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if groups.empty?
|
67
|
+
return Group.new(@board, self)
|
68
|
+
else
|
69
|
+
group = groups.pop
|
70
|
+
group.merge(groups)
|
71
|
+
group << self
|
72
|
+
return group
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
@point
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gorb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Aku Kotkavuo
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-09-06 00:00:00 +03:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: gorb is a go (board game) library written in pure Ruby.
|
22
|
+
email: aku@hibana.net
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- Rakefile
|
31
|
+
- lib/gorb/board.rb
|
32
|
+
- lib/gorb/group.rb
|
33
|
+
- lib/gorb/player.rb
|
34
|
+
- lib/gorb/stone.rb
|
35
|
+
- lib/gorb.rb
|
36
|
+
- README
|
37
|
+
- LICENSE
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/arkx/gorb
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
none: false
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.7
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Ruby go library
|
70
|
+
test_files: []
|
71
|
+
|