neuro_gammon 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+