rubykon 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +32 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +7 -0
- data/Guardfile +12 -0
- data/LICENSE +22 -0
- data/POSSIBLE_IMPROVEMENTS.md +25 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/benchmark/benchmark.sh +22 -0
- data/benchmark/full_playout.rb +17 -0
- data/benchmark/mcts_avg.rb +23 -0
- data/benchmark/playout.rb +15 -0
- data/benchmark/playout_micros.rb +188 -0
- data/benchmark/profiling/full_playout.rb +7 -0
- data/benchmark/profiling/mcts.rb +6 -0
- data/benchmark/results/HISTORY.md +541 -0
- data/benchmark/scoring.rb +20 -0
- data/benchmark/scoring_micros.rb +60 -0
- data/benchmark/support/benchmark-ips.rb +11 -0
- data/benchmark/support/benchmark-ips_shim.rb +143 -0
- data/benchmark/support/playout_help.rb +13 -0
- data/examples/mcts_laziness.rb +22 -0
- data/exe/rubykon +5 -0
- data/lib/benchmark/avg.rb +14 -0
- data/lib/benchmark/avg/benchmark_suite.rb +59 -0
- data/lib/benchmark/avg/job.rb +92 -0
- data/lib/mcts.rb +11 -0
- data/lib/mcts/examples/double_step.rb +68 -0
- data/lib/mcts/mcts.rb +13 -0
- data/lib/mcts/node.rb +88 -0
- data/lib/mcts/playout.rb +22 -0
- data/lib/mcts/root.rb +49 -0
- data/lib/rubykon.rb +13 -0
- data/lib/rubykon/board.rb +188 -0
- data/lib/rubykon/cli.rb +122 -0
- data/lib/rubykon/exceptions/exceptions.rb +1 -0
- data/lib/rubykon/exceptions/illegal_move_exception.rb +4 -0
- data/lib/rubykon/eye_detector.rb +27 -0
- data/lib/rubykon/game.rb +115 -0
- data/lib/rubykon/game_scorer.rb +62 -0
- data/lib/rubykon/game_state.rb +93 -0
- data/lib/rubykon/group.rb +99 -0
- data/lib/rubykon/group_tracker.rb +144 -0
- data/lib/rubykon/gtp_coordinate_converter.rb +25 -0
- data/lib/rubykon/move_validator.rb +55 -0
- data/lib/rubykon/version.rb +3 -0
- data/rubykon.gemspec +21 -0
- metadata +97 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b0c6f51cb33c58227a82783ff663d546397ef750
|
4
|
+
data.tar.gz: 2764e68e3b5a9ecc0ed8b1e62273201bf8748962
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8d3d477881d123c94556c396885002ff3e01708f6f147c8aa1ceb7b625189de8d6f59202393d8f9806e75ef7743ccbd204c6f37e136b5dbe26b7f631eb5067f3
|
7
|
+
data.tar.gz: e5b6acffc005f3e3ad2098cb7c3efc37b95253e8d58288a486d46f43b7c12dd047e80b528dd95ad47fd7d5ad4b510bcea5db48b6da333d1767dd9b810c2b4993
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rubykon
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.2.3
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
## 0.3 (2015-11-12)
|
2
|
+
Implement full bot together with Monte Carlo Tree Search, as well as a more coarse grained benchmarking tool to benchmark full MCTS runs. Also add a CLI. Mostly a feature release.
|
3
|
+
|
4
|
+
### Performance
|
5
|
+
* Faster and more reliable move selection
|
6
|
+
* optimize neighbours_of by enumerating possibilities, raw but effective
|
7
|
+
|
8
|
+
### Features
|
9
|
+
* Full Monte Carlo Tree Search implementation
|
10
|
+
* Basic CLI implementation
|
11
|
+
* benchmark/avg to do more coarse grained benchmarking
|
12
|
+
* More readable string board representation
|
13
|
+
* Added License (oops)
|
14
|
+
* Added CoC
|
15
|
+
|
16
|
+
### Bugfixes
|
17
|
+
* correctly count captures for score
|
18
|
+
|
19
|
+
## 0.2 (2015-10-03)
|
20
|
+
Rewrote data representation to be smaller and do way less allocations.
|
21
|
+
|
22
|
+
### Performance
|
23
|
+
* board is now a one dimensional array with each element corresponding to one cutting point (e.g. 1-1 is 0, 3-1 is 2, 1-2 is 19 (on 19x19).
|
24
|
+
* no more stone class, the board just stores the color of the stone there. Instead of `Stone` objects, an identifier (see above) and its color are passed around.
|
25
|
+
* what would be a ko move is now stored on the game, making checking faster and easier
|
26
|
+
* dupping games is easier thanks to simpler data structures
|
27
|
+
|
28
|
+
### Bugfixes
|
29
|
+
* captures are correctly included when scoring a game
|
30
|
+
|
31
|
+
## 0.1 (2015-09-25)
|
32
|
+
Basic game version, able to perform random playouts and benchmark those.
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
2
|
+
require "guard/rspec/dsl"
|
3
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
4
|
+
|
5
|
+
rspec = dsl.rspec
|
6
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
7
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
8
|
+
watch(rspec.spec_files)
|
9
|
+
|
10
|
+
ruby = dsl.ruby
|
11
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
12
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (C) 2012-2015 Tobias Pfeiffer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without restriction,
|
6
|
+
including without limitation the rights to use, copy, modify, merge,
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software,
|
8
|
+
and to permit persons to whom the Software is furnished to do so,
|
9
|
+
subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
15
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
16
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
17
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
18
|
+
SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
|
20
|
+
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Possible Improvements
|
2
|
+
|
3
|
+
Possible improvements to try out in the implementation :)
|
4
|
+
|
5
|
+
## Playout speed
|
6
|
+
|
7
|
+
### Board representation
|
8
|
+
* Reuse stones instead of creating new ones (would probably require a separate move class)
|
9
|
+
* just use symbols on the board instead of full fledged objects
|
10
|
+
* use a one dimensional array as the board (can map back to original using modulo etc.)
|
11
|
+
* use multiple bitmasks (or a bigger one) to represent board state (we have 3 states so a simple mask won't do)
|
12
|
+
* neighbour_colors methods
|
13
|
+
|
14
|
+
### Group
|
15
|
+
* remove references to stones/liberties from obsolete groups (when it is merged in another group or taken off the board)
|
16
|
+
* in liberties, don't point to stones but point to groups (be careful as no group is nil... and that then is not good with hash lookups)
|
17
|
+
|
18
|
+
### Move gen
|
19
|
+
* check self atari?
|
20
|
+
|
21
|
+
### Scoring
|
22
|
+
* use more efficient scoring algorithm (michi floodfill like?)
|
23
|
+
|
24
|
+
### Move generation
|
25
|
+
* sensibly choose moves and not just that random barrage... possiby using an Enumerator
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Rubykon [![Build Status](https://secure.travis-ci.org/PragTob/rubykon.png?branch=master)](https://travis-ci.org/PragTob/rubykon)[![Code Climate](https://codeclimate.com/github/PragTob/Rubykon.png)](https://codeclimate.com/github/PragTob/Rubykon)[![Test Coverage](https://codeclimate.com/github/PragTob/Rubykon/badges/coverage.svg)](https://codeclimate.com/github/PragTob/Rubykon/coverage)
|
2
|
+
A Go-Engine being built in Ruby.
|
3
|
+
|
4
|
+
### Status?
|
5
|
+
A naive very slow implementation of random playouts. Next up making it faster... or implementing the mcts bits. We'll see. Don't use and judge yet, some pretty inefficient stuff in here :D
|
6
|
+
|
7
|
+
### Why would you build a Go-Bot in Ruby?
|
8
|
+
Cause it's fun.
|
9
|
+
|
10
|
+
### Running truffle
|
11
|
+
|
12
|
+
Go ahead and [install from source](https://github.com/jruby/jruby/wiki/Truffle#from-source). Then you have to specify the graal VM when you execute something like this:
|
13
|
+
|
14
|
+
JAVACMD=~/dev/graalvm-jdk1.8.0/bin/java ../jruby/bin/jruby -X+T -e 'puts Truffle.graal?'
|
15
|
+
|
16
|
+
If this (adjusted to your paths) prints `true` then the setup is good so far.
|
17
|
+
|
18
|
+
Next up, install the jruby+truffle tool. Go into the jruby directory you checked out and make sure you use the same ruby version/gemset you want to use (this installs a gem). Then do:
|
19
|
+
|
20
|
+
tool/jt.rb install-tool
|
21
|
+
|
22
|
+
With this installed you can then setup graal in your repository (i.e. rubykon), this install gems etc.:
|
23
|
+
|
24
|
+
jruby+truffle setup
|
25
|
+
|
26
|
+
This should now still print true:
|
27
|
+
|
28
|
+
jruby+truffle --graal-path ../graalvm-jdk1.8.0/bin/java run --graal -- -e 'p Truffle.graal?'
|
29
|
+
|
30
|
+
You can then use it like this to run benchmarks et. al.:
|
31
|
+
|
32
|
+
jruby+truffle --graal-path ../graalvm-jdk1.8.0/bin/java run --graal -J-Xmx1500m benchmark/mcts.rb
|
33
|
+
|
34
|
+
The `-J-Xmx1500m` is important as truffle needs more heap space.
|
35
|
+
|
36
|
+
You can also run the specs via `jruby+truffle run -S rspec spec/`
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#! /bin/bash --login
|
2
|
+
|
3
|
+
script_name=$1
|
4
|
+
|
5
|
+
declare -A RUBY_TO_ARG=( ["2.2"]="" [jruby]="" [jruby-9]="--server -Xcompile.invokedynamic=true -J-Xmx1500m" ["rbx-2.5.8"]="" [jruby-1]="" [1.9.3]="" )
|
6
|
+
|
7
|
+
for ruby in "${!RUBY_TO_ARG[@]}"
|
8
|
+
do
|
9
|
+
echo Running $ruby with ${RUBY_TO_ARG[$ruby]}
|
10
|
+
rvm use $ruby
|
11
|
+
ruby -v
|
12
|
+
ruby ${RUBY_TO_ARG[$ruby]} $script_name
|
13
|
+
echo
|
14
|
+
echo
|
15
|
+
done
|
16
|
+
|
17
|
+
rvm use 2.2@rubykon
|
18
|
+
echo Running truffle graal with enough heap space
|
19
|
+
jruby+truffle run --graal -- -e "puts RUBY_DESCRIPTION"
|
20
|
+
jruby+truffle run --graal -J-Xmx1500m $script_name
|
21
|
+
echo
|
22
|
+
echo
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative '../lib/rubykon'
|
2
|
+
require_relative 'support/playout_help'
|
3
|
+
require_relative 'support/benchmark-ips'
|
4
|
+
|
5
|
+
Benchmark.ips do |benchmark|
|
6
|
+
benchmark.config time: 30, warmup: 60
|
7
|
+
|
8
|
+
benchmark.report '9x9 full playout (+ score)' do
|
9
|
+
full_playout_for 9
|
10
|
+
end
|
11
|
+
benchmark.report '13x13 full playout (+ score)' do
|
12
|
+
full_playout_for 13
|
13
|
+
end
|
14
|
+
benchmark.report '19x19 full playout (+ score)' do
|
15
|
+
full_playout_for 19
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../lib/rubykon'
|
2
|
+
require_relative '../lib/benchmark/avg'
|
3
|
+
|
4
|
+
Benchmark.avg do |benchmark|
|
5
|
+
game_state_9 = Rubykon::GameState.new Rubykon::Game.new(9)
|
6
|
+
game_state_13 = Rubykon::GameState.new Rubykon::Game.new(13)
|
7
|
+
game_state_19 = Rubykon::GameState.new Rubykon::Game.new(19)
|
8
|
+
mcts = MCTS::MCTS.new
|
9
|
+
|
10
|
+
benchmark.config warmup: 180, time: 120
|
11
|
+
|
12
|
+
benchmark.report "9x9 10_000 iterations" do
|
13
|
+
mcts.start game_state_9, 10_000
|
14
|
+
end
|
15
|
+
|
16
|
+
benchmark.report "13x13 2_000 iterations" do
|
17
|
+
mcts.start game_state_13, 2_000
|
18
|
+
end
|
19
|
+
|
20
|
+
benchmark.report "19x19 1_000 iterations" do
|
21
|
+
mcts.start game_state_19, 1_000
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative '../lib/rubykon'
|
2
|
+
require_relative 'support/playout_help'
|
3
|
+
require_relative 'support/benchmark-ips'
|
4
|
+
|
5
|
+
Benchmark.ips do |benchmark|
|
6
|
+
benchmark.report '9x9 playout' do
|
7
|
+
playout_for 9
|
8
|
+
end
|
9
|
+
benchmark.report '13x13 playout' do
|
10
|
+
playout_for 13
|
11
|
+
end
|
12
|
+
benchmark.report '19x19 playout' do
|
13
|
+
playout_for 19
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require_relative '../lib/rubykon'
|
2
|
+
require_relative 'support/playout_help'
|
3
|
+
require_relative 'support/benchmark-ips'
|
4
|
+
|
5
|
+
class Rubykon::GameState
|
6
|
+
public :plausible_move?
|
7
|
+
public :next_turn_color
|
8
|
+
end
|
9
|
+
|
10
|
+
class Rubykon::EyeDetector
|
11
|
+
public :candidate_eye_color
|
12
|
+
public :is_real_eye?
|
13
|
+
end
|
14
|
+
|
15
|
+
class Rubykon::MoveValidator
|
16
|
+
public :no_suicide_move?
|
17
|
+
end
|
18
|
+
|
19
|
+
class Rubykon::GroupTracker
|
20
|
+
public :color_to_neighbour
|
21
|
+
public :create_own_group
|
22
|
+
public :take_liberties_of_enemies
|
23
|
+
public :join_group_of_friendly_stones
|
24
|
+
public :add_liberties
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
Benchmark.ips do |benchmark|
|
29
|
+
empty_game = Rubykon::GameState.new Rubykon::Game.new 19
|
30
|
+
mid_game = empty_game.dup
|
31
|
+
200.times do
|
32
|
+
mid_game.set_move mid_game.generate_move
|
33
|
+
end
|
34
|
+
finished_game = playout_for(19).game_state
|
35
|
+
|
36
|
+
games = {
|
37
|
+
empty_game => 'empty game',
|
38
|
+
mid_game => 'mid game (200 moves played)',
|
39
|
+
finished_game => 'finished game'
|
40
|
+
}
|
41
|
+
|
42
|
+
games.each do |game_state, description|
|
43
|
+
|
44
|
+
game = game_state.game
|
45
|
+
board = game.board
|
46
|
+
|
47
|
+
benchmark.report "#{description}: finished?" do
|
48
|
+
game_state.finished?
|
49
|
+
end
|
50
|
+
|
51
|
+
benchmark.report "#{description}: generate_move" do
|
52
|
+
game_state.generate_move
|
53
|
+
end
|
54
|
+
|
55
|
+
color = game_state.next_turn_color
|
56
|
+
|
57
|
+
benchmark.report "#{description}: plausible_move?" do
|
58
|
+
identifier = rand(361)
|
59
|
+
game_state.plausible_move?(identifier, color)
|
60
|
+
end
|
61
|
+
|
62
|
+
validator = Rubykon::MoveValidator.new
|
63
|
+
|
64
|
+
benchmark.report "#{description}: valid?" do
|
65
|
+
identifier = rand(361)
|
66
|
+
validator.valid?(identifier, color, game)
|
67
|
+
end
|
68
|
+
|
69
|
+
benchmark.report "#{description}: no_suicide_move?" do
|
70
|
+
identifier = rand(361)
|
71
|
+
validator.no_suicide_move?(identifier, color, game)
|
72
|
+
end
|
73
|
+
|
74
|
+
eye_detector = Rubykon::EyeDetector.new
|
75
|
+
|
76
|
+
benchmark.report "#{description}: is_eye?" do
|
77
|
+
identifier = rand(361)
|
78
|
+
eye_detector.is_eye?(identifier, board)
|
79
|
+
end
|
80
|
+
|
81
|
+
benchmark.report "#{description}: candidate_eye_color" do
|
82
|
+
identifier = rand(361)
|
83
|
+
eye_detector.candidate_eye_color(identifier, board)
|
84
|
+
end
|
85
|
+
|
86
|
+
candidate_identifier = rand(361)
|
87
|
+
candidate_eye_color = eye_detector.candidate_eye_color(candidate_identifier, board)
|
88
|
+
|
89
|
+
benchmark.report "#{description}: is_real_eye?" do
|
90
|
+
eye_detector.is_real_eye?(candidate_identifier, board, candidate_eye_color)
|
91
|
+
end
|
92
|
+
|
93
|
+
benchmark.report "#{description}: diagonal_colors_of" do
|
94
|
+
identifier = rand(361)
|
95
|
+
board.diagonal_colors_of(identifier)
|
96
|
+
end
|
97
|
+
|
98
|
+
benchmark.report "#{description}: dup" do
|
99
|
+
game_state.dup
|
100
|
+
end
|
101
|
+
|
102
|
+
benchmark.report "#{description}: set_valid_move" do
|
103
|
+
game.dup.set_valid_move rand(361), color
|
104
|
+
end
|
105
|
+
|
106
|
+
benchmark.report "#{description}: assign" do
|
107
|
+
group_tracker = game.group_tracker.dup
|
108
|
+
group_tracker.assign(rand(361), color, board)
|
109
|
+
end
|
110
|
+
|
111
|
+
group_tracker = game.group_tracker.dup
|
112
|
+
|
113
|
+
benchmark.report "#{description}: color_to_neighbour" do
|
114
|
+
group_tracker.color_to_neighbour(board, rand(361))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
# more rigorous setup, values gotta be right so we can't just take
|
120
|
+
# our randomly played out boards. Also this doesn't make much sense
|
121
|
+
# on the empty or finished board.
|
122
|
+
|
123
|
+
game = Rubykon::Game.new(19)
|
124
|
+
group_tracker = game.group_tracker
|
125
|
+
|
126
|
+
stone1 = 55
|
127
|
+
liberties_1 = {
|
128
|
+
54 => Rubykon::Board::EMPTY,
|
129
|
+
56 => 56,
|
130
|
+
36 => Rubykon::Board::EMPTY,
|
131
|
+
74 => Rubykon::Board::EMPTY,
|
132
|
+
57 => Rubykon::Board::EMPTY
|
133
|
+
}
|
134
|
+
group_1 = Rubykon::Group.new(stone1, [stone1], liberties_1, 4)
|
135
|
+
group_tracker.stone_to_group[55] = 1
|
136
|
+
group_tracker.groups[1] = group_1
|
137
|
+
group_tracker.create_own_group(56)
|
138
|
+
|
139
|
+
stone2 = 33
|
140
|
+
liberties_2 = {
|
141
|
+
32 => Rubykon::Board::EMPTY,
|
142
|
+
34 => 34,
|
143
|
+
24 => Rubykon::Board::EMPTY,
|
144
|
+
54 => Rubykon::Board::EMPTY,
|
145
|
+
55 => Rubykon::Board::EMPTY
|
146
|
+
}
|
147
|
+
group_2 = Rubykon::Group.new(stone2, [stone2], liberties_2, 4)
|
148
|
+
group_tracker.stone_to_group[33] = 2
|
149
|
+
group_tracker.groups[2] = group_2
|
150
|
+
group_tracker.create_own_group(34)
|
151
|
+
|
152
|
+
# "small groups" as they have few liberties and stones assigned to them,
|
153
|
+
# groups can easily have 20+ stones and even more liberties, but that'd
|
154
|
+
# be even more setup :)
|
155
|
+
benchmark.report 'connecting two small groups' do
|
156
|
+
stones = [stone1, stone2]
|
157
|
+
my_stone = 44
|
158
|
+
group_tracker.dup.join_group_of_friendly_stones(stones, my_stone)
|
159
|
+
end
|
160
|
+
|
161
|
+
benchmark.report 'add_liberties' do
|
162
|
+
liberties = [24, 78, 36, 79]
|
163
|
+
group_tracker.add_liberties(liberties, stone1)
|
164
|
+
end
|
165
|
+
|
166
|
+
enemy_stones = [56, 34]
|
167
|
+
liberties = {
|
168
|
+
stone1 => stone1,
|
169
|
+
23 => Rubykon::Board::EMPTY,
|
170
|
+
73 => Rubykon::Board::EMPTY
|
171
|
+
}
|
172
|
+
enemy_group_1 = Rubykon::Group.new(enemy_stones[0], [enemy_stones[0]], liberties, 2)
|
173
|
+
enemy_group_2 = Rubykon::Group.new(enemy_stones[1], [enemy_stones[1]], liberties.dup, 2)
|
174
|
+
group_tracker.stone_to_group[enemy_stones[0]] = 3
|
175
|
+
group_tracker.groups[3] = enemy_group_1
|
176
|
+
|
177
|
+
group_tracker.stone_to_group[enemy_stones[1]] = 4
|
178
|
+
group_tracker.groups[4] = enemy_group_2
|
179
|
+
|
180
|
+
|
181
|
+
# Does not trigger enemy_group.caught? and removing the group.
|
182
|
+
# That doesn't happen THAT often and it'd require to setup an according
|
183
|
+
# board (with each test run)
|
184
|
+
benchmark.report 'remove liberties of enemies' do
|
185
|
+
|
186
|
+
group_tracker.dup.take_liberties_of_enemies(enemy_stones, stone1, game.board, :black)
|
187
|
+
end
|
188
|
+
end
|