Tsaukpaetra

Enhanced GUI MarI/O

Jun 15th, 2015
914
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 31.18 KB | None | 0 0
  1. -- MarI/O by SethBling
  2. -- Feel free to use this code, but please do not redistribute it.
  3. -- Intended for use with the BizHawk emulator and Super Mario World or Super Mario Bros. ROM.
  4.  
  5. romtype_SMW = gameinfo.getromname() == "Super Mario World (USA)"
  6.  
  7. if romtype_SMW then
  8.     Filename = "DP1.state"
  9.     ButtonNames = {
  10.         "A",
  11.         "B",
  12.         "X",
  13.         "Y",
  14.         "Up",
  15.         "Down",
  16.         "Left",
  17.         "Right",
  18.     }
  19.     console.writeline("Detected Super Mario World (USA)")
  20. else
  21.     Filename = "SMB1-1.state"
  22.     ButtonNames = {
  23.         "A",
  24.         "B",
  25.         "Up",
  26.         "Down",
  27.         "Left",
  28.         "Right",
  29.     }
  30. end
  31.  
  32. BoxRadius = 6
  33. InputSize = (BoxRadius*2+1)*(BoxRadius*2+1)
  34.  
  35. Inputs = InputSize+1
  36. Outputs = #ButtonNames
  37.  
  38. Population = 50
  39. DeltaDisjoint = 2.0
  40. DeltaWeights = 0.4
  41. DeltaThreshold = 1.0
  42.  
  43. StaleSpecies = 15
  44.  
  45. MutateConnectionsChance = 0.25
  46. PerturbChance = 0.90
  47. CrossoverChance = 0.75
  48. LinkMutationChance = 2.0
  49. NodeMutationChance = 0.50
  50. BiasMutationChance = 0.40
  51. StepSize = 0.1
  52. DisableMutationChance = 0.4
  53. EnableMutationChance = 0.3
  54.  
  55. TimeoutConstant = 20
  56.  
  57. MaxNodes = 1000000
  58. debugMode = false
  59. forceNewRun = false
  60.  
  61. function getPositions()
  62.     if romtype_SMW then
  63.         marioX = memory.read_s16_le(0x94)
  64.         marioY = memory.read_s16_le(0x96)
  65.        
  66.         local layer1x = memory.read_s16_le(0x1A);
  67.         local layer1y = memory.read_s16_le(0x1C);
  68.        
  69.         screenX = marioX-layer1x
  70.         screenY = marioY-layer1y
  71.     else
  72.         marioX = memory.readbyte(0x6D) * 0x100 + memory.readbyte(0x86)
  73.         marioY = memory.readbyte(0x03B8)+16
  74.    
  75.         screenX = memory.readbyte(0x03AD)
  76.         screenY = memory.readbyte(0x03B8)
  77.     end
  78. end
  79.  
  80. function getTile(dx, dy)
  81.     if romtype_SMW then
  82.         x = math.floor((marioX+dx+8)/16)
  83.         y = math.floor((marioY+dy)/16)
  84.        
  85.         return memory.readbyte(0x1C800 + math.floor(x/0x10)*0x1B0 + y*0x10 + x%0x10)
  86.     else
  87.         local x = marioX + dx + 8
  88.         local y = marioY + dy - 16
  89.         local page = math.floor(x/256)%2
  90.  
  91.         local subx = math.floor((x%256)/16)
  92.         local suby = math.floor((y - 32)/16)
  93.         local addr = 0x500 + page*13*16+suby*16+subx
  94.        
  95.         if suby >= 13 or suby < 0 then
  96.             return 0
  97.         end
  98.        
  99.         if memory.readbyte(addr) ~= 0 then
  100.             return 1
  101.         else
  102.             return 0
  103.         end
  104.     end
  105. end
  106.  
  107. function getSprites()
  108.     if romtype_SMW then
  109.         local sprites = {}
  110.         for slot=0,11 do
  111.             local status = memory.readbyte(0x14C8+slot)
  112.             if status ~= 0 then
  113.                 spritex = memory.readbyte(0xE4+slot) + memory.readbyte(0x14E0+slot)*256
  114.                 spritey = memory.readbyte(0xD8+slot) + memory.readbyte(0x14D4+slot)*256
  115.                 sprites[#sprites+1] = {["x"]=spritex, ["y"]=spritey}
  116.             end
  117.         end    
  118.        
  119.         return sprites
  120.     else
  121.         local sprites = {}
  122.         for slot=0,4 do
  123.             local enemy = memory.readbyte(0xF+slot)
  124.             if enemy ~= 0 then
  125.                 local ex = memory.readbyte(0x6E + slot)*0x100 + memory.readbyte(0x87+slot)
  126.                 local ey = memory.readbyte(0xCF + slot)+24
  127.                 sprites[#sprites+1] = {["x"]=ex,["y"]=ey}
  128.             end
  129.         end
  130.        
  131.         return sprites
  132.     end
  133. end
  134.  
  135. function getExtendedSprites()
  136.     if gameinfo.getromname() == "Super Mario World (USA)" then
  137.         local extended = {}
  138.         for slot=0,11 do
  139.             local number = memory.readbyte(0x170B+slot)
  140.             if number ~= 0 then
  141.                 spritex = memory.readbyte(0x171F+slot) + memory.readbyte(0x1733+slot)*256
  142.                 spritey = memory.readbyte(0x1715+slot) + memory.readbyte(0x1729+slot)*256
  143.                 extended[#extended+1] = {["x"]=spritex, ["y"]=spritey}
  144.             end
  145.         end    
  146.        
  147.         return extended
  148.     elseif gameinfo.getromname() == "Super Mario Bros." then
  149.         return {}
  150.     end
  151. end
  152.  
  153. function getInputs()
  154.     getPositions()
  155.    
  156.     sprites = getSprites()
  157.     extended = getExtendedSprites()
  158.    
  159.     local inputs = {}
  160.    
  161.     for dy=-BoxRadius*16,BoxRadius*16,16 do
  162.         for dx=-BoxRadius*16,BoxRadius*16,16 do
  163.             inputs[#inputs+1] = 0
  164.            
  165.             tile = getTile(dx, dy)
  166.             if tile == 1 and marioY+dy < 0x1B0 then
  167.                 inputs[#inputs] = 1
  168.             end
  169.            
  170.             for i = 1,#sprites do
  171.                 distx = math.abs(sprites[i]["x"] - (marioX+dx))
  172.                 disty = math.abs(sprites[i]["y"] - (marioY+dy))
  173.                 if distx <= 8 and disty <= 8 then
  174.                     inputs[#inputs] = -1
  175.                 end
  176.             end
  177.  
  178.             for i = 1,#extended do
  179.                 distx = math.abs(extended[i]["x"] - (marioX+dx))
  180.                 disty = math.abs(extended[i]["y"] - (marioY+dy))
  181.                 if distx < 8 and disty < 8 then
  182.                     inputs[#inputs] = -1
  183.                 end
  184.             end
  185.         end
  186.     end
  187.    
  188.     --mariovx = memory.read_s8(0x7B)
  189.     --mariovy = memory.read_s8(0x7D)
  190.    
  191.     return inputs
  192. end
  193.  
  194. function sigmoid(x)
  195.     return 2/(1+math.exp(-4.9*x))-1
  196. end
  197.  
  198. function newInnovation()
  199.     pool.innovation = pool.innovation + 1
  200.     return pool.innovation
  201. end
  202.  
  203. function newPool()
  204.     local pool = {}
  205.     pool.species = {}
  206.     pool.generation = 0
  207.     pool.innovation = Outputs
  208.     pool.currentSpecies = 1
  209.     pool.currentGenome = 1
  210.     pool.currentFrame = 0
  211.     pool.maxFitness = 0
  212.    
  213.     return pool
  214. end
  215.  
  216. function newSpecies()
  217.     local species = {}
  218.     species.topFitness = 0
  219.     species.staleness = 0
  220.     species.genomes = {}
  221.     species.averageFitness = 0
  222.    
  223.     return species
  224. end
  225.  
  226. function newGenome()
  227.     local genome = {}
  228.     genome.genes = {}
  229.     genome.fitness = 0
  230.     genome.adjustedFitness = 0
  231.     genome.network = {}
  232.     genome.maxneuron = 0
  233.     genome.globalRank = 0
  234.     genome.mutationRates = {}
  235.     genome.mutationRates["connections"] = MutateConnectionsChance
  236.     genome.mutationRates["link"] = LinkMutationChance
  237.     genome.mutationRates["bias"] = BiasMutationChance
  238.     genome.mutationRates["node"] = NodeMutationChance
  239.     genome.mutationRates["enable"] = EnableMutationChance
  240.     genome.mutationRates["disable"] = DisableMutationChance
  241.     genome.mutationRates["step"] = StepSize
  242.    
  243.     return genome
  244. end
  245.  
  246. function copyGenome(genome)
  247.     local genome2 = newGenome()
  248.     for g=1,#genome.genes do
  249.         table.insert(genome2.genes, copyGene(genome.genes[g]))
  250.     end
  251.     genome2.maxneuron = genome.maxneuron
  252.     genome2.mutationRates["connections"] = genome.mutationRates["connections"]
  253.     genome2.mutationRates["link"] = genome.mutationRates["link"]
  254.     genome2.mutationRates["bias"] = genome.mutationRates["bias"]
  255.     genome2.mutationRates["node"] = genome.mutationRates["node"]
  256.     genome2.mutationRates["enable"] = genome.mutationRates["enable"]
  257.     genome2.mutationRates["disable"] = genome.mutationRates["disable"]
  258.    
  259.     return genome2
  260. end
  261.  
  262. function basicGenome()
  263.     local genome = newGenome()
  264.     local innovation = 1
  265.  
  266.     genome.maxneuron = Inputs
  267.     mutate(genome)
  268.    
  269.     return genome
  270. end
  271.  
  272. function newGene()
  273.     local gene = {}
  274.     gene.into = 0
  275.     gene.out = 0
  276.     gene.weight = 0.0
  277.     gene.enabled = true
  278.     gene.innovation = 0
  279.    
  280.     return gene
  281. end
  282.  
  283. function copyGene(gene)
  284.     local gene2 = newGene()
  285.     gene2.into = gene.into
  286.     gene2.out = gene.out
  287.     gene2.weight = gene.weight
  288.     gene2.enabled = gene.enabled
  289.     gene2.innovation = gene.innovation
  290.    
  291.     return gene2
  292. end
  293.  
  294. function newNeuron()
  295.     local neuron = {}
  296.     neuron.incoming = {}
  297.     neuron.value = 0.0
  298.    
  299.     return neuron
  300. end
  301.  
  302. function generateNetwork(genome)
  303.     local network = {}
  304.     network.neurons = {}
  305.    
  306.     for i=1,Inputs do
  307.         network.neurons[i] = newNeuron()
  308.     end
  309.    
  310.     for o=1,Outputs do
  311.         network.neurons[MaxNodes+o] = newNeuron()
  312.     end
  313.    
  314.     table.sort(genome.genes, function (a,b)
  315.         return (a.out < b.out)
  316.     end)
  317.     for i=1,#genome.genes do
  318.         local gene = genome.genes[i]
  319.         if gene.enabled then
  320.             if network.neurons[gene.out] == nil then
  321.                 network.neurons[gene.out] = newNeuron()
  322.             end
  323.             local neuron = network.neurons[gene.out]
  324.             table.insert(neuron.incoming, gene)
  325.             if network.neurons[gene.into] == nil then
  326.                 network.neurons[gene.into] = newNeuron()
  327.             end
  328.         end
  329.     end
  330.    
  331.     genome.network = network
  332. end
  333.  
  334. function evaluateNetwork(network, inputs)
  335.     table.insert(inputs, 1)
  336.     if #inputs ~= Inputs then
  337.         console.writeline("Incorrect number of neural network inputs.")
  338.         return {}
  339.     end
  340.    
  341.     for i=1,Inputs do
  342.         network.neurons[i].value = inputs[i]
  343.     end
  344.    
  345.     for _,neuron in pairs(network.neurons) do
  346.         local sum = 0
  347.         for j = 1,#neuron.incoming do
  348.             local incoming = neuron.incoming[j]
  349.             local other = network.neurons[incoming.into]
  350.             sum = sum + incoming.weight * other.value
  351.         end
  352.        
  353.         if #neuron.incoming > 0 then
  354.             neuron.value = sigmoid(sum)
  355.         end
  356.     end
  357.    
  358.     local outputs = {}
  359.     for o=1,Outputs do
  360.         local button = "P1 " .. ButtonNames[o]
  361.         if network.neurons[MaxNodes+o].value > 0 then
  362.             outputs[button] = true
  363.         else
  364.             outputs[button] = false
  365.         end
  366.     end
  367.    
  368.     return outputs
  369. end
  370.  
  371. function crossover(g1, g2)
  372.     -- Make sure g1 is the higher fitness genome
  373.     if g2.fitness > g1.fitness then
  374.         tempg = g1
  375.         g1 = g2
  376.         g2 = tempg
  377.     end
  378.  
  379.     local child = newGenome()
  380.    
  381.     local innovations2 = {}
  382.     for i=1,#g2.genes do
  383.         local gene = g2.genes[i]
  384.         innovations2[gene.innovation] = gene
  385.     end
  386.    
  387.     for i=1,#g1.genes do
  388.         local gene1 = g1.genes[i]
  389.         local gene2 = innovations2[gene1.innovation]
  390.         if gene2 ~= nil and math.random(2) == 1 and gene2.enabled then
  391.             table.insert(child.genes, copyGene(gene2))
  392.         else
  393.             table.insert(child.genes, copyGene(gene1))
  394.         end
  395.     end
  396.    
  397.     child.maxneuron = math.max(g1.maxneuron,g2.maxneuron)
  398.    
  399.     for mutation,rate in pairs(g1.mutationRates) do
  400.         child.mutationRates[mutation] = rate
  401.     end
  402.    
  403.     return child
  404. end
  405.  
  406. function randomNeuron(genes, nonInput)
  407.     local neurons = {}
  408.     if not nonInput then
  409.         for i=1,Inputs do
  410.             neurons[i] = true
  411.         end
  412.     end
  413.     for o=1,Outputs do
  414.         neurons[MaxNodes+o] = true
  415.     end
  416.     for i=1,#genes do
  417.         if (not nonInput) or genes[i].into > Inputs then
  418.             neurons[genes[i].into] = true
  419.         end
  420.         if (not nonInput) or genes[i].out > Inputs then
  421.             neurons[genes[i].out] = true
  422.         end
  423.     end
  424.  
  425.     local count = 0
  426.     for _,_ in pairs(neurons) do
  427.         count = count + 1
  428.     end
  429.     local n = math.random(1, count)
  430.    
  431.     for k,v in pairs(neurons) do
  432.         n = n-1
  433.         if n == 0 then
  434.             return k
  435.         end
  436.     end
  437.    
  438.     return 0
  439. end
  440.  
  441. function containsLink(genes, link)
  442.     for i=1,#genes do
  443.         local gene = genes[i]
  444.         if gene.into == link.into and gene.out == link.out then
  445.             return true
  446.         end
  447.     end
  448. end
  449.  
  450. function pointMutate(genome)
  451.     local step = genome.mutationRates["step"]
  452.    
  453.     for i=1,#genome.genes do
  454.         local gene = genome.genes[i]
  455.         if math.random() < PerturbChance then
  456.             gene.weight = gene.weight + math.random() * step*2 - step
  457.         else
  458.             gene.weight = math.random()*4-2
  459.         end
  460.     end
  461. end
  462.  
  463. function linkMutate(genome, forceBias)
  464.     local neuron1 = randomNeuron(genome.genes, false)
  465.     local neuron2 = randomNeuron(genome.genes, true)
  466.      
  467.     local newLink = newGene()
  468.     if neuron1 <= Inputs and neuron2 <= Inputs then
  469.         --Both input nodes
  470.         return
  471.     end
  472.     if neuron2 <= Inputs then
  473.         -- Swap output and input
  474.         local temp = neuron1
  475.         neuron1 = neuron2
  476.         neuron2 = temp
  477.     end
  478.  
  479.     newLink.into = neuron1
  480.     newLink.out = neuron2
  481.     if forceBias then
  482.         newLink.into = Inputs
  483.     end
  484.    
  485.     if containsLink(genome.genes, newLink) then
  486.         return
  487.     end
  488.     newLink.innovation = newInnovation()
  489.     newLink.weight = math.random()*4-2
  490.    
  491.     table.insert(genome.genes, newLink)
  492. end
  493.  
  494. function nodeMutate(genome)
  495.     if #genome.genes == 0 then
  496.         return
  497.     end
  498.  
  499.     genome.maxneuron = genome.maxneuron + 1
  500.  
  501.     local gene = genome.genes[math.random(1,#genome.genes)]
  502.     if not gene.enabled then
  503.         return
  504.     end
  505.     gene.enabled = false
  506.    
  507.     local gene1 = copyGene(gene)
  508.     gene1.out = genome.maxneuron
  509.     gene1.weight = 1.0
  510.     gene1.innovation = newInnovation()
  511.     gene1.enabled = true
  512.     table.insert(genome.genes, gene1)
  513.    
  514.     local gene2 = copyGene(gene)
  515.     gene2.into = genome.maxneuron
  516.     gene2.innovation = newInnovation()
  517.     gene2.enabled = true
  518.     table.insert(genome.genes, gene2)
  519. end
  520.  
  521. function enableDisableMutate(genome, enable)
  522.     local candidates = {}
  523.     for _,gene in pairs(genome.genes) do
  524.         if gene.enabled == not enable then
  525.             table.insert(candidates, gene)
  526.         end
  527.     end
  528.    
  529.     if #candidates == 0 then
  530.         return
  531.     end
  532.    
  533.     local gene = candidates[math.random(1,#candidates)]
  534.     gene.enabled = not gene.enabled
  535. end
  536.  
  537. function mutate(genome)
  538.     for mutation,rate in pairs(genome.mutationRates) do
  539.         if math.random(1,2) == 1 then
  540.             genome.mutationRates[mutation] = 0.95*rate
  541.         else
  542.             genome.mutationRates[mutation] = 1.05263*rate
  543.         end
  544.     end
  545.  
  546.     if math.random() < genome.mutationRates["connections"] then
  547.         pointMutate(genome)
  548.     end
  549.    
  550.     local p = genome.mutationRates["link"]
  551.     while p > 0 do
  552.         if math.random() < p then
  553.             linkMutate(genome, false)
  554.         end
  555.         p = p - 1
  556.     end
  557.  
  558.     p = genome.mutationRates["bias"]
  559.     while p > 0 do
  560.         if math.random() < p then
  561.             linkMutate(genome, true)
  562.         end
  563.         p = p - 1
  564.     end
  565.    
  566.     p = genome.mutationRates["node"]
  567.     while p > 0 do
  568.         if math.random() < p then
  569.             nodeMutate(genome)
  570.         end
  571.         p = p - 1
  572.     end
  573.    
  574.     p = genome.mutationRates["enable"]
  575.     while p > 0 do
  576.         if math.random() < p then
  577.             enableDisableMutate(genome, true)
  578.         end
  579.         p = p - 1
  580.     end
  581.  
  582.     p = genome.mutationRates["disable"]
  583.     while p > 0 do
  584.         if math.random() < p then
  585.             enableDisableMutate(genome, false)
  586.         end
  587.         p = p - 1
  588.     end
  589. end
  590.  
  591. function disjoint(genes1, genes2)
  592.     local i1 = {}
  593.     for i = 1,#genes1 do
  594.         local gene = genes1[i]
  595.         i1[gene.innovation] = true
  596.     end
  597.  
  598.     local i2 = {}
  599.     for i = 1,#genes2 do
  600.         local gene = genes2[i]
  601.         i2[gene.innovation] = true
  602.     end
  603.    
  604.     local disjointGenes = 0
  605.     for i = 1,#genes1 do
  606.         local gene = genes1[i]
  607.         if not i2[gene.innovation] then
  608.             disjointGenes = disjointGenes+1
  609.         end
  610.     end
  611.    
  612.     for i = 1,#genes2 do
  613.         local gene = genes2[i]
  614.         if not i1[gene.innovation] then
  615.             disjointGenes = disjointGenes+1
  616.         end
  617.     end
  618.    
  619.     local n = math.max(#genes1, #genes2)
  620.    
  621.     return disjointGenes / n
  622. end
  623.  
  624. function weights(genes1, genes2)
  625.     local i2 = {}
  626.     for i = 1,#genes2 do
  627.         local gene = genes2[i]
  628.         i2[gene.innovation] = gene
  629.     end
  630.  
  631.     local sum = 0
  632.     local coincident = 0
  633.     for i = 1,#genes1 do
  634.         local gene = genes1[i]
  635.         if i2[gene.innovation] ~= nil then
  636.             local gene2 = i2[gene.innovation]
  637.             sum = sum + math.abs(gene.weight - gene2.weight)
  638.             coincident = coincident + 1
  639.         end
  640.     end
  641.    
  642.     return sum / coincident
  643. end
  644.    
  645. function sameSpecies(genome1, genome2)
  646.     local dd = DeltaDisjoint*disjoint(genome1.genes, genome2.genes)
  647.     local dw = DeltaWeights*weights(genome1.genes, genome2.genes)
  648.     return dd + dw < DeltaThreshold
  649. end
  650.  
  651. function rankGlobally()
  652.     local global = {}
  653.     for s = 1,#pool.species do
  654.         local species = pool.species[s]
  655.         for g = 1,#species.genomes do
  656.             table.insert(global, species.genomes[g])
  657.         end
  658.     end
  659.     table.sort(global, function (a,b)
  660.         return (a.fitness < b.fitness)
  661.     end)
  662.    
  663.     for g=1,#global do
  664.         global[g].globalRank = g
  665.     end
  666. end
  667.  
  668. function calculateAverageFitness(species)
  669.     local total = 0
  670.    
  671.     for g=1,#species.genomes do
  672.         local genome = species.genomes[g]
  673.         total = total + genome.globalRank
  674.     end
  675.    
  676.     species.averageFitness = total / #species.genomes
  677. end
  678.  
  679. function totalAverageFitness()
  680.     local total = 0
  681.     for s = 1,#pool.species do
  682.         local species = pool.species[s]
  683.         total = total + species.averageFitness
  684.     end
  685.  
  686.     return total
  687. end
  688.  
  689. function cullSpecies(cutToOne)
  690.     for s = 1,#pool.species do
  691.         local species = pool.species[s]
  692.        
  693.         table.sort(species.genomes, function (a,b)
  694.             return (a.fitness > b.fitness)
  695.         end)
  696.        
  697.         local remaining = math.ceil(#species.genomes/2)
  698.         if cutToOne then
  699.             remaining = 1
  700.         end
  701.         while #species.genomes > remaining do
  702.             table.remove(species.genomes)
  703.         end
  704.     end
  705. end
  706.  
  707. function breedChild(species)
  708.     local child = {}
  709.     if math.random() < CrossoverChance then
  710.         g1 = species.genomes[math.random(1, #species.genomes)]
  711.         g2 = species.genomes[math.random(1, #species.genomes)]
  712.         child = crossover(g1, g2)
  713.     else
  714.         g = species.genomes[math.random(1, #species.genomes)]
  715.         child = copyGenome(g)
  716.     end
  717.    
  718.     mutate(child)
  719.    
  720.     return child
  721. end
  722.  
  723. function removeStaleSpecies()
  724.     local survived = {}
  725.  
  726.     for s = 1,#pool.species do
  727.         local species = pool.species[s]
  728.        
  729.         table.sort(species.genomes, function (a,b)
  730.             return (a.fitness > b.fitness)
  731.         end)
  732.        
  733.         if species.genomes[1].fitness > species.topFitness then
  734.             species.topFitness = species.genomes[1].fitness
  735.             species.staleness = 0
  736.         else
  737.             species.staleness = species.staleness + 1
  738.         end
  739.         if species.staleness < StaleSpecies or species.topFitness >= pool.maxFitness then
  740.             table.insert(survived, species)
  741.         end
  742.     end
  743.  
  744.     pool.species = survived
  745. end
  746.  
  747. function removeWeakSpecies()
  748.     local survived = {}
  749.  
  750.     local sum = totalAverageFitness()
  751.     for s = 1,#pool.species do
  752.         local species = pool.species[s]
  753.         breed = math.floor(species.averageFitness / sum * Population)
  754.         if breed >= 1 then
  755.             table.insert(survived, species)
  756.         end
  757.     end
  758.  
  759.     pool.species = survived
  760. end
  761.  
  762.  
  763. function addToSpecies(child)
  764.     local foundSpecies = false
  765.     for s=1,#pool.species do
  766.         local species = pool.species[s]
  767.         if not foundSpecies and sameSpecies(child, species.genomes[1]) then
  768.             table.insert(species.genomes, child)
  769.             foundSpecies = true
  770.         end
  771.     end
  772.    
  773.     if not foundSpecies then
  774.         local childSpecies = newSpecies()
  775.         table.insert(childSpecies.genomes, child)
  776.         table.insert(pool.species, childSpecies)
  777.     end
  778. end
  779.  
  780. function newGeneration()
  781.     cullSpecies(false) -- Cull the bottom half of each species
  782.     rankGlobally()
  783.     removeStaleSpecies()
  784.     rankGlobally()
  785.     for s = 1,#pool.species do
  786.         local species = pool.species[s]
  787.         calculateAverageFitness(species)
  788.     end
  789.     removeWeakSpecies()
  790.     local sum = totalAverageFitness()
  791.     local children = {}
  792.     for s = 1,#pool.species do
  793.         local species = pool.species[s]
  794.         breed = math.floor(species.averageFitness / sum * Population) - 1
  795.         for i=1,breed do
  796.             table.insert(children, breedChild(species))
  797.         end
  798.     end
  799.     cullSpecies(true) -- Cull all but the top member of each species
  800.     while #children + #pool.species < Population do
  801.         local species = pool.species[math.random(1, #pool.species)]
  802.         table.insert(children, breedChild(species))
  803.     end
  804.     for c=1,#children do
  805.         local child = children[c]
  806.         addToSpecies(child)
  807.     end
  808.    
  809.     pool.generation = pool.generation + 1
  810.     writeFile(forms.gettext(saveLoadFile))
  811.     writeFile("backup." .. pool.generation .. "." .. forms.gettext(saveLoadFile))
  812. end
  813.    
  814. function initializePool()
  815.     if debugMode then console.writeline("Initializing Pool...") end
  816.     pool = newPool()
  817.  
  818.     if debugMode then console.writeline("Generating initial population... (" .. Population .. " Genomes)") end
  819.     for i=1,Population do
  820.        
  821.         basic = basicGenome()
  822.         addToSpecies(basic)
  823.     end
  824.     initializeRun()
  825. end
  826.  
  827. function clearJoypad()
  828.     controller = {}
  829.     for b = 1,#ButtonNames do
  830.         controller["P1 " .. ButtonNames[b]] = false
  831.     end
  832.     joypad.set(controller)
  833. end
  834.  
  835. function createPool()
  836.     if forms.ischecked(initSave) or not(file_exists(Filename)) then
  837.         console.writeline("Saving savestate: " .. Filename)
  838.         savestate.save(Filename)
  839.         forms.setproperty(initSave,"Checked", false)
  840.     end
  841.     initializePool()
  842.     savePool()
  843. end
  844.  
  845. function initializeRun()
  846.     if debugMode then console.writeline("Loading savestate: " .. Filename) end
  847.     savestate.load(Filename);
  848.     if debugMode then console.writeline("Setting basic parameters...") end
  849.     rightmost = 0
  850.     pool.currentFrame = 0
  851.     timeout = TimeoutConstant
  852.    
  853.     clearJoypad()
  854.    
  855.     if debugMode then console.writeline("Initializing species") end
  856.     local species = pool.species[pool.currentSpecies]
  857.    
  858.     if debugMode then console.writeline("Initializing genome") end
  859.     local genome = species.genomes[pool.currentGenome]
  860.     if debugMode then console.writeline("Generating network for current genome")end
  861.    
  862.     generateNetwork(genome)
  863.    
  864.     if debugMode then console.writeline("Setting initial current status")end
  865.     evaluateCurrent()
  866. end
  867.  
  868. function evaluateCurrent()
  869.     local species = pool.species[pool.currentSpecies]
  870.     local genome = species.genomes[pool.currentGenome]
  871.  
  872.     inputs = getInputs()
  873.     controller = evaluateNetwork(genome.network, inputs)
  874.    
  875.     if controller["P1 Left"] and controller["P1 Right"] then
  876.         controller["P1 Left"] = false
  877.         controller["P1 Right"] = false
  878.     end
  879.     if controller["P1 Up"] and controller["P1 Down"] then
  880.         controller["P1 Up"] = false
  881.         controller["P1 Down"] = false
  882.     end
  883.  
  884. end
  885.  
  886.  
  887. function nextGenome()
  888.     pool.currentGenome = pool.currentGenome + 1
  889.     if pool.currentGenome > #pool.species[pool.currentSpecies].genomes then
  890.         pool.currentGenome = 1
  891.         pool.currentSpecies = pool.currentSpecies+1
  892.         if pool.currentSpecies > #pool.species then
  893.             newGeneration()
  894.             pool.currentSpecies = 1
  895.         end
  896.     end
  897. end
  898.  
  899. function fitnessAlreadyMeasured()
  900.     local species = pool.species[pool.currentSpecies]
  901.     local genome = species.genomes[pool.currentGenome]
  902.    
  903.     return genome.fitness ~= 0
  904. end
  905.  
  906. function displayGenome(genome)
  907.     local network = genome.network
  908.     local cells = {}
  909.     local i = 1
  910.     local cell = {}
  911.     for dy=-BoxRadius,BoxRadius do
  912.         for dx=-BoxRadius,BoxRadius do
  913.             cell = {}
  914.             cell.x = 50+5*dx
  915.             cell.y = 70+5*dy
  916.             cell.value = network.neurons[i].value
  917.             cells[i] = cell
  918.             i = i + 1
  919.         end
  920.     end
  921.     local biasCell = {}
  922.     biasCell.x = 80
  923.     biasCell.y = 110
  924.     biasCell.value = network.neurons[Inputs].value
  925.     cells[Inputs] = biasCell
  926.    
  927.     for o = 1,Outputs do
  928.         cell = {}
  929.         cell.x = 220
  930.         cell.y = 30 + 8 * o
  931.         cell.value = network.neurons[MaxNodes + o].value
  932.         cells[MaxNodes+o] = cell
  933.         local color
  934.         if cell.value > 0 then
  935.             color = 0xFF0000FF
  936.         else
  937.             color = 0xFF000000
  938.         end
  939.         gui.drawText(223, 24+8*o, ButtonNames[o], color, 9)
  940.     end
  941.    
  942.     for n,neuron in pairs(network.neurons) do
  943.         cell = {}
  944.         if n > Inputs and n <= MaxNodes then
  945.             cell.x = 140
  946.             cell.y = 40
  947.             cell.value = neuron.value
  948.             cells[n] = cell
  949.         end
  950.     end
  951.    
  952.     for n=1,4 do
  953.         for _,gene in pairs(genome.genes) do
  954.             if gene.enabled then
  955.                 local c1 = cells[gene.into]
  956.                 local c2 = cells[gene.out]
  957.                 if gene.into > Inputs and gene.into <= MaxNodes then
  958.                     c1.x = 0.75*c1.x + 0.25*c2.x
  959.                     if c1.x >= c2.x then
  960.                         c1.x = c1.x - 40
  961.                     end
  962.                     if c1.x < 90 then
  963.                         c1.x = 90
  964.                     end
  965.                    
  966.                     if c1.x > 220 then
  967.                         c1.x = 220
  968.                     end
  969.                     c1.y = 0.75*c1.y + 0.25*c2.y
  970.                    
  971.                 end
  972.                 if gene.out > Inputs and gene.out <= MaxNodes then
  973.                     c2.x = 0.25*c1.x + 0.75*c2.x
  974.                     if c1.x >= c2.x then
  975.                         c2.x = c2.x + 40
  976.                     end
  977.                     if c2.x < 90 then
  978.                         c2.x = 90
  979.                     end
  980.                     if c2.x > 220 then
  981.                         c2.x = 220
  982.                     end
  983.                     c2.y = 0.25*c1.y + 0.75*c2.y
  984.                 end
  985.             end
  986.         end
  987.     end
  988.    
  989.     gui.drawBox(50-BoxRadius*5-3,70-BoxRadius*5-3,50+BoxRadius*5+2,70+BoxRadius*5+2,0xFF000000, 0x80808080)
  990.     for n,cell in pairs(cells) do
  991.         if n > Inputs or cell.value ~= 0 then
  992.             local color = math.floor((cell.value+1)/2*256)
  993.             if color > 255 then color = 255 end
  994.             if color < 0 then color = 0 end
  995.             local opacity = 0xFF000000
  996.             if cell.value == 0 then
  997.                 opacity = 0x50000000
  998.             end
  999.             color = opacity + color*0x10000 + color*0x100 + color
  1000.             gui.drawBox(cell.x-2,cell.y-2,cell.x+2,cell.y+2,opacity,color)
  1001.         end
  1002.     end
  1003.     for _,gene in pairs(genome.genes) do
  1004.         if gene.enabled then
  1005.             local c1 = cells[gene.into]
  1006.             local c2 = cells[gene.out]
  1007.             local opacity = 0xA0000000
  1008.             if c1.value == 0 then
  1009.                 opacity = 0x20000000
  1010.             end
  1011.            
  1012.             local color = 0x80-math.floor(math.abs(sigmoid(gene.weight))*0x80)
  1013.             if gene.weight > 0 then
  1014.                 color = opacity + 0x8000 + 0x10000*color
  1015.             else
  1016.                 color = opacity + 0x800000 + 0x100*color
  1017.             end
  1018.             gui.drawLine(c1.x+1, c1.y, c2.x-3, c2.y, color)
  1019.         end
  1020.     end
  1021.    
  1022.     gui.drawBox(49,71,51,78,0x00000000,0x80FF0000)
  1023.    
  1024.     if forms.ischecked(showMutationRates) then
  1025.         local pos = 100
  1026.         for mutation,rate in pairs(genome.mutationRates) do
  1027.             gui.drawText(100, pos, mutation .. ": " .. rate, 0xFF000000, 10)
  1028.             pos = pos + 8
  1029.         end
  1030.     end
  1031. end
  1032.  
  1033. function file_exists(name)
  1034.    local f=io.open(name,"r")
  1035.    if f~=nil then io.close(f) return true else return false end
  1036. end
  1037.  
  1038. function writeFile(filename)
  1039.     local file = io.open(filename, "w")
  1040.     if file == nil then
  1041.         console.writeline("Failed to open for write: " .. filename)
  1042.     else
  1043.     console.writeline("Saving " .. filename)
  1044.     file:write(pool.generation .. "\n")
  1045.     file:write(pool.maxFitness .. "\n")
  1046.     file:write(#pool.species .. "\n")
  1047.     if debugMode then console.write("Saving Species")end
  1048.         for n,species in pairs(pool.species) do
  1049.         if debugMode then console.write(".")end
  1050.         file:write(species.topFitness .. "\n")
  1051.         file:write(species.staleness .. "\n")
  1052.         file:write(#species.genomes .. "\n")
  1053.         for m,genome in pairs(species.genomes) do
  1054.             file:write(genome.fitness .. "\n")
  1055.             file:write(genome.maxneuron .. "\n")
  1056.             for mutation,rate in pairs(genome.mutationRates) do
  1057.                 file:write(mutation .. "\n")
  1058.                 file:write(rate .. "\n")
  1059.             end
  1060.             file:write("done\n")
  1061.            
  1062.             file:write(#genome.genes .. "\n")
  1063.             for l,gene in pairs(genome.genes) do
  1064.                 file:write(gene.into .. " ")
  1065.                 file:write(gene.out .. " ")
  1066.                 file:write(gene.weight .. " ")
  1067.                 file:write(gene.innovation .. " ")
  1068.                 if(gene.enabled) then
  1069.                     file:write("1\n")
  1070.                 else
  1071.                     file:write("0\n")
  1072.                 end
  1073.             end
  1074.         end
  1075.         end
  1076.         file:close()
  1077.         if debugMode then console.writeline("Saved.")end
  1078.     end
  1079. end
  1080.  
  1081. function savePool()
  1082.     if saveLoadFile == nil then
  1083.    
  1084.     else
  1085.         local filename = forms.gettext(saveLoadFile)
  1086.         writeFile(filename)
  1087.     end
  1088. end
  1089.  
  1090. function loadPool()
  1091.     pool = newPool()
  1092.     local filename = forms.gettext(saveLoadFile)
  1093.     local file = io.open(filename, "r")
  1094.     if file == nil then
  1095.         console.writeline("Couldn't load " .. filename)
  1096.     else
  1097.     if debugMode then console.writeline("Loading " .. filename)end
  1098.         pool.generation = file:read("*number")
  1099.         pool.maxFitness = file:read("*number")
  1100.         forms.settext(maxFitnessLabel, "Max Fitness: " .. math.floor(pool.maxFitness))
  1101.             local numSpecies = file:read("*number")
  1102.             if debugMode then console.write("Loading Species")end
  1103.             for s=1,numSpecies do
  1104.             if debugMode then console.write(".")end
  1105.             local species = newSpecies()
  1106.             table.insert(pool.species, species)
  1107.             species.topFitness = file:read("*number")
  1108.             species.staleness = file:read("*number")
  1109.             local numGenomes = file:read("*number")
  1110.             for g=1,numGenomes do
  1111.                 local genome = newGenome()
  1112.                 table.insert(species.genomes, genome)
  1113.                 genome.fitness = file:read("*number")
  1114.                 genome.maxneuron = file:read("*number")
  1115.                 local line = file:read("*line")
  1116.                 while line ~= "done" do
  1117.                     genome.mutationRates[line] = file:read("*number")
  1118.                     line = file:read("*line")
  1119.                 end
  1120.                 local numGenes = file:read("*number")
  1121.                 for n=1,numGenes do
  1122.                     local gene = newGene()
  1123.                     table.insert(genome.genes, gene)
  1124.                     local enabled
  1125.                     gene.into, gene.out, gene.weight, gene.innovation, enabled = file:read("*number", "*number", "*number", "*number", "*number")
  1126.                     if enabled == 0 then
  1127.                         gene.enabled = false
  1128.                     else
  1129.                         gene.enabled = true
  1130.                     end
  1131.                    
  1132.                 end
  1133.             end
  1134.         end
  1135.         if debugMode then console.writeline("Loaded.")end
  1136.             file:close()
  1137.    
  1138.     end
  1139.    
  1140.     while fitnessAlreadyMeasured() do
  1141.         nextGenome()
  1142.     end
  1143.     forceNewRun = true
  1144.     console.writeline("Loaded " .. filename)
  1145. end
  1146.  
  1147. function playTop()
  1148.     local maxfitness = 0
  1149.     local maxs, maxg
  1150.     for s,species in pairs(pool.species) do
  1151.         for g,genome in pairs(species.genomes) do
  1152.             if genome.fitness > maxfitness then
  1153.                 maxfitness = genome.fitness
  1154.                 maxs = s
  1155.                 maxg = g
  1156.             end
  1157.         end
  1158.     end
  1159.    
  1160.     pool.currentSpecies = maxs
  1161.     pool.currentGenome = maxg
  1162.     pool.maxFitness = maxfitness
  1163.     forms.settext(maxFitnessLabel, "Max Fitness: " .. math.floor(pool.maxFitness))
  1164.     console.writeline("Best run: Spec:" .. maxs .. " Gnm:" .. maxg)
  1165.     forceNewRun = true
  1166.    
  1167.     return
  1168. end
  1169.  
  1170. function onExit()
  1171.     forms.destroy(form)
  1172. end
  1173.  
  1174.  
  1175. event.onexit(onExit)
  1176.  
  1177. form = forms.newform(200, 265, "Fitness")
  1178. maxFitnessLabel = forms.label(form, "Max Fitness: ?" , 5, 8)
  1179. showNetwork = forms.checkbox(form, "Show Network", 5, 30)
  1180. showMutationRates = forms.checkbox(form, "Show M-Rates", 5, 52)
  1181. --restartButton = forms.button(form, "Reset AI", initializePool, 5, 77)
  1182. --initButton = forms.button(form, "Init", createPool, 80, 77)
  1183. initSave = forms.checkbox(form, "Init Save", 80, 77)
  1184. resetAI = forms.checkbox(form, "Reset AI", 5, 77)
  1185. saveButton = forms.button(form, "Save", savePool, 5, 102)
  1186. loadButton = forms.button(form, "Load", loadPool, 80, 102)
  1187. saveLoadFile = forms.textbox(form, Filename .. ".pool", 170, 25, nil, 5, 148)
  1188. saveLoadLabel = forms.label(form, "Save/Load:", 5, 129)
  1189. playTopButton = forms.button(form, "Play Top", playTop, 5, 170)
  1190. showBanner = forms.checkbox(form, "Show Banner", 5, 190)
  1191. debugOutput = forms.checkbox(form, "ConsoleDebug", 5, 210)
  1192. enableRun = forms.checkbox(form,"Run",130,210)
  1193.  
  1194.  
  1195. local species
  1196. local genome
  1197.    
  1198. while not (forms.gettext(form) == "") do
  1199.     debugMode = forms.ischecked(debugOutput)
  1200.    
  1201.     --Check if we're enabled
  1202.     if forms.ischecked(enableRun) then
  1203.         -- Some pre-checks to ensure we /can/ run
  1204.  
  1205.         if pool == nil  or forms.ischecked(resetAI) then
  1206.             if pool == null then console.writeline("Huh. Trying to run without a gene pool!") end
  1207.            
  1208.  
  1209.             if forms.ischecked(initSave) or not(file_exists(Filename)) then
  1210.                 --No prior pool
  1211.                 console.writeline("I'm going to create one then.")
  1212.                 createPool()
  1213.             else
  1214.                 loadPool()
  1215.             end
  1216.            
  1217.             forms.setproperty(resetAI,"Checked",false)
  1218.            
  1219.             --Set the initial MaxFitness
  1220.             forms.settext(maxFitnessLabel, "Max Fitness: " .. math.floor(pool.maxFitness))
  1221.         end    
  1222.        
  1223.         if forceNewRun then
  1224.             forceNewRun = false
  1225.             console.writeline("New run called.")
  1226.             initializeRun()
  1227.             pool.currentFrame = pool.currentFrame + 1
  1228.         end
  1229.        
  1230.        
  1231.         species = pool.species[pool.currentSpecies]
  1232.         genome = species.genomes[pool.currentGenome]
  1233.        
  1234.         if pool.currentFrame%5 == 0 then
  1235.             evaluateCurrent()
  1236.         end
  1237.  
  1238.         joypad.set(controller)
  1239.  
  1240.         getPositions()
  1241.         if marioX > rightmost then
  1242.             rightmost = marioX
  1243.             timeout = TimeoutConstant
  1244.         end
  1245.        
  1246.         timeout = timeout - 1
  1247.        
  1248.        
  1249.         local timeoutBonus = pool.currentFrame / 4
  1250.         if timeout + timeoutBonus <= 0 then
  1251.             local fitness = rightmost - pool.currentFrame / 2
  1252.             if romtype_SMW and rightmost > 4816 then
  1253.                 fitness = fitness + 1000
  1254.             end
  1255.             if (not romtype_SMW) and rightmost > 3186 then
  1256.                 fitness = fitness + 1000
  1257.             end
  1258.             if fitness == 0 then
  1259.                 fitness = -1
  1260.             end
  1261.             genome.fitness = fitness
  1262.            
  1263.             if fitness > pool.maxFitness then
  1264.                 pool.maxFitness = fitness
  1265.                 forms.settext(maxFitnessLabel, "Max Fitness: " .. math.floor(pool.maxFitness))
  1266.                 if debugMode then console.writeline("Saving Pool (New High Fit)") end
  1267.    
  1268.                 writeFile(forms.gettext(saveLoadFile))
  1269.             end
  1270.            
  1271.             console.writeline("Gen " .. pool.generation .. " Spec " .. pool.currentSpecies .. " Gnm " .. pool.currentGenome .. " Fit: " .. fitness)
  1272.             pool.currentSpecies = 1
  1273.             pool.currentGenome = 1
  1274.             while fitnessAlreadyMeasured() do
  1275.                 nextGenome()
  1276.             end
  1277.             initializeRun()
  1278.         end
  1279.  
  1280.         local measured = 0
  1281.         local total = 0
  1282.         for _,species in pairs(pool.species) do
  1283.             for _,genome in pairs(species.genomes) do
  1284.                 total = total + 1
  1285.                 if genome.fitness ~= 0 then
  1286.                     measured = measured + 1
  1287.                 end
  1288.             end
  1289.         end
  1290.            
  1291.         pool.currentFrame = pool.currentFrame + 1
  1292.         local backgroundColor = 0xD0FFFFFF
  1293.         if forms.ischecked(showBanner) then
  1294.             gui.drawBox(0, 0, 300, 26, backgroundColor, backgroundColor)
  1295.             gui.drawText(0, 0, "Gen " .. pool.generation .. " species " .. pool.currentSpecies .. " genome " .. pool.currentGenome .. " (" .. math.floor(measured/total*100) .. "%)", 0xFF000000, 11)
  1296.             gui.drawText(0, 12, "Fitness: " .. math.floor(rightmost - (pool.currentFrame) / 2 - (timeout + timeoutBonus)*2/3), 0xFF000000, 11)
  1297.             gui.drawText(100, 12, "Max Fitness: " .. math.floor(pool.maxFitness), 0xFF000000, 11)
  1298.         end
  1299.     elseif forms.ischecked(showNetwork) then
  1300.         evaluateCurrent()
  1301.        
  1302.     end
  1303.     --Draw the GUI elements
  1304.    
  1305.  
  1306.     if forms.ischecked(showNetwork) then
  1307.         displayGenome(genome)
  1308.     end
  1309.  
  1310.  
  1311.     emu.frameadvance();
  1312. end
  1313. console.writeline("Form closed. Terminating script.")
Advertisement
Add Comment
Please, Sign In to add comment