deepbeige 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/arena.rb +6 -0
- data/deep_beige.rb +213 -0
- data/deepbeige.gemspec +73 -0
- data/game.rb +18 -0
- data/human.rb +14 -0
- data/lib/deepbeige.rb +0 -0
- data/main.rb +114 -0
- data/match.rb +43 -0
- data/neural_net.rb +174 -0
- data/node.rb +106 -0
- data/noughts_and_crosses.rb +141 -0
- data/pick_a_number.rb +97 -0
- data/player.rb +4 -0
- data/population/best.txt +20 -0
- data/table.rb +22 -0
- data/test/helper.rb +10 -0
- data/test/test_deepbeige.rb +7 -0
- data/tournament.rb +1 -0
- data/ui/Rakefile +5 -0
- data/ui/config/build.yml +8 -0
- data/ui/lib/application.rb +47 -0
- data/ui/lib/menu.rb +32 -0
- data/ui/resources/DeepBeige.icns +0 -0
- metadata +106 -0
data/neural_net.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'uuid'
|
3
|
+
require 'node'
|
4
|
+
|
5
|
+
class NeuralNet
|
6
|
+
attr_accessor :input, :id
|
7
|
+
attr_reader :network
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@id = UUID.new.to_s.split(':')[1].chop
|
11
|
+
@network = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate
|
15
|
+
#we expect to find one input value in input for each of our input nodes
|
16
|
+
input_nodes = @network.first
|
17
|
+
|
18
|
+
i = 0
|
19
|
+
input_nodes.each do |node|
|
20
|
+
node.input_value = @input[i]
|
21
|
+
i += 1
|
22
|
+
end
|
23
|
+
|
24
|
+
@network.each do |tier|
|
25
|
+
tier.each do |node|
|
26
|
+
node.evaluate
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
self.output
|
31
|
+
end
|
32
|
+
|
33
|
+
def output
|
34
|
+
@network.last
|
35
|
+
end
|
36
|
+
|
37
|
+
def output_value
|
38
|
+
value = 0
|
39
|
+
self.output.each do |node|
|
40
|
+
value += node.output_value
|
41
|
+
end
|
42
|
+
value
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate inputs, outputs, tiers
|
46
|
+
@network = []
|
47
|
+
input_nodes = []
|
48
|
+
inputs.times do
|
49
|
+
input_nodes << Node.new
|
50
|
+
end
|
51
|
+
if input_nodes.count > 0
|
52
|
+
@network << input_nodes
|
53
|
+
end
|
54
|
+
(tiers - 2).times do
|
55
|
+
tier = []
|
56
|
+
10.times do
|
57
|
+
tier << Node.new
|
58
|
+
end
|
59
|
+
@network << tier
|
60
|
+
end
|
61
|
+
|
62
|
+
output_nodes = []
|
63
|
+
outputs.times do
|
64
|
+
output_nodes << Node.new
|
65
|
+
end
|
66
|
+
if output_nodes.count >0
|
67
|
+
@network << output_nodes
|
68
|
+
end
|
69
|
+
link_tiers
|
70
|
+
end
|
71
|
+
|
72
|
+
def fingerprint
|
73
|
+
topline = ""
|
74
|
+
fingerprint = ""
|
75
|
+
@network.each do |tier|
|
76
|
+
topline << "#{tier.count},"
|
77
|
+
tier.each do |node|
|
78
|
+
fingerprint << node.fingerprint
|
79
|
+
end
|
80
|
+
end
|
81
|
+
topline.chop + "\n" + fingerprint
|
82
|
+
end
|
83
|
+
|
84
|
+
def reload fingerprint
|
85
|
+
#fingerprint contains an array of strings
|
86
|
+
i = 0
|
87
|
+
tiers = fingerprint[i].split(',').to_a
|
88
|
+
i += 1
|
89
|
+
|
90
|
+
@network = []
|
91
|
+
tiers.each do |tier|
|
92
|
+
nodes = []
|
93
|
+
tier.to_i.times do
|
94
|
+
node_fingerprint = fingerprint[i]
|
95
|
+
i += 1
|
96
|
+
node = Node.new
|
97
|
+
node.reload node_fingerprint
|
98
|
+
nodes << node
|
99
|
+
end
|
100
|
+
@network << nodes
|
101
|
+
end
|
102
|
+
|
103
|
+
link_tiers
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
#Nets can make small changes (mutations) to themselves
|
108
|
+
def mutate
|
109
|
+
#for the time being we won't take on
|
110
|
+
#the ability to mutate the number of
|
111
|
+
#nodes and their configuration
|
112
|
+
#focussing instead on simple node weight mutation
|
113
|
+
@network.each do |tier|
|
114
|
+
tier.each do |node|
|
115
|
+
node.mutate
|
116
|
+
end
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def clone
|
122
|
+
clone = NeuralNet.new
|
123
|
+
#iterate in through each tier
|
124
|
+
@network.each do |tier|
|
125
|
+
nodes = []
|
126
|
+
tier.each do |node|
|
127
|
+
cloned_node = node.clone
|
128
|
+
cloned_node.detatch_all_forward_nodes
|
129
|
+
nodes << cloned_node
|
130
|
+
end
|
131
|
+
clone.network << nodes
|
132
|
+
end
|
133
|
+
#now relink the network
|
134
|
+
clone.link_tiers
|
135
|
+
#and send back our clone
|
136
|
+
clone
|
137
|
+
end
|
138
|
+
|
139
|
+
def save_to_file file
|
140
|
+
File.open(file, 'w') do |f|
|
141
|
+
f.puts id
|
142
|
+
f.write(self.fingerprint)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def load_from_file file
|
147
|
+
fingerprint = []
|
148
|
+
File.open(file, 'r') do |f|
|
149
|
+
@id = f.gets.chop
|
150
|
+
while line = f.gets do
|
151
|
+
fingerprint << line
|
152
|
+
end
|
153
|
+
end
|
154
|
+
reload fingerprint
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
def link_tiers
|
159
|
+
#first cut lets link every node on a tier to each node on the subsequent tier
|
160
|
+
i = 1
|
161
|
+
@network.each do |tier|
|
162
|
+
if i < @network.count
|
163
|
+
tier.each do |node|
|
164
|
+
j = 0
|
165
|
+
@network[i].each do |next_node|
|
166
|
+
node.attach_forward_node next_node, j
|
167
|
+
j += 1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
i +=1
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/node.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
class Node
|
2
|
+
attr_reader :forward_nodes, :output_value
|
3
|
+
attr_accessor :input_value, :deviation, :weights
|
4
|
+
def initialize
|
5
|
+
@forward_nodes = {}
|
6
|
+
@weights =[]
|
7
|
+
@input_value = 0
|
8
|
+
@deviation = rand / 4
|
9
|
+
end
|
10
|
+
#take our input value, pass it through our sigmoid function (tanh)
|
11
|
+
#and then pass on our output value to each of our forward nodes
|
12
|
+
def evaluate
|
13
|
+
@output_value = Math.tanh(@input_value)
|
14
|
+
#p "output value #{@output_value}"
|
15
|
+
@forward_nodes.each do |node, weight|
|
16
|
+
#p "weight #{weight} old input #{node.input_value}"
|
17
|
+
node.input_value += @output_value * weight
|
18
|
+
#p "new input #{node.input_value}"
|
19
|
+
end
|
20
|
+
@input_value = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def attach_forward_node node, sequence
|
24
|
+
if @weights.count <= sequence
|
25
|
+
@weights << rand
|
26
|
+
end
|
27
|
+
|
28
|
+
@forward_nodes[node] = @weights[sequence]
|
29
|
+
end
|
30
|
+
|
31
|
+
def detatch_all_forward_nodes
|
32
|
+
@forward_nodes = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def mutate
|
36
|
+
new_weights = []
|
37
|
+
@weights.each do |weight|
|
38
|
+
new_weights << gaussian_random * @deviation + weight # new_random_number = gaussian_rand * standard_deviation + average
|
39
|
+
end
|
40
|
+
@weights = new_weights
|
41
|
+
if @forward_values
|
42
|
+
i = 0
|
43
|
+
@forward_values.each do |key,value|
|
44
|
+
@forward_values[key] = @weights[i]
|
45
|
+
i += 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
#and now mutate the deviation
|
50
|
+
@deviation = (gaussian_random * @deviation)/2 + @deviation
|
51
|
+
end
|
52
|
+
|
53
|
+
def breed
|
54
|
+
end
|
55
|
+
|
56
|
+
def clone
|
57
|
+
clone = Node.new
|
58
|
+
|
59
|
+
clone.deviation = self.deviation
|
60
|
+
@weights.each do |weight|
|
61
|
+
clone.weights << weight
|
62
|
+
end
|
63
|
+
|
64
|
+
#being pure we clone the forward node refs as well
|
65
|
+
#although in practice these are about to be updated with new nodes
|
66
|
+
self.forward_nodes.each do |key,value|
|
67
|
+
clone.forward_nodes[key] = value
|
68
|
+
end
|
69
|
+
|
70
|
+
clone
|
71
|
+
end
|
72
|
+
|
73
|
+
def fingerprint
|
74
|
+
fingerprint = "#{@deviation}:"
|
75
|
+
@weights.each do |weight|
|
76
|
+
fingerprint += "#{weight.to_s},"
|
77
|
+
end
|
78
|
+
fingerprint = fingerprint.chop + "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
def reload fingerprint
|
82
|
+
self.detatch_all_forward_nodes
|
83
|
+
@weights = []
|
84
|
+
self.deviation = fingerprint.split(':')[0].to_f
|
85
|
+
if fingerprint.split(":").count == 2
|
86
|
+
fingerprint.split(":")[1].split(',').each do |weight|
|
87
|
+
@weights << weight.to_f
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def gaussian_random
|
94
|
+
u1 = u2 = w = g1 = g2 = 0 # declare
|
95
|
+
begin
|
96
|
+
u1 = 2 * rand - 1
|
97
|
+
u2 = 2 * rand - 1
|
98
|
+
w = u1 * u1 + u2 * u2
|
99
|
+
end while w >= 1
|
100
|
+
|
101
|
+
w = Math::sqrt( ( -2 * Math::log(w)) / w )
|
102
|
+
g2 = u1 * w;
|
103
|
+
g1 = u2 * w;
|
104
|
+
# g1 is returned
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'game'
|
2
|
+
class NoughtsAndCrosses < Game
|
3
|
+
|
4
|
+
attr_reader :winner, :move_list, :next_player
|
5
|
+
attr_accessor :quiet, :verbose
|
6
|
+
def initialize
|
7
|
+
@position = {:A1 => 0, :B1 => 0, :C1 => 0, :A2 => 0, :B2 => 0, :C2 => 0, :A3 => 0, :B3 => 0, :C3 => 0}
|
8
|
+
@next_player = 0
|
9
|
+
@move_list = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def name
|
13
|
+
"NoughtsAndCrosses"
|
14
|
+
end
|
15
|
+
|
16
|
+
def show_board
|
17
|
+
board = " - - - \n"
|
18
|
+
i = 0
|
19
|
+
@position.each do |move|
|
20
|
+
i += 1
|
21
|
+
board += "|"
|
22
|
+
if move[1] == 0
|
23
|
+
board += " "
|
24
|
+
elsif move[1] == 1
|
25
|
+
board += "X"
|
26
|
+
else
|
27
|
+
board += "O"
|
28
|
+
end
|
29
|
+
|
30
|
+
if i == 3
|
31
|
+
board += "|\n _ _ _\n"
|
32
|
+
i = 0
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
board
|
37
|
+
end
|
38
|
+
|
39
|
+
def legal_moves
|
40
|
+
legal_moves = []
|
41
|
+
@position.each do |move|
|
42
|
+
if move[1] == 0
|
43
|
+
legal_moves << move[0].to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
legal_moves
|
47
|
+
end
|
48
|
+
|
49
|
+
def play_move(player, move)
|
50
|
+
sym_move = move.to_sym
|
51
|
+
unless player == @next_player
|
52
|
+
display "not player #{player}'s turn"
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
unless @position[sym_move] == 0
|
56
|
+
display "illegal move by player #{player}"
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
if won?
|
60
|
+
display "player #{winner} has already won"
|
61
|
+
return false
|
62
|
+
elsif drawn?
|
63
|
+
display "game was drawn"
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
@move_list << [player,move]
|
68
|
+
|
69
|
+
if player == 1
|
70
|
+
@position[sym_move] = 1
|
71
|
+
else
|
72
|
+
@position[sym_move] = -1
|
73
|
+
end
|
74
|
+
if won?
|
75
|
+
display "player #{player} wins!"
|
76
|
+
@winner = player
|
77
|
+
elsif drawn?
|
78
|
+
display "game drawn"
|
79
|
+
end
|
80
|
+
|
81
|
+
if @next_player == 1
|
82
|
+
@next_player = 0
|
83
|
+
else
|
84
|
+
@next_player = 1
|
85
|
+
end
|
86
|
+
if @verbose
|
87
|
+
display show_board
|
88
|
+
display @position
|
89
|
+
end
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
def current_position
|
94
|
+
return @position
|
95
|
+
end
|
96
|
+
|
97
|
+
def won?
|
98
|
+
if @position[:A1] != 0
|
99
|
+
if @position[:A1] == @position[:A2] && @position[:A2] == @position[:A3]
|
100
|
+
return true
|
101
|
+
elsif @position[:A1] == @position[:B2] && @position[:B2] == @position[:C3]
|
102
|
+
return true
|
103
|
+
elsif @position[:A1] == @position[:B1] && @position[:B1] == @position[:C1]
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
if @position[:B1] != 0
|
108
|
+
if @position[:B1] == @position[:B2] && @position[:B2] == @position[:B3]
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
if @position[:C1] != 0
|
113
|
+
if @position[:C1] == @position[:C2] && @position[:C2] == @position[:C3]
|
114
|
+
return true
|
115
|
+
elsif @position[:C1] == @position[:B2] && @position[:B2] == @position[:A3]
|
116
|
+
return true
|
117
|
+
end
|
118
|
+
end
|
119
|
+
if @position[:A2] != 0
|
120
|
+
if @position[:A2] == @position[:B2] && @position[:B2] == @position[:C2]
|
121
|
+
return true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
if @position[:A3] != 0
|
125
|
+
if @position[:A3] == @position[:B3] && @position[:B3] == @position[:C3]
|
126
|
+
return true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
false
|
130
|
+
end
|
131
|
+
|
132
|
+
def drawn?
|
133
|
+
unless legal_moves.count == 0
|
134
|
+
return false
|
135
|
+
end
|
136
|
+
if won?
|
137
|
+
return false
|
138
|
+
end
|
139
|
+
true
|
140
|
+
end
|
141
|
+
end
|
data/pick_a_number.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'game'
|
2
|
+
|
3
|
+
class PickANumber < Game
|
4
|
+
|
5
|
+
attr_reader :winner, :move_list, :next_player
|
6
|
+
attr_accessor :quiet, :verbose
|
7
|
+
def initialize
|
8
|
+
@position = {"1" => 0,"2" => 0, "3" => 0}
|
9
|
+
@next_player = 0
|
10
|
+
@move_list = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
"PickANumber"
|
15
|
+
end
|
16
|
+
|
17
|
+
def legal_moves
|
18
|
+
legal_moves = []
|
19
|
+
@position.each do |move|
|
20
|
+
if move[1] == 0
|
21
|
+
legal_moves << move[0].to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
legal_moves
|
25
|
+
end
|
26
|
+
|
27
|
+
def show_board
|
28
|
+
board = "Please Pick from the following Numbers:\n"
|
29
|
+
|
30
|
+
@position.each do |move|
|
31
|
+
if move[1] == 0
|
32
|
+
board += "#{move[0]},"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
board +"\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
def play_move(player, move)
|
39
|
+
unless player == @next_player
|
40
|
+
display "not player #{player}'s turn"
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
unless @position[move] == 0
|
44
|
+
display "illegal move by player #{player}"
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
if won?
|
48
|
+
display "player #{winner} has already won"
|
49
|
+
return false
|
50
|
+
elsif drawn?
|
51
|
+
display "game was drawn"
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
|
55
|
+
@move_list << [player,move]
|
56
|
+
|
57
|
+
if player == 1
|
58
|
+
@position[move] = 1
|
59
|
+
else
|
60
|
+
@position[move] = -1
|
61
|
+
end
|
62
|
+
if won?
|
63
|
+
display "player #{player} wins!"
|
64
|
+
@winner = player
|
65
|
+
elsif drawn?
|
66
|
+
display "game drawn"
|
67
|
+
end
|
68
|
+
|
69
|
+
if @next_player == 1
|
70
|
+
@next_player = 0
|
71
|
+
else
|
72
|
+
@next_player = 1
|
73
|
+
end
|
74
|
+
if @verbose
|
75
|
+
display @position
|
76
|
+
end
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def current_position
|
81
|
+
return @position
|
82
|
+
end
|
83
|
+
|
84
|
+
def won?
|
85
|
+
if @position["3"] != 0
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
91
|
+
def drawn?
|
92
|
+
if @position["2"] != 0
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
false
|
96
|
+
end
|
97
|
+
end
|
data/player.rb
ADDED
data/population/best.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
9,9,1
|
2
|
+
1.9888959877114751:1.1713694063264486,0.2131550943633837,-1.4389857774549193,-2.3761064404471623,1.4125388307828852,1.7962657856192086,3.98673652586089,-0.710563267563527,-4.693733927431782
|
3
|
+
-0.5537104997469708:4.353881179541187,-3.7749306975412615,3.9092173844936307,-1.5574066495330294,-1.9867317023123854,2.4016056161982515,0.19093366894771083,2.583574894805702,-0.7814428807197851
|
4
|
+
-3.718402720378594:0.21375589104869108,-0.5952570280952669,3.163222183572866,-2.0666315462205604,1.8466894347641758,2.5737190736563127,0.09083797921525782,-1.7152005515495705,-1.9147906116034963
|
5
|
+
-1.1774113223565559:0.9684176432281265,1.2633322113651906,0.9105205858738161,0.9952880033669778,2.428832023642873,1.1378171183057832,1.2346794709653666,1.614577823722882,-0.5727461748243102
|
6
|
+
-4.240095691598348:5.619686966308787,2.496099436318761,4.236594219999468,-4.089909848411157,-8.583782402447191,7.909784634413837,3.709149525845639,-0.1221209138110586,-1.7739191236891263
|
7
|
+
-0.45644848049775455:0.7525477703501269,0.4416094908434702,0.5151634042799693,0.6494784464984971,-0.21066540646710413,0.9328293326003011,0.4934365894119864,-0.5075989699963754,0.2534248521563641
|
8
|
+
2.000438515839109:-0.18715599814429473,-4.079321241532847,3.0082374216290035,1.6554995963981736,1.2134208468740142,-0.7234607792621472,0.9168360804604427,-0.5284806911574953,3.6316017295618748
|
9
|
+
0.9920544697574241:-0.15133215482382756,0.9977877837645512,-0.06456539820799904,-1.0288035250624143,0.9008138652841244,0.6826185088601288,0.8553527568600676,0.21975379677495055,1.0495630337690707
|
10
|
+
3.8437737235828924:-2.429075885974877,3.0482320934712477,-9.512832982617503,-8.273686259303359,1.5413233764892178,-2.2156195050047747,-7.2849426757667315,5.202236097167509,-0.9365606034293558
|
11
|
+
2.4821899389695874:2.1743832447567346
|
12
|
+
-2.9564446473766672:-3.2541845089551904
|
13
|
+
3.2605627963240384:-0.20795953070592393
|
14
|
+
2.605813320828736:3.154050922464929
|
15
|
+
-0.4579599997104075:2.349414728456047
|
16
|
+
2.653465191369256:4.365018317781122
|
17
|
+
0.6373485836872791:1.7163198231060346
|
18
|
+
0.711515408592374:0.8702577537323323
|
19
|
+
-1.7351499679943323:1.24799151515672
|
20
|
+
2.902919261517124
|
data/table.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#A Table is where players come to play a game (possibly as part of a match as part of a tournament if required)
|
2
|
+
#Essentially a Table is a Game Controller
|
3
|
+
class Table
|
4
|
+
attr_reader :players, :games
|
5
|
+
attr_accessor :quiet
|
6
|
+
|
7
|
+
def initialize game, players
|
8
|
+
@game = game
|
9
|
+
@players = players
|
10
|
+
end
|
11
|
+
|
12
|
+
def play_game
|
13
|
+
until @game.won? || @game.drawn?
|
14
|
+
next_player = @players[@game.next_player]
|
15
|
+
move = next_player.get_move @game.current_position, @game.move_list
|
16
|
+
unless @quiet
|
17
|
+
p "player #{@game.next_player} plays move #{move }"
|
18
|
+
end
|
19
|
+
@game.play_move @game.next_player, move
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/test/helper.rb
ADDED
data/tournament.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
#A tournament is a series of matches between a set of players
|
data/ui/Rakefile
ADDED
data/ui/config/build.yml
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
$Cocoa = true
|
2
|
+
require 'rubygems'
|
3
|
+
require 'hotcocoa'
|
4
|
+
$: << File.expand_path(File.dirname(__FILE__)) + "/../../"
|
5
|
+
require 'main.rb'
|
6
|
+
|
7
|
+
|
8
|
+
class Application
|
9
|
+
|
10
|
+
include HotCocoa
|
11
|
+
|
12
|
+
def start
|
13
|
+
application :name => "Deep Beige" do |app|
|
14
|
+
app.delegate = self
|
15
|
+
window :frame => [100, 100, 500, 500], :title => "Test" do |win|
|
16
|
+
win << label(:text => "Hello from Deep Beige", :layout => {:start => false})
|
17
|
+
win.will_close { exit }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# file/open
|
23
|
+
def on_open(menu)
|
24
|
+
end
|
25
|
+
|
26
|
+
# file/new
|
27
|
+
def on_new(menu)
|
28
|
+
end
|
29
|
+
|
30
|
+
# help menu item
|
31
|
+
def on_help(menu)
|
32
|
+
end
|
33
|
+
|
34
|
+
# This is commented out, so the minimize menu item is disabled
|
35
|
+
#def on_minimize(menu)
|
36
|
+
#end
|
37
|
+
|
38
|
+
# window/zoom
|
39
|
+
def on_zoom(menu)
|
40
|
+
end
|
41
|
+
|
42
|
+
# window/bring_all_to_front
|
43
|
+
def on_bring_all_to_front(menu)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Application.new.start
|
data/ui/lib/menu.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module HotCocoa
|
2
|
+
def application_menu
|
3
|
+
menu do |main|
|
4
|
+
main.submenu :apple do |apple|
|
5
|
+
apple.item :about, :title => "About #{NSApp.name}"
|
6
|
+
apple.separator
|
7
|
+
apple.item :preferences, :key => ","
|
8
|
+
apple.separator
|
9
|
+
apple.submenu :services
|
10
|
+
apple.separator
|
11
|
+
apple.item :hide, :title => "Hide #{NSApp.name}", :key => "h"
|
12
|
+
apple.item :hide_others, :title => "Hide Others", :key => "h", :modifiers => [:command, :alt]
|
13
|
+
apple.item :show_all, :title => "Show All"
|
14
|
+
apple.separator
|
15
|
+
apple.item :quit, :title => "Quit #{NSApp.name}", :key => "q"
|
16
|
+
end
|
17
|
+
main.submenu :file do |file|
|
18
|
+
file.item :new, :key => "n"
|
19
|
+
file.item :open, :key => "o"
|
20
|
+
end
|
21
|
+
main.submenu :window do |win|
|
22
|
+
win.item :minimize, :key => "m"
|
23
|
+
win.item :zoom
|
24
|
+
win.separator
|
25
|
+
win.item :bring_all_to_front, :title => "Bring All to Front", :key => "o"
|
26
|
+
end
|
27
|
+
main.submenu :help do |help|
|
28
|
+
help.item :help, :title => "#{NSApp.name} Help"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
Binary file
|