Advertisement
Code-Akisame

LuigI/O v5 SMB3

Apr 11th, 2020
1,234
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 112.68 KB | None | 0 0
  1. require "drawlib"
  2. require "drawtext"
  3.  
  4. levelname = "1-1"
  5.  
  6. savestateSlotMap = 1 --savestate slot to save the map selection from.
  7. savestateSlotLevel = 2
  8. savestateSlotBOSS = 3
  9. savestateObj = savestate.object(savestateSlotMap)
  10. savestate.load(savestateObj)
  11. LM = memory.readbyte(0x0726)
  12. Player = 1 + LM
  13.  
  14. BoxRadius = 6 --The radius of the vision box around Luigi
  15. InitialOscillations = {50,60,90} --Initial set of oscillation timers
  16. BoxWidth = BoxRadius*2+1 --Full width of the box
  17. BoxSize = BoxWidth*BoxWidth --Number of neurons in the box
  18. Inputs = BoxSize + 3 + #InitialOscillations
  19.  
  20. InitialMutationRates = {
  21.     linkInputBox=0.1,
  22.     linkBottomRow=0.05,
  23.     linkLayers=0.1,
  24.     linkRandom=2.0,
  25.     node=0.5,
  26.     disable=0.4,
  27.     enable=0.2,
  28.     oscillation=0.2,
  29.     nswitch=0.01,
  30.     step=0.1
  31. }
  32.  
  33. FramesPerEval = 5 --Number of frames between each network update
  34.  
  35. ButtonNames = {"A","B","up","down","left","right"}
  36. ButtonNumbers = {A=1,B=2,up=3,down=4,left=5,right=6}
  37.  
  38. DeltaDisjoint = 4.0 --multiplier for disjoint in same species function
  39. DeltaWeights = 0.4 --multiplier for weights in same species function
  40. DeltaThreshold = 1.0 --threshold for delta to be in the same species
  41. DeltaSwitch = 0.6 --threshold for delta of the network switch location
  42.  
  43. DisplaySlots = false --Display the sprite slots?
  44. DisplaySprites = false --Display the sprite hitboxes?
  45. DisplayGrid = 0 --Display a large network inputs grid?
  46. DisplayNetwork = true --Display the neural network state?
  47. DisplayStats = true --Display top stats bar?
  48. DisplayRanges = false --Display special fitness ranges?
  49. DisplayCounters = false --Display death/time counter?
  50. ScrollWalkPause = false
  51.  
  52. Replay = false
  53.  
  54. ManualInput = false --Input manually rather than by network?
  55.  
  56. --Penalty coeffs for fitness rank of netswitched species
  57. NetworkPenaltyInner = 0.8
  58. NetworkPenaltyOuter = 0.87
  59.  
  60. CrossoverChance = 0.75 --Chance of crossing 2 genomes rather than copying 1
  61.  
  62. MajorBreakFitness = 30 --Fitness before a breakthrough is not counted as minor
  63.  
  64. BaseInterbreedChance = 0.07 --base interbreed chance per species
  65. InterbreedCutoff = 2.86 --average species size when interbreed is turned off
  66. InterbreedDegree = 1.0 --degree of interbreed curve
  67.  
  68. TimeBeforePopOscil = 6 --time in each oscillation before the population changes
  69.  
  70. MaxStaleness = 25 --max staleness before death
  71.  
  72. FramesOfDeathAnimation = 50 --amount of the death animation to show
  73.  
  74. WeightPerturbChance = 0.9
  75. WeightResetChance = 0.9
  76.  
  77. MaxNodes = 1000000
  78.  
  79. FPS = 60
  80.  
  81. TurboMin = 0
  82. TurboMax = 0
  83. CompleteAutoTurbo = false
  84. currentTurbo = false
  85. emu.speedmode("normal")
  86. marioAutoscroll = 0
  87. basescroll = 0
  88. marioWhiteblock = 0
  89. boss = false
  90. battle = false
  91. basetimeout = 120
  92. roottimeout = 120
  93. preloaded = false
  94. cardDistance = 9999
  95. powerupFitness = 150
  96. roottimeoutboss = 900
  97.  
  98. dirsep = "\\" --forward or backward slash for file separation? depends on OS
  99.  
  100. ProgramStartTime = os.time()
  101. Startlocations={["0-1-2"]=true,["1-1-5"]=true,["2-1-5"]=true,["3-1-2"]=true,["4-1-4"]=true,["5-1-3"]=true,["6-1-1.5"]=true,["7-1-2.5"]=true}
  102.  
  103. marioPrevWhiteblock = 0
  104. marioPrevAutoscroll = 0
  105. scrollBonus = 0
  106. scrollBonusAdjust = 0
  107. lockBound = false
  108. function getPositions(initial)
  109.     local marioHiX = memory.readbyte(0x0075)
  110.     local marioLoX = memory.readbyte(0x0090)
  111.     marioX = marioLoX+256*marioHiX
  112.     marioScreenX = memory.readbyte(0x00AB)
  113.     marioXVel = memory.readbyte(0x00BD)
  114.     marioY = 400-(memory.readbyte(0x00A2)+256*memory.readbyte(0x0087))
  115.     marioScreenY = memory.readbyte(0xB4)--memory.readbyte(0x0228)
  116.     marioYVel = memory.readbyte(0x00CF)
  117.     marioScore = memory.readbyte(0x0716)*2560 + memory.readbyte(0x0717)*10
  118.     marioActive = memory.readbyte(0x00CE) == 0 and memory.readbyte(0x03DE) == 0 --loading and using pipe/door
  119.     local marioWhiteblock = memory.readbyte(0x0570)
  120.     local marioAutoscroll = memory.readbyte(0x7A0C)
  121.     --print(marioScreenY)
  122.     --local marioAutoscrollY = memory.readbyte(0x7A0D)
  123.     marioHold = memory.readbyte(0x06A4)
  124.     marioPower = memory.readbyte(0x00ED)
  125.     marioMusic = memory.readbyte(0x04E5)
  126.     koopalingDefeated = memory.readbyte(0x07BD)
  127.     kingconvo = memory.readbyte(0x0728)
  128.     local whiteblockAdjustment = 0
  129.     if (not marioActive and marioScreenY > 184) or doorflag then
  130.         doorflag = true
  131.         marioOutOfBound = false
  132.         if marioActive and marioScreenY < 184 then
  133.             doorflag = false
  134.         end
  135.     else
  136.         marioOutOfBound = (marioY < 0 or (marioScreenY > 184 and memory.readbyte(0x0544) == 0 and cardDistance > 0.5)) and marioActive -- byte is for above screen
  137.     end
  138.     if marioPrevWhiteblock >100 and marioWhiteblock < -100 then
  139.         whiteblockAdjustment = 256
  140.     else
  141.         whiteblockAdjustment = 0
  142.     end
  143.     marioPrevWhiteblock = marioWhiteblock
  144.     WhiteblockBonus = whiteblockAdjustment + marioWhiteblock*10
  145.     if scrollBonus == 0 and not marioActive then
  146.         -- scrollBonusAdjust = -marioAutoscroll
  147.         memory.writebyte(0x7A0C,0)
  148.         --basemarioA
  149.         local marioAutoscroll = 0
  150.     end
  151.     if marioAutoscroll == 0 and marioPrevAutoscroll ~= 0 and scrollBonus > 0 then
  152.         basescroll = basescroll + marioPrevAutoscroll
  153.     -- elseif marioAutoscroll == marioPrevAutoscroll and scrollBonus > 0 then
  154.         -- scrollBonus = 0
  155.         -- basescroll = 0
  156.     end
  157.     scrollBonus = basescroll + marioAutoscroll + scrollBonusAdjust
  158.     marioPrevAutoscroll = marioAutoscroll
  159.     local numSprites = 6
  160.     sprites = {}
  161.     for s=1,numSprites do
  162.         local sprite = {}
  163.         sprite.x = memory.readbyte(0x0090+s)+256*memory.readbyte(0x0075+s)
  164.         sprite.y = 416-(memory.readbyte(0x00A2+s)+256*memory.readbyte(0x0087+s))
  165.         sprite.state = memory.readbyte(0x0660+s)
  166.         sprite.type = memory.readbyte(0x670+s)
  167.         if sprite.type == 54 then
  168.             table.insert(sprites,{x=sprite.x+16,y=sprite.y,state=256})
  169.             table.insert(sprites,{x=sprite.x+32,y=sprite.y,state=256})
  170.             --print(sprite)
  171.         end
  172.         if sprite.type == 14 then
  173.             marioScore = marioScore + memory.readbyte(0x0083)*1000
  174.         end
  175.         if sprite.type == 74 or sprite.type == 82 then --added this because luigi does not want to pick up the ball once he defeats boomboom
  176.             if sprite.x - marioX > 0 then
  177.                 for a=1,30 do
  178.                     joypad.set(Player,{right=true})
  179.                    
  180.                     coroutine.yield()
  181.                 end
  182.             else
  183.                 for a=1,30 do
  184.                     joypad.set(Player,{left=true})
  185.                     coroutine.yield()
  186.                 end
  187.             end
  188.         end
  189.         table.insert(sprites,sprite)
  190.     end
  191.     for s=1,8 do --special sprites
  192.         local sprite = {}
  193.         local id = memory.readbyte(0x7FC5+s)
  194.         if id ~= 0 then
  195.             sprite.type = id
  196.             sprite.y = 416-memory.readbyte(0x05BE+s)+256*memory.readbyte(0x7FD4+s)
  197.             local xlow = memory.readbyte(0x05C8+s)
  198.             local xhigh = 0
  199.             local differ = xlow-marioLoX
  200.             if differ > 128 then
  201.                 xhigh = marioHiX -1
  202.             elseif differ < -128 then
  203.                 xhigh = marioHiX +1
  204.             else
  205.                 xhigh = marioHiX
  206.             end
  207.             sprite.x = xlow + 256*xhigh
  208.             sprite.state=256
  209.             table.insert(sprites,sprite)
  210.         end
  211.     end
  212. end
  213.  
  214. function getTile(dx,dy)
  215.     local x = math.floor((marioX + dx + 8)/16)
  216.     local y = math.floor((432-marioY + dy - 8)/16)
  217.     local page = math.floor(x/16)
  218.     local px = x % 16
  219.     local tile = memory.readbyte(0x6000+px+y*16+page*432)
  220.     --print(x .. " " .. marioScreenX/16)
  221.     local scrollX = (marioX-marioScreenX)/16
  222.     local scrollY = (marioY+marioScreenY-161)/16
  223.     if x < scrollX or x > scrollX + 16 or 26-y < scrollY or 26-y > scrollY + 11 then return 0 end
  224.     return tile
  225. end
  226.  
  227. CollisionTileSet = {}
  228. -- CollisionTileArrays[1]={air=128,0x25,0x26,0x27,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0xA0,0xA1,0xA2,0xAD,0xAE,0xAF,0xB1,0xB2,0xB3,0xB4,0xB5,0xB5,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xE2,0xE3,0xE4,0xF0,0xF1,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9}
  229. -- CollisionTileArrays[3]={air=134,0x2E,0x2F,0x30,0x31,0x32,0x48,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x87,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xF0,0xF1,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9}
  230. -- CollisionTileArrays[4]={air=128,0x11,0x12,0x13,0x22,0x23,0x24,0x25,0x2C,0x2E,0x2F,0x30,0x31,0x32,0x34,0x35,0x36,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x5B,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8E,0x8F,0x90,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xFA,0xFB}
  231. CollisionTileArray= {0x80,0x02,0x86,0x80,0x80,0x8C,0x42,0x80,0x80,0x80,0x80,0x80,0xCC,0x86,0x09,0x09,0x09,0x02}
  232.  
  233. function getTileset()
  234.     local tileset = memory.readbyte(0x070A)
  235.     local x1a = memory.readbyte(0x7E94)
  236.     local x1b = memory.readbyte(0x7E95)
  237.     local x1c = memory.readbyte(0x7E96)
  238.     local x1d = memory.readbyte(0x7E97)
  239.     card_x = nil
  240.     for j=1,48 do
  241.         local id = memory.readbyte(0x07B3E+(3*j))
  242.         if id == 0x41 then --0x41 is end card
  243.        
  244.             card_x = memory.readbyte(0x07B3E+(3*j)+1)*16
  245.             card_y = memory.readbyte(0x07B3E+(3*j)+2)*16
  246.         end
  247.         if id == 0xFF then --ff indicates last entry
  248.             if card_x == nil then
  249.                 card_x = 9999
  250.                 card_y = 9999
  251.             end
  252.             break
  253.         end
  254.     end
  255.     CollisionTileSet = {a=x1a,b=x1b,c=x1c,d=x1d}
  256.     CollisionTileSet.air=CollisionTileArray[tileset]
  257. end
  258.  
  259. function getInputs() --Create the grid around Mario that is the network's vision
  260.     getPositions() --Get all required memory data
  261.     local inputs = {} --Grid around Mario that shows blocks/sprites
  262.     --Loop through each space in the grid
  263.     local BoxRadius = BoxRadius
  264.     local BoxWidth = BoxWidth
  265.     local CollisionTileSet = CollisionTileSet
  266.     local marioX = marioX
  267.     local marioY = marioY
  268.     local sprites = sprites
  269.     for dy=-BoxRadius*16,BoxRadius*16,16 do
  270.         for dx=-BoxRadius*16,BoxRadius*16,16 do
  271.             inputs[#inputs+1] = 0
  272.             --If the tile contains a block, set to 1
  273.             local tile = getTile(dx, dy)
  274.             if tile ~= CollisionTileSet.air and marioY+dy < 0x1B0 then
  275.                 if (tile >= CollisionTileSet.a and tile < 0x40) or (tile >= CollisionTileSet.b and tile < 0x80) or (tile >= CollisionTileSet.c and tile < 0xC0) or (tile >= CollisionTileSet.d and tile < 0xFF) then inputs[#inputs] = 1
  276.                 else inputs[#inputs] = 0.5
  277.                 end
  278.             end
  279.         end
  280.     end
  281.     --for each sprite set it's location to -1
  282.     local spriteNearlocal = false
  283.     for i,sprite in ipairs(sprites) do
  284.         if sprite.state ~= 0 then
  285.             local distx = math.floor((sprite.x - marioX)/16 + 0.5)
  286.             if math.abs(distx) < 3.5 then
  287.                 spriteNearlocal = true
  288.             end
  289.             local disty = math.floor((-sprite.y + marioY)/16 + 0.5)
  290.             if math.abs(distx)<=BoxRadius and math.abs(disty)<=BoxRadius then
  291.                 inputs[distx+BoxRadius+1+(disty+BoxRadius)*BoxWidth] = -1
  292.             end
  293.         end
  294.     end
  295.     spriteNear = spriteNearlocal
  296.     return inputs
  297. end
  298.  
  299. --Initialization for the genetic system
  300. function initPool(Boss) --The pool contains all data for the genetics of the AI
  301.     local poolold = pool
  302.     pool = {}
  303.     pool.species = {} --List of species
  304.     pool.generation = 0 --Generation number
  305.     pool.innovation = 1 --Innovation tracks genes that are copies of each other vs unique
  306.     pool.currentGenome = 1 --current genome/species to display on top bar
  307.     pool.currentSpecies = 1
  308.     pool.maxFitness = 0 --Maximum fitness
  309.     pool.bestSpecies = 1 --best species
  310.     pool.secondFitness = 0 --Second best fitness
  311.     pool.maxCounter = 0 --Number of times it has gotten close to max fitness
  312.     pool.gsid = 0 --GSID tracks species so each one gets a unique id
  313.     pool.bottleneck = 0 --Number of gens since last breakthrough
  314.     pool.bottleneckloops = 0 --Number of oscillation loops in the bottleneck
  315.     pool.population = 999 --Number of genomes
  316.     pool.lastMajorBreak = 0 --Last breakthrough of more than majorBreakFitness
  317.     pool.current = 0
  318.     pool.total = 0
  319.     pool.average = 0
  320.     pool.lastbreaktime = 0
  321.     if not Boss then
  322.         pool.attempts = 1 --number of attempts
  323.         pool.deaths = 0 --number of deaths (auto timeouts do not count)
  324.         pool.totalTime = 0 --total time elapsed in-game
  325.         pool.realTime = 0 --total time elapsed out of game
  326.        
  327.         pool.history = "" --breakthrough tracker
  328.         pool.breakthroughX = 0 --indicator stuff
  329.         pool.breakthroughZ = ""
  330.         pool.maphistogram = {}
  331.         pool.breakthroughfiles = {}
  332.     else
  333.         pool.attempts = poolold.attempts
  334.         pool.deaths = poolold.deaths
  335.         pool.totalTime = poolold.totalTime
  336.         pool.realTime = poolold.realTime
  337.         pool.history = poolold.history
  338.         pool.breakthroughX = poolold.breakthroughX
  339.         pool.breakthroughZ = poolold.breakthroughZ
  340.         pool.maphistogram = poolold.maphistogram
  341.         pool.breakthroughfiles = poolold.breakthroughfiles
  342.     end
  343. end
  344.  
  345. function newSpecies() --Each species is a group of genomes that are similar
  346.     local species = {}
  347.     species.maxFitness = 0 --farthest the species has gotten
  348.     species.maxRightmost = 0 --Farthest the species has gotten, ignoring time
  349.     species.averageFitness = 0 --Average fitness of the species
  350.     species.staleness = 0 --Number of gens since the species improved
  351.     species.genomes = {} --List of genomes
  352.     species.nick = '' --nickname of the species
  353.     species.turbo = true --whether the species will use turbo or not
  354.     species.gsid = pool.gsid --give the species a unique GSID
  355.     pool.gsid = pool.gsid + 1
  356.     species.breakthroughX = 0 --indicator stuff
  357.     species.breakthroughZ = ""
  358.     return species
  359. end
  360.  
  361. function newGene() --A gene is a link between two neurons
  362.     local gene = {}
  363.     gene.into = 0 --input neuron
  364.     gene.out = 0 --output neuron
  365.     gene.weight = 0.0 --multiplier
  366.     gene.enabled = true --gene is turned on
  367.     gene.innovation = 0 --gene ID to find duplicates
  368.     gene.delay = 0
  369.     return gene
  370. end
  371.  
  372. function copyGene(gene) --copy a gene
  373.     local newGene = {}
  374.     newGene.into = gene.into
  375.     newGene.out = gene.out
  376.     newGene.weight = gene.weight
  377.     newGene.enabled = gene.enabled
  378.     newGene.innovation = gene.innovation
  379.     newGene.delay = gene.delay
  380.     return newGene
  381. end
  382.  
  383. function newGenome(numNetworks) --Each genome is an evolved 'player' that attempts the level
  384.     local genome = {}
  385.     genome.genes = {} --List of networks in raw gene form
  386.     genome.networks = {} --List of networks in neuron form
  387.     genome.oscillations = {} --Periods of each of 3 oscillating nodes
  388.     genome.maxNeuron = Inputs --Index of largest neuron
  389.     for n=1,numNetworks do --Initialize these as blank arrays for each network
  390.         genome.genes[n] = {}
  391.         genome.oscillations[n] = {}
  392.         for i=1,#InitialOscillations do --Initialize periods to default values
  393.             genome.oscillations[1][i] = InitialOscillations[i]
  394.         end
  395.     end
  396.     genome.fitstate = {} --information about the genome's final state
  397.     genome.globalRank = 0 --Ranking of the genome (low is worse)
  398.     genome.networkswitch = {} --List of positions where the network switches
  399.     for n=1,numNetworks-1 do
  400.         genome.networkswitch[n] = 0.0
  401.     end
  402.     genome.mutationRates = {} --List of mutation rates
  403.     for name,rate in pairs(InitialMutationRates) do
  404.         genome.mutationRates[name] = rate
  405.     end
  406.     return genome
  407. end
  408.  
  409. function copyGenome(genome) --copy a genome
  410.     local ngenome = newGenome(#genome.genes) --create basic new genome
  411.     for n=1,#genome.genes do --copy each network over
  412.         for i,gene in pairs(genome.genes[n]) do
  413.             table.insert(ngenome.genes[n],copyGene(gene))
  414.         end
  415.         for i=1,#genome.oscillations[n] do
  416.             ngenome.oscillations[n][i] = genome.oscillations[n][i]
  417.         end
  418.     end
  419.     ngenome.maxNeuron = genome.maxNeuron
  420.     for n=1,#genome.genes-1 do
  421.         ngenome.networkswitch[n] = genome.networkswitch[n]
  422.     end
  423.     for name,rate in pairs(genome.mutationRates) do
  424.         ngenome.mutationRates[name] = rate
  425.     end
  426.     return ngenome
  427. end
  428.  
  429. function newNeuron() --A neuron is a stored value that is calculated based on inputs and broadcasts to its outputs
  430.     local neuron = {}
  431.     neuron.incoming = {} --list of incoming genes
  432.     neuron.queues = {} -- incoming queues for delay
  433.     neuron.value = 0.0 --current value
  434.     neuron.layer = 0 --What layer the neuron is in (0 means it has not been calculated)
  435.     return neuron
  436. end
  437.  
  438. --Evaluation of networks
  439. function generateNetwork(genome)
  440.     for n=1,#genome.genes do --For each network
  441.         local neurons = {}
  442.         for i=1,Inputs do --Put in input nodes
  443.             neurons[i] = newNeuron()
  444.         end
  445.         for g=1,#genome.genes[n] do --Loop through each gene
  446.             local gene = genome.genes[n][g]
  447.             if gene.enabled then --Only add enabled genes
  448.                 --If either side of gene is not added to neurons yet then add it
  449.                 if neurons[gene.into] == nil then
  450.                     neurons[gene.into] = newNeuron()
  451.                 end
  452.                 if neurons[gene.out] == nil then
  453.                     neurons[gene.out] = newNeuron()
  454.                 end
  455.                 if gene.into > 0 then
  456.                     table.insert(neurons[gene.out].incoming,gene) --Add gene to the output neuron's incoming
  457.                     queue = {}
  458.                     for i=1,gene.delay do
  459.                         table.insert(queue,0)
  460.                     end
  461.                     table.insert(neurons[gene.out].queues,queue)
  462.                 end
  463.             end
  464.         end
  465.        
  466.         local layers = {}
  467.         while true do --loop until all layers found
  468.             local layer = {}
  469.             local finished = true --if all have been sorted
  470.             for i,neuron in pairs(neurons) do --loop through and find all possible for calculation
  471.                 if neuron.layer == 0 then --Dont re-check already placed neurons
  472.                     local possible = true
  473.                     for j,gene in pairs(neuron.incoming) do --see if any incoming genes are not calculated yet
  474.                         if neurons[gene.into].layer == 0 then
  475.                             possible = false
  476.                             break
  477.                         end
  478.                     end
  479.                     if possible then --if has enough information to evaluate
  480.                         table.insert(layer,i)
  481.                         finished = false --a neuron was placed so it has not been all sorted yet
  482.                     end
  483.                 end
  484.             end
  485.             if finished then --exit the loop
  486.                 break
  487.             end
  488.             for i=1,#layer do --For each neuron in the new layer set it to now be sorted
  489.                 neurons[layer[i]].layer = #layers+1
  490.             end
  491.             table.insert(layers,layer)
  492.         end
  493.         local network = {}
  494.         network.neurons = neurons
  495.         network.layers = layers
  496.         network.genes = genome.genes[n]
  497.         genome.networks[n] = network
  498.     end
  499. end
  500.  
  501.  
  502. function sigmoid(x)
  503.     return 2/(1+math.exp(-4.9*x))-1
  504. end
  505.        
  506.  
  507. function evaluateNetwork(network, inputs, oscillating, frame)
  508.     table.insert(inputs,1.0) --Bias node
  509.     local marioXVel = marioXVel
  510.     local marioYVel = marioYVel
  511.     local ScrollWalkPauselocal = ScrollWalkPause
  512.     local marioScreenX = marioScreenX
  513.     local endscroll = endscroll
  514.     local spriteNear = spriteNear
  515.     local ButtonNames = ButtonNames
  516.     local scrollBonus = scrollBonus
  517.     local Inputs = Inputs
  518.     local mmax = math.max
  519.     for i=1,#oscillating do --Oscillating nodes
  520.         if frame % (oscillating[i]*2) < oscillating[i] then
  521.             table.insert(inputs,1.0)
  522.         else
  523.             table.insert(inputs,-1.0)
  524.         end
  525.     end
  526.     --Speed nodes
  527.     if (marioXVel < 100) then
  528.         table.insert(inputs,marioXVel / 56)
  529.     else
  530.         table.insert(inputs,(marioXVel - 256) / 56)
  531.     end
  532.     if (marioYVel < 70) then
  533.         table.insert(inputs,marioYVel / 69)
  534.     else
  535.         table.insert(inputs,mmax((marioYVel - 256) / 69,1))
  536.     end
  537.    
  538.     if #inputs ~= Inputs then
  539.         emu.print("Incorrect number of neural network inputs.")
  540.         return {}
  541.     end
  542.    
  543.     for i=1,Inputs do
  544.         network.neurons[i].value = inputs[i]
  545.     end
  546.    
  547.     -- for _,neuron in pairs(network.neurons) do
  548.         -- local sum = 0
  549.         -- for j = 1,#neuron.incoming do
  550.             -- local incoming = neuron.incoming[j]
  551.             -- local other = network.neurons[incoming.into]
  552.             -- sum = sum + incoming.weight * other.value
  553.         -- end
  554.        
  555.     -- end
  556.     for l=2,#network.layers do
  557.         local layer = network.layers[l]
  558.         for n=1,#layer do
  559.             local neuron = network.neurons[layer[n]]
  560.             local sum = 0.0
  561.             for j = 1,#neuron.incoming do
  562.                 local incoming = neuron.incoming[j]
  563.                 local other = network.neurons[incoming.into]
  564.                 if incoming.delay == 0 then
  565.                     sum = sum + incoming.weight * other.value
  566.                 else
  567.                     sum = sum + incoming.weight * neuron.queues[j][1]
  568.                     for i=1,#neuron.queues[j]-1 do
  569.                         neuron.queues[j][i] = neuron.queues[j][i+1]
  570.                     end
  571.                     neuron.queues[j][#neuron.queues[j]] = other.value
  572.                 end
  573.             end
  574.             neuron.value = sigmoid(sum)
  575.         end
  576.     end
  577.     local output = {}
  578.     --print("Yvel " .. marioYVel)
  579.     --print("Xvel " .. marioXVel)
  580.     --print("x " .. marioScreenX)
  581.     if (ScrollWalkPauselocal and (marioScreenX < 45 or (marioYVel > 5 and marioYVel < 100) or spriteNear)) or endscroll then
  582.         ScrollWalkPause = false
  583.         ScrollWalkPauselocal = false
  584.         --print("off")
  585.     end
  586.     if ((scrollBonus > 0 and marioScreenX > 200 and (marioYVel >240 or marioYVel < 5)) or ScrollWalkPauselocal) and not endscroll then
  587.         --print("first")
  588.         for o=1,#ButtonNames do
  589.             output[ButtonNames[o]] = false
  590.         end
  591.         if marioXVel > 10 and marioXVel < 128 then
  592.             output["left"] = true
  593.         end
  594.         if marioXVel > 128 and marioXVel < 245 then
  595.             output["right"] = true
  596.         end
  597.         ScrollWalkPause = true
  598.         ScrollWalkPauselocal = true
  599.     elseif (scrollBonus > 0 and marioScreenX > 155 and marioYVel ==0) and not spriteNear and not endscroll then
  600.         --print("second")
  601.         for o=1,#ButtonNames do
  602.             output[ButtonNames[o]] = false
  603.         end
  604.         if marioXVel > 10 and marioXVel < 128 then
  605.             output["left"] = true
  606.         end
  607.         if marioXVel > 128 and marioXVel < 245 then
  608.             output["right"] = true
  609.         end
  610.         ScrollWalkPause = true
  611.         ScrollWalkPauselocal = true
  612.     else
  613.         --print("1")
  614.         for o=1,#ButtonNames do --Find neural network outputs
  615.             output[ButtonNames[o]] = network.neurons[-o] ~= nil and network.neurons[-o].value > 0
  616.         end
  617.     end
  618.     --print(ScrollWalkPause)
  619.     --Disable opposite d-pad presses
  620.     if forcebutton ~= nil then
  621.         for k,v in pairs(forcebutton) do
  622.             output[k]=v
  623.         end
  624.         --print(output)
  625.     end
  626.     if output["up"] and output["down"] then
  627.         output["up"] = false
  628.         output["down"] = false
  629.     end
  630.     if output["left"] and output["right"] then
  631.         output["left"] = false
  632.         output["right"] = false
  633.     end
  634.     return output
  635. end
  636.  
  637. --Mutation and evolution
  638. function genomeDelta(genes1, genes2) --sees the delta between 2 networks.
  639.     local i2 = {} --i2 maps the innovation of a gene to the gene, for the second genome.
  640.     for i = 1,#genes2 do
  641.         local gene = genes2[i]
  642.         i2[gene.innovation] = gene
  643.     end
  644.  
  645.     local sum = 0 --total weight difference among matching genes
  646.     local coincident = 0 --number of matching genes
  647.     for i,gene in pairs(genes1) do
  648.         if i2[gene.innovation] ~= nil then
  649.             local gene2 = i2[gene.innovation]
  650.             sum = sum + math.abs(gene.weight - gene2.weight)
  651.             coincident = coincident + 1
  652.         end
  653.     end
  654.     if coincident == 0 then --if there are no matching genes it does not match so return a very large value
  655.         return 100
  656.     end
  657.     local total = #genes1 + #genes2 --number of total genes
  658.     local disjoint = total - (coincident*2) --number of non-matching genes
  659.     return DeltaWeights * sum / coincident + DeltaDisjoint * disjoint / total --return the delta
  660. end
  661.  
  662. function sameSpecies(genome1,genome2) --sees whether 2 genomes are in the same species or not.
  663.     if #genome1.networkswitch ~= #genome2.networkswitch then
  664.         return false --must have same number of networks
  665.     end
  666.     for n=1,#genome1.networkswitch do
  667.         if math.abs(genome1.networkswitch[n] - genome2.networkswitch[n]) > DeltaSwitch then
  668.             return false --if the network switches are too far apart they are different
  669.         end
  670.     end
  671.     for n=1,#genome1.genes do
  672.         delta = genomeDelta(genome1.genes[n],genome2.genes[n])
  673.         if delta > DeltaThreshold then
  674.             return false --if the delta between corresponding networks is too great they are different
  675.         end
  676.     end
  677.     return true --otherwise, same species
  678. end
  679.  
  680. function shuffle(num) --shuffles an array from 1 to num.
  681.     local output = {}
  682.     for i=1,num do
  683.         local offset = i - 1
  684.         local randomIndex = offset*math.random()
  685.         local flooredIndex = randomIndex - randomIndex%1
  686.         if flooredIndex == offset then
  687.             output[#output + 1] = i
  688.         else
  689.             output[#output + 1] = output[flooredIndex + 1]
  690.             output[flooredIndex + 1] = i
  691.         end
  692.     end
  693.     return output
  694. end
  695.  
  696. function addToSpecies(genome)
  697.  
  698.     local foundSpecies = false
  699.    
  700.     local order = shuffle(#pool.species) --Used to go through in random order so as to avoid feeding patterns.
  701.    
  702.     for i=1,#order do
  703.         local species = pool.species[order[i]]
  704.         if sameSpecies(genome, species.genomes[1]) and not foundSpecies then
  705.             table.insert(species.genomes, genome)
  706.             foundSpecies = true
  707.         end
  708.     end
  709.  
  710.     if not foundSpecies then --create new species
  711.         local newSpecies = newSpecies()
  712.         table.insert(newSpecies.genomes, genome)
  713.         table.insert(pool.species, newSpecies)
  714.     end
  715. end
  716.  
  717. function randomNeuron(genes, nonInput, nonOutput) --pick a random neuron number
  718.     local neurons = {}
  719.     if not nonInput then --If inputs are an option add them
  720.         for i=1,Inputs do
  721.             neurons[i] = true
  722.         end
  723.     end
  724.     if not nonOutput then --If outputs are an option add them
  725.         for o=1,#ButtonNames do
  726.             neurons[-o] = true
  727.         end
  728.     end
  729.     for i,gene in pairs(genes) do --Add input and output of each gene
  730.         if not nonInput or gene.into > Inputs then --add input as long as it is valid
  731.             neurons[genes[i].into] = true
  732.         end
  733.         if not nonOutput or gene.out > 0 then --add output as long as it is valid
  734.             neurons[genes[i].out] = true
  735.         end
  736.     end
  737.     local count = 0
  738.     for _,_ in pairs(neurons) do --count number of neuron possibilities
  739.         count = count + 1
  740.     end
  741.     if count == 0 then return 0 end --return 0 means that there were no possibilities
  742.     local n = math.random(1, count) --pick a random possibility
  743.     for k,v in pairs(neurons) do
  744.         n = n-1 --count down until you get to correct #
  745.         if n == 0 then
  746.             return k
  747.         end
  748.     end
  749. end
  750.  
  751. function linkDirection(genes, link) --Finds a connection's status. 0 means already exists, 1 means >, -1 means < or incomparable
  752.     for i=1,#genes do --check each gene
  753.         local gene = genes[i] --if the new added gene already exists return 0
  754.         if gene.into == link.into and gene.out == link.out then
  755.             return 0
  756.         end
  757.     end
  758.     --Attempt to find a connection opposing the suggested one (that would create a loop)
  759.     local afterNodes = {} --each node that is >= than the link.out
  760.     afterNodes[link.out] = true
  761.     local readAll = false --has seen every gene without adding any to the list of nodes after link.into
  762.     while not readAll do --loop until each gene has been read and used
  763.         readAll = true
  764.         for i,gene in pairs(genes) do --loop through the list of genes
  765.             if afterNodes[gene.into] == true and afterNodes[gene.out] == nil then --if new connection found
  766.                 afterNodes[gene.out] = true
  767.                 readAll = false
  768.                 if gene.out == link.into then --connection found
  769.                     return -1
  770.                 end
  771.             end
  772.         end
  773.     end
  774.     return 1 --Has not found that link.into >= link.out so it is a fine direction
  775. end
  776.  
  777. function linkMutate(genome, which, force) --Create a random new gene
  778.     local genes = genome.genes[which]
  779.     --create two random neurons
  780.     local neuronInto
  781.     if force == 0 then --only input box
  782.         neuronInto = math.random(1,BoxSize)
  783.     elseif force == 1 then --only bottom row
  784.         neuronInto = BoxSize + math.random(1,3+#InitialOscillations)
  785.     elseif force == 2 then --non-input
  786.         neuronInto = randomNeuron(genes, true, true)
  787.         if neuronInto == 0 then return end --no valid non-input nodes
  788.     else --anything
  789.         neuronInto = randomNeuron(genes, false, true)
  790.     end
  791.     local neuronOut = randomNeuron(genes, true, false)
  792.     --Create a new link
  793.     local newLink = newGene()
  794.     newLink.into = neuronInto
  795.     newLink.out = neuronOut
  796.     if neuronInto == neuronOut then return end --must not be the same neuron
  797.     connectionStatus = linkDirection(genes,newLink) --find current status of that link
  798.     if connectionStatus == 0 then return end --if link already exists then return
  799.     --opposite direction
  800.     if connectionStatus == -1 then
  801.         newLink.out = neuronInto
  802.         newLink.into = neuronOut
  803.     end
  804.     --create new innovation value and weight
  805.     newLink.innovation = pool.innovation
  806.     pool.innovation = pool.innovation + 1
  807.     newLink.weight = math.random()*4-2
  808.     newLink.delay = 0
  809.     if math.random()>0.85 then
  810.         newLink.delay = math.min(5,math.ceil(math.log(math.random())/math.log(0.5)))
  811.     end
  812.     --add to the genes
  813.     table.insert(genes,newLink)
  814. end
  815.  
  816. function nodeMutate(genome, which) --Split a random gene and put a neuron in the middle
  817.     local genes = genome.genes[which]
  818.     if #genes == 0 then return end --cannot make a node with no genes
  819.     local gene = genes[math.random(1,#genes)] --pick a random gene
  820.     --create the new genes
  821.     local geneI = newGene()
  822.     local geneO = newGene()
  823.     geneI.into = gene.into
  824.     geneI.out = genome.maxNeuron + 1
  825.     geneO.into = genome.maxNeuron + 1
  826.     geneO.out = gene.out
  827.     --set innovation
  828.     geneI.innovation = pool.innovation
  829.     geneO.innovation = pool.innovation + 1
  830.     pool.innovation = pool.innovation + 2
  831.     --set weights
  832.     geneI.weight = math.sqrt(math.abs(gene.weight))
  833.     if math.random() < 0.5 then
  834.         geneI.weight = -geneI.weight --half the time first one is negative
  835.         geneI.delay = gene.delay
  836.     end
  837.     if geneI.weight == 0 then
  838.         geneI.weight = 0.001
  839.     end
  840.     geneO.weight = gene.weight / geneI.weight --they should multiply to original weight
  841.     geneO.delay = gene.delay - geneI.delay
  842.            
  843.     genome.maxNeuron = genome.maxNeuron + 1
  844.     gene.enabled = false --disable old gene
  845.     --insert the new genes
  846.     table.insert(genes,geneI)
  847.     table.insert(genes,geneO)
  848. end
  849.  
  850. function enableDisableMutate(genome, which, enable) --changes a random gene to enabled or disabled
  851.     local candidates = {} --list of genes that would be changed by this mutation
  852.     for _,gene in pairs(genome.genes[which]) do
  853.         if gene.enabled == not enable then --only insert if the current enabling is different from what we will set it to
  854.             table.insert(candidates, gene)
  855.         end
  856.     end
  857.  
  858.     if #candidates == 0 then return end --if no valid genes then do nothing
  859.  
  860.     local gene = candidates[math.random(1,#candidates)] --pick random from candidates
  861.     gene.enabled = enable --enable that gene
  862. end
  863.  
  864. function oscillationMutate(genome, which) --change an oscillation node timer
  865.     i = math.random(1,#InitialOscillations) --pick a random oscillation to change
  866.     genome.oscillations[which][i] = genome.oscillations[which][i] + math.random(-10,10) --change by random amount
  867.     if genome.oscillations[which][i]<0 then --no negative oscillation values
  868.         genome.oscillations[which][i] = 0
  869.     end
  870. end
  871.  
  872. function nswitchMutate(genome) --change the position of a network switch
  873.     which = #genome.networkswitch --most of the time pick the most recent switch
  874.     if math.random() < 0.15 then
  875.         which = math.random(1,#genome.networkswitch) --occasionally pick a random other one
  876.     end
  877.     genome.networkswitch[which] = genome.networkswitch[which] + math.random(-250,250) --change by random amount
  878.     local netlimit = 0
  879.     for i,species in ipairs(pool.species) do
  880.         if species.maxRightmost > netlimit then
  881.             netlimit = species.maxRightmost
  882.         end
  883.     end
  884.     if genome.networkswitch[which] < 0 or genome.networkswitch[which] > netlimit then --if out of range of where genome can reach
  885.         table.remove(genome.networkswitch) --remove the latest network
  886.         table.remove(genome.genes)
  887.     end
  888.     table.sort(genome.networkswitch, function(a,b) --make sure network switches are in order through the level
  889.         return (a < b)
  890.     end)
  891. end
  892.  
  893. function weightsMutate(genome, which) --change the weights of genes around a node
  894.     local genes = genome.genes[which]
  895.     local node = randomNeuron(genes,false,false) --pick a random neuron to change weights in the area of
  896.     local step = genome.mutationRates["step"]
  897.     for i,gene in pairs(genes) do
  898.         if gene.into == node or gene.out == node then --if either end
  899.             if math.random() < WeightPerturbChance then --modify the weight slightly
  900.                 gene.weight = gene.weight + (math.random()-0.5) * step * 2
  901.             elseif math.random() < WeightResetChance then --reset weight entirely
  902.                 gene.weight = math.random()*4 - 2
  903.             end
  904.         end
  905.     end
  906. end
  907.  
  908. MutationFunctions = {} --each mutation function, sorted by name
  909.  
  910. MutationFunctions.linkInputBox = function(genome, which) return linkMutate(genome, which, 0) end
  911. MutationFunctions.linkBottomRow = function(genome, which) return linkMutate(genome, which, 1) end
  912. MutationFunctions.linkLayers = function(genome, which) return linkMutate(genome, which, 2) end
  913. MutationFunctions.linkRandom = function(genome, which) return linkMutate(genome, which, 3) end
  914. MutationFunctions.node = nodeMutate
  915. MutationFunctions.enable = function(genome, which) return enableDisableMutate(genome, which, true) end
  916. MutationFunctions.disable = function(genome, which) return enableDisableMutate(genome, which, false) end
  917. MutationFunctions.oscillation = oscillationMutate
  918. MutationFunctions.nswitch = function(genome, which) if which>1 then nswitchMutate(genome,which-1) end end
  919. MutationFunctions.weights = weightsMutate
  920.  
  921. function mutate(genome, which) --Mutates a genome's network based on all it's mutation rates
  922.     for name,rate in pairs(genome.mutationRates) do --For each mutation type
  923.         local p = rate --Number of times to do that mutation type
  924.         if MutationFunctions[name] ~= nil then --is a mutation and not another rate like STEP
  925.             while p > 0 do --Loop until all mutations are done
  926.                 if math.random() < p then --when p > 1 always do it, if it is a fraction then it is random
  927.                     if genome.genes[which] ~= nil then --if network exists
  928.                         MutationFunctions[name](genome,which) --call that rate
  929.                     end
  930.                 end
  931.                 p = p - 1 --reduce p
  932.             end
  933.         end
  934.     end
  935. end
  936.  
  937. function crossover(g1,g2)
  938.     if g2.fitstate.fitness > g1.fitstate.fitness then
  939.         g2, g1 = g1, g2 --switch so that g1 is always the greater fitness
  940.     end
  941.     local numnets = math.min(#g1.genes,#g2.genes) --take the smaller # of networks
  942.     local child = newGenome(numnets)
  943.     for n=1,numnets do --for each network to be calculated
  944.         local i2 = {} --map from gene2 innovations to gene innovations
  945.         for i,gene in ipairs(g2.genes[n]) do
  946.             i2[gene.innovation] = gene
  947.         end
  948.         local i1 = {} --map from gene1 innovations to gene innovations
  949.         for i,gene in ipairs(g1.genes[n]) do
  950.             i1[gene.innovation] = gene
  951.         end
  952.         for i=1,#g1.oscillations[n] do
  953.             child.oscillations[n][i] = g1.oscillations[n][i] --set oscillations to g1's oscillations
  954.         end
  955.         for i,gene1 in ipairs(g1.genes[n]) do --some version of every gene from best genome
  956.             local gene2 = i2[gene1.innovation] --use innovations2 to find the copied gene in g1
  957.             if gene2 ~= nil and math.random(1,2) == 1 and gene2.enabled then
  958.                 table.insert(child.genes[n], copyGene(gene2)) --if the copied gene exists pick randomly
  959.             else
  960.                 table.insert(child.genes[n], copyGene(gene1)) --otherwise take the version from the best genome
  961.             end
  962.         end
  963.         if n > 1 then
  964.             child.networkswitch[n-1] = g1.networkswitch[n-1]
  965.         end
  966.         for i,gene in ipairs(g2.genes[n]) do
  967.             if i1[gene.innovation] == nil and math.random() < 0.01 then --low chance for every gene in g2 only
  968.                 table.insert(child.genes[n], copyGene(gene)) --add those genes to the child as well
  969.             end
  970.         end
  971.     end
  972.     child.maxNeuron = math.max(g1.maxNeuron,g2.maxNeuron)
  973.     return child
  974. end
  975.  
  976. function rankGlobally() --Give each species a global rank, 1 is lowest Population is highest
  977.     local globalRanks = {}
  978.     for s = 1,#pool.species do
  979.         local species = pool.species[s]
  980.         for g = 1,#species.genomes do
  981.             table.insert(globalRanks, species.genomes[g]) --Insert every genome into the table
  982.         end
  983.     end
  984.     table.sort(globalRanks, function (a,b) --Sort based on fitness
  985.         return (a.fitstate.fitness < b.fitstate.fitness)
  986.     end)
  987.  
  988.     for g=1,#globalRanks do --Put position of each genome into that genome's data
  989.         globalRanks[g].globalRank = g
  990.     end
  991. end
  992.  
  993. function calculatePoolStats() --Calculate avg and avg deviation of max fitness
  994.     local total = 0 --Average of each species max fitness
  995.     for i,species in ipairs(pool.species) do
  996.         total = total + species.maxFitness
  997.     end
  998.     pool.averagemaxfitness = total / #pool.species
  999.    
  1000.     local deviationtotal = 0 --Average distance between max fit and avg max fit
  1001.     for i,species in ipairs(pool.species) do
  1002.         deviationtotal = deviationtotal + math.abs(species.maxFitness - pool.averagemaxfitness)
  1003.     end
  1004.     pool.avgDeviation = deviationtotal / #pool.species
  1005. end
  1006.  
  1007. function calculateFitnessRank(species) --Returns a number for each species showing how well that species did
  1008.     local totalRank = 0
  1009.  
  1010.     for g=1,#species.genomes do --Total rank is equal to the average of the ranks of each genome
  1011.         local genome = species.genomes[g]
  1012.         totalRank = totalRank + genome.globalRank
  1013.     end
  1014.     totalRank = totalRank / #species.genomes
  1015.    
  1016.     local multiplier = 1.0 --multiplier based on how many avg deviations from the mean the top fitness is
  1017.     if species.maxFitness < pool.averagemaxfitness - (pool.avgDeviation *2) then
  1018.         multiplier = 0.25
  1019.     elseif species.maxFitness < pool.averagemaxfitness - pool.avgDeviation then
  1020.         multiplier = 0.4
  1021.     elseif species.maxFitness > pool.averagemaxfitness + (pool.avgDeviation *3) then
  1022.         multiplier = 2.0
  1023.     elseif species.maxFitness > pool.averagemaxfitness + (pool.avgDeviation *2) then
  1024.         multiplier = 1.5
  1025.     end
  1026.    
  1027.     --Penalty for having multiple networks, to cancel out the benefit of consistency that they give
  1028.     local netMulti = math.pow(NetworkPenaltyOuter*math.pow(NetworkPenaltyInner,#species.genomes[1].networkswitch),#species.genomes[1].networkswitch)
  1029.    
  1030.     species.fitnessRank = totalRank * multiplier * netMulti
  1031. end
  1032.  
  1033. function totalPoolRank() --Sums each species fitness rank
  1034.     local total = 0
  1035.     for i,species in ipairs(pool.species) do
  1036.         total = total + species.fitnessRank
  1037.     end
  1038.     return total
  1039. end
  1040.  
  1041. function cullSpecies(cutToOne) --Cuts down each species, either to a percent based on bottleneck, or only to it's top genome.
  1042.     for i,species in ipairs(pool.species) do
  1043.         table.sort(species.genomes, function (a,b) --sorts the genomes by how well they did
  1044.             return (a.fitstate.fitness > b.fitstate.fitness)
  1045.         end)
  1046.  
  1047.         local percent = 0.5 --calculate percent
  1048.         local minpercent = math.max(0.15,0.5-0.1*pool.bottleneckloops) --min and max of oscillation
  1049.         local maxpercent = math.min(0.85,0.5+0.1*pool.bottleneckloops)
  1050.        
  1051.         local length = math.min(10,(5 + 2*pool.bottleneckloops)) --length used in pop oscillation
  1052.  
  1053.         local totallength = length*3 + TimeBeforePopOscil --total length of oscillation
  1054.        
  1055.         if pool.bottleneck < totallength/2 then --if in first half go up to max
  1056.             percent = minpercent + (0.1+maxpercent-minpercent)*(pool.bottleneck*2/totallength)
  1057.         else --if in second half go down to min
  1058.             percent = 0.1+maxpercent - (0.2+maxpercent-minpercent)*(pool.bottleneck*2/totallength-1)
  1059.         end
  1060.        
  1061.         local remaining = math.ceil(#species.genomes*percent) --number that remain
  1062.         if cutToOne or remaining < 1 then --always keep at least 1
  1063.             remaining = 1
  1064.         end
  1065.         while #species.genomes > remaining do --remove all that do not survive
  1066.             table.remove(species.genomes)
  1067.         end
  1068.     end
  1069. end
  1070.  
  1071. function breedChild(species) --makes an offspring from a species
  1072.     local child = {}
  1073.     if math.random() < CrossoverChance then --chance to cross over two genomes or just copy one
  1074.         g1 = species.genomes[math.random(1, #species.genomes)]
  1075.         g2 = species.genomes[math.random(1, #species.genomes)]
  1076.         child = crossover(g1, g2)
  1077.     else
  1078.         g = species.genomes[math.random(1, #species.genomes)]
  1079.         child = copyGenome(g)
  1080.     end
  1081.    
  1082.     local which = #child.genes -- mutate usually at the latest network
  1083.     if which > 1 and species.maxRightmost > 0 then --if there are multiple networks possibly modify one before most recent based on distance past that
  1084.         local mutatePrevNetworkChance = 0.5 + (child.networkswitch[#child.networkswitch]-species.maxRightmost)/500
  1085.         if math.random() < mutatePrevNetworkChance then
  1086.             which = #child.genes - 1
  1087.         end
  1088.     end
  1089.     mutate(child,which) --mutate the child in that network
  1090.  
  1091.     return child
  1092. end
  1093.  
  1094. function removeStaleSpecies() --update staleness and remove stale species
  1095.     local survived = {} --species that survive
  1096.     for i,species in ipairs(pool.species) do
  1097.         species.staleness = species.staleness + 1
  1098.         if species.staleness < MaxStaleness or species.maxFitness >= pool.secondFitness then --non stale species and top 2 species survive
  1099.             table.insert(survived,species)
  1100.         end
  1101.     end
  1102.     pool.species = survived
  1103. end
  1104.  
  1105. function removeWeakSpecies()
  1106.     local survived = {} --species that survive
  1107.     local sum = totalPoolRank()
  1108.     for s = 1,#pool.species do
  1109.         local species = pool.species[s]
  1110.         local breed = math.floor(species.fitnessRank / sum * pool.population)
  1111.        
  1112.         if breed+1 >= 999/pool.population or species.fitnessRank > sum / #pool.species then
  1113.             table.insert(survived, species)
  1114.         end
  1115.     end
  1116.     pool.species = survived
  1117. end
  1118.  
  1119. function replaceNetwork(g1,g2) --replaces the most recent network of g1 with a random network of g2
  1120.     local child = copyGenome(g1) --mostly a copy of genome 1
  1121.     child.genes[#child.genes] = {} --clear out the most recent network
  1122.     which = math.random(1,#g2.genes) --pick a random network to copy from which
  1123.     for i,gene in ipairs(g2.genes[which]) do --copy each gene
  1124.         table.insert(child.genes[#child.genes], copyGene(gene))
  1125.     end
  1126.     return child
  1127. end
  1128.  
  1129. function writescene(scene)
  1130.     local f = io.open('currentscene.txt','w')
  1131.     if scene == 1 then
  1132.         f:write("1")
  1133.     elseif scene == 2 then
  1134.         f:write("2")
  1135.     else
  1136.         f:write("3")
  1137.     end
  1138.     f:close()
  1139. end
  1140.  
  1141. function writetable(file,tbl) --writes a string of a table to a file
  1142.     function tablestring(a)
  1143.         if type(a)=='number' then
  1144.             local s = string.gsub(tostring(a),",",".")
  1145.             file:write(s)
  1146.         elseif type(a)=='boolean' then
  1147.             file:write(tostring(a))
  1148.         elseif type(a)=='string' then
  1149.             file:write('"')
  1150.             for i=1,string.len(a) do
  1151.                 local c = string.sub(a,i,i)
  1152.                 if c=="\n" then
  1153.                     file:write("\\n")
  1154.                 else
  1155.                     local s = string.gsub(c,'\\','\\\\')
  1156.                     file:write(s)
  1157.                 end
  1158.             end
  1159.             file:write('"')
  1160.         else
  1161.             file:write('{')
  1162.             for key,value in pairs(a) do
  1163.                 file:write('[')
  1164.                 tablestring(key)
  1165.                 file:write(']=')
  1166.                 tablestring(value)
  1167.                 file:write(',')
  1168.             end
  1169.             file:write('}')
  1170.         end
  1171.     end
  1172.     tablestring(tbl)
  1173. end
  1174. savgx={0}
  1175. function searchstaff()
  1176.     getPositions()
  1177.     local marioScreenX = marioScreenX
  1178.     local koopalingDefeated = koopalingDefeated
  1179.     local cont = {}
  1180.     cont['B'] = true
  1181.     cont['down'] = false
  1182.     if marioScreenX < 122 then
  1183.         cont['right'] = true
  1184.        
  1185.     else
  1186.         cont['left'] = true
  1187.     end
  1188.     if marioScreenX - savgx[1] < 5 then
  1189.         cont['A'] = true
  1190.         table.insert(savgx,0)
  1191.     else
  1192.         cont['A'] = false
  1193.     end
  1194.     table.insert(savgx,marioScreenX)
  1195.     if #savgx > 120 then
  1196.         table.remove(savgx,1)
  1197.     end
  1198.     joypad.set(Player,cont)
  1199.     coroutine.yield()
  1200. end
  1201.  
  1202. function file_exists(name)
  1203.    local f=io.open(name,"r")
  1204.    if f~=nil then io.close(f) return true else return false end
  1205. end
  1206.  
  1207. function saveGenome(name,winner) --saves a species containing the winning genome to a file
  1208.     local lvlf=io.open("level.txt","r")
  1209.     local lvl = lvlf:read("*line")
  1210.     lvlf:close()
  1211.     local levelname = lvl --name of level in the file name
  1212.     local file = io.open("backups"..dirsep..levelname..dirsep..name..".lua","w")
  1213.     spec = pool.species[pool.currentSpecies]
  1214.     genome = spec.genomes[pool.currentGenome]
  1215.     genome.nick = spec.nick
  1216.     genome.gen = pool.generation
  1217.     genome.s = pool.currentSpecies
  1218.     genome.g = pool.currentGenome
  1219.     print("backups"..dirsep..levelname..dirsep..name..".lua")
  1220.     if not winner then
  1221.         if boss then
  1222.             file:write("boss=true\nloadedgenome=")
  1223.         else
  1224.             file:write("boss=false\nloadedgenome=")
  1225.         end
  1226.     else
  1227.         file:write("loadedgenome=")
  1228.     end
  1229.     writetable(file,genome)
  1230.     file:close()
  1231.     if winner then
  1232.         file = io.open("backups"..dirsep.."winners.txt","a")
  1233.         file:write("backups"..dirsep..levelname..dirsep..name..".lua\n")
  1234.         file:close()
  1235.     else
  1236.         table.insert(pool.breakthroughfiles,"backups"..dirsep..levelname..dirsep..name..".lua")
  1237.     end
  1238. end
  1239.  
  1240. function savePool(filename) --saves the pool into a file
  1241.     local file = io.open(filename,"w")
  1242.     file:write("pool=")
  1243.     writetable(file,pool)
  1244.     file:close()
  1245. end
  1246.  
  1247. function loadPool(filename) --loads the pool from a file
  1248.     dofile(filename)
  1249.     ProgramStartTime = ProgramStartTime - pool.realTime
  1250. end
  1251.  
  1252. maxNewGenProgress = -1
  1253. function newgenProgress(progress) --draws a progress bar for new gen calculation
  1254.     if math.floor(progress)>maxNewGenProgress then
  1255.         maxNewGenProgress=math.floor(progress)
  1256.         text = "Calculating Generation"
  1257.         white = toRGBA(0xFFFFFFFF)
  1258.         blue = toRGBA(0xFF003FFF)
  1259.         black = toRGBA(0xFF000000)
  1260.         gui.drawbox(38,98,218,122,black,blue)
  1261.         drawtext.draw(text,40,100,white,black)
  1262.         prog = "["
  1263.         for i=1,progress do prog=prog.."#" end
  1264.         for i=progress,19 do prog=prog.." " end
  1265.         prog=prog.."]"
  1266.         drawtext.draw(prog,40,112,blue,black)
  1267.         coroutine.yield()
  1268.     end
  1269. end
  1270.  
  1271. function newGeneration() --runs the evolutionary algorithms to advance a generation
  1272.     local mfloor = math.floor
  1273.     maxNewGenProgress = -1
  1274.     newgenProgress(0)
  1275.     autoturbo()
  1276.     generationTime = pool.realTime
  1277.    
  1278.     os.remove("backups"..dirsep..levelname..dirsep.."gen"..(pool.generation+1)..".lua")
  1279.     savePool("backups"..dirsep..levelname..dirsep.."gen"..pool.generation..".lua")
  1280.    
  1281.     local length = math.min(10,(5 + 2*pool.bottleneckloops)) --Length used for the population oscillation
  1282.     local targetPop = math.min(900,500 + 100*pool.bottleneckloops) --population to rise to
  1283.     local targetPopLower = math.min(300,600 - targetPop/2) --population to fall back down to
  1284.     local currentPopLower = math.min(300,targetPopLower+50) --population starting at
  1285.    
  1286.     if pool.bottleneck <= TimeBeforePopOscil and pool.bottleneckloops == 0 then --decrease very rapidly after a bottleneck reset
  1287.         pool.population = math.max(math.floor(pool.population * 0.8),300)
  1288.     end
  1289.    
  1290.     if pool.bottleneck > TimeBeforePopOscil then --perform the oscillation
  1291.         if pool.bottleneck < TimeBeforePopOscil + length then --increase up
  1292.             pool.population = currentPopLower + math.floor((targetPop - currentPopLower)/length*(pool.bottleneck-TimeBeforePopOscil))
  1293.         elseif pool.bottleneck == TimeBeforePopOscil + length then --peak
  1294.             pool.population = targetPop
  1295.         elseif pool.bottleneck <= TimeBeforePopOscil + length*3 then --go back down
  1296.             pool.population = targetPopLower + math.floor((targetPop - targetPopLower)/length/2*(TimeBeforePopOscil+length*3-pool.bottleneck))
  1297.         end
  1298.         if pool.bottleneck == TimeBeforePopOscil + length*3 then --go to next loop
  1299.             pool.bottleneck = 0
  1300.             pool.bottleneckloops = pool.bottleneckloops + 1
  1301.         end
  1302.     end
  1303.     newgenProgress(1)
  1304.     cullSpecies(false) --cut down each species
  1305.     newgenProgress(2)
  1306.     rankGlobally() --rank all genomes
  1307.     newgenProgress(3)
  1308.     removeStaleSpecies() --remove stale species
  1309.     newgenProgress(4)
  1310.     calculatePoolStats() --find pool maxes avg/deviation
  1311.     newgenProgress(5)
  1312.     for i,species in ipairs(pool.species) do --find the fitness rank for each species
  1313.         calculateFitnessRank(species)
  1314.     end
  1315.     newgenProgress(6)
  1316.     removeWeakSpecies() --remove species without a good fitness rank
  1317.     newgenProgress(7)
  1318.     local rankSum = totalPoolRank() --used to see how good a fitness rank is in comparison to everything
  1319.    
  1320.     local children = {} --new genomes that will be added this generation
  1321.    
  1322.     for i,species in ipairs(pool.species) do --generate children for each species
  1323.         local breed = mfloor(species.fitnessRank / rankSum * pool.population) - 1 --num of children to generate
  1324.         for j=1,breed do
  1325.             table.insert(children, breedChild(species))
  1326.         end
  1327.         newgenProgress(7+3*i/#pool.species)
  1328.     end
  1329.     newgenProgress(10)
  1330.     local interbreedchance = BaseInterbreedChance --base interbreed chance
  1331.     if pool.generation > 5 then --do not reduce interbreed at very beginning, we want lots of it in first 5 gens
  1332.         --reduce interbreed when there are lots of species with low average size
  1333.         interbreedchance = interbreedchance * math.pow(1/InterbreedCutoff-math.min(1/InterbreedCutoff,#pool.species/pool.population),InterbreedDegree)
  1334.     end
  1335.    
  1336.     local replacechance = interbreedchance/3 --replacements rarer than interbreeds
  1337.    
  1338.     local minnetwork = 10000 --minimum number of networks a species eligible for a NS has
  1339.     local netspecies = {} --list of species eligible for a NS with min networks
  1340.    
  1341.     for i,species in ipairs(pool.species) do --apply interbreed chance to each species
  1342.         if math.random() < interbreedchance then
  1343.             --pick which species to breed with, proportional to fitness rank
  1344.             local ibcheck = math.random() * rankSum
  1345.             for j,species2 in ipairs(pool.species) do
  1346.                 ibcheck = ibcheck - species2.fitnessRank --reduce by the fitness rank
  1347.                 if ibcheck < 0 then --the higher it is reduced by greater chance it goes below 0 this time
  1348.                     table.insert(children, crossover(species.genomes[1],species2.genomes[1])) --interbreed
  1349.                     break
  1350.                 end
  1351.             end
  1352.         end
  1353.        
  1354.         if math.random() < replacechance then
  1355.             --pick which species to get network from
  1356.             local species2 = pool.species[math.random(1,#pool.species)]
  1357.             table.insert(children, replaceNetwork(species.genomes[1],species2.genomes[1])) --replace network
  1358.         end
  1359.        
  1360.         local numnet = #species.genomes[1].genes --number of networks
  1361.         if math.min(numnet,3) <= pool.bottleneckloops and numnet <= minnetwork and species.maxFitness + 150 > pool.maxFitness then
  1362.             if numnet < minnetwork then --even smaller network found, clear old array
  1363.                 minnetwork = numnet
  1364.                 netspecies = {}
  1365.             end
  1366.             table.insert(netspecies,species)
  1367.         end
  1368.         newgenProgress(10+3*i/#pool.species)
  1369.     end
  1370.    
  1371.     if #netspecies > 0 then --at least one species eligible for a network switch
  1372.         local species = netspecies[math.random(1,#netspecies)] --pick random eligible species
  1373.         local child = copyGenome(species.genomes[1]) --make a copy of best genome
  1374.         local switchloc = species.maxRightmost - 150 - 150*math.random() --pick a switch location
  1375.         table.insert(child.networkswitch,switchloc)
  1376.         table.insert(child.oscillations,{})
  1377.         for i=1,#InitialOscillations do
  1378.             child.oscillations[#child.oscillations][i] = InitialOscillations[i]
  1379.         end
  1380.         table.insert(child.genes,{})
  1381.        
  1382.         table.insert(children,child) --add child
  1383.        
  1384.         mutate(child,#child.genes)
  1385.         mutate(child,#child.genes)
  1386.        
  1387.         table.sort(child.networkswitch, function(a,b) --make sure network splits are in order
  1388.             return (a < b)
  1389.         end)
  1390.     end
  1391.     newgenProgress(13)
  1392.     while #children+#pool.species < pool.population do --fill from random species
  1393.         table.insert(children, breedChild(pool.species[math.random(1, #pool.species)]))
  1394.     end
  1395.     newgenProgress(14)
  1396.     cullSpecies(true) --remove all but the best of each species
  1397.     newgenProgress(15)
  1398.     for i,child in ipairs(children) do --add all children to species
  1399.         addToSpecies(child)
  1400.         newgenProgress(15+3*i/#children)
  1401.     end
  1402.     newgenProgress(18)
  1403.     for i,species in ipairs(pool.species) do --limit size of species
  1404.         local limit = pool.population/12 --cap on the species size
  1405.         if species.maxFitness >= pool.secondFitness then --if top two then larger cap
  1406.             limit = pool.population/7
  1407.         end
  1408.         while #species.genomes > limit do --remove
  1409.             table.remove(species.genomes)
  1410.         end
  1411.     end
  1412.    
  1413.     pool.generation = pool.generation + 1
  1414.     pool.bottleneck = pool.bottleneck + 1
  1415.     newgenProgress(19)
  1416.     savePool("backups/current.lua")
  1417.     newgenProgress(20)
  1418. end
  1419.  
  1420. Ranges = {
  1421. ['0-3-3-1']= {{yrange={}, xrange={min=360, max=480}, timeout=900}},
  1422. ["1-2-3-0"]= {{yrange={}, xrange={min=1173, max=1300}, coeffs={x=1,y=2,c=0}}, {yrange={}, xrange={min=1300, max=1303}, resetoffset=true}, {yrange={min=10, max=95}, xrange={min=1281, max=1450}, coeffs={x=0,y=0,c=0}, P=0},{yrange={min=10, max=36}, xrange={min=1181, max=1290}, coeffs={x=0,y=0,c=0}, P=0}},
  1423. ["1-6-2-0"]= {{yrange={}, xrange={min=2167, max=2300}, timeout=900}, {yrange={min=80,max=96}, xrange={min=2230, max=2249}, coeffs={x=1,y=-1,c=700}},{yrange={min=96,max=112}, xrange={min=2220, max=2279}, coeffs={x=1,y=-1,c=600}},{yrange={min=112,max=128}, xrange={min=2230, max=2249}, coeffs={x=1,y=-1,c=500}},{yrange={min=128,max=144}, xrange={min=2230, max=2249}, coeffs={x=1,y=-1,c=400}}, {yrange={min=144,max=160}, xrange={min=2256,max=2272},coeffs={x=1,y=-1,c=250}},{yrange={}, xrange={min=2272,max=2300},coeffs={x=0,y=0,c=0}},{yrange={min=170}, xrange={min=2200,max=2300},coeffs={x=0,y=0,c=0}},{yrange={min=80,max=90}, xrange={min=2274, max=2300}}},
  1424. }
  1425.  
  1426. function fitness(fitstate) --Returns the distance into the level - the non-time component of fitness
  1427.     local coeffs={x=1,y=1,c=0} --position base coefficients
  1428.     local marioX = marioX
  1429.     local marioY = marioY
  1430.     local marioYVel = marioYVel
  1431.     local marioScore = marioScore
  1432.     local marioScreenX = marioScreenX
  1433.     local marioPower = marioPower
  1434.     local boss = boss
  1435.     local battle = battle
  1436.     local marioActive = marioActive
  1437.     local scrollBonus = scrollBonus
  1438.     local mmax = math.max
  1439.     local mfloor = math.floor
  1440.     local mabs = math.abs
  1441.     local card_x = card_x
  1442.     local card_y = card_y
  1443.     local lspecialBonus = specialBonus
  1444.     local CardDistance = cardDistance
  1445.     local levelname = levelname
  1446.     local pStatus = math.sqrt(memory.readbyte(0x03DD))
  1447.  
  1448.     local ranges = Ranges[levelname .. "-" .. fitstate.area]
  1449.     --[[local levelstring = "" --string to represent the level and subworld, to index Ranges
  1450.     if LostLevels == 1 then levelstring = "LL" end
  1451.     levelstring = levelstring .. currentWorld .. "-" .. currentLevel .. " " .. fitstate.area
  1452.     local ranges = Ranges[levelstring] ]]
  1453.     if boss then
  1454.         basetimeout = roottimeoutboss
  1455.     else
  1456.         basetimeout = roottimeout
  1457.     end
  1458.     forcebutton = nil
  1459.     if ranges ~= nil then
  1460.         for r=1,#ranges do --for each special fitness range
  1461.             local range = ranges[r]
  1462.             if (range.xrange.min == nil or marioX > range.xrange.min) and (range.xrange.max == nil or marioX <= range.xrange.max) then --in x range
  1463.                 if (range.yrange.min == nil or marioY >= range.yrange.min) and (range.yrange.max == nil or marioY < range.yrange.max) then --in y range
  1464.                     if range.coeffs ~= nil then
  1465.                         coeffs = range.coeffs --replace default coefficients
  1466.                     end
  1467.                     if range.P ~= nil then
  1468.                         pStatus = pStatus * range.P
  1469.                     end
  1470.                     if range.area ~= nil then
  1471.                         fitstate.area = range.area
  1472.                     end
  1473.                     if range.timeout ~= nil then
  1474.                         basetimeout = range.timeout
  1475.                     else
  1476.                         basetimeout = 120
  1477.                     end
  1478.                     if range.turbo ~= nil then
  1479.                         if range.turbo then
  1480.                             currentTurbo = true
  1481.                             emu.speedmode("turbo")
  1482.                         else
  1483.                             currentTurbo = false
  1484.                             emu.speedmode("normal")
  1485.                         end
  1486.                     end
  1487.                     if range.forcebutton ~= nil then
  1488.                         forcebutton = range.forcebutton
  1489.                     else
  1490.                         forcebutton = nil
  1491.                     end
  1492.                     if range.resetoffset ~= nil then
  1493.                         fitstate.offset = fitstate.rightmost - fitstate.position
  1494.                     end
  1495.                 end
  1496.             end
  1497.         end
  1498.     end
  1499.     if marioXVel ~= 0 and not (marioYVel > 0 and marioYVel < 70) then
  1500.         pBonus = pBonus + pStatus/5
  1501.     end
  1502.     local distance
  1503.     if boss or battle then
  1504.         for i,sprite in ipairs(sprites) do
  1505.             if sprite.state ~= 0 then
  1506.                 local distx = math.floor((sprite.x - marioX)/16 + 0.5)
  1507.                 distance = 16 - mabs(distx)
  1508.             end
  1509.             if distance == nil then
  1510.                 distance = 0
  1511.             end
  1512.         end
  1513.     end
  1514.     local currentCardDistance = math.sqrt(mabs(card_x-marioX)*mabs(card_y-marioY))/50
  1515.     if currentCardDistance < CardDistance and not battle and not boss then
  1516.         CardDistance = currentCardDistance
  1517.         cardDistance = CardDistance
  1518.         if CardDistance < 0.4 then
  1519.             --roottimeout = 600
  1520.             basetimeout = 600
  1521.         end
  1522.        
  1523.     end
  1524.     if CardDistance == 0 then
  1525.         CardDistance = 0.1
  1526.     end
  1527.     fitstate.position = coeffs.x*marioX + coeffs.y*marioY + coeffs.c --set the position value
  1528.     if marioActive then
  1529.         prevPower = marioPower
  1530.         if not fitstate.lastActive then --update offset
  1531.             fitstate.area = memory.readbyte(0x03DF)
  1532.             --print(500/CardDistance)
  1533.             fitstate.offset = fitstate.rightmost - fitstate.position
  1534.             --print(fitstate.offset)
  1535.             --print("adjusted offset: " .. fitstate.offset)
  1536.             if battle then fitstate.offset = - (marioScore * 3 + distance * 10) end
  1537.             getTileset()
  1538.         end
  1539.         fitstate.lastright = fitstate.position --update lastright when in control of mario
  1540.         if scrollBonus > 0 and scrollBonus == prevScrollBonus then
  1541.             fitstate.timeout = fitstate.timeout + 1
  1542.             --endscroll = true
  1543.             --print(scrollBonus)
  1544.             --print(prevScrollBonus)
  1545.         elseif boss then
  1546.             if marioScore == prevscore then
  1547.                 fitstate.timeout = fitstate.timeout + 1 --increase timeout
  1548.             else
  1549.                 fitstate.timeout = 0
  1550.             end
  1551.             prevscore = marioScore
  1552.         elseif scrollBonus > 0 then
  1553.             fitstate.timeout = 0
  1554.             endscroll = false
  1555.         elseif battle then
  1556.             if marioScore == prevscore then
  1557.                 fitstate.timeout = fitstate.timeout + 1 --increase timeout
  1558.             else
  1559.                 fitstate.timeout = 0
  1560.             end
  1561.             if distance > 9 then
  1562.                 basetimeout = 600
  1563.             else
  1564.                 basetimeout = 120
  1565.             end
  1566.             prevscore = marioScore
  1567.             prevscreenx = marioScreenX
  1568.         else
  1569.             fitstate.timeout = fitstate.timeout + 1 --increase timeout
  1570.         end
  1571.         if fitstate.timeout > basetimeout then --if has been idle for a second, start penalizing
  1572.             fitstate.timepenalty = fitstate.timepenalty + 1
  1573.             endscroll = true
  1574.         end    
  1575.         if pipe then
  1576.             pipe = false
  1577.         end
  1578.         --print("scrollBonus: " .. scrollBonus)
  1579.         --print("prev: " .. prevScrollBonus)
  1580.         prevScrollBonus = scrollBonus
  1581.     else
  1582.         fitstate.position = fitstate.lastright --freeze position when not in control of mario
  1583.         if prevPower == marioPower then
  1584.             if scrollBonus >0 and not pipe then
  1585.                 specialBonus = specialBonus + 200
  1586.                 lspecialBonus = specialBonus
  1587.                 --fitstate.fitness = scrollBonus + fitstate.rightmost*0.6 + 150
  1588.                 fitstate.rightmost = fitstate.fitness
  1589.                 scrollBonus = 0
  1590.                 basescroll = 0
  1591.                 memory.writebyte(0x7A0C,0)
  1592.                
  1593.                 --fitstate.rightmost = fitstate.rightmost + fitstate.offset
  1594.                 pipe = true
  1595.                 --pipeflip = true
  1596.                 --print("pipe")
  1597.                 --fitstate.rightmost = fitstate.offset + 0.6*fitstate.rightmost
  1598.            
  1599.             elseif scrollBonus >0 and pipe then
  1600.                 scrollBonus = 0
  1601.                 basescroll = 0
  1602.                 memory.writebyte(0x7A0C,0)
  1603.             end
  1604.            
  1605.         else
  1606.             marioActive = true
  1607.         end
  1608.     end
  1609.     fitstate.lastActive = marioActive
  1610.     if not (marioYVel > 0 and marioYVel < 70) and fitstate.position + fitstate.offset > fitstate.rightmost then
  1611.         fitstate.timeout = mmax(0,fitstate.timeout + (fitstate.rightmost - fitstate.position - fitstate.offset)*2) --decrease timeout
  1612.         fitstate.rightmost = fitstate.position + fitstate.offset --rightmost is maximum that the position+offset has ever been
  1613.         --fitstate.savedtimepenalty = fitstate.timepenalty DISABLED FOR AUTOSCROLLER TEST
  1614.     end
  1615.     if boss then
  1616.         fitstate.fitness = marioScore + fitstate.frame + distance
  1617.     elseif battle then
  1618.         fitstate.fitness = marioScore * 3 + distance * 10 + fitstate.offset
  1619.     elseif scrollBonus > 0 then
  1620.         fitstate.fitness = scrollBonus + fitstate.rightmost*0.6 + pBonus/10
  1621.         --print("scrollBonus")
  1622.         -- if lspecialBonus > 0 then
  1623.             -- print("scrollBonus: " .. scrollBonus)
  1624.             -- print("marioX: " .. marioX)
  1625.         -- end
  1626.     else
  1627.         fitstate.fitness = fitstate.rightmost - mfloor(fitstate.savedtimepenalty / 2) + WhiteblockBonus + lspecialBonus + 100/CardDistance +pBonus/10 + powerupFitness * math.min(marioPower, 2)  --+ marioScore --return fitness
  1628.         -- if fitstate.offset ~= -104 then
  1629.             -- print("after pipe fitness: " .. fitstate.fitness)
  1630.         -- end
  1631.         --print("right: " .. fitstate.rightmost .. " penalty: " .. mfloor(fitstate.savedtimepenalty / 2) .. " whiteblock: " .. WhiteblockBonus .. " special: " .. lspecialBonus .. " card: " .. 500/CardDistance .. " pBonus: " .. pBonus/10)
  1632.     end
  1633. end
  1634.  
  1635.  
  1636.  
  1637. function autoturbo() --Determines the automatic turbo
  1638.     local pool = pool
  1639.     if (pool.realTime - pool.lastbreaktime) > 14400 then
  1640.         if (pool.realTime - generationTime) > 5400 then
  1641.             TurboMax = TurboMax + 30
  1642.             autoTurbo = TurboMax
  1643.         elseif (pool.realTime - generationTime) < 3600 and autoTurbo == TurboMax and autoTurboCounter > 1 then
  1644.             TurboMax = TurboMax - 50
  1645.             if TurboMax < 0 then
  1646.                 TurboMax = 0
  1647.             end
  1648.             autoTurbo = TurboMax
  1649.         elseif (pool.realTime - generationTime) < 3600 and autoTurbo == TurboMax then
  1650.             autoTurboCounter = autoTurboCounter + 1
  1651.         else
  1652.             autoTurboCounter = 0
  1653.         end
  1654.     end
  1655.     if (pool.realTime - pool.lastbreaktime) < 43200 then
  1656.         automaticturboswitch = 0
  1657.     elseif (pool.realTime - pool.lastbreaktime) > 43200 and automaticturboswitch == 0 then
  1658.         local increase = math.ceil(pool.maxFitness / 10)
  1659.         if TurboMax < increase then
  1660.             TurboMax = increase
  1661.             autoTurbo = increase
  1662.         end
  1663.         automaticturboswitch = 1
  1664.     elseif (pool.realTime - pool.lastbreaktime) > 86400 and automaticturboswitch == 1  then
  1665.         local increase = math.ceil(pool.maxFitness / 5)
  1666.         if TurboMax < increase then
  1667.             TurboMax = increase
  1668.             autoTurbo = increase
  1669.         end
  1670.         automaticturboswitch = 2
  1671.     elseif (pool.realTime - pool.lastbreaktime) > 172800 and automaticturboswitch == 2 then
  1672.         local increase = math.ceil(pool.maxFitness / 3)
  1673.         if TurboMax < increase then
  1674.             TurboMax = increase
  1675.             autoTurbo = increase
  1676.         end
  1677.         automaticturboswitch = 3
  1678.     end
  1679. end
  1680.  
  1681. function playGenome(genome) --Run a genome through an attempt at the level
  1682.     local mmax = math.max
  1683.     local boss = boss
  1684.     local Replay = Replay
  1685.     local FramesPerEval = FramesPerEval
  1686.     if boss then
  1687.         savestate.load(bosssavestateObj) --load boss savestate
  1688.     else
  1689.         savestate.load(savestateObj) --load savestate
  1690.     end
  1691.     --local falseload = false
  1692.     --[[while memory.readbyte(0x0787) == 0 do --wait until the game has fully loaded in mario
  1693.         coroutine.yield()
  1694.         falseload = true
  1695.     end
  1696.     if falseload then --move a savestate forward so that it doesn't have any load frames
  1697.         local groundtime = 0 --must not be falling for a certain time to be on ground
  1698.         while groundtime < 10 do --wait for mario to hit the ground
  1699.             coroutine.yield()
  1700.             if memory.readbyte(0x009F) == 0 then
  1701.                 groundtime = groundtime + 1
  1702.             else
  1703.                 groundtime = 0
  1704.             end
  1705.         end
  1706.         savestate.save(savestateObj)
  1707.     end]]--
  1708.    
  1709.     local fitstate = genome.fitstate
  1710.     fitstate.frame = 0 --frame counter for the run
  1711.     fitstate.position = 0 --current position
  1712.     fitstate.rightmost = 0 --max it has gotten to the right
  1713.     fitstate.offset = 0 --offset to make sure fitness doesn't jump upon room change
  1714.     fitstate.lastright = 0 --last position before a transition.cancel()
  1715.     fitstate.lastActive = false --mario's state the previous frame (to check state transitions)
  1716.     fitstate.fitness = 0 --current fitness score
  1717.     fitstate.timeout = 0 --time increasing when mario is idle, kills him if it is too much
  1718.     fitstate.timepenalty = 0 --number of frames mario was too idle, to subtract from his fitness
  1719.     --fitstate.area = "Level"
  1720.     --fitstate.lastarea = ""
  1721.     fitstate.savedtimepenalty = 0 --does not save time penalty until mario moves forward
  1722.     basescroll = 0
  1723.     prevScrollBonus = scrollBonus
  1724.     marioPrevAutoscroll = 0
  1725.     specialBonus = 0
  1726.     cardDistance = 9999
  1727.     pBonus = 0
  1728.     lockBound = false
  1729.     getPositions()
  1730.    
  1731.     specialBonus = - scrollBonus - 500/cardDistance - powerupFitness * math.min(marioPower, 2)
  1732.     local nsw = 1 --network switch
  1733.     generateNetwork(genome) --generate the network
  1734.     local controller = {} --inputs to the controller
  1735.     getTileset()
  1736.     fitstate.area = memory.readbyte(0x03DF)
  1737.     while true do --main game loop
  1738.         local koopalingDefeated = koopalingDefeated
  1739.         local marioMusic = marioMusic
  1740.         local manualControl = keyboardInput()
  1741.         local ManualInput = ManualInput
  1742.         local marioX = marioX
  1743.         local card_x = card_x
  1744.         local marioScreenY = marioScreenY
  1745.         local marioYVel = marioYVel
  1746.         --Get inputs to the network
  1747.        
  1748.         if fitstate.frame % FPS == 0 and not Replay then
  1749.             timerOutput()
  1750.         end
  1751.        
  1752.         if fitstate.frame % FramesPerEval == 0 then
  1753.             local inputs = getInputs()
  1754.             --Find the current network to be using
  1755.             local nscompare = fitstate.rightmost
  1756.             nsw = 1
  1757.             for n=1,#genome.networkswitch do
  1758.                 if nscompare > genome.networkswitch[n] then
  1759.                     nsw = n+1
  1760.                 end
  1761.             end
  1762.             --Put outputs to the controller
  1763.             if marioX > card_x -74 and marioX < card_x -64 then
  1764.                 forcebutton = {right=true,A=false,B=true}
  1765.             elseif marioX <= card_x-8 and marioX >= card_x -64 then
  1766.                 forcebutton = {right=true,A=true,B=true}
  1767.             elseif marioX > card_x+8 then
  1768.                 forcebutton = {left=true,right=false}
  1769.             end
  1770.             controller = evaluateNetwork(genome.networks[nsw], inputs, genome.oscillations[nsw], fitstate.frame)
  1771.         else
  1772.             getPositions()
  1773.         end
  1774.        
  1775.         if ManualInput then
  1776.             joypad.set(Player,manualControl)
  1777.         else
  1778.             if fitstate.frame % (FramesPerEval*2) == 0 then
  1779.                 if (marioPower == 3 or marioPower == 5) and controller['A'] then
  1780.                     if memory.readbyte(0x03DD) == 127 then
  1781.                         controller['A'] = false --allows mario to fly with tanooki suit. this type of network would otherwise not be capable of switching that fast
  1782.                     end
  1783.                 end
  1784.             end
  1785.             joypad.set(Player,controller)
  1786.         end
  1787.        
  1788.         fitness(fitstate) --update the fitness
  1789.         if pool.generation == 0 and not Replay then
  1790.             TurboMax = 1
  1791.         elseif TurboMax == 1 then
  1792.             TurboMax = 0
  1793.         end
  1794.         local turbocompare = mmax(0,fitstate.rightmost)
  1795.         if CompleteAutoTurbo or (turbocompare >= TurboMin and turbocompare < TurboMax and pool.species[pool.currentSpecies].turbo) then
  1796.             if not currentTurbo then
  1797.                 currentTurbo = true
  1798.                 emu.speedmode("turbo")
  1799.             end
  1800.         else
  1801.             if currentTurbo then
  1802.                 currentTurbo = false
  1803.                 emu.speedmode("normal")
  1804.             end
  1805.         end
  1806.            
  1807.         --Display the GUI
  1808.         displayGUI(genome.networks[nsw], fitstate)
  1809.         --Advance a frame
  1810.         coroutine.yield() --coroutine.yield()
  1811.         fitstate.frame = fitstate.frame + 1
  1812.        
  1813.         --exit if dead or won
  1814.         if memory.readbyte(0x00EE) == 75 or marioOutOfBound then --if he dies to an enemy or a pit
  1815.             if Replay then
  1816.                 genome.networks = {}
  1817.                 return false
  1818.             end
  1819.             pool.attempts = pool.attempts + 1
  1820.             pool.deaths = pool.deaths + 1
  1821.             addToHistogram()
  1822.             deathCounterOutput()
  1823.             for frame=1,FramesOfDeathAnimation do
  1824.                 displayGUI(genome.networks[nsw], fitstate)
  1825.                 coroutine.yield() --coroutine.yield()
  1826.             end
  1827.             genome.networks = {} --reset networks to save on RAM
  1828.             pool.totalTime = pool.totalTime + fitstate.frame
  1829.             turboOutput()
  1830.             timerOutput()
  1831.             return false
  1832.         end
  1833.         if fitstate.timeout > fitstate.rightmost/3+basetimeout and not ManualInput then --timeout threshold increases throughout level. kill if timeout is enabled and timer is not frozen
  1834.             genome.networks = {} --reset networks to save on RAM
  1835.             if Replay then return false end
  1836.             pool.attempts = pool.attempts + 1
  1837.             addToHistogram()
  1838.             deathCounterOutput()
  1839.             turboOutput()
  1840.             timerOutput()
  1841.             pool.totalTime = pool.totalTime + fitstate.frame
  1842.             return false
  1843.         end
  1844.         if memory.readbyte(0x0014) == 1 then --if he beats the level
  1845.             print("beat level")
  1846.             genome.fitstate.fitness = genome.fitstate.fitness + 1000
  1847.             genome.networks = {} --reset networks to save on RAM
  1848.             if Replay then return true end
  1849.             pool.totalTime = pool.totalTime + fitstate.frame
  1850.             addToHistogram()
  1851.             TurboMin = 0
  1852.             TurboMax = 0
  1853.             turboOutput()
  1854.             battle = false
  1855.             return true
  1856.         end
  1857.         if (marioMusic == 0x50 or marioMusic == 0xB0) and not boss then
  1858.             print('activate final state')
  1859.             genome.fitstate.fitness = genome.fitstate.fitness + 1000
  1860.             genome.networks = {} --reset networks to save on RAM
  1861.             if Replay then return true end
  1862.             pool.totalTime = pool.totalTime + fitstate.frame
  1863.             addToHistogram()
  1864.             TurboMin = 0
  1865.             TurboMax = 0
  1866.             turboOutput()
  1867.             --print('playboss')
  1868.             memory.writebyte(0x0715,0) --set score to 0
  1869.             memory.writebyte(0x0716,0)
  1870.             memory.writebyte(0x0717,0)
  1871.             bosssavestateObj = savestate.object(savestateSlotBOSS)
  1872.             savestate.save(bosssavestateObj)
  1873.             savestate.persist(bosssavestateObj)
  1874.             startboss = true
  1875.             return false
  1876.         end
  1877.         if (marioMusic ~= 0x50 and marioMusic ~= 0xB0 and koopalingDefeated == 0) and boss then
  1878.             genome.fitstate.fitness = genome.fitstate.fitness + 1000
  1879.             genome.networks = {} --reset networks to save on RAM
  1880.             for x=1,800 do
  1881.                 coroutine.yield()
  1882.             end
  1883.            
  1884.             if Replay then return true end
  1885.             pool.totalTime = pool.totalTime + fitstate.frame
  1886.             addToHistogram()
  1887.             TurboMin = 0
  1888.             TurboMax = 0
  1889.             turboOutput()
  1890.             return true
  1891.         end
  1892.         if koopalingDefeated > 0 and boss then
  1893.             print("second")
  1894.             -- print(marioMusic)
  1895.             -- print(koopalingDefeated)
  1896.             genome.fitstate.fitness = genome.fitstate.fitness + 1000
  1897.             genome.networks = {} --reset networks to save on RAM
  1898.             while koopalingDefeated == 1 do
  1899.                 koopalingDefeated = memory.readbyte(0x07BD)
  1900.                 coroutine.yield()
  1901.             end
  1902.             while koopalingDefeated == 2 do
  1903.                 koopalingDefeated = memory.readbyte(0x07BD)
  1904.                 searchstaff()
  1905.             end
  1906.             while koopalingDefeated > 1 do
  1907.                 koopalingDefeated = memory.readbyte(0x07BD)
  1908.                 coroutine.yield()
  1909.             end
  1910.             while kingconvo < 4 do
  1911.                 getPositions()
  1912.                 coroutine.yield()
  1913.             end
  1914.             if kingconvo == 4 then
  1915.                 for a=1,60 do
  1916.                     coroutine.yield()
  1917.                 end
  1918.                 joypad.set(Player,{A = true})
  1919.                 coroutine.yield()
  1920.                 joypad.set(Player,{A = false})
  1921.             end
  1922.             if Replay then return true end
  1923.             pool.totalTime = pool.totalTime + fitstate.frame
  1924.             addToHistogram()
  1925.             TurboMin = 0
  1926.             TurboMax = 0
  1927.             turboOutput()
  1928.             return true
  1929.         end
  1930.     end
  1931. end
  1932.  
  1933. function playSpecies(species,showBest) --Plays through all members of a species
  1934.     spindicatorOutput(species)
  1935.     local startGenome = 2 --which genome to start showing from
  1936.     local oldBest = 0
  1937.     if showBest or not species.genomes[1].fitstate.fitness then
  1938.         startGenome = 1
  1939.         oldBest = species.maxFitness --used to make sure staleness does not reset every run if showBest is toggled on always
  1940.         species.maxFitness = 0
  1941.         species.maxRightmost = 0
  1942.     end
  1943.     speciesDataOutput()
  1944.     for g=startGenome,#species.genomes do --loop through each genome
  1945.         local genome = species.genomes[g]
  1946.         pool.currentGenome = g
  1947.         local won = playGenome(genome) --test the genome
  1948.         if genome.fitstate.fitness + 30 > pool.maxFitness then --if it has gotten very close to the max
  1949.             pool.maxCounter = pool.maxCounter + 1
  1950.         end
  1951.         if genome.fitstate.fitness > pool.maxFitness then --if the fitness is the new best
  1952.             if species.gsid ~= pool.bestSpecies then --change the second best if the current best is a different species
  1953.                 pool.secondFitness = pool.maxFitness
  1954.             end
  1955.             if genome.fitstate.fitness > pool.maxFitness + MajorBreakFitness or genome.fitstate.fitness > pool.lastMajorBreak + MajorBreakFitness*4 then
  1956.                 --Counts as a major breakthrough
  1957.                 pool.lastMajorBreak = genome.fitstate.fitness
  1958.                 pool.bottleneck = 0
  1959.                 pool.bottleneckloops = 0
  1960.                 pool.lastbreaktime = pool.realTime
  1961.             end
  1962.             pool.bestSpecies = species.gsid --update the best species number
  1963.             pool.maxFitness = genome.fitstate.fitness --update the best fitness
  1964.             writeBreakthroughOutput()
  1965.         elseif genome.fitstate.fitness > pool.secondFitness then --if the fitness is the new second best
  1966.             if species.gsid ~= pool.bestSpecies then --change the second best if this is not the current best species
  1967.                 pool.secondFitness = genome.fitstate.fitness
  1968.             end
  1969.         end
  1970.         --print("end calculation fitness: " .. genome.fitstate.fitness)
  1971.         if genome.fitstate.fitness > species.maxFitness then --update the species max fitness
  1972.             species.maxFitness = genome.fitstate.fitness
  1973.             species.breakthroughX = marioX
  1974.             species.breakthroughZ = genome.fitstate.area
  1975.             spindicatorOutput(species)
  1976.             if genome.fitstate.fitness > oldBest then
  1977.                 species.staleness = 0 --reset the staleness
  1978.             end
  1979.         end
  1980.         if genome.fitstate.rightmost > species.maxRightmost then --update the species max right
  1981.             species.maxRightmost = genome.fitstate.rightmost
  1982.         end
  1983.         if startboss then
  1984.             saveGenome("winner",true)
  1985.             startboss = false
  1986.             PlayBoss()
  1987.             won = true
  1988.         end
  1989.         pool.current = pool.current +1
  1990.         pool.total = pool.total + genome.fitstate.fitness
  1991.         pool.average = math.ceil(pool.total/pool.current)
  1992.         if won then return true end --return true if we won
  1993.     end
  1994.     return false --return false otherwise
  1995. end
  1996.  
  1997. function playGeneration(showBest) --Plays through the entire generation
  1998.     local NumberWinner = numberWinner
  1999.     pool.maxCounter = 0
  2000.     pool.current = 0
  2001.     pool.total = 0
  2002.     for s=1,#pool.species do
  2003.         local species = pool.species[s]
  2004.        
  2005.         pool.currentSpecies = s
  2006.         if s > NumberWinner and NumberWinner ~= -1 then
  2007.             if not currentTurbo then
  2008.                 currentTurbo = true
  2009.                 emu.speedmode("turbo")
  2010.             end
  2011.             numberWinner = -1
  2012.         end
  2013.         if playSpecies(species,showBest) then
  2014.             return true
  2015.         end
  2016.     end
  2017.     return false
  2018. end
  2019.  
  2020. --File Outputs
  2021. function deathCounterOutput()
  2022.     fileDeaths = io.open("deaths.txt","w")
  2023.     fileDeaths:write(pool.deaths)
  2024.     fileDeaths:close()
  2025.     fileAttempts = io.open("attempts.txt","w")
  2026.     fileAttempts:write(pool.attempts)
  2027.     fileAttempts:close()
  2028. end
  2029.  
  2030. function speciesDataOutput()
  2031.     local species = pool.species[pool.currentSpecies]
  2032.     speciesdata = "GSID: "..species.gsid.." SMax: "..species.maxFitness.." Stale: "..species.staleness
  2033.     if species.nick ~= "" then
  2034.         speciesdata = speciesdata.." Nick: "..species.nick
  2035.     end
  2036.     fileSData = io.open("speciesdata.txt","w")
  2037.     fileSData:write(speciesdata)
  2038.     fileSData:close()
  2039. end
  2040.  
  2041. function mapupdate()
  2042.     io.open("../mapupdate.txt","w"):close()
  2043. end
  2044.  
  2045. function mapupdateHist()
  2046.     local f = io.open("../mapupdate.txt","w")
  2047.     f:write("hist")
  2048.     f:close()
  2049. end
  2050.  
  2051. function spindicatorOutput(species)
  2052.     fileIPos = io.open("spindicatorpos.txt","w")
  2053.     fileIPos:write(species.breakthroughX.." "..species.breakthroughZ)
  2054.     fileIPos:close()
  2055.     pcall(mapupdate)
  2056. end
  2057.  
  2058. function indicatorOutput()
  2059.     fileIPos = io.open("indicatorpos.txt","w")
  2060.     fileIPos:write(pool.breakthroughX.." "..pool.breakthroughZ)
  2061.     fileIPos:close()
  2062.     pcall(mapupdate)
  2063. end
  2064.  
  2065. function addToHistogram()
  2066.     local i = math.floor(marioX/32)
  2067.     key = i.." "..pool.species[pool.currentSpecies].genomes[pool.currentGenome].fitstate.area
  2068.     if not pool.maphistogram[key] then
  2069.         pool.maphistogram[key] = 0
  2070.     end
  2071.     pool.maphistogram[key] = pool.maphistogram[key] + 1
  2072. end
  2073.  
  2074. function histogramOutput()
  2075.     fileHistogram = io.open("histogram.txt","w")
  2076.     for key,count in pairs(pool.maphistogram) do
  2077.         fileHistogram:write(key.." "..count.."\n")
  2078.     end
  2079.     fileHistogram:close()
  2080. end
  2081.  
  2082. function writeBreakthroughOutput()
  2083.     local species = pool.species[pool.currentSpecies]
  2084.     local genome = species.genomes[pool.currentGenome]
  2085.     info = tostring(pool.generation)
  2086.     for i=1 ,4-string.len(info) do info=info.." " end
  2087.     info = info..tostring(pool.currentSpecies)
  2088.     for i=1,9-string.len(info) do info=info.." " end
  2089.     info = info..tostring(pool.currentGenome)
  2090.     for i=1,13-string.len(info) do info=info.." " end
  2091.     info = info..tostring(species.gsid)
  2092.     for i=1,18-string.len(info) do info=info.." " end
  2093.     info = info..tostring(math.floor(genome.fitstate.fitness))
  2094.     for i=1,23-string.len(info) do info=info.." " end
  2095.     local seconds = pool.realTime
  2096.     local minutes = math.floor(seconds/60)
  2097.     seconds = seconds - minutes*60
  2098.     local hours = math.floor(minutes/60)
  2099.     minutes = minutes - hours*60
  2100.     local days = math.floor(hours/24)
  2101.     hours = hours - days*24
  2102.     if pool.realTime < 3600 then
  2103.         info=info..minutes.."m"..seconds.."s"
  2104.     elseif pool.realTime < 86400 then
  2105.         info=info..hours.."h"..minutes.."m"
  2106.     else
  2107.         info=info..days.."d"..hours.."h"
  2108.     end
  2109.     pool.history=pool.history..info.."\n"
  2110.     fileFTracker = io.open("fitnesstracker.txt","w")
  2111.     fileFTracker:write(pool.history)
  2112.     fileFTracker:close()
  2113.    
  2114.     pool.breakthroughX = marioX
  2115.     pool.breakthroughZ = genome.fitstate.area
  2116.     if boss then
  2117.         saveGenome("Boss-G"..pool.generation.."s"..pool.currentSpecies.."g"..pool.currentGenome,false)
  2118.     else
  2119.         saveGenome("G"..pool.generation.."s"..pool.currentSpecies.."g"..pool.currentGenome,false)
  2120.     end
  2121.    
  2122.     indicatorOutput()
  2123. end
  2124.  
  2125. function levelNameOutput()
  2126.     getPositions()
  2127.     fileLevel = io.open("level.txt","w")
  2128.    
  2129.     fileLevel:write(levelname)
  2130.     fileLevel:close()
  2131. end
  2132.  
  2133. function turboOutput()
  2134.     fileTurbo = io.open("turbo.txt","w")
  2135.     if TurboMin == 0 then
  2136.         fileTurbo:write("T="..TurboMax)
  2137.     else
  2138.         fileTurbo:write("T="..TurboMin.."\nto "..TurboMax)
  2139.     end
  2140.     fileTurbo:close()
  2141. end
  2142.  
  2143. function twoDigit(num)
  2144.     str = tostring(num)
  2145.     if string.len(str) == 1 then
  2146.         str = "0"..str
  2147.     end
  2148.     return str
  2149. end
  2150.  
  2151. function numToTime(seconds)
  2152.     local minutes = math.floor(seconds/60)
  2153.     seconds = seconds - minutes*60
  2154.     local hours = math.floor(minutes/60)
  2155.     minutes = minutes - hours*60
  2156.     local days = math.floor(hours/24)
  2157.     hours = hours - days*24
  2158.     return days.."d "..twoDigit(hours)..":"..twoDigit(minutes)..":"..twoDigit(seconds)
  2159. end
  2160.  
  2161. function timerOutput()
  2162.     fileRealTime = io.open("realtime.txt","w")
  2163.     fileGameTime = io.open("gametime.txt","w")
  2164.     pool.realTime = os.time()-ProgramStartTime
  2165.     fileRealTime:write(numToTime(pool.realTime))
  2166.     fileGameTime:write(numToTime(math.floor(pool.totalTime/FPS)))
  2167.     fileRealTime:close()
  2168.     fileGameTime:close()
  2169. end
  2170.  
  2171. function fitnessDataOutput()
  2172.     rankGlobally()
  2173.     local fitnesses = {}
  2174.     local genAvg = 0
  2175.     local smaxAvg = 0
  2176.     local minGsid = 1000000
  2177.     local maxGsid = 0
  2178.     for s,species in pairs(pool.species) do
  2179.         for g,genome in pairs(species.genomes) do
  2180.             fitnesses[genome.globalRank] = genome.fitstate.fitness
  2181.             genAvg = genAvg + genome.fitstate.fitness
  2182.         end
  2183.         smaxAvg = smaxAvg + species.maxFitness
  2184.         if species.gsid > maxGsid then maxGsid = species.gsid end
  2185.         if species.gsid < minGsid then minGsid = species.gsid end
  2186.     end
  2187.     local genMin = fitnesses[1]
  2188.     local genBqt = fitnesses[math.floor(#fitnesses/4)]
  2189.     local genMed = fitnesses[math.floor(#fitnesses/2)]
  2190.     local genTqt = fitnesses[math.floor(3*#fitnesses/4)]
  2191.     local genMax = fitnesses[#fitnesses]
  2192.     local genAvg = genAvg / #fitnesses
  2193.     local smaxAvg = smaxAvg / #pool.species
  2194.     local smaxSdv = 0
  2195.     for s,species in pairs(pool.species) do
  2196.         smaxSdv = smaxSdv + (species.maxFitness - smaxAvg)*(species.maxFitness - smaxAvg)
  2197.     end
  2198.     smaxSdv = math.sqrt(smaxSdv / #pool.species)
  2199.     local avgSdv = 0
  2200.     for f,fitness in pairs(fitnesses) do
  2201.         avgSdv = avgSdv + (fitness-genAvg)*(fitness-genAvg)
  2202.     end
  2203.     avgSdv = math.sqrt(avgSdv / #fitnesses)
  2204.     os.remove("data"..dirsep..levelname..dirsep.."data"..(pool.generation+1)..".drd")
  2205.     fileDRD = io.open("data"..dirsep..levelname..dirsep.."data"..pool.generation..".drd","w")
  2206.     fileDOut = io.open("discorddataout.txt","w")
  2207.     fileDRD:write(#fitnesses.." "..pool.population.." "..#pool.species.." ")
  2208.     fileDOut:write("**Generation: `"..pool.generation.."`** Target Pop: `"..pool.population.."` Actual Pop: `"..#fitnesses.."` Number of Species: `"..#pool.species.."` ")
  2209.     fileDRD:write(genMin.." "..genBqt.." "..genMed.." "..genTqt.." "..genMax.." ")
  2210.     fileDOut:write("Minimum: `"..genMin.."` Median: `"..genMed.."` Maximum: `"..genMax.."` ")
  2211.     fileDRD:write(genAvg.." "..avgSdv.." "..smaxAvg.." "..smaxSdv.."\n")
  2212.     fileDOut:write("Average: `"..genAvg.."` Standard Deviation: `"..avgSdv.."` Average Max: `"..smaxAvg.."`\n")
  2213.     for gsid=minGsid,maxGsid do
  2214.         for s,species in pairs(pool.species) do
  2215.             if species.gsid == gsid then
  2216.                 local speciesRanks = {}
  2217.                 for g,genome in pairs(species.genomes) do
  2218.                     table.insert(speciesRanks, genome) --Insert every genome into the table
  2219.                 end
  2220.                 table.sort(speciesRanks, function (a,b) --Sort based on fitness
  2221.                     return (a.fitstate.fitness < b.fitstate.fitness)
  2222.                 end)
  2223.                 local specAvg = 0
  2224.                 local sfitnesses = {}
  2225.                 for g=1,#speciesRanks do --Put position of each genome into that genome's data
  2226.                     sfitnesses[g] = speciesRanks[g].fitstate.fitness
  2227.                     specAvg = specAvg + speciesRanks[g].fitstate.fitness
  2228.                 end
  2229.                 local specMin = sfitnesses[1]
  2230.                 local specBqt = sfitnesses[math.ceil(#sfitnesses/4)]
  2231.                 local specMed = sfitnesses[math.ceil(#sfitnesses/2)]
  2232.                 local specTqt = sfitnesses[math.ceil(3*#sfitnesses/4)]
  2233.                 local specMax = sfitnesses[#sfitnesses]
  2234.                 specAvg = specAvg / #sfitnesses
  2235.                 local specSdv = 0
  2236.                 for f,fitness in pairs(sfitnesses) do
  2237.                     specSdv = specSdv + (fitness-specAvg)*(fitness-specAvg)
  2238.                 end
  2239.                 specSdv = math.sqrt(specSdv / #sfitnesses)
  2240.                 fileDRD:write(species.gsid.." "..#species.genomes.." "..#species.genomes[1].genes.." ")
  2241.                 fileDRD:write(specMin.." "..specBqt.." "..specMed.." "..specTqt.." "..specMax.." "..specAvg.." "..specSdv)
  2242.                 for f,fitness in pairs(sfitnesses) do
  2243.                     fileDRD:write(" "..fitness)
  2244.                 end
  2245.                 fileDRD:write("\n")
  2246.                 if species.nick ~= "" then
  2247.                     fileDOut:write("Nickname: `"..species.nick.."` Maximum: `"..specMax.."` ")
  2248.                     fileDOut:write("Staleness: `"..species.staleness.."` Average: `"..specAvg.."` ")
  2249.                     fileDOut:write("Number of Genomes: `"..#sfitnesses.."` Num Networks: `"..#species.genomes[1].genes.."`\n")                 
  2250.                 end
  2251.             end
  2252.         end
  2253.     end
  2254.     fileDRD:close()
  2255.     fileDOut:close()
  2256. end
  2257.  
  2258. keyFlag = false
  2259. function keyboardInput()
  2260.     local keyboard = input.get()
  2261.     if keyboard['N'] and keyFlag == false then --N toggles the network display
  2262.         DisplayNetwork = not DisplayNetwork
  2263.     end
  2264.     if keyboard['R'] and keyFlag == false then --R toggles the sprite hitboxes
  2265.         DisplaySprites = not DisplaySprites
  2266.     end
  2267.     if keyboard['G'] and keyFlag == false then --G toggles the large grid display
  2268.         DisplayGrid = (DisplayGrid + 1) % 3
  2269.     end
  2270.     if keyboard['O'] and keyFlag == false then --O toggles the sprite slots
  2271.         DisplaySlots = not DisplaySlots
  2272.     end
  2273.     if keyboard['A'] and keyFlag == false then --A toggles the top stats bar
  2274.         DisplayStats = not DisplayStats
  2275.     end
  2276.     if keyboard['M'] and keyFlag == false then --M toggles manual vs network input
  2277.         ManualInput = not ManualInput
  2278.     end
  2279.     if keyboard['E'] and keyFlag == false then --E toggles
  2280.         DisplayRanges = not DisplayRanges
  2281.     end
  2282.     if keyboard['C'] and keyFlag == false then --E toggles
  2283.         DisplayCounters = not DisplayCounters
  2284.     end
  2285.     if keyboard['L'] and keyFlag == false then --L loads
  2286.         --to load put an interrupt that will restart the program and initialize with the previous pool
  2287.         local interrupt = io.open("interrupt.lua","w")
  2288.         interrupt:write("restartprog = luigiofilename")
  2289.         interrupt:close()
  2290.         local initialize = io.open("initialize.lua","w")
  2291.         initialize:write("loadPool('backups' .. dirsep .. 'current.lua')")
  2292.         initialize:close()
  2293.     end
  2294.     local controller = {}
  2295.     controller['A'] = keyboard['F']
  2296.     controller['B'] = keyboard['D']
  2297.     controller['up'] = keyboard['up']
  2298.     controller['down'] = keyboard['down']
  2299.     controller['left'] = keyboard['left']
  2300.     controller['right'] = keyboard['right']
  2301.     if controller["up"] and controller["down"] then
  2302.         controller["up"] = false
  2303.         controller["down"] = false
  2304.     end
  2305.     if controller["left"] and controller["right"] then
  2306.         controller["left"] = false
  2307.         controller["right"] = false
  2308.     end
  2309.     local allkeys = {'N','R','G','O','A','M','E','C','L'}
  2310.     keyFlag = false --Set keyflag to true if any keys were pressed otherwise it is false
  2311.     for k=1,#allkeys do
  2312.         if keyboard[allkeys[k]] then
  2313.             keyFlag = true
  2314.         end
  2315.     end
  2316.     return controller
  2317. end
  2318.  
  2319. function drawStatsBox(fitstate)
  2320.     local genimage = {' ####  #### ##  ## ','##---##----#--##--#','#--####--###---#--#','#--#--#----#------#','#--#--#--###--#---#','##----#----#--##--#','  #### #### ##  ## '}
  2321.     local specimage = {' ##### ##### ####  ### ## ####  #### ','##----#----##----##---#--#----##----#','#--####--#--#--###--###--#--###--### ','##---##----##----#--###--#----##---##','####--#--## #--###--###--#--######--#','#----##--#  #----##---#--#----#----# ',' ##### ##    ####  ### ## #### ####  '}
  2322.     local gnmimage = {' ##### #### ##  ## #####  ######  #### ','##---##----#--##--##---####--#--##----#','#--####--###---#--#--#--#--------#--###','#--#--#----#------#--#--#--#--#--#----#','#--#--#--###--#---#--#--#--#--#--#--###','##----#----#--##--##---##--#--#--#----#','  #### #### ##  ## ##### ## ## ## #### '}
  2323.     local popimage = {' ##### ##### ##### ','#----###---##----##','#--#--#--#--#--#--#','#----##--#--#----##','#--####--#--#--##  ','#--#  ##---##--#   ',' ##    ##### ##    '}
  2324.     local fitimage = {' #### ## ###### ##  ## ####  #### #### ','#----#--#------#--##--#----##----#----#','#--###--###--###---#--#--###--###--### ','#----#--# #--# #------#----##---##---##','#--###--# #--# #--#---#--######--###--#','#--###--# #--# #--##--#----#----#----# ',' ##   ##   ##  #### ####### #### ##### '}
  2325.     local firstimage = {' ###   #### ###### ','#---###----#------#','##--##--### ##--## ','##--###---#  #--#  ','##--#####--# #--#  ','#----#----#  #--#  ',' #### ####    ##   '}
  2326.     local secondimage = {'  ####  ##   ## ####   ','##----##--###--#----## ','#--##--#---##--#--#--# ','###--###----#--#--##--#','##--####--#----#--#--# ','#-----##--##---#----## ',' #####  ##  ### ####   '}
  2327.     local maxnumimage = {' #####  #####    ####  ##  ## ','##-#-###--#--####----##--##--#','#-----#--------#--##--#--##--#','##-#-##--#--#--#------##----##','#-----#--#--#--#--##--#--##--#','##-#-##--#--#--#--##--#--##--#',' ##### #######################'}
  2328.     local avgxbar = {' #######                 ','#-------#                ',' #######                 ','#--# #--# #### ## ###### ','#--###--##----#--#------#',' #--#--# #--###--###--## ','  #---#  #----#--# #--#  ',' #--#--# #--###--# #--#  ','#--###--##--###--# #--#  ','#--###--##--###--# #--#  ',' ### ###  ##   ##   ##   '}
  2329.     local draw = draw
  2330.     local pool = pool
  2331.     local fl = math.floor
  2332.     if currentTurbo then
  2333.         gui.drawrect(8,194,254,230,draw.CYAN,draw.WHITE)
  2334.         gui.text(10, 197, "GEN "..pool.generation .. " Species "..pool.currentSpecies.." Genome "..pool.currentGenome, draw.GRAYSCALE, draw.CYAN)
  2335.         gui.text(10, 210, "Fitness "..math.max(0,fl(fitstate.fitness)) .. " 1ST "..fl(pool.maxFitness).." 2ND "..fl(pool.secondFitness), draw.GRAYSCALE, draw.CYAN)
  2336.         gui.text(10, 222, "#Max "..pool.maxCounter .. " XFit "..pool.average, draw.GRAYSCALE, draw.CYAN)
  2337.     else
  2338.         -- draw.rect(8,194,32,1,draw.BLACK)
  2339.         -- draw.rect(9,195,246,36,draw.WHITE)
  2340.         -- draw.rect(10,196,244,34,draw.CYAN)
  2341.         gui.drawrect(8,194,254,230,draw.CYAN,draw.WHITE)
  2342.         gui.drawrect(230,204,251,207,draw.GRAYSCALE,draw.CYAN)
  2343.         --draw.rect(230,205,21,2,draw.BLACK)
  2344.         draw.image(10,197,genimage,draw.GRAYSCALE)
  2345.         draw.number(31,197,pool.generation,3)
  2346.         draw.image(65,197,specimage,draw.GRAYSCALE)
  2347.         draw.number(104,197,pool.currentSpecies,3)
  2348.         draw.image(138,197,gnmimage,draw.GRAYSCALE)
  2349.         draw.number(179,197,pool.currentGenome,3)
  2350.         --draw.image(176,198,brackets,draw.GRAYSCALE)
  2351.         --draw.number(182,198,19,2)
  2352.         draw.image(207,202,popimage,draw.GRAYSCALE)
  2353.         draw.number(229,197,pool.current,3)
  2354.         draw.number(229,208,pool.population,3)
  2355.         draw.image(10,210,fitimage,draw.GRAYSCALE)
  2356.         draw.number(50,210,math.max(0,fitstate.fitness),4)
  2357.         draw.image(88,210,firstimage,draw.GRAYSCALE)
  2358.         draw.number(108,210,pool.maxFitness,4)
  2359.         draw.image(147,210,secondimage,draw.GRAYSCALE)
  2360.         draw.number(171,210,pool.secondFitness,4)
  2361.         draw.image(10,222,maxnumimage,draw.GRAYSCALE)
  2362.         draw.number(43,222,pool.maxCounter,3)
  2363.         draw.image(81,218,avgxbar,draw.GRAYSCALE)
  2364.         draw.number(109,222,pool.average,4)
  2365.     end
  2366. end
  2367.  
  2368. arrowimage = {'###     ','#--##   ','#----## ','#------#','#----## ','#--##   ','###     '}
  2369. pmeterimage = {' ############# ','##-----------##','#----#####----#','#----#---#----#','#----####-----#','##---#-------##',' ############# '}
  2370. reversePalette = {[' ']=0,['#']=draw.WHITE,['-']=draw.BLACK}
  2371. function drawPMeter(x,y)
  2372.     local pStatus = memory.readbyte(0x03DD)
  2373.     local currentPalette = draw.GRAYSCALE
  2374.     for i=1,6 do
  2375.         if pStatus < math.pow(2,i)-1 then
  2376.             currentPalette = reversePalette
  2377.         end
  2378.         draw.image(x-8+i*12,y,arrowimage,currentPalette)
  2379.     end
  2380.     if pStatus < 127 then currentPalette = reversePalette end
  2381.     draw.image(x+75,y,pmeterimage,currentPalette)
  2382. end
  2383.  
  2384. clockimage = {'  ####  ',' #-#--# ','#--#---#','#--###-#','#------#',' #----# ','  ####  '}
  2385. function drawClock(x,y)
  2386.     local timeLeft = memory.readbyte(0x05EE)*100+memory.readbyte(0x05EF)*10+memory.readbyte(0x05F0)
  2387.     draw.image(x,y,clockimage,draw.GRAYSCALE)
  2388.     draw.number(x+8,y,timeLeft,3)
  2389. end
  2390.  
  2391. function drawNeuron(network,neuron)
  2392.     if neuron.x then
  2393.         inside = draw.tcolor(0x01010100*math.floor(127*(neuron.value+1))+0xE6)
  2394.         border = draw.tcolor(0x01010100*math.floor(127*(1-neuron.value))+0xE6)
  2395.         if neuron.blocked then inside = draw.tcolor(0xFF3F00E6) end
  2396.         if neuron.value ~= 0 then
  2397.             draw.rect(neuron.x,neuron.y,5,5,border)
  2398.             draw.rect(neuron.x+1,neuron.y+1,3,3,inside)
  2399.         else
  2400.             draw.rect(neuron.x,neuron.y,5,5,draw.tcolor(0x7F7FCFC7))
  2401.         end
  2402.         if neuron.incoming then
  2403.             for g,gene in pairs(neuron.incoming) do
  2404.                 if gene.enabled then
  2405.                     local n1 = network.neurons[gene.into]
  2406.                     local layerdiff = neuron.layer - n1.layer
  2407.                     --Green or red for positive or negative weight
  2408.                     local color = 0x3FFF00
  2409.                     if gene.weight < 0 then
  2410.                         color = 0xFF3F00
  2411.                     end
  2412.                     local opacity = 0xCF
  2413.                     if gene.into > BoxSize and gene.into <= Inputs then --fade or remove if bottom-row neuron
  2414.                         if #network.genes > 100 then
  2415.                             opacity = 0x00
  2416.                         elseif #network.genes > 50 then
  2417.                             opacity = 0x5F
  2418.                         end
  2419.                     end
  2420.                     if n1.value == 0 then --fade or remove if not transmitting a value
  2421.                         if #network.genes > 50 then
  2422.                             opacity = 0x00
  2423.                         else
  2424.                             opacity = 0x5F
  2425.                         end
  2426.                     end
  2427.                     --draw the genome
  2428.                     if n1.x and n1.layer > 0 then
  2429.                         gui.drawline(n1.x+4,n1.y+2,neuron.x,neuron.y+2, draw.tcolor(256*color+opacity))
  2430.                     end
  2431.                 end
  2432.             end
  2433.         end
  2434.     end
  2435. end
  2436.  
  2437. charAimage = {' #### ','##--##','#-##-#','#----#','#-##-#','#-##-#',' #### '}
  2438. charBimage = {' #### ','#---##','#-##-#','#---##','#-##-#','#---##',' #### '}
  2439. charUimage = {' #### ','#-##-#','#-##-#','#-##-#','#-##-#','##--##',' #### '}
  2440. charDimage = {' #### ','#---##','#-##-#','#-##-#','#-##-#','#---##',' #### '}
  2441. charLimage = {' #### ','#-####','#-####','#-####','#-####','#----#',' #### '}
  2442. charRimage = {' #### ','#---##','#-##-#','#---##','#-#-##','#-##-#',' #### '}
  2443. buttonImages = {charAimage,charBimage,charUimage,charDimage,charLimage,charRimage}
  2444. function drawNetwork(network)
  2445.     local neurons = {} --Array that will contain the position and value of each displayed neuron
  2446.     local i = 1
  2447.     local ButtonNumbers = ButtonNumbers
  2448.     local buttonImages = buttonImages
  2449.     for dy=-BoxRadius,BoxRadius do --Add the input box neurons
  2450.         for dx=-BoxRadius,BoxRadius do
  2451.             network.neurons[i].x = 8+5*(dx+BoxRadius)
  2452.             network.neurons[i].y = 20+5*(dy+BoxRadius)
  2453.             i = i + 1
  2454.         end
  2455.     end
  2456.    
  2457.     local botRowSpacing = 10 --Number of pixels between oscillating nodes
  2458.        
  2459.     local botRowSize = (3 + #InitialOscillations)*2 - 1
  2460.     if botRowSize > BoxWidth then
  2461.         botRowSpacing = 5 --If bottom row can't fit then have no spacing
  2462.     end
  2463.    
  2464.     for j=0,#InitialOscillations do --Add the bias and oscillation neurons
  2465.         network.neurons[i].x = 3+BoxWidth*5-botRowSpacing*j
  2466.         network.neurons[i].y = 25+BoxWidth*5
  2467.         i = i + 1
  2468.     end
  2469.    
  2470.     for j=0,1 do --Add the bias and oscillation neurons
  2471.         network.neurons[i].x = 8+botRowSpacing*j
  2472.         network.neurons[i].y = 25+BoxWidth*5
  2473.         i = i + 1
  2474.     end
  2475.    
  2476.     for l=1,#network.layers do --Draw each layer in the NN
  2477.         local layer = network.layers[l]
  2478.         for n=1,#layer do
  2479.             if l > 1 or layer[n] > Inputs then --display only non-inputs in layer 1
  2480.                 network.neurons[layer[n]].x = math.ceil(13+BoxWidth*5+(220-BoxWidth*5)*((l-1) / (#network.layers)))
  2481.                 network.neurons[layer[n]].y = math.ceil(20+(BoxWidth+1)*5*(n / (#layer+1)))
  2482.             end
  2483.         end
  2484.     end
  2485.    
  2486.     --When to block opposite directional inputs
  2487.     local blockUD = network.neurons[-3] ~= nil and network.neurons[-4] ~= nil and network.neurons[-3].value>0 and network.neurons[-4].value>0
  2488.     local blockLR = network.neurons[-5] ~= nil and network.neurons[-6] ~= nil and network.neurons[-5].value>0 and network.neurons[-6].value>0
  2489.     local fb = {}
  2490.     if forcebutton ~= nil then
  2491.         for k,v in pairs(forcebutton) do
  2492.             fb[ButtonNumbers[k]] = v
  2493.         end
  2494.     end
  2495.     local neuron = {}
  2496.     for o=1,6 do --outputs
  2497.         neuron = {}
  2498.         neuron.x = 238
  2499.         neuron.y = 25+(BoxWidth-1)*(o-1)
  2500.         neuron.value = -1
  2501.         local onode = network.neurons[-o]
  2502.         if onode and onode.value > 0 then neuron.value = 1 end
  2503.         if blockUD and (o == 3 or o == 4) then neuron.blocked = true end
  2504.         if blockLR and (o == 5 or o == 6) then neuron.blocked = true end
  2505.         if fb[o] ~= nil then
  2506.             if fb[0] then
  2507.                 neuron.value = 1
  2508.             else
  2509.                 neuron.value = -1
  2510.             end
  2511.         end
  2512.         drawNeuron(network,neuron)
  2513.         draw.image(246,24+(BoxWidth-1)*(o-1),buttonImages[o],draw.GRAYSCALE)
  2514.         if onode ~= nil and network.neurons[o].layer ~= 0 and onode.x then
  2515.             if neuron.value == 0 then --draw line between output and ouput box.
  2516.                 gui.drawline(onode.x+4,onode.y+2,neuron.x,neuron.y+2,draw.tcolor(0x3FFF005F)) --fade if not sending anything
  2517.             else   
  2518.                 gui.drawline(onode.x+4,onode.y+2,neuron.x,neuron.y+2,draw.tcolor(0x3FFF00CF))
  2519.             end
  2520.         end
  2521.     end
  2522.    
  2523.     for n,neuron in pairs(network.neurons) do
  2524.         drawNeuron(network,neuron)
  2525.     end
  2526. end
  2527.  
  2528. function drawRanges()
  2529.     local levelstring = levelname
  2530.     local ranges = Ranges[levelstring.."-"..fitstate.area]
  2531.     local marioX = marioX
  2532.     local marioScreenX = marioScreenX
  2533.     local marioY = marioY
  2534.     local marioScreenY = marioScreenY
  2535.     if ranges ~= nil then
  2536.         for r=1,#ranges do
  2537.             --default values for parts without ranges
  2538.             local minx = 0
  2539.             local maxx = 65536
  2540.             local miny = 0
  2541.             local maxy = 240
  2542.             local disp = true
  2543.             local range = ranges[r]
  2544.             --if range limits exist set the limits to those
  2545.             if range.xrange.min ~= nil then minx = range.xrange.min end
  2546.             if range.xrange.max ~= nil then maxx = range.xrange.max end
  2547.             if range.yrange.min ~= nil then miny = range.yrange.min end
  2548.             if range.yrange.max ~= nil then maxy = range.yrange.max end
  2549.            
  2550.             --set color based on the direction of fitness increase
  2551.             local rgb = 0x003FFF
  2552.             textcolor = toRGBA(0xFFFFFFFF)
  2553.             if range.area ~= nil then
  2554.                 rgb=0xFF00FF
  2555.             elseif range.coeffs ~= nil then
  2556.                 if range.coeffs.x == 1 and range.coeffs.y == 1 and range.coeffs.c ~=0 then
  2557.                     rgb=0xE5FF00
  2558.                     textcolor = toRGBA(0xFF000000)
  2559.                 end
  2560.                 if range.coeffs.x >0 and range.coeffs.y==0 then
  2561.                     rgb=0X0FFF1B
  2562.                 end
  2563.                 if range.coeffs.y > 0 and range.coeffs.x <1 then
  2564.                     rgb=0x0F37FF
  2565.                 end
  2566.                 if range.coeffs.x == 0 and range.coeffs.y == 0 then
  2567.                     rgb=0xFF3F00
  2568.                 end
  2569.                 if range.coeffs.y <0 then
  2570.                     rgb=0xFFBF00
  2571.                 end
  2572.                 if range.coeffs.x < 0 then
  2573.                     rgb=0xBF7F00
  2574.                 end
  2575.             elseif range.timeout ~= nil then
  2576.                 rgb=0x42AAFF
  2577.             end
  2578.             if rgb~=0x003FFF then
  2579.                 color = toRGBA(0xFF000000 + rgb)
  2580.                 --draw the box
  2581.                 if marioY + marioScreenY == 161 and memory.readbyte(0x0544) == 0 then
  2582.                     gui.drawbox(minx-marioX+marioScreenX-1,193-miny,maxx-marioX+marioScreenX-2,193-maxy-1,toRGBA(0x7F000000 + rgb),toRGBA(0x7F000000 + rgb))
  2583.                     gui.drawbox(minx-marioX+marioScreenX-1,193-miny,minx-marioX+marioScreenX-1,193-maxy-1,color,color)
  2584.                     gui.drawbox(minx-marioX+marioScreenX-1,193-miny,maxx-marioX+marioScreenX-2,193-miny,color,color)
  2585.                     gui.drawbox(minx-marioX+marioScreenX-1,193-maxy-1,maxx-marioX+marioScreenX-2,193-maxy-1,color,color)
  2586.                     gui.drawbox(maxx-marioX+marioScreenX-2,193-miny,maxx-marioX+marioScreenX-2,193-maxy-1,color,color)
  2587.                     --draw the numbers in the corners that show the fitness values
  2588.                     --local TLcorner = minx*range.coeffs.x+(208-miny)*range.coeffs.y+range.coeffs.c
  2589.                     --local TRcorner = maxx*range.coeffs.x+(208-miny)*range.coeffs.y+range.coeffs.c
  2590.                     --local BLcorner = minx*range.coeffs.x+(192-maxy)*range.coeffs.y+range.coeffs.c
  2591.                     --local BRcorner = maxx*range.coeffs.x+(192-maxy)*range.coeffs.y+range.coeffs.c
  2592.                     -- gui.drawtext(minx-marioX+marioScreenX,miny+1,TLcorner,textcolor,color)
  2593.                     -- gui.drawtext(maxx-marioX+marioScreenX-6*string.len(TRcorner)-1,miny+1,TRcorner,textcolor,color)
  2594.                     -- gui.drawtext(minx-marioX+marioScreenX,maxy-8,BLcorner,textcolor,color)
  2595.                     -- gui.drawtext(maxx-marioX+marioScreenX-6*string.len(TRcorner)-1,maxy-8,BRcorner,textcolor,color)
  2596.                 else
  2597.                     local offset = 161 - (marioY + marioScreenY)
  2598.                     gui.drawbox(minx-marioX+marioScreenX-1,193-miny-offset,maxx-marioX+marioScreenX-2,193-maxy-1-offset,toRGBA(0x7F000000 + rgb),toRGBA(0x7F000000 + rgb))
  2599.                     gui.drawbox(minx-marioX+marioScreenX-1,193-miny-offset,minx-marioX+marioScreenX-1,193-maxy-1-offset,color,color)
  2600.                     gui.drawbox(minx-marioX+marioScreenX-1,193-miny-offset,maxx-marioX+marioScreenX-2,193-miny-offset,color,color)
  2601.                     gui.drawbox(minx-marioX+marioScreenX-1,193-maxy+1-offset,maxx-marioX+marioScreenX-2,193-maxy-1-offset,color,color)
  2602.                     gui.drawbox(maxx-marioX+marioScreenX-2,193-miny-offset,maxx-marioX+marioScreenX-2,193-maxy-1-offset,color,color)
  2603.                 end
  2604.             end
  2605.         end
  2606.     end
  2607. end
  2608.  
  2609. function displayGUI(network,fitstate)
  2610.     if DisplayRanges then
  2611.         drawRanges()
  2612.     end
  2613.     if DisplayStats then
  2614.         drawStatsBox(fitstate)
  2615.         if not currentTurbo then
  2616.             drawPMeter(159,222)
  2617.             --drawClock(222,10)
  2618.         end
  2619.     end
  2620.  
  2621.     if DisplayNetwork and not currentTurbo then
  2622.         drawNetwork(network)
  2623.     end
  2624. end
  2625.  
  2626. function worldselection()
  2627.     local selecting = true
  2628.     local selection = 1
  2629.     local color = draw.BLACK
  2630.     local z = 0
  2631.     local c = 0
  2632.     for i=1,70 do
  2633.         draw.rect(8,204-i,248,22+i,draw.BLACK)
  2634.         draw.rect(9,205-i,246,20+i,draw.WHITE)
  2635.         draw.rect(10,206-i,244,18+i,draw.CYAN)
  2636.         gui.text(12, 208-i, "Please select a world. Use !press up/down to \nnavigate and !press A to select and B to cancel", draw.BLACK, draw.CYAN)
  2637.         color = draw.BLACK
  2638.         for x=1,8 do
  2639.             if file_exists("fcs"..dirsep.."world-" .. x .. ".fcs") then
  2640.                 color = draw.BLACK
  2641.             else
  2642.                 color = draw.RED
  2643.             end
  2644.             if x < 5 then
  2645.                 z = x
  2646.                 c = 0
  2647.             else
  2648.                 z = x -4
  2649.                 c = 80
  2650.             end
  2651.             if x == selection then
  2652.                 gui.text(12+c, 216+16*z-i, "> World: ".. x, color, draw.CYAN)
  2653.             else
  2654.                 gui.text(12+c, 216+16*z-i, "World: ".. x, color, draw.CYAN)
  2655.             end
  2656.         end
  2657.         coroutine.yield()
  2658.     end
  2659.     while selecting do
  2660.         draw.rect(8,134,248,94,draw.BLACK)
  2661.         draw.rect(9,135,246,92,draw.WHITE)
  2662.         draw.rect(10,136,244,90,draw.CYAN)
  2663.         gui.text(12, 138, "Please select a world. Use !press up/down to \nnavigate and !press A to select and B to cancel", draw.BLACK, draw.CYAN)
  2664.         color = draw.BLACK
  2665.         for x=1,8 do
  2666.             if file_exists("fcs"..dirsep.."world-" .. x .. ".fcs") then
  2667.                 color = draw.BLACK
  2668.             else
  2669.                 color = draw.RED
  2670.             end
  2671.             if x < 5 then
  2672.                 z = x
  2673.                 c = 0
  2674.             else
  2675.                 z = x -4
  2676.                 c = 80
  2677.             end
  2678.             if x == selection then
  2679.                 gui.text(12+c, 146+16*z, "> World: ".. x, color, draw.CYAN)
  2680.             else
  2681.                 gui.text(12+c, 146+16*z, "World: ".. x, color, draw.CYAN)
  2682.             end
  2683.         end
  2684.         if file_exists("userinput.lua") then
  2685.             userinput=loadfile("userinput.lua")
  2686.             if userinput then
  2687.                 os.remove('userinput.lua')
  2688.                 uinput = {}
  2689.                 pcall(userinput)
  2690.                 if uinput.down then
  2691.                     selection = selection + 1
  2692.                     if selection > 8 then
  2693.                         selection = 1
  2694.                     end
  2695.                 elseif uinput.up then
  2696.                     selection = selection - 1
  2697.                     if selection <1 then
  2698.                         selection = 8
  2699.                     end
  2700.                 elseif uinput.left then
  2701.                     selection = selection -4
  2702.                     if selection <1 then
  2703.                         selection = selection + 8
  2704.                     end
  2705.                 elseif uinput.right then
  2706.                     selection = selection +4
  2707.                     if selection >8 then
  2708.                         selection = selection - 8
  2709.                     end
  2710.                 elseif uinput.A and file_exists("fcs"..dirsep.."world-" .. selection .. ".fcs") then
  2711.                     local interrupt = io.open("interrupt.lua","w")
  2712.                     interrupt:write("savestateObj = savestate.object('fcs"..dirsep.."world-" .. selection ..".fcs')\nprint('one point five')\nsavestate.load(savestateObj)\nprint('two')")
  2713.                     interrupt:close()
  2714.                     for i=1,90 do
  2715.                         draw.rect(8,134+i,248,94-i,draw.BLACK)
  2716.                         draw.rect(9,135+i,246,92-i,draw.WHITE)
  2717.                         draw.rect(10,136+i,244,90-i,draw.CYAN)
  2718.                         gui.text(12, 138+i, "Please select a world. Use !press up/down to \nnavigate and !press A to select and B to cancel", draw.BLACK, draw.CYAN)
  2719.                         color = draw.BLACK
  2720.                         for x=1,8 do
  2721.                             if file_exists("fcs"..dirsep.."world-" .. x .. ".fcs") then
  2722.                                 color = draw.BLACK
  2723.                             else
  2724.                                 color = draw.RED
  2725.                             end
  2726.                             if x < 5 then
  2727.                                 z = x
  2728.                                 c = 0
  2729.                             else
  2730.                                 z = x -4
  2731.                                 c = 80
  2732.                             end
  2733.                             if x == selection then
  2734.                                 gui.text(12+c, 146+16*z+i, "> World: ".. x, color, draw.CYAN)
  2735.                             else
  2736.                                 gui.text(12+c, 146+16*z+i, "World: ".. x, color, draw.CYAN)
  2737.                             end
  2738.                         end
  2739.                         coroutine.yield()
  2740.                     end
  2741.                     return
  2742.                 elseif uinput.B then
  2743.                     for i=1,90 do
  2744.                         draw.rect(8,134+i,248,94-i,draw.BLACK)
  2745.                         draw.rect(9,135+i,246,92-i,draw.WHITE)
  2746.                         draw.rect(10,136+i,244,90-i,draw.CYAN)
  2747.                         gui.text(12, 138+i, "Please select a world. Use !press up/down to \nnavigate and !press A to select and B to cancel", draw.BLACK, draw.CYAN)
  2748.                         color = draw.BLACK
  2749.                         for x=1,8 do
  2750.                             if file_exists("fcs"..dirsep.."world-" .. x .. ".fcs") then
  2751.                                 color = draw.BLACK
  2752.                             else
  2753.                                 color = draw.RED
  2754.                             end
  2755.                             if x < 5 then
  2756.                                 z = x
  2757.                                 c = 0
  2758.                             else
  2759.                                 z = x -4
  2760.                                 c = 80
  2761.                             end
  2762.                             if x == selection then
  2763.                                 gui.text(12+c, 146+16*z+i, "> World: ".. x, color, draw.CYAN)
  2764.                             else
  2765.                                 gui.text(12+c, 146+16*z+i, "World: ".. x, color, draw.CYAN)
  2766.                             end
  2767.                         end
  2768.                         coroutine.yield()
  2769.                     end
  2770.                     return
  2771.                 end
  2772.             end
  2773.         end
  2774.         coroutine.yield()
  2775.     end
  2776. end
  2777.  
  2778. function SolveMemory()
  2779.     local Tset = memory.readbyte(0x070A)
  2780.     for a=1,60 do
  2781.         coroutine.yield()
  2782.     end
  2783.     while Tset == 17 do
  2784.         local index = memory.readbyte(0x428)
  2785.         local card = {}
  2786.         local selected = 0
  2787.         local selectedindex = 0
  2788.         for x=1,18 do
  2789.             card[x] = memory.readbyte(0x7E81+x)
  2790.         end
  2791.         if card[index+1] < 0x10 then
  2792.             joypad.set(Player,{A=true})
  2793.             for a=1,90 do
  2794.                 coroutine.yield()
  2795.             end
  2796.             selected = card[index+1]
  2797.             selectedindex = index+1
  2798.             local desired = 0
  2799.             for i=1,#card do
  2800.                 if card[i] == selected and i ~= selectedindex then
  2801.                     desired = i-1
  2802.                 end
  2803.             end
  2804.             while index ~= desired do
  2805.                 if desired <6 then
  2806.                     if index >=6 then
  2807.                         joypad.set(Player,{up=true})
  2808.                     elseif desired > index then
  2809.                         joypad.set(Player,{right=true})
  2810.                     else
  2811.                         joypad.set(Player,{left=true})
  2812.                     end
  2813.                 elseif desired >= 12 then
  2814.                     if index <12 then
  2815.                         joypad.set(Player,{down=true})
  2816.                     elseif desired > index then
  2817.                         joypad.set(Player,{right=true})
  2818.                     else
  2819.                         joypad.set(Player,{left=true})
  2820.                     end
  2821.                 else
  2822.                     if index >= 12 then
  2823.                         joypad.set(Player,{up=true})
  2824.                     elseif index < 6 then
  2825.                         joypad.set(Player,{down=true})
  2826.                     elseif desired > index then
  2827.                         joypad.set(Player,{right=true})
  2828.                     else
  2829.                         joypad.set(Player,{left=true})
  2830.                     end
  2831.                 end
  2832.                 for a=1,20 do
  2833.                     coroutine.yield()
  2834.                 end
  2835.                 index = memory.readbyte(0x428)
  2836.             end
  2837.             joypad.set(Player,{A=true})
  2838.             for a=1,90 do
  2839.                 coroutine.yield()
  2840.             end
  2841.         else
  2842.             joypad.set(Player,{right=true})
  2843.             for a=1,20 do
  2844.                 coroutine.yield()
  2845.             end
  2846.         end
  2847.         Tset = memory.readbyte(0x070A)
  2848.     end
  2849. end
  2850.  
  2851. function writehumanlevel()
  2852.     local file = io.open("HumanLevelName.txt","w")
  2853.     local world = memory.readbyte(0x0727)+1
  2854.     file:write(world.."-" .. HumanLevelName)
  2855.     file:close()
  2856. end
  2857.  
  2858. function PlayBoss()
  2859.     boss = true
  2860.     roottimeout = roottimeoutboss
  2861.     local prevhistory
  2862.     if pool then prevhistory = pool.history end
  2863.     initPool(true)
  2864.     local file = io.open("backups"..dirsep.."winners.txt","r")
  2865.     i = 1
  2866.     if file then
  2867.         while true do
  2868.             winnername = file:read("*line")
  2869.             if not winnername then break end
  2870.             dofile(winnername)
  2871.             addToSpecies(loadedgenome)
  2872.             if pool.species[#pool.species].nick == '' then
  2873.                 pool.species[#pool.species].nick = loadedgenome.nick
  2874.             else
  2875.                 pool.species[#pool.species].nick = pool.species[#pool.species].nick .. "/" .. loadedgenome.nick
  2876.             end
  2877.             i=i +1
  2878.         end
  2879.     end
  2880.     for g=i,999 do
  2881.         local genome = newGenome(1)
  2882.         mutate(genome,1)
  2883.         addToSpecies(genome)
  2884.     end
  2885.     getPositions()
  2886.     local score = {memory.readbyte(0x0715),memory.readbyte(0x0716),memory.readbyte(0x0717)}
  2887.     memory.writebyte(0x0715,0) --set score to 0
  2888.     memory.writebyte(0x0716,0)
  2889.     memory.writebyte(0x0717,0)
  2890.     getTileset()
  2891.     indicatorOutput()
  2892.     if prevhistory then pool.history = prevhistory end
  2893.     redospectop = pool.generation % 10 == 0
  2894.     while not playGeneration(redospectop) do
  2895.         fitnessDataOutput()
  2896.         newGeneration()
  2897.         redospectop = pool.generation % 10 == 0
  2898.     end
  2899.     local score2 = {memory.readbyte(0x0715),memory.readbyte(0x0716),memory.readbyte(0x0717)}
  2900.     memory.writebyte(0x0715,score[1]+score2[1]) --restore score
  2901.     memory.writebyte(0x0716,score[2]+score2[2])
  2902.     memory.writebyte(0x0717,score[3]+score2[3])
  2903.     savestateBackup = savestate.object(savestateSlotMap)
  2904.     savestate.save(savestateBackup)
  2905.     savestate.persist(savestateBackup)
  2906.     histogramOutput()
  2907.     saveGenome("bosswinner",true)
  2908. end
  2909.  
  2910.  
  2911. function playLevel()
  2912.     roottimeout = 120
  2913.     boss = false
  2914.     battle = false
  2915.     writehumanlevel()
  2916.     writescene(1)
  2917.     local warppipe = false
  2918.     while memory.readbyte(0x0020) == 1 do
  2919.         coroutine.yield()
  2920.     end
  2921.     if memory.readbyte(0x05FD) == 2 then
  2922.         while memory.readbyte(0x05FD) == 2 do
  2923.             joypad.set(Player,{A = true})
  2924.             coroutine.yield()
  2925.             joypad.set(Player,{A = false})
  2926.             coroutine.yield()
  2927.         end
  2928.         for x=1,480 do
  2929.             coroutine.yield()
  2930.         end
  2931.     end
  2932.     local Tset = memory.readbyte(0x070A)
  2933.     if Tset == 7 then
  2934.         local TASTime = {0,25,50}
  2935.         if memory.readbyte(0x7971) == 1 then
  2936.             TASTime = {25}
  2937.         end
  2938.         for i=1,300 do
  2939.             coroutine.yield()
  2940.         end
  2941.         for i=1,50+TASTime[math.random(#TASTime)] do
  2942.             joypad.set(Player,{right = true})
  2943.             coroutine.yield()
  2944.         end
  2945.         joypad.set(Player,{right = false})
  2946.         joypad.set(Player,{B = true})
  2947.         coroutine.yield()
  2948.         for i=1,300 do
  2949.             joypad.set(Player,{B = false})
  2950.             coroutine.yield()
  2951.         end
  2952.         savestateBackup = savestate.object(savestateSlotMap)
  2953.         savestate.save(savestateBackup)
  2954.         savestate.persist(savestateBackup)
  2955.         return
  2956.     end
  2957.  
  2958.     local prevhistory
  2959.     if pool then prevhistory = pool.history end
  2960.     initPool(false)
  2961.     local file = io.open("backups"..dirsep.."winners.txt","r")
  2962.     local i = 1
  2963.     if file then
  2964.         while true do
  2965.             winnername = file:read("*line")
  2966.             if not winnername then break end
  2967.             dofile(winnername)
  2968.             addToSpecies(loadedgenome)
  2969.             if pool.species[#pool.species].nick == '' then
  2970.                 pool.species[#pool.species].nick = loadedgenome.nick
  2971.             else
  2972.                 pool.species[#pool.species].nick = pool.species[#pool.species].nick .. "/" .. loadedgenome.nick
  2973.             end
  2974.             i=i +1
  2975.         end
  2976.     end
  2977.     numberWinner = i
  2978.     for g=i,999 do
  2979.         local genome = newGenome(1)
  2980.         mutate(genome,1)
  2981.         addToSpecies(genome)
  2982.     end
  2983.     preloaded = false
  2984.     userreplay = false
  2985.     init = loadfile("initialize.lua")
  2986.     initializeclear = io.open("initialize.lua","w")
  2987.     if initializeclear then initializeclear:close() end
  2988.     if init then init() end
  2989.     fileFTracker = io.open("fitnesstracker.txt","w")
  2990.     fileFTracker:write(pool.history)
  2991.     fileFTracker:close()
  2992.     coroutine.yield()
  2993.     getPositions()
  2994.     local startx = marioX
  2995.     local starty = marioY
  2996.     for i,sprite in ipairs(sprites) do
  2997.         if sprite.type == 0x25 then
  2998.             warppipe = true
  2999.             userreplay = true
  3000.             preloaded = true
  3001.         end
  3002.     end
  3003.     --memory.writebyte(0x0715,0) --set score to 0
  3004.     --memory.writebyte(0x0716,0)
  3005.     --memory.writebyte(0x0717,0)
  3006.     getTileset()
  3007.     local toad = false
  3008.     if memory.readbyte(0x7965) == 1 then
  3009.         toad = true
  3010.     end
  3011.     local score = {memory.readbyte(0x0715),memory.readbyte(0x0716),memory.readbyte(0x0717)}
  3012.     if marioMusic == 0x70 then
  3013.         battle = true
  3014.         roottimeout = 900
  3015.         memory.writebyte(0x0715,0) --set score to 0
  3016.         memory.writebyte(0x0716,0)
  3017.         memory.writebyte(0x0717,0)
  3018.     end
  3019.     savestateObj = savestate.object(savestateSlotLevel)
  3020.     if not preloaded then
  3021.         savestate.save(savestateObj)
  3022.         savestate.persist(savestateObj)
  3023.         levelNameOutput()
  3024.         os.execute("mkdir backups"..dirsep..levelname)
  3025.         os.execute("mkdir data"..dirsep..levelname)
  3026.         indicatorOutput()
  3027.     end
  3028.     if prevhistory then pool.history = prevhistory end
  3029.     if warppipe then
  3030.         local cont = {}
  3031.         local avgx = {0}
  3032.         local goal = 0
  3033.         if startx < 60 then
  3034.             goal = 218
  3035.         elseif startx > 180 then
  3036.             goal = 24
  3037.         else
  3038.             goal = 122
  3039.         end
  3040.         while memory.readbyte(0x0014) ~= 1 do
  3041.             getPositions()
  3042.             if marioScreenX > goal+2 then
  3043.                 cont['left'] = true
  3044.                 cont['right'] = false
  3045.             elseif marioScreenX < goal-2 then
  3046.                 cont['left'] = false
  3047.                 cont['right'] = true
  3048.             elseif marioScreenX >= goal-2 and marioScreenX <= goal+2 then
  3049.                 cont['left'] = false
  3050.                 cont['right'] = false
  3051.                 cont['down'] = true
  3052.             end
  3053.            
  3054.             if marioScreenX - avgx[1] < 5 then
  3055.                 cont['A'] = true
  3056.                 cont['up'] = true
  3057.                 cont['down']=false
  3058.                 table.insert(avgx,0)
  3059.             else
  3060.                 cont['A'] = false
  3061.             end
  3062.             table.insert(avgx,marioScreenX)
  3063.             if #avgx > 150 then
  3064.                 table.remove(avgx,1)
  3065.             end
  3066.             joypad.set(Player,cont)
  3067.             coroutine.yield()
  3068.         end
  3069.         for i=1,60 do
  3070.             coroutine.yield()
  3071.         end
  3072.     end
  3073.     if not userreplay then
  3074.         redospectop = pool.generation == 0
  3075.         while not playGeneration(redospectop) do
  3076.             fitnessDataOutput()
  3077.             newGeneration()
  3078.             redospectop = false
  3079.         end
  3080.         if battle then
  3081.             local score2 = {memory.readbyte(0x0715),memory.readbyte(0x0716),memory.readbyte(0x0717)}
  3082.             memory.writebyte(0x0715,score[1]+score2[1]) --restore score
  3083.             memory.writebyte(0x0716,score[2]+score2[2])
  3084.             memory.writebyte(0x0717,score[3]+score2[3])
  3085.         end
  3086.         for i=1,60 do
  3087.             if toad then
  3088.                 memory.writebyte(0x7966,8) --coins required
  3089.             end
  3090.             coroutine.yield()
  3091.         end
  3092.         if boss then
  3093.             for i=1,600 do
  3094.                 joypad.set(Player,{A = true, left = false, right = false, up = false, down = false, B = false})
  3095.                 coroutine.yield()
  3096.                 joypad.set(Player,{A = false, left = false, right = false, up = false, down = false, B = false})
  3097.                 coroutine.yield()
  3098.             end
  3099.         end
  3100.         savestateBackup = savestate.object(savestateSlotMap)
  3101.         savestate.save(savestateBackup)
  3102.         savestate.persist(savestateBackup)
  3103.         histogramOutput()
  3104.         saveGenome("winner",true)
  3105.         mapupdateHist()
  3106.         savePool("backups".. dirsep .. levelname .. dirsep .. "winpool.lua")
  3107.     end
  3108.     if not warppipe then
  3109.         Replay = true
  3110.         for i=1,#pool.breakthroughfiles do
  3111.             print(pool.breakthroughfiles[i])
  3112.             dofile(pool.breakthroughfiles[i])
  3113.             pool.generation = loadedgenome.gen
  3114.             pool.currentSpecies = loadedgenome.s
  3115.             pool.currentGenome = loadedgenome.g
  3116.             playGenome(loadedgenome)
  3117.         end
  3118.         if preloaded then
  3119.             savestateObj = savestate.object(savestateSlotMap)
  3120.         else
  3121.             savestateObj = savestateBackup
  3122.         end
  3123.         local f = io.open("../mapupdate.txt","w")
  3124.         f:write("")
  3125.         f:close()
  3126.         savestate.load(savestateObj)
  3127.         Replay = false
  3128.         os.rename("2", 'fcs'..dirsep..levelname ..'.fcs')
  3129.         if boss then
  3130.             os.rename("3", 'fcs'..dirsep..levelname ..'BOSS.fcs')
  3131.         end
  3132.         local file = io.open("savegamebackup.txt","w")
  3133.         file:write(memory.readbyte(0x0727))
  3134.         file:close()
  3135.     end
  3136.     writescene(3)
  3137.     os.remove('userinput.lua')
  3138. end
  3139. prevLN = ''
  3140. local leveldict = {[3]="1",[4]="2",[5]="3",[6]="4",[7]="5",[8]="6",[9]="7",[10]="8",[11]="9",[12]="10",[13]="1",[14]="2",[15]="3",[16]="4",[17]="5",[18]="6",[19]="7",[20]="8",[21]="9",[95]="Tower",[103]="Fortress",[104]="Quicksand",[105]="Piramid",[201]="Castle",[204]="Bowser's castle",[223]="Tower",[229]="Start",[235]="Fortress"}
  3141. HumanLevelName = ''
  3142. while true do
  3143.     if memory.readbyte(0x00EE) ~= 0 then
  3144.         if Player == 1 then
  3145.             levelname = memory.readbyte(0x0727).."-"..(memory.readbyte(0x7978)*8+memory.readbyte(0x797A)/32).."-"..memory.readbyte(0x7976)/32
  3146.         else
  3147.             levelname = memory.readbyte(0x0727).."-"..(memory.readbyte(0x7979)*8+memory.readbyte(0x797B)/32).."-"..memory.readbyte(0x7977)/32
  3148.         end
  3149.         if levelname ~= prevLN then
  3150.             local LocFile = io.open("OPosition.txt",'w')
  3151.             LocFile:write(levelname)
  3152.             LocFile:close()
  3153.             prevLN = levelname
  3154.         end
  3155.         playLevel()
  3156.     elseif memory.readbyte(0x070A) == 17 then
  3157.         SolveMemory()
  3158.     else
  3159.         if Player == 1 then
  3160.             levelname = memory.readbyte(0x0727).."-"..(memory.readbyte(0x77)*8+memory.readbyte(0x79)/32).."-"..memory.readbyte(0x75)/32
  3161.         else
  3162.             levelname = memory.readbyte(0x0727).."-"..(memory.readbyte(0x78)*8+memory.readbyte(0x7A)/32).."-"..memory.readbyte(0x76)/32
  3163.         end
  3164.         if levelname ~= prevLN then
  3165.             local LocFile = io.open("OPosition.txt",'w')
  3166.             LocFile:write(levelname)
  3167.             LocFile:close()
  3168.             prevLN = levelname
  3169.         end
  3170.         if file_exists("userinput.lua") then
  3171.             userinput=loadfile("userinput.lua")
  3172.             if userinput then
  3173.                 os.remove('userinput.lua')
  3174.                 uinput = {}
  3175.                 pcall(userinput)
  3176.                 if leveldict[memory.readbyte(0xE5)] ~= nil then
  3177.                     HumanLevelName = leveldict[memory.readbyte(0xE5)]
  3178.                 end
  3179.                 if uinput.A and file_exists("fcs" .. dirsep .. levelname .. ".fcs") and memory.readbyte(0x05F2) == 0 then --0x5F2 1 = item folder. 0 = map
  3180.                     local interrupt = io.open("interrupt.lua","w")
  3181.                     interrupt:write("os.rename('fcs/" .. levelname ..".fcs', "..savestateSlotLevel..")\nsavestateObj = savestate.object(" .. savestateSlotLevel .. ")\nprint('one point five')\nsavestate.load(savestateObj)\nprint('two')")
  3182.                    
  3183.                    
  3184.                     if file_exists('fcs' .. dirsep .. levelname .. 'BOSS.fcs') then
  3185.                         interrupt:write("os.rename('fcs/" .. levelname .."BOSS.fcs', "..savestateSlotBOSS..")\nbosssavestateObj = savestate.object(" .. savestateSlotBOSS ..")")
  3186.                     end
  3187.                     interrupt:close()
  3188.                     local initialize = io.open("initialize.lua","w")
  3189.                     initialize:write("loadPool('backups/".. levelname .. "/winpool.lua')\n")
  3190.                     initialize:write("preloaded = true\nuserreplay = true\nwritescene(2)")
  3191.                     initialize:close()
  3192.                 elseif uinput.A and Startlocations[levelname] then
  3193.                     worldselection()
  3194.                 else
  3195.                     joypad.set(Player,uinput)
  3196.                 end
  3197.             end
  3198.         end
  3199.     end
  3200.     coroutine.yield()
  3201. end
  3202.  
  3203. return true
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement