neuro_gammon 0.7.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.
@@ -0,0 +1,46 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+ require 'neuro_gammon/board'
6
+
7
+ module NeuroGammon
8
+ module BoardTools
9
+
10
+ def reverse_state state
11
+ #first reverse all the colours (except the bar)
12
+ new_state=[]
13
+ new_state[0]=[]
14
+ new_state[1]=[]
15
+ state[0].each_index do |i|
16
+ new_state[0] << state[0][i]*-1
17
+ end
18
+
19
+ state[1].each_index do |i|
20
+ new_state[1] << state[1][i]
21
+ end
22
+
23
+ new_state[0].reverse!
24
+ new_state[1].reverse!
25
+ return new_state
26
+ end
27
+
28
+ def test_for_gammon state,colour
29
+ on_bar= (colour==Board::WHITE ? state[1][0] != 0 : state[1][1] != 0)
30
+ on_board= state[0].find() {|x| x*colour>0}
31
+ return false if (on_board or on_bar)
32
+ blank=state.flatten.find {|x| x!=0}.nil?
33
+ return false if blank
34
+ if state[1].find() {|x| x!=0}
35
+ return false if colour==Board::WHITE ? state[1][0]!=0 : state[1][1]!=0
36
+ end
37
+
38
+ range=colour==Board::WHITE ? (18..23) : (0..5)
39
+ return range.find(){|x| state[0][x]*(-colour) > 0} == nil
40
+
41
+ end
42
+
43
+
44
+
45
+ end
46
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # Copyright (C) 2008 Stuart Owen
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module NeuroGammon
18
+ class Dice
19
+
20
+ def initialize
21
+ @values=[]
22
+ roll
23
+ end
24
+
25
+ def roll
26
+ @values=[]
27
+ @values << rand(6)+1
28
+ @values << rand(6)+1
29
+ check_state
30
+ end
31
+
32
+ def double?
33
+ @double
34
+ end
35
+
36
+ def [] x
37
+ return @values[x]
38
+ end
39
+
40
+ def to_a
41
+ @values
42
+ end
43
+
44
+ def each
45
+ to_a.each {|obj| yield(obj)}
46
+ end
47
+
48
+ def use! value
49
+ #TODO is there a method on Array to delete one value only?
50
+ i=@values.index(value)
51
+ raise Exception.new("No value " << value << " in dice " << self.to_a.to_s) if i==nil
52
+ @values.delete_at(i)
53
+ end
54
+
55
+ def check_state
56
+ @double=(@values[0]==@values[1])
57
+ @values = double? ? Array.new(4,self[0]) : @values
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,116 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+ require 'neuro_gammon/base_neural_player'
6
+ require 'rubygems'
7
+ require 'ruby_fann/neural_network'
8
+ require 'neuro_gammon/board_tools'
9
+ require 'tempfile'
10
+
11
+ module NeuroGammon
12
+
13
+ class FannPlayer < BaseNeuralPlayer
14
+ include BoardTools
15
+
16
+ def initialize
17
+ super
18
+ end
19
+
20
+ def cascade_learn! games
21
+ corpus=generate_corpus games
22
+ training_data=RubyFann::TrainData.new(:inputs=>corpus[0], :desired_outputs=>corpus[1])
23
+ @net.cascadetrain_on_data(training_data,5,5,0.0004)
24
+ store_net_data
25
+ end
26
+
27
+ def learn! games
28
+ corpus=generate_corpus games
29
+ training_data=RubyFann::TrainData.new(:inputs=>corpus[0], :desired_outputs=>corpus[1])
30
+ @net.train_on_data(training_data,1500,30,0.0004)
31
+ store_net_data
32
+ end
33
+
34
+ def to_yaml_properties
35
+ ["@n_inputs","@n_outputs","@hidden_layers","@learning_rate","@net_data"]
36
+ end
37
+
38
+ def self.from_yaml yaml
39
+ obj=YAML::load(yaml)
40
+ class << obj
41
+ def pub_read_net_data
42
+ read_net_data
43
+ end
44
+ end
45
+ obj.pub_read_net_data
46
+ obj
47
+ end
48
+
49
+ #see store_net_data
50
+ def read_net_data
51
+ f=Tempfile.new("fann-net-in")
52
+ f.write(@net_data)
53
+ f.flush
54
+ @net=RubyFann::Standard.new(:filename=>f.path)
55
+ f.close
56
+ end
57
+
58
+ protected
59
+
60
+ def run_net input
61
+ return @net.run(input)
62
+ end
63
+
64
+ def evaluate_output output,board,dice,colour
65
+ score=0
66
+ if (colour==Board::WHITE)
67
+ score = output[0]-output[2]
68
+ score += 2*(output[1]-output[3])
69
+ else
70
+ score = output[2]-output[0]
71
+ score += 2*(output[3]-output[1])
72
+ end
73
+ return score
74
+ end
75
+
76
+ def generate_input_pattern board_state
77
+ pattern=[]
78
+ board_state.flatten.each do |v|
79
+ pattern << v
80
+ end
81
+ return pattern
82
+ end
83
+
84
+ def generate_output_pattern board_state,game
85
+ result=[-1,-1,-1,-1]
86
+ if test_for_gammon(board_state,game.winner_colour)
87
+ game.winner_colour==Board::WHITE ? result[1]=1 : result[3]=1
88
+ end
89
+ game.winner_colour==Board::WHITE ? result[0]=1 : result[2]=1
90
+ return result
91
+ end
92
+
93
+ def init_net
94
+ @n_inputs=26
95
+ @n_outputs=4
96
+ @hidden_layers=[300]
97
+ @learning_rate=0.4
98
+ @net=RubyFann::Standard.new(:num_inputs=>n_inputs,:hidden_neurons=>hidden_layers,:num_outputs=>n_outputs)
99
+ # @net.set_training_algorithm :incremental
100
+ @net.set_learning_rate(learning_rate)
101
+ store_net_data
102
+ end
103
+
104
+ #this is required to store the binary of the @net, for YAML conversion.
105
+ #since ruby-fann only seems to like saving to and from a file, this is created by saving the net to a tmp file, then reading back in
106
+ def store_net_data
107
+ f=Tempfile.new("fann-net-out")
108
+ @net.save(f.path)
109
+ f2=open(f.path)
110
+ @net_data=f2.read
111
+ f.close
112
+ end
113
+
114
+
115
+ end
116
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+ module NeuroGammon
6
+ class Game
7
+ attr_accessor :winner_colour
8
+ attr_reader :board_states
9
+ attr_reader :dice_states
10
+ attr_reader :moves
11
+ attr_reader :black_player
12
+ attr_reader :white_player
13
+ attr_reader :colour_moved
14
+
15
+ def initialize white_player,black_player
16
+ @black_player=black_player
17
+ @white_player=white_player
18
+ @board_states=[]
19
+ @moves=[]
20
+ @dice_states=[]
21
+ @colour_moved=[]
22
+ end
23
+
24
+ def add_board_state state
25
+ self.board_states << Marshal.load(Marshal.dump(state))
26
+ end
27
+
28
+ def add_dice_state dice
29
+ dice_states << Marshal.load(Marshal.dump(dice))
30
+ end
31
+
32
+ def add_move move
33
+ moves << Marshal.load(Marshal.dump(move))
34
+ #TODO test for a valid range for coming on and bearing off, throw Exceptions otherwise, and harden unit tests for this
35
+ if move[0]==-1 #come on
36
+ colour_moved << (move[1]>17 ? Board::WHITE : Board::BLACK)
37
+ elsif move[1]==-1 #bear off
38
+ colour_moved << (move[0]<=5 ? Board::WHITE : Board::BLACK)
39
+ else
40
+ colour_moved << (move[0]-move[1]>0 ? Board::WHITE : Board::BLACK)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,75 @@
1
+ #
2
+ # Copyright (C) 2008 Stuart Owen
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'neuro_gammon/board'
18
+ require 'neuro_gammon/dice'
19
+ require 'neuro_gammon/game'
20
+
21
+ module NeuroGammon
22
+ class GameEngine
23
+ attr_accessor :board
24
+ attr_accessor :dice
25
+ attr_accessor :colour
26
+ attr_reader :black_player,:white_player
27
+ attr_reader :game_data
28
+ attr_reader :winner
29
+
30
+ def initialize white_player,black_player
31
+ @board=Board.new
32
+ @dice=Dice.new
33
+ @black_player=black_player
34
+ @white_player=white_player
35
+ @colour=Board::WHITE
36
+ end
37
+
38
+ def play_game
39
+ game=Game.new white_player,black_player
40
+ while !(board.piece_count(Board::BLACK)==0 || board.piece_count(Board::WHITE)==0)
41
+ dice.roll
42
+ while dice.to_a.size>0
43
+ game.add_dice_state(dice.to_a)
44
+ game.add_board_state(board.state) #TODO final board state isn't recorded (i.e. always ends with 1 piece remainining)
45
+ player=player_to_move
46
+ move=player.suggest_move(board,dice,@colour)
47
+ if (move!=nil)
48
+ board.move!(move,colour,dice)
49
+ game.add_move(move)
50
+ else #can't move
51
+ #TODO this needs some more careful thought, and unit tests. Do we want to record when a move couldn't be made??
52
+ game.dice_states.delete(game.dice_states.last)
53
+ game.board_states.delete(game.board_states.last)
54
+ break
55
+ end
56
+ end
57
+ toggle_player
58
+ end
59
+ game.winner_colour=board.winner
60
+ @winner=(board.winner==Board::WHITE ? white_player : black_player)
61
+
62
+ return game
63
+ end
64
+
65
+ private
66
+
67
+ def player_to_move
68
+ @colour==Board::BLACK ? black_player : white_player
69
+ end
70
+
71
+ def toggle_player
72
+ @colour=(@colour==Board::BLACK ? Board::WHITE : Board::BLACK)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,25 @@
1
+ #
2
+ # To change this template, choose Tools | Templates
3
+ # and open the template in the editor.
4
+
5
+ #always picks the first move avaliable - just for testing really
6
+
7
+ require 'neuro_gammon/base_player'
8
+
9
+ module NeuroGammon
10
+ class LazyPlayer < BasePlayer
11
+ def initialize
12
+ super
13
+ end
14
+
15
+ def suggest_move board,dice,colour
16
+ valid=board.valid_moves(colour, dice)
17
+ if (valid.size>0)
18
+ move=valid.first
19
+ return move
20
+ else
21
+ return nil
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ #
2
+ # Copyright (C) 2008 Stuart Owen
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'neuro_gammon/board'
18
+ require 'neuro_gammon/dice'
19
+ require 'neuro_gammon/base_player'
20
+
21
+ module NeuroGammon
22
+ class RandomPlayer < BasePlayer
23
+ def initialize
24
+ super
25
+
26
+ end
27
+
28
+ def suggest_move board,dice,colour
29
+ valid=board.valid_moves(colour, dice)
30
+ if (valid.size>0)
31
+ move=valid[rand(valid.size)]
32
+ return move
33
+ else
34
+ return nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1 @@
1
+ (in C:/Users/sowen/Documents/NetBeansProjects/NeuralGammon)
@@ -0,0 +1,6 @@
1
+ javac.classpath=
2
+ main.file=main.rb
3
+ ruby.includejava=false
4
+ source.encoding=UTF-8
5
+ src.dir=lib
6
+ test.src.dir=test
@@ -0,0 +1,15 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project xmlns="http://www.netbeans.org/ns/project/1">
3
+ <type>org.netbeans.modules.ruby.rubyproject</type>
4
+ <configuration>
5
+ <data xmlns="http://www.netbeans.org/ns/ruby-project/1">
6
+ <name>NeuralGammon</name>
7
+ <source-roots>
8
+ <root id="src.dir"/>
9
+ </source-roots>
10
+ <test-roots>
11
+ <root id="test.src.dir"/>
12
+ </test-roots>
13
+ </data>
14
+ </configuration>
15
+ </project>
@@ -0,0 +1,82 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{neuro_gammon}
8
+ s.version = "0.7.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Stu"]
12
+ s.date = %q{2010-08-04}
13
+ s.email = %q{sowen@stuzart.org}
14
+ s.extra_rdoc_files = [
15
+ "README"
16
+ ]
17
+ s.files = [
18
+ ".gitignore",
19
+ ".hgignore",
20
+ ".loadpath",
21
+ ".project",
22
+ "README",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "fred.net",
26
+ "lib/main.rb",
27
+ "lib/main2.rb",
28
+ "lib/neuro_gammon.rb",
29
+ "lib/neuro_gammon/base_neural_player.rb",
30
+ "lib/neuro_gammon/base_player.rb",
31
+ "lib/neuro_gammon/best_of_tournament_engine.rb",
32
+ "lib/neuro_gammon/board.rb",
33
+ "lib/neuro_gammon/board_tools.rb",
34
+ "lib/neuro_gammon/dice.rb",
35
+ "lib/neuro_gammon/fann_player.rb",
36
+ "lib/neuro_gammon/game.rb",
37
+ "lib/neuro_gammon/game_engine.rb",
38
+ "lib/neuro_gammon/lazy_player.rb",
39
+ "lib/neuro_gammon/random_player.rb",
40
+ "nbproject/private/rake-t.txt",
41
+ "nbproject/project.properties",
42
+ "nbproject/project.xml",
43
+ "neuro_gammon.gemspec",
44
+ "test/test_base_player.rb",
45
+ "test/test_board.rb",
46
+ "test/test_board_tools.rb",
47
+ "test/test_dice.rb",
48
+ "test/test_fann_player.rb",
49
+ "test/test_game.rb",
50
+ "test/test_game_engine.rb"
51
+ ]
52
+ s.rdoc_options = ["--charset=UTF-8"]
53
+ s.require_paths = ["lib"]
54
+ s.rubygems_version = %q{1.3.7}
55
+ s.summary = %q{Neural Net Backgammon utility library - just a bit of messing about - nothing serious.}
56
+ s.test_files = [
57
+ "test/test_game_engine.rb",
58
+ "test/test_dice.rb",
59
+ "test/test_base_player.rb",
60
+ "test/test_board_tools.rb",
61
+ "test/test_fann_player.rb",
62
+ "test/test_game.rb",
63
+ "test/test_board.rb"
64
+ ]
65
+
66
+ if s.respond_to? :specification_version then
67
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
68
+ s.specification_version = 3
69
+
70
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
71
+ s.add_runtime_dependency(%q<uuidtools>, [">= 2.1.1"])
72
+ s.add_runtime_dependency(%q<ruby-fann>, [">= 1.1.3"])
73
+ else
74
+ s.add_dependency(%q<uuidtools>, [">= 2.1.1"])
75
+ s.add_dependency(%q<ruby-fann>, [">= 1.1.3"])
76
+ end
77
+ else
78
+ s.add_dependency(%q<uuidtools>, [">= 2.1.1"])
79
+ s.add_dependency(%q<ruby-fann>, [">= 1.1.3"])
80
+ end
81
+ end
82
+