gorb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
File without changes
data/README ADDED
@@ -0,0 +1,6 @@
1
+ gorb is a go (board game) library written in pure Ruby.
2
+
3
+ To use it, do the following:
4
+
5
+ gem build gorb.gemspec
6
+ gem install gorb-0.0.1.gem
@@ -0,0 +1,11 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ task :default => [:test]
5
+
6
+ desc "Run tests"
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.pattern = 'test/test_*.rb'
9
+ t.verbose = false
10
+ t.warning = true
11
+ end
@@ -0,0 +1,4 @@
1
+ require 'gorb/board'
2
+ require 'gorb/group'
3
+ require 'gorb/player'
4
+ require 'gorb/stone'
@@ -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
@@ -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
@@ -0,0 +1,12 @@
1
+ class Player
2
+
3
+ def initialize(name="Anonymous", rank=nil)
4
+ @name = name
5
+ @rank = rank
6
+ end
7
+
8
+ def to_str
9
+ @name
10
+ end
11
+
12
+ end
@@ -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
+