Created
May 27, 2012 02:15
-
-
Save Sujimichi/2795962 to your computer and use it in GitHub Desktop.
Reduced Version of my NN solution to RubyWarrior. Without support for using different layered NN's and extra comments removed. Includes two evolved genomes, put one in a file called 'genome' with player.rb in warrior folder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0.4208665093792384,0.8271085823481578,-0.08677969236421801,-1.3812424909479102,0.25662357967972205,0.895993960666072,0.5969217612341047,1.003827461932993,0.3535111488721142,1.626234122590899,-0.9807624824831435,0.8455667360510928,-0.6596045396559488,-0.32330752252947614,0.30962364095597517,-1.051266687042697,-0.8619251071880633,1.0131278329152824,0.7045901871883101,0.5102103240087068,-0.5795087495450799,-1.4718975639160914,1.110149168791025,0.4848770449182779,-0.131034115009259,-0.8994602986777726,1.379669139856652,-0.24300620776607484,-0.032104129747120846,0.21746064790006747,-1.1751996077694868,-0.4416596710613584,1.2838430768445492,-0.33854260905174827,-0.5552927940371294,0.19992797470487422,-1.2768902810663525,-3.223132063475557,2.6814521898725237,-1.9607168460213784,1.9180731391978023,0.6077600309107104,0.4123090743791793,0.8246054475587798,-0.9596703206415195,0.2646596987299211,-0.06851920283518687,0.9944253709871688,0.24848358116043623,0.7495855784944395,-1.108429896105167,1.3753052571268305,3.181034120558026,-1.2709155470015525,0.7253500256394799,2.4192587701666324,2.122847393625867,-0.7075864717716149,2.694571198462502,-0.23738793552525383,-2.6191541131659126,0.6470749529047221,-2.0785370070988187,0.21706440490961854,-1.0164747730432557,0.16792697963601755,0.5023039485374504,-1.6222578128267933,-1.6384362302042,0.3516923946976447,-0.08501410283203104,0.10333251322753634,0.9694370284329388,0.7053274460037942,-1.9104000853472534,1.347447081663761,-0.875632334774423,0.4292051447798868,-0.1650455988119438,-0.3090559012185816,0.8122849956890801,-0.43795253002994095,-0.6324174808071471,1.052703281088451,-1.1261157583338193,1.073884982118626,1.602349239517356,-1.5930163275608602,-0.5929434775878311,-0.08861092539187976,2.2426990558759963,-0.6078690447899587,-1.4689188415414924,0.8433812929082517,-0.09344770334669705,0.5036286454281341,-0.6455383167824994,-2.2115467726742777,0.9784517143150431,-1.5474794887298606,-1.0121117734936058,1.315809794779084,-0.038522836988336806,-0.1922363540190457,0.34537500635109775,-0.07035164026793084,0.1304654458666375,0.046499273727501345,-0.776155984209477,-0.37063833219461223,2.953491431451073,-0.05017712876532365,-0.43929723081370353,-0.9906992496151041,-1.9884220925888678,-2.869539337671243,-1.4158283863183865,0.7505673085798398,0.9765036522488568,0.2733308317062427,1.7150446139071789,-1.1283890568444024,-0.11792247213316376,2.4010116280638307,-0.4041747776518825,0.17187082634317985,2.9376700582278765,-0.5871316925721045,1.3928431558645926,-0.1618869375393336,0.5749040004267305,-0.4636279527831687,1.3626708417630131,0.711740301132716,-0.0659435614151952,0.28541086127064585,-1.649405424787955,-1.1626701947387152,0.8682689002089043,-0.8377851096785819,-1.65359097309008,-0.818010200816063,-0.6911964701717429,1.273553027323357 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0.5802744208575772,-0.28574107165038476,-0.4662748785179248,-1.2171913093525357,0.5656330027974735,-0.5586257421359373,0.5084206048395205,1.003827461932993,0.709080278772143,1.6392156876576782,-1.5816557649098901,1.324602802894768,0.8260058048894272,0.7178511436920003,-0.5804795029026557,-0.6639538111439616,-1.0830547059169757,1.9874278830514984,1.060462572920094,0.043450244058545695,-0.9386108314118661,-0.20380921105848493,0.9387378967454564,1.2449728237120754,-0.22972591303958378,-0.24970922127807615,1.62529564108517,0.5417832369383961,-0.11528627896579335,1.4118376011513845,-0.7192935919079477,-0.3072746576279173,0.5584597818074516,-0.7422452538004506,-0.4634791859355173,0.004736003702502156,-0.522971915073979,-1.6080762596194578,1.2867003151679492,-1.251724631556766,1.2491658636125211,1.3107157100065336,0.1900789190385367,1.8111732526381408,-1.401736698163552,0.5447534774158844,-0.8207816593573641,1.4844152049571304,0.7437535038324593,1.392577856212811,-1.0991699952789844,0.8711055513346737,1.6172844865648934,0.8405928179966495,0.4943506198504791,1.4284968522597425,-0.12065215283619857,-1.4419055454769207,2.7846954519824108,-0.7774835582067521,-2.145877659337063,0.9779931808120903,-0.410640494708302,0.03333410784760027,-1.451123469025841,0.5979415584663418,0.4598369719701315,-1.2593196304976257,-0.35010422037185673,1.1346703795954287,-0.9273859981034838,-0.4201631565453021,1.0212588282106947,0.700940688812856,-1.8844466294018205,0.7347508702944914,-1.0349553738651185,-0.04385884711391752,-0.13241728359821703,0.42457537461219197,1.3247043094193365,-0.8305801586055288,-0.7672892639721568,1.6630102110931597,-1.4630564793478578,1.7414649188338496,0.2974684205486997,-0.5212079800825287,0.18517333251448853,0.411561910031378,1.0719799685548463,-1.7452448790752302,-0.9517129751400798,-0.08448533537832714,-1.2325858448108846,-0.034609688077519296,-1.1319323029736506,-2.504005551714977,-0.3791599946626758,-1.1627861021688033,-0.694483154179575,0.897682867193146,0.1689166248283508,0.1244239416555194,-0.03729333603100804,0.18323346496734283,-0.34400573706550336,-0.09399750371134646,-1.7745907738007982,-0.6104668588467705,2.7888692908831696,0.35413361313989,-1.6159956231111794,-1.2513571752160617,-1.121825686506059,-1.96465470421759,-1.8425058033652832,1.23775809354918,0.6562636799454941,-0.17649029415055761,1.6087244520620438,0.4469884419946354,0.6381523759405975,-0.15787817155644912,-0.6123560840087449,0.48782473306474006,3.4922828293755748,-1.048601918281,0.7836219082927285,-0.8332571162284346,0.6919216046299118,-0.6089382368131837,0.2010862248447225,-0.3640148193914603,-0.7425593566026867,1.3646274903635693,-0.26189256664438787,-1.7190295526618184,-0.4506178405825584,0.1614189724301064,-1.5381221619979772,-0.8047572832141019,-0.3636923895878802,-0.07888040239556415 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Player | |
def initialize | |
genome = File.open("./genome", "r"){|f| f.readlines}.join.split(",").map{|s| s.to_f} #Read genome from file. | |
@brain = Brain.new({:in => 16, :inner => 6, :out => 8}, genome) #Initialize warriors brain (neural net) | |
end | |
def play_turn(warrior) | |
@previous_health ||= 20 | |
inputs = input_array_for(warrior) #Sense world and present as an array of inputs for NN | |
action, impulse = @brain.act_on(inputs) #send inputs to neural network and interpret its output as :action and :impulse | |
begin #send 'impulse' and 'action' from brain to warrior. done inside rescue as brain may request actions the body can't yet do, like rest! in the eariler levels. | |
warrior.send(*["#{action}!", (action.eql?(:rest) ? nil : impulse)].compact) | |
rescue NoMethodError => e | |
puts "body failed to understand brain! #{e.message}" | |
end | |
@previous_health = warrior.health if warrior.respond_to?(:health) | |
end | |
def input_array_for warrior | |
dirs = [:left, :forward, :right, :backward] #directions in which things can be | |
things = [:wall, :enemy, :captive] #type of things there can be | |
vis_scale = [0, 0.6, 0.3] #used to scale the values returned by :look. | |
if warrior.respond_to?(:feel) | |
can_look = warrior.respond_to?(:look) | |
inputs = things.map do |thing| #for each of the things | |
dirs.map do |dir| #in each of the directions | |
v = (warrior.feel(dir).send("#{thing}?").eql?(true) ? 1 : 0) #test if that thing is there, returning 1 for true else 0 | |
if can_look #if warrior can also look | |
look = warrior.look(dir) #look in direction | |
v = v + look.map{|l| (l.send("#{thing}?").eql?(true) ? 1 : 0) * vis_scale[look.index(l)] }.max #reduce to a single val from given 3 ie [0,1,1] => [0, 0.6, 0.3] => [0.6] | |
end | |
v | |
end | |
end | |
else | |
inputs = [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] #inputs for empty corridor. | |
end | |
inputs << (warrior.respond_to?(:shoot!) ? 1 : 0) | |
w_health = warrior.respond_to?(:health) ? warrior.health : 20 | |
inputs << (1 - 1.0/20 * w_health).round(2) | |
inputs << ((@previous_health > w_health) ? 1 : 0) #sense of health dropping | |
inputs << 1 #representational bias. yeah, I should prob explain this! its REALLY important! | |
inputs.flatten | |
end | |
end | |
class Brain | |
def initialize nodes, genome | |
p1 = (nodes[:in] * nodes[:inner]) #number of genes(weights) needed for first layer | |
@network = NeuralNetwork.new([ | |
NeuralLayer.new(genome[0..p1-1].in_groups_of(nodes[:in])), #init 1st layer with weights from genome | |
NeuralLayer.new(genome[p1..(genome.size-1)].in_groups_of(nodes[:inner])) #init 2nd layer with weights from genome | |
]) | |
end | |
def act_on inputs | |
output = @network.process(inputs) #send inputs to neural net and return result as array of values (nodes). | |
if output[0].abs > output[1].abs #moving forward or backwards | |
impulse = (output[0] > 0) ? :forward : :backward | |
else #moving left or right | |
impulse = (output[1] > 0) ? :left : :right | |
end | |
actions = [[:walk, output[2]], [:attack, output[3]], [:rest, output[4]], [:rescue, output[5]], [:pivot, output[6]], [:shoot, output[7]]] | |
action = actions.max_by{|grp| grp.last}.first #order actions by largest output value and select | |
impulse = :rest if action.eql?(:rest) #rest is the only non-directional action (this line is only needed by assertions in BootCamp, not core to solutions funtion) | |
[action, impulse] #return the action and impulse | |
end | |
end | |
class NeuralNetwork | |
def initialize layers | |
@layers = layers[0..2] | |
end | |
def process inputs | |
output = inputs | |
@layers.each{ |layer| output = layer.process(output) } #<---This calls the neural network calculation step | |
output | |
end | |
end | |
class NeuralLayer | |
def initialize weights | |
@weights = weights | |
@nodes = {:output => @weights.size, :input => @weights.first.size} | |
end | |
def process inputs | |
@nodes[:output].times.map{ |i| inputs.zip(@weights[i]).map{|d| d.product_with_activation}.sum.round(2) } #<---Main NN calculation step. | |
end | |
end | |
class Array | |
def sum; self.inject{|i,j| i.to_f + j.to_f}; end | |
def product; self.inject{|i,j| i.to_f * j.to_f}; end | |
def product_with_activation; Math.sin(product); end | |
def in_groups_of n | |
col = [] | |
self.each_slice(n){|slice| col << slice} | |
return col | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment