Advertisement
Code-Akisame

LuigI/O version 1

May 18th, 2018
952
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 48.05 KB | None | 0 0
  1. -- MarI/O by SethBling rewritten for use with FCEUX by Akisame (used 3DI70R's version as a reference to save time but I have been told by him that he used juvester's script as a reference https://github.com/juvester/mari-o-fceux.)
  2. -- There is a version that is closer to the original. This is not that version. In this version I have changed various things to speed up the learning process and give mario more tool with which he can complete levels
  3. -- Pressing N will show the Neural net, pressing M will show the mutation rates, pressing L will load the last saved generation and pressing B will display the genome with the top fitness and T will activate the turbo.
  4. -- This file includes all the fixes from my bizhawk version (https://pastebin.com/dKYFKg2E) and an extra fix to hopefully prevent the formation of a monospecies when we encounter a major bottleneck
  5. -- If you want to start your own stream with this script I would appreciate it if you would include this file as well as SethBling's original version in the description
  6. -- Keep in mind that this code is provided for Non-Commercial usage and it is not permitted to request financial compensation (either by means of ads, donations, subscriptions or other means) for selling, streaming or otherwise previewing this code or its execution.
  7. -- This is still just an early version and it may be subjected to further changes in the future.
  8. -- To use this script just download FCEUX and create a savestate in state 1 for smb. click file -> lua -> 'new lua script window' and load the script.
  9. -- Have fun!
  10.  
  11. --FCEUX doesn't allow for user string input but if you press 'L' on your keyboard it should load the latest generation that was saved
  12. --for loading specific generations replace nil with the filename as shown in the example below
  13. savedpool = nil
  14. --savedpool = 'backups/backup.429.SMB1-1.state.pool'
  15. --savedpool = "backups/backup.5.SMB1-1.state.pool"
  16.  
  17. -- HUD options. These can be changed on the fly by pressing 'M' for the mutation rates and 'N' for the network
  18. mutation_rates_disp = false
  19. neural_net_disp = true
  20.  
  21. --set to false if you want warning popups before loading anything that might cause you to lose your current progress
  22. nopopup = true
  23.  
  24. savestate_slot = 1 -- Load the level from FCEUX savestate slot. Set to 3 if you are loading a level that mario has reached by completing another level
  25.  
  26. SAVE_LOAD_FILE = "SMB1-1.state.pool" --filename to be used
  27.  
  28. player = 2 --1 for mario 2 for luigi
  29.  
  30. AllowAutoTurbo = true --Automatic turbo
  31. AllowSlowdownNearMax = true --This is slow down the turbo when it nears to furthest it has currently gotten
  32. FirstGenSpeedup = true --This will speed up the first generation
  33. BottleneckTurbo = true --Activate turbo on bottleneck detection
  34. TurboGenBottleneck = 20 --will start the turbo when it is in a bottleneck for 25 generations. Please adjust to match you taste
  35.  
  36. os.execute("mkdir backups") --create the folder to save the pools in
  37.  
  38. SavestateObj = savestate.object(savestate_slot)
  39.  
  40. ButtonNames = {
  41. "A",
  42. "B",
  43. "up",
  44. "down",
  45. "left",
  46. "right",
  47. }
  48.  
  49.  
  50. BoxRadius = 6
  51. InputSize = (BoxRadius*2+1)*(BoxRadius*2+1)
  52.  
  53. switchtime = {10,12,18}
  54. switchtimer = {}
  55. initialswitchvalue = {}
  56. for i=1,#switchtime do
  57. switchtimer[i] = switchtime[i]
  58. initialswitchvalue[i] = 1
  59. end
  60.  
  61. Inputs = InputSize+1+#initialswitchvalue
  62. Outputs = #ButtonNames
  63.  
  64. Population = 1000
  65. DeltaDisjoint = 2.0
  66. DeltaWeights = 0.4
  67. DeltaThreshold = 1.0
  68.  
  69. keyflag = false --used for keyboard input
  70. endlevel = false
  71. preparenext = false
  72. secondbest = 0
  73. maxright = 0
  74. restoreturbo = false
  75.  
  76. StaleSpecies = 25 --increased slightly to allow for longer evolution
  77. mariohole = false --flag for mario falling into a hole
  78. mariopipe = false --flag for mario exiting a pipe
  79. marioPipeEnter = false --new flag for entering a pipe
  80. offset = 0 --offset value for when mario exits a pipe
  81. timebonus = 0 --timebonus to prevent the hard drops in fitness when exiting a pipe
  82. maxcounter = 0 --species that reach the max fitness
  83. currentTracker = 0 --tracks location on screen
  84. loop = false --flag for when a loop is detected
  85. previousmaxfitness = 0
  86. bottleneckcounter = 0
  87.  
  88. MutateConnectionsChance = 0.25
  89. PerturbChance = 0.90
  90. CrossoverChance = 0.75
  91. LinkMutationChance = 2.0
  92. NodeMutationChance = 0.50
  93. BiasMutationChance = 0.40
  94. StepSize = 0.1
  95. DisableMutationChance = 0.4
  96. EnableMutationChance = 0.2
  97. oscilationmutationchance = 0.2
  98. interbreedchance = 0.03
  99.  
  100. TimeoutConstant = 120 --set to a higher value but see for yourself what is acceptable
  101.  
  102. MaxNodes = 1000000
  103.  
  104. function getPositions()
  105. marioX = memory.readbyte(0x6D) * 0x100 + memory.readbyte(0x86)
  106. marioY = memory.readbyte(0x03B8)+16
  107. mariostate = memory.readbyte(0x0E) --get mario's state. (entering exiting pipes, dying, picking up a mushroom etc etc)
  108.  
  109. currentscreen = memory.readbyte(0x071A) --finds current screen for loop detection
  110. nextscreen = memory.readbyte(0x071B) --finds next screen
  111.  
  112. CurrentWorld = memory.readbyte(0x075F) --finds the current world (value + 1 is world)
  113. CurrentLevel = memory.readbyte(0x0760) --finds the current level (0=1 2=2 3=3 4=4)
  114. demoruncheck = memory.readbyte(0x0770) --equals 0 for demo and 1 for normal run
  115.  
  116. screenX = memory.readbyte(0x03AD)
  117. screenY = memory.readbyte(0x03B8)
  118. end
  119.  
  120. function getTile(dx, dy)
  121. local x = marioX + dx + 8
  122. local y = marioY + dy - 16
  123. local page = math.floor(x/256)%2
  124.  
  125. local subx = math.floor((x%256)/16)
  126. local suby = math.floor((y - 32)/16)
  127. local addr = 0x500 + page*13*16+suby*16+subx
  128.  
  129. if suby >= 13 or suby < 0 then
  130. return 0
  131. end
  132.  
  133. if memory.readbyte(addr) ~= 0 then
  134. return 1
  135. else
  136. return 0
  137. end
  138. end
  139.  
  140. function getSprites()
  141. local sprites = {}
  142. for slot=0,4 do
  143. local enemy = memory.readbyte(0xF+slot)
  144. if enemy ~= 0 then
  145. local ex = memory.readbyte(0x6E + slot)*0x100 + memory.readbyte(0x87+slot)
  146. local ey = memory.readbyte(0xCF + slot)+36 --changed this to stop sprites from floating in mid air on the neural network
  147. sprites[#sprites+1] = {["x"]=ex,["y"]=ey}
  148. end
  149. end
  150.  
  151. return sprites
  152. end
  153.  
  154. function getInputs()
  155. getPositions()
  156.  
  157. sprites = getSprites()
  158.  
  159. local inputs = {}
  160.  
  161. for dy=-BoxRadius*16,BoxRadius*16,16 do
  162. for dx=-BoxRadius*16,BoxRadius*16,16 do
  163. inputs[#inputs+1] = 0
  164.  
  165. tile = getTile(dx, dy)
  166. if tile == 1 and marioY+dy < 0x1B0 then
  167. inputs[#inputs] = 1
  168. end
  169.  
  170. for i = 1,#sprites do
  171. distx = math.abs(sprites[i]["x"] - (marioX+dx))
  172. disty = math.abs(sprites[i]["y"] - (marioY+dy+16))
  173. if distx <= 8 and disty <= 8 then
  174. inputs[#inputs] = -1
  175. end
  176. end
  177. end
  178. end
  179.  
  180. --mariovx = memory.read_s8(0x7B)
  181. --mariovy = memory.read_s8(0x7D)
  182.  
  183. return inputs
  184. end
  185.  
  186. function sigmoid(x)
  187. return 2/(1+math.exp(-4.9*x))-1
  188. end
  189.  
  190. function newInnovation()
  191. pool.innovation = pool.innovation + 1
  192. return pool.innovation
  193. end
  194.  
  195. function newPool()
  196. local pool = {}
  197. pool.species = {}
  198. pool.generation = 0
  199. pool.innovation = Outputs
  200. pool.currentSpecies = 1
  201. pool.currentGenome = 1
  202. pool.currentFrame = 0
  203. pool.maxFitness = 0
  204. pool.maxcounter = 0 --counter for %max species reached
  205.  
  206. return pool
  207. end
  208.  
  209. function newSpecies()
  210. local species = {}
  211. species.topFitness = 0
  212. species.staleness = 0
  213. species.genomes = {}
  214. species.averageFitness = 0
  215.  
  216. return species
  217. end
  218.  
  219. function getCurrentGenome()
  220. local species = pool.species[pool.currentSpecies]
  221. return species.genomes[pool.currentGenome]
  222. end
  223.  
  224. function toRGBA(ARGB)
  225. return bit.lshift(ARGB, 8) + bit.rshift(ARGB, 24)
  226. end
  227.  
  228.  
  229. function newGenome()
  230. local genome = {}
  231. genome.genes = {}
  232. genome.fitness = 0
  233. genome.adjustedFitness = 0
  234. genome.network = {}
  235. genome.maxneuron = 0
  236. genome.globalRank = 0
  237. genome.oscilations = {}
  238. for i=1,#switchtime do
  239. genome.oscilations[i] = switchtime[i]
  240. end
  241. genome.mutationRates = {}
  242. genome.mutationRates["connections"] = MutateConnectionsChance
  243. genome.mutationRates["link"] = LinkMutationChance
  244. genome.mutationRates["bias"] = BiasMutationChance
  245. genome.mutationRates["node"] = NodeMutationChance
  246. genome.mutationRates["enable"] = EnableMutationChance
  247. genome.mutationRates["disable"] = DisableMutationChance
  248. genome.mutationRates["step"] = StepSize
  249. genome.mutationRates["oscilation"] = oscilationmutationchance
  250.  
  251. return genome
  252. end
  253.  
  254. function copyGenome(genome)
  255. local genome2 = newGenome()
  256. for g=1,#genome.genes do
  257. table.insert(genome2.genes, copyGene(genome.genes[g]))
  258. end
  259. for i=1,#genome.oscilations do
  260. genome2.oscilations[i] = genome.oscilations[i]
  261. end
  262. genome2.maxneuron = genome.maxneuron
  263. genome2.mutationRates["connections"] = genome.mutationRates["connections"]
  264. genome2.mutationRates["link"] = genome.mutationRates["link"]
  265. genome2.mutationRates["bias"] = genome.mutationRates["bias"]
  266. genome2.mutationRates["node"] = genome.mutationRates["node"]
  267. genome2.mutationRates["enable"] = genome.mutationRates["enable"]
  268. genome2.mutationRates["disable"] = genome.mutationRates["disable"]
  269. genome2.mutationRates["oscilation"] = genome.mutationRates["oscilation"]
  270.  
  271. return genome2
  272. end
  273.  
  274. function basicGenome()
  275. local genome = newGenome()
  276. local innovation = 1
  277.  
  278. genome.maxneuron = Inputs
  279. mutate(genome)
  280.  
  281. return genome
  282. end
  283.  
  284. function newGene()
  285. local gene = {}
  286. gene.into = 0
  287. gene.out = 0
  288. gene.weight = 0.0
  289. gene.enabled = true
  290. gene.innovation = 0
  291.  
  292. return gene
  293. end
  294.  
  295. function copyGene(gene)
  296. local gene2 = newGene()
  297. gene2.into = gene.into
  298. gene2.out = gene.out
  299. gene2.weight = gene.weight
  300. gene2.enabled = gene.enabled
  301. gene2.innovation = gene.innovation
  302.  
  303. return gene2
  304. end
  305.  
  306. function newNeuron()
  307. local neuron = {}
  308. neuron.incoming = {}
  309. neuron.value = 0.0
  310. neuron.switcher = false
  311. neuron.timer = 60
  312. neuron.multiplier = 1
  313.  
  314. return neuron
  315. end
  316.  
  317. function generateNetwork(genome)
  318. local network = {}
  319. network.neurons = {}
  320.  
  321. for i=1,Inputs do
  322. network.neurons[i] = newNeuron()
  323. end
  324.  
  325. for o=1,Outputs do
  326. network.neurons[MaxNodes+o] = newNeuron()
  327. end
  328.  
  329. table.sort(genome.genes, function (a,b)
  330. return (a.out < b.out)
  331. end)
  332. for i=1,#genome.genes do
  333. local gene = genome.genes[i]
  334. if gene.enabled then
  335. if network.neurons[gene.out] == nil then
  336. network.neurons[gene.out] = newNeuron()
  337. end
  338. local neuron = network.neurons[gene.out]
  339. table.insert(neuron.incoming, gene)
  340. if network.neurons[gene.into] == nil then
  341. network.neurons[gene.into] = newNeuron()
  342. end
  343. end
  344. end
  345.  
  346. genome.network = network
  347. end
  348.  
  349. function evaluateNetwork(network, inputs, genome)
  350. table.insert(inputs, 1) -- bias input node
  351. for i = 1,#initialswitchvalue do
  352. if genome.oscilations[i] > 0 then
  353. if switchtimer[i] > 0 then
  354. switchtimer[i] = switchtimer[i] -1
  355. else
  356. switchtimer[i] = genome.oscilations[i]
  357. initialswitchvalue[i] = initialswitchvalue[i]*-1
  358. end
  359. end
  360. table.insert(inputs, initialswitchvalue[i])
  361.  
  362. end
  363.  
  364. if #inputs ~= Inputs then
  365. emu.print("Incorrect number of neural network inputs.")
  366. return {}
  367. end
  368.  
  369. for i=1,Inputs do
  370. network.neurons[i].value = inputs[i]
  371. end
  372.  
  373. for _,neuron in pairs(network.neurons) do
  374. local sum = 0
  375. for j = 1,#neuron.incoming do
  376. local incoming = neuron.incoming[j]
  377. local other = network.neurons[incoming.into]
  378. sum = sum + incoming.weight * other.value
  379. end
  380.  
  381. if #neuron.incoming > 0 then
  382. if neuron.switcher and sigmoid(sum)>0 then
  383. neuron.timer = neuron.timer - 1
  384. elseif neuron.switcher then
  385. neuron.timer = 120
  386. neuron.multiplier = 1
  387. end
  388.  
  389. if neuron.switcher and neuron.timer < 0 then
  390. neuron.multiplier = -neuron.multiplier
  391. neuron.timer = 10
  392. end
  393. neuron.value = sigmoid(sum) * neuron.multiplier
  394.  
  395. end
  396. end
  397.  
  398. local outputs = {}
  399. for o=1,Outputs do
  400. local button = ButtonNames[o]
  401. if network.neurons[MaxNodes+o].value > 0 then
  402. outputs[button] = true
  403. else
  404. outputs[button] = false
  405. end
  406. end
  407.  
  408. return outputs
  409. end
  410.  
  411. function crossover(g1, g2)
  412. -- Make sure g1 is the higher fitness genome
  413. if g2.fitness > g1.fitness then
  414. tempg = g1
  415. g1 = g2
  416. g2 = tempg
  417. end
  418.  
  419. local child = newGenome()
  420. for i=1,#g1.oscilations do
  421. child.oscilations[i] = g1.oscilations[i]
  422. end
  423. local innovations2 = {}
  424. for i=1,#g2.genes do
  425. local gene = g2.genes[i]
  426. innovations2[gene.innovation] = gene
  427. end
  428.  
  429. for i=1,#g1.genes do
  430. local gene1 = g1.genes[i]
  431. local gene2 = innovations2[gene1.innovation]
  432. if gene2 ~= nil and math.random(2) == 1 and gene2.enabled then
  433. table.insert(child.genes, copyGene(gene2))
  434. else
  435. table.insert(child.genes, copyGene(gene1))
  436. end
  437. end
  438.  
  439. child.maxneuron = math.max(g1.maxneuron,g2.maxneuron)
  440.  
  441. for mutation,rate in pairs(g1.mutationRates) do
  442. child.mutationRates[mutation] = rate
  443. end
  444.  
  445. return child
  446. end
  447.  
  448. function randomNeuron(genes, nonInput)
  449. local neurons = {}
  450. if not nonInput then
  451. for i=1,Inputs do
  452. neurons[i] = true
  453. end
  454. end
  455. for o=1,Outputs do
  456. neurons[MaxNodes+o] = true
  457. end
  458. for i=1,#genes do
  459. if (not nonInput) or genes[i].into > Inputs then
  460. neurons[genes[i].into] = true
  461. end
  462. if (not nonInput) or genes[i].out > Inputs then
  463. neurons[genes[i].out] = true
  464. end
  465. end
  466.  
  467. local count = 0
  468. for _,_ in pairs(neurons) do
  469. count = count + 1
  470. end
  471. local n = math.random(1, count)
  472.  
  473. for k,v in pairs(neurons) do
  474. n = n-1
  475. if n == 0 then
  476. return k
  477. end
  478. end
  479.  
  480. return 0
  481. end
  482.  
  483. function containsLink(genes, link)
  484. for i=1,#genes do
  485. local gene = genes[i]
  486. if gene.into == link.into and gene.out == link.out then
  487. return true
  488. end
  489. end
  490. end
  491.  
  492. function pointMutate(genome)
  493. local step = genome.mutationRates["step"]
  494.  
  495. for i=1,#genome.genes do
  496. local gene = genome.genes[i]
  497. if math.random() < PerturbChance then
  498. gene.weight = gene.weight + math.random() * step*2 - step
  499. else
  500. gene.weight = math.random()*4-2
  501. end
  502. end
  503. end
  504.  
  505. function linkMutate(genome, forceBias)
  506. local neuron1 = randomNeuron(genome.genes, false)
  507. local neuron2 = randomNeuron(genome.genes, true)
  508.  
  509. local newLink = newGene()
  510. if neuron1 <= Inputs and neuron2 <= Inputs then
  511. --Both input nodes
  512. return
  513. end
  514. if neuron2 <= Inputs then
  515. -- Swap output and input
  516. local temp = neuron1
  517. neuron1 = neuron2
  518. neuron2 = temp
  519. end
  520.  
  521. newLink.into = neuron1
  522. newLink.out = neuron2
  523. if forceBias then
  524. newLink.into = Inputs
  525. end
  526.  
  527. if containsLink(genome.genes, newLink) then
  528. return
  529. end
  530. newLink.innovation = newInnovation()
  531. newLink.weight = math.random()*4-2
  532.  
  533. table.insert(genome.genes, newLink)
  534. end
  535.  
  536. function nodeMutate(genome)
  537. if #genome.genes == 0 then
  538. return
  539. end
  540.  
  541. genome.maxneuron = genome.maxneuron + 1
  542.  
  543. local gene = genome.genes[math.random(1,#genome.genes)]
  544. if not gene.enabled then
  545. return
  546. end
  547. gene.enabled = false
  548.  
  549. local gene1 = copyGene(gene)
  550. gene1.out = genome.maxneuron
  551. gene1.weight = 1.0
  552. gene1.innovation = newInnovation()
  553. gene1.enabled = true
  554. table.insert(genome.genes, gene1)
  555.  
  556. local gene2 = copyGene(gene)
  557. gene2.into = genome.maxneuron
  558. gene2.innovation = newInnovation()
  559. gene2.enabled = true
  560. table.insert(genome.genes, gene2)
  561. end
  562.  
  563. function enableDisableMutate(genome, enable)
  564. local candidates = {}
  565. for _,gene in pairs(genome.genes) do
  566. if gene.enabled == not enable then
  567. table.insert(candidates, gene)
  568. end
  569. end
  570.  
  571. if #candidates == 0 then
  572. return
  573. end
  574.  
  575. local gene = candidates[math.random(1,#candidates)]
  576. gene.enabled = not gene.enabled
  577. end
  578.  
  579. function oscilationMutate(genome)
  580. mutationpoint = math.random(1,#genome.oscilations)
  581. if genome.oscilations[mutationpoint]>0 then
  582. if math.random(1,2) == 1 then
  583. genome.oscilations[mutationpoint] = genome.oscilations[mutationpoint] + 1
  584. else
  585. genome.oscilations[mutationpoint] = genome.oscilations[mutationpoint] - 1
  586. end
  587. end
  588. if genome.oscilations[mutationpoint]<0 then
  589. genome.oscilations[mutationpoint] = 0
  590. end
  591. end
  592.  
  593. function mutate(genome)
  594. for mutation,rate in pairs(genome.mutationRates) do
  595. if math.random(1,2) == 1 then
  596. genome.mutationRates[mutation] = 0.95*rate
  597. else
  598. genome.mutationRates[mutation] = 1.05263*rate
  599. end
  600. end
  601.  
  602. if math.random() < genome.mutationRates["connections"] then
  603. pointMutate(genome)
  604. end
  605.  
  606. local p = genome.mutationRates["link"]
  607. while p > 0 do
  608. if math.random() < p then
  609. linkMutate(genome, false)
  610. end
  611. p = p - 1
  612. end
  613.  
  614. p = genome.mutationRates["bias"]
  615. while p > 0 do
  616. if math.random() < p then
  617. linkMutate(genome, true)
  618. end
  619. p = p - 1
  620. end
  621.  
  622. p = genome.mutationRates["node"]
  623. while p > 0 do
  624. if math.random() < p then
  625. nodeMutate(genome)
  626. end
  627. p = p - 1
  628. end
  629.  
  630. p = genome.mutationRates["enable"]
  631. while p > 0 do
  632. if math.random() < p then
  633. enableDisableMutate(genome, true)
  634. end
  635. p = p - 1
  636. end
  637.  
  638. p = genome.mutationRates["disable"]
  639. while p > 0 do
  640. if math.random() < p then
  641. enableDisableMutate(genome, false)
  642. end
  643. p = p - 1
  644. end
  645.  
  646. p = genome.mutationRates["oscilation"]
  647. while p > 0 do
  648. if math.random() < p then
  649. oscilationMutate(genome)
  650. end
  651. p = p - 1
  652. end
  653. end
  654.  
  655. function disjoint(genes1, genes2)
  656. local i1 = {}
  657. for i = 1,#genes1 do
  658. local gene = genes1[i]
  659. i1[gene.innovation] = true
  660. end
  661.  
  662. local i2 = {}
  663. for i = 1,#genes2 do
  664. local gene = genes2[i]
  665. i2[gene.innovation] = true
  666. end
  667.  
  668. local disjointGenes = 0
  669. for i = 1,#genes1 do
  670. local gene = genes1[i]
  671. if not i2[gene.innovation] then
  672. disjointGenes = disjointGenes+1
  673. end
  674. end
  675.  
  676. for i = 1,#genes2 do
  677. local gene = genes2[i]
  678. if not i1[gene.innovation] then
  679. disjointGenes = disjointGenes+1
  680. end
  681. end
  682.  
  683. local n = math.max(#genes1, #genes2)
  684.  
  685. return disjointGenes / n
  686. end
  687.  
  688. function weights(genes1, genes2)
  689. local i2 = {}
  690. for i = 1,#genes2 do
  691. local gene = genes2[i]
  692. i2[gene.innovation] = gene
  693. end
  694.  
  695. local sum = 0
  696. local coincident = 0
  697. for i = 1,#genes1 do
  698. local gene = genes1[i]
  699. if i2[gene.innovation] ~= nil then
  700. local gene2 = i2[gene.innovation]
  701. sum = sum + math.abs(gene.weight - gene2.weight)
  702. coincident = coincident + 1
  703. end
  704. end
  705.  
  706. return sum / coincident
  707. end
  708.  
  709. function sameSpecies(genome1, genome2)
  710. local dd = DeltaDisjoint*disjoint(genome1.genes, genome2.genes)
  711. local dw = DeltaWeights*weights(genome1.genes, genome2.genes)
  712. return dd + dw < DeltaThreshold
  713. end
  714.  
  715. function rankGlobally()
  716. local global = {}
  717. for s = 1,#pool.species do
  718. local species = pool.species[s]
  719. for g = 1,#species.genomes do
  720. table.insert(global, species.genomes[g])
  721. end
  722. end
  723. table.sort(global, function (a,b)
  724. return (a.fitness < b.fitness)
  725. end)
  726.  
  727. for g=1,#global do
  728. global[g].globalRank = g
  729. end
  730. end
  731.  
  732. function calculateAverageFitness(species)
  733. local total = 0
  734.  
  735. for g=1,#species.genomes do
  736. local genome = species.genomes[g]
  737. total = total + genome.globalRank
  738. end
  739.  
  740. species.averageFitness = total / #species.genomes
  741. end
  742.  
  743. function totalAverageFitness()
  744. local total = 0
  745. for s = 1,#pool.species do
  746. local species = pool.species[s]
  747. total = total + species.averageFitness
  748. end
  749.  
  750. return total
  751. end
  752.  
  753. function cullSpecies(cutToOne)
  754. for s = 1,#pool.species do
  755. local species = pool.species[s]
  756.  
  757. table.sort(species.genomes, function (a,b)
  758. return (a.fitness > b.fitness)
  759. end)
  760.  
  761. local remaining = math.ceil(#species.genomes/2)
  762. if cutToOne then
  763. remaining = 1
  764. end
  765. while #species.genomes > remaining do
  766. table.remove(species.genomes)
  767. end
  768. end
  769. end
  770.  
  771. function breedChild(species)
  772. local child = {}
  773. if math.random() < CrossoverChance then
  774. g1 = species.genomes[math.random(1, #species.genomes)]
  775. g2 = species.genomes[math.random(1, #species.genomes)]
  776. child = crossover(g1, g2)
  777. else
  778. g = species.genomes[math.random(1, #species.genomes)]
  779. child = copyGenome(g)
  780. end
  781.  
  782. mutate(child)
  783.  
  784. return child
  785. end
  786.  
  787. function secondbestspecies() --added this to help prevent the formation of monospecies when MarI/O encounters a major bottleneck
  788. local bestfitness = 0
  789. local secondbestfitness = 0
  790. for s = 1,#pool.species do
  791. local species = pool.species[s]
  792.  
  793. if species.topFitness > bestfitness then
  794. secondbestfitness = bestfitness
  795. bestfitness = species.topFitness
  796. elseif species.topFitness > secondbestfitness then
  797. secondbestfitness = species.topFitness
  798. end
  799. end
  800. return secondbestfitness
  801. end
  802.  
  803.  
  804. function removeStaleSpecies()
  805. local survived = {}
  806. secondbest = secondbestspecies()
  807. emu.print("Max Fitness: ".. pool.maxFitness ..". second best: ".. secondbest)
  808. for s = 1,#pool.species do
  809. local species = pool.species[s]
  810.  
  811. table.sort(species.genomes, function (a,b)
  812. return (a.fitness > b.fitness)
  813. end)
  814.  
  815. if species.genomes[1].fitness > species.topFitness then
  816. species.topFitness = species.genomes[1].fitness
  817. species.staleness = 0
  818. else
  819. species.staleness = species.staleness + 1
  820. end
  821. if species.staleness < StaleSpecies or species.topFitness >= secondbest then --originally species.topFitness >= pool.maxFitness
  822. table.insert(survived, species)
  823. end
  824. end
  825.  
  826. pool.species = survived
  827. end
  828.  
  829. function removeWeakSpecies()
  830. local survived = {}
  831.  
  832. local sum = totalAverageFitness()
  833. for s = 1,#pool.species do
  834. local species = pool.species[s]
  835. breed = math.floor(species.averageFitness / sum * Population)
  836. if breed >= 1 then
  837. table.insert(survived, species)
  838. end
  839. end
  840.  
  841. pool.species = survived
  842. end
  843.  
  844.  
  845. function addToSpecies(child)
  846. local foundSpecies = false
  847. for s=1,#pool.species do
  848. local species = pool.species[s]
  849. if not foundSpecies and sameSpecies(child, species.genomes[1]) then
  850. table.insert(species.genomes, child)
  851. foundSpecies = true
  852. end
  853. end
  854.  
  855. if not foundSpecies then
  856. local childSpecies = newSpecies()
  857. table.insert(childSpecies.genomes, child)
  858. table.insert(pool.species, childSpecies)
  859. end
  860. end
  861.  
  862. function newGeneration()
  863. if pool.maxFitness > previousmaxfitness then
  864. previousmaxfitness = pool.maxFitness
  865. bottleneckcounter = 0
  866. if AllowAutoTurbo then
  867. emu.speedmode("normal")
  868. turbo = false
  869. end
  870. else
  871. bottleneckcounter = bottleneckcounter + 1
  872. end
  873. if bottleneckcounter < 7 and Population > 300 then
  874. if pool.generation < 5 then
  875. Population = math.floor(Population * 0.9)
  876. else
  877. Population = math.floor(Population * 0.95)
  878. end
  879. if Population < 300 then
  880. Population = 300
  881. end
  882. elseif bottleneckcounter > 10 and Population <900 then
  883. Population = math.floor(Population * 1.10)
  884. if Population > 900 then
  885. Population = 900
  886. end
  887. end
  888. if bottleneckcounter > 20 then
  889. bottleneckcounter = 0
  890. end
  891. if bottleneckcounter > TurboGenBottleneck and AllowAutoTurbo and BottleneckTurbo then
  892. emu.speedmode("turbo")
  893. turbo = true
  894. end
  895. cullSpecies(false) -- Cull the bottom half of each species
  896. rankGlobally()
  897. removeStaleSpecies()
  898. rankGlobally()
  899. for s = 1,#pool.species do
  900. local species = pool.species[s]
  901. calculateAverageFitness(species)
  902. end
  903. removeWeakSpecies()
  904. local sum = totalAverageFitness()
  905. local children = {}
  906. for s = 1,#pool.species do
  907. local species = pool.species[s]
  908. breed = math.floor(species.averageFitness / sum * Population) - 1
  909. for i=1,breed do
  910. table.insert(children, breedChild(species))
  911. end
  912. end
  913. cullSpecies(true) -- Cull all but the top member of each species
  914. if #pool.species < 30 then
  915. interbreedchance = 0.1
  916. else
  917. interbreedchance = 0.03
  918. end
  919. for i=1,#pool.species do
  920. if math.random() < interbreedchance then
  921. local counterbreed = math.random(1, #pool.species)
  922. if counterbreed ~= i then
  923. table.insert(children, crossover(pool.species[i].genomes[1], pool.species[counterbreed].genomes[1]))
  924. end
  925. end
  926. end
  927. while #children + #pool.species < Population do
  928. local species = pool.species[math.random(1, #pool.species)]
  929. table.insert(children, breedChild(species))
  930. end
  931. for c=1,#children do
  932. local child = children[c]
  933. addToSpecies(child)
  934. end
  935.  
  936. pool.generation = pool.generation + 1
  937. pool.maxcounter = 0 --reset max fitness counter
  938.  
  939. writeFile("backups/backup." .. pool.generation .. "." .. SAVE_LOAD_FILE)
  940. writelatestgen() --tracker for the latest generation
  941. end
  942.  
  943. function initializePool()
  944. pool = newPool()
  945.  
  946. for i=1,Population do
  947. basic = basicGenome()
  948. addToSpecies(basic)
  949. end
  950.  
  951. initializeRun()
  952. end
  953.  
  954. function clearJoypad()
  955. controller = {}
  956. for b = 1,#ButtonNames do
  957. controller[ButtonNames[b]] = false
  958. end
  959. joypad.set(player, controller)
  960. end
  961.  
  962. function initializeRun()
  963.  
  964. savestate.load(SavestateObj);
  965. rightmost = 0
  966. pool.currentFrame = 0
  967. timeout = TimeoutConstant
  968. clearJoypad()
  969. currentTracker = memory.readbyte(0x071A) --sets the current tracker to the current screen
  970.  
  971. local species = pool.species[pool.currentSpecies]
  972. local genome = species.genomes[pool.currentGenome]
  973. for i=1,#genome.oscilations do
  974. switchtimer[i] = genome.oscilations[i]
  975. initialswitchvalue[i] = 1
  976. end
  977. generateNetwork(genome)
  978. evaluateCurrent()
  979. end
  980.  
  981. function evaluateCurrent()
  982. local species = pool.species[pool.currentSpecies]
  983. local genome = species.genomes[pool.currentGenome]
  984.  
  985. inputs = getInputs()
  986. controller = evaluateNetwork(genome.network, inputs, genome)
  987.  
  988. if controller["left"] and controller["right"] then
  989. controller["left"] = false
  990. controller["right"] = false
  991. end
  992. if controller["up"] and controller["down"] then
  993. controller["up"] = false
  994. controller["down"] = false
  995. end
  996.  
  997. joypad.set(player, controller)
  998. end
  999.  
  1000. if pool == nil then
  1001. initializePool()
  1002. end
  1003.  
  1004.  
  1005. function nextGenome()
  1006. pool.currentGenome = pool.currentGenome + 1
  1007. if pool.currentGenome > #pool.species[pool.currentSpecies].genomes then
  1008. pool.currentGenome = 1
  1009. pool.currentSpecies = pool.currentSpecies+1
  1010. if pool.currentSpecies > #pool.species then
  1011. newGeneration()
  1012. pool.currentSpecies = 1
  1013. end
  1014. end
  1015. end
  1016.  
  1017. function fitnessAlreadyMeasured()
  1018. local species = pool.species[pool.currentSpecies]
  1019. local genome = species.genomes[pool.currentGenome]
  1020.  
  1021. return genome.fitness ~= 0
  1022. end
  1023.  
  1024. function GenerateNewKeepBest(genome)
  1025. Population = 1000
  1026. previousbest = copyGenome(genome)
  1027. previousmaxfitness = 1
  1028. maxright = 1
  1029. offset = 0 --offset in x coordinates for pipe
  1030. timebonus = 0 --used to freeze fitness
  1031. mariohole = false --reset the fallen into hole variable
  1032. mariopipe = false --is mario exiting a pipe or just teleporting
  1033. loop = false --is this a loop?
  1034. marioPipeEnter = false --has mario entered a pipe
  1035. currentTracker = 1 --tracker for the loop
  1036. pool = newPool()
  1037. addToSpecies(previousbest)
  1038. for i=1,Population-1 do
  1039. basic = basicGenome()
  1040. addToSpecies(basic)
  1041. end
  1042. initializeRun()
  1043. end
  1044.  
  1045. function displayGenome(genome)
  1046. gui.opacity(0.53)
  1047. if not genome then return end
  1048. local network = genome.network
  1049. local cells = {}
  1050. local i = 1
  1051. local cell = {}
  1052. for dy=-BoxRadius,BoxRadius do
  1053. for dx=-BoxRadius,BoxRadius do
  1054. cell = {}
  1055. cell.x = 50+5*dx
  1056. cell.y = 70+5*dy
  1057. cell.value = network.neurons[i].value
  1058. cells[i] = cell
  1059. i = i + 1
  1060. end
  1061. end
  1062. local biasCell = {}
  1063. biasCell.x = 80
  1064. biasCell.y = 110
  1065. biasCell.value = network.neurons[Inputs-#initialswitchvalue].value
  1066. cells[Inputs-#initialswitchvalue] = biasCell
  1067. for i=0,#initialswitchvalue-1 do
  1068. local switchCell = {}
  1069. switchCell.x = 80-10*#initialswitchvalue+10*i
  1070. switchCell.y = 110
  1071. switchCell.value = network.neurons[Inputs-i].value
  1072. cells[Inputs-i] = switchCell
  1073. end
  1074.  
  1075. gui.drawbox(215,32,245,82,toRGBA(0x80808080),toRGBA(0x00000000))
  1076.  
  1077. gui.opacity(0.85)
  1078. for o = 1,Outputs do
  1079. cell = {}
  1080. cell.x = 220
  1081. cell.y = 30 + 8 * o
  1082. cell.value = network.neurons[MaxNodes + o].value
  1083. cells[MaxNodes+o] = cell
  1084. local color
  1085. if cell.value > 0 then
  1086. color = 0xFF0000FF
  1087. else
  1088. color = 0xFF777777-- original color = 0xFF000000
  1089. end
  1090. gui.drawtext(225, 26+8*o, ButtonNames[o], toRGBA(color), 0x0) --moved slightly for better alignment
  1091. end
  1092.  
  1093. for n,neuron in pairs(network.neurons) do
  1094. cell = {}
  1095. if n > Inputs and n <= MaxNodes then
  1096. cell.x = 140
  1097. cell.y = 40
  1098. cell.value = neuron.value
  1099. -- if neuron.value >0 then
  1100. -- neuron.switcher = true
  1101. -- end
  1102. cells[n] = cell
  1103. end
  1104. end
  1105.  
  1106. for n=1,4 do
  1107. for _,gene in pairs(genome.genes) do
  1108. if gene.enabled then
  1109. local c1 = cells[gene.into]
  1110. local c2 = cells[gene.out]
  1111. if gene.into > Inputs and gene.into <= MaxNodes then
  1112. c1.x = 0.75*c1.x + 0.25*c2.x
  1113. if c1.x >= c2.x then
  1114. c1.x = c1.x - 40
  1115. end
  1116. if c1.x < 90 then
  1117. c1.x = 90
  1118. end
  1119.  
  1120. if c1.x > 220 then
  1121. c1.x = 220
  1122. end
  1123. c1.y = 0.75*c1.y + 0.25*c2.y
  1124.  
  1125. end
  1126. if gene.out > Inputs and gene.out <= MaxNodes then
  1127. c2.x = 0.25*c1.x + 0.75*c2.x
  1128. if c1.x >= c2.x then
  1129. c2.x = c2.x + 40
  1130. end
  1131. if c2.x < 90 then
  1132. c2.x = 90
  1133. end
  1134. if c2.x > 220 then
  1135. c2.x = 220
  1136. end
  1137. c2.y = 0.25*c1.y + 0.75*c2.y
  1138. end
  1139. end
  1140. end
  1141. end
  1142.  
  1143. gui.drawbox(50-BoxRadius*5-3,70-BoxRadius*5-3,50+BoxRadius*5+2,70+BoxRadius*5+2, toRGBA(0x80808080), toRGBA(0xFF000000))
  1144. for n,cell in pairs(cells) do
  1145. if n > Inputs or cell.value ~= 0 then
  1146. local color = math.floor((cell.value+1)/2*256)
  1147. if color > 255 then color = 255 end
  1148. if color < 0 then color = 0 end
  1149. local opacity = 0xFF000000
  1150. if cell.value == 0 then
  1151. opacity = 0x50000000
  1152. end
  1153. color = opacity + color*0x10000 + color*0x100 + color
  1154. gui.drawbox(cell.x-2,cell.y-2,cell.x+2,cell.y+2,toRGBA(color), toRGBA(opacity))
  1155. end
  1156. end
  1157. for _,gene in pairs(genome.genes) do
  1158. if gene.enabled then
  1159. local c1 = cells[gene.into]
  1160. local c2 = cells[gene.out]
  1161. local opacity = 0xF0000000
  1162. if pool.generation > 50 then
  1163. opacity = 0x80000000
  1164. end
  1165. if c1.value == 0 then
  1166. if pool.generation <10 then --slowly reducing the visibility of non active connections
  1167. opacity = 0x80000000
  1168. elseif pool.generation < 15 then
  1169. opacity = 0x60000000
  1170. elseif pool.generation < 25 then
  1171. opacity = 0x40000000
  1172. end
  1173. end
  1174. local color = 0x80-math.floor(math.abs(sigmoid(gene.weight))*0x80)
  1175. if gene.weight > 0 then
  1176. color = opacity + 0x8000 + 0x10000*color
  1177. else
  1178. color = opacity + 0x800000 + 0x100*color
  1179. end
  1180. if c1.value ~= 0 or pool.generation < 50 then -- remove non active connections from generation 50 onwards
  1181. gui.drawline(c1.x+1, c1.y, c2.x-3, c2.y, toRGBA(color))
  1182. end
  1183. end
  1184. end
  1185.  
  1186. gui.drawbox(49,71,51,78,toRGBA(0x80FF0000),toRGBA(0x00000000))
  1187. if mutation_rates_disp then
  1188. local pos = 120
  1189. for mutation,rate in pairs(genome.mutationRates) do
  1190. gui.drawtext(16, pos, mutation .. ": " .. rate, toRGBA(0xFF000000), 0x0)
  1191. pos = pos + 8
  1192. end
  1193. end
  1194. end
  1195.  
  1196. function writeFile(filename)
  1197. local file = io.open(filename, "w")
  1198. file:write(pool.generation .. "\n")
  1199. file:write(pool.maxFitness .. "\n")
  1200. file:write(Population .. "\n")
  1201. file:write(bottleneckcounter .. "\n")
  1202. file:write(#pool.species .. "\n")
  1203. for n,species in pairs(pool.species) do
  1204. file:write(species.topFitness .. "\n")
  1205. file:write(species.staleness .. "\n")
  1206. file:write(#species.genomes .. "\n")
  1207. for m,genome in pairs(species.genomes) do
  1208. file:write(genome.fitness .. "\n")
  1209. file:write(genome.maxneuron .. "\n")
  1210. for mutation,rate in pairs(genome.mutationRates) do
  1211. file:write(mutation .. "\n")
  1212. file:write(rate .. "\n")
  1213. end
  1214. file:write("done\n")
  1215.  
  1216. file:write(#genome.genes .. "\n")
  1217. for l,gene in pairs(genome.genes) do
  1218. file:write(gene.into .. " ")
  1219. file:write(gene.out .. " ")
  1220. file:write(gene.weight .. " ")
  1221. file:write(gene.innovation .. " ")
  1222. if(gene.enabled) then
  1223. file:write("1\n")
  1224. else
  1225. file:write("0\n")
  1226. end
  1227. end
  1228. file:write(#genome.oscilations .. "\n")
  1229. for i=1,#genome.oscilations do
  1230. file:write(genome.oscilations[i] .. "\n")
  1231. end
  1232. end
  1233. end
  1234. file:close()
  1235. end
  1236.  
  1237. function savePool() --used to save when a new max fitness is reached
  1238. local filename = "backups/backup." .. pool.generation .. "." .. SAVE_LOAD_FILE
  1239. writeFile(filename)
  1240. emu.print("saved pool due to new max fitness") --suggestion by DeltaLeeds
  1241. end
  1242.  
  1243. function loadFile(filename)
  1244. local file = io.open(filename, "r")
  1245. pool = newPool()
  1246. pool.generation = file:read("*number")
  1247. pool.maxFitness = file:read("*number")
  1248. Population = file:read("*number")
  1249. bottleneckcounter = file:read("*number")
  1250. local numSpecies = file:read("*number")
  1251. for s=1,numSpecies do
  1252. local species = newSpecies()
  1253. table.insert(pool.species, species)
  1254. species.topFitness = file:read("*number")
  1255. species.staleness = file:read("*number")
  1256. local numGenomes = file:read("*number")
  1257. for g=1,numGenomes do
  1258. local genome = newGenome()
  1259. table.insert(species.genomes, genome)
  1260. genome.fitness = file:read("*number")
  1261. genome.maxneuron = file:read("*number")
  1262. local line = file:read("*line")
  1263. while line ~= "done" do
  1264. genome.mutationRates[line] = file:read("*number")
  1265. line = file:read("*line")
  1266. end
  1267. local numGenes = file:read("*number")
  1268. for n=1,numGenes do
  1269. local gene = newGene()
  1270. table.insert(genome.genes, gene)
  1271. local enabled
  1272. --I am able to revert back to the original loading script with FCEUX
  1273. gene.into, gene.out, gene.weight, gene.innovation, enabled = file:read("*number", "*number", "*number", "*number", "*number")
  1274.  
  1275. if enabled == 0 then
  1276. gene.enabled = false
  1277. else
  1278. gene.enabled = true
  1279. end
  1280.  
  1281. end
  1282. local numosci = file:read("*number")
  1283. for i=1,numosci do
  1284. genome.oscilations[i] = file:read("*number")
  1285. end
  1286. end
  1287. end
  1288. file:close()
  1289.  
  1290. while fitnessAlreadyMeasured() do
  1291. nextGenome()
  1292. end
  1293. initializeRun()
  1294. pool.currentFrame = pool.currentFrame + 1
  1295. end
  1296.  
  1297.  
  1298. function playTop()
  1299. local maxfitness = 0
  1300. local maxs, maxg
  1301. for s,species in pairs(pool.species) do
  1302. for g,genome in pairs(species.genomes) do
  1303. if genome.fitness > maxfitness then
  1304. maxfitness = genome.fitness
  1305. maxs = s
  1306. maxg = g
  1307. end
  1308. end
  1309. end
  1310.  
  1311. pool.currentSpecies = maxs
  1312. pool.currentGenome = maxg
  1313. pool.maxFitness = maxfitness
  1314. initializeRun()
  1315. pool.currentFrame = pool.currentFrame + 1
  1316. return
  1317. end
  1318.  
  1319. function fitnesstracker()
  1320. if pool.maxFitness > 0 then
  1321. local file = io.open('fitnesstracker.txt', "a")
  1322. file:write("Gen: " .. pool.generation .. " Species: " .. pool.currentSpecies .. " Genome: " .. pool.currentGenome .. " Fitness: ".. pool.maxFitness .. "\n")
  1323. file:close()
  1324. end
  1325. end
  1326.  
  1327. function writelatestgen() --used to write the generation tracker to a file
  1328. local file = io.open('backups/latestgen', "w")
  1329. file:write(SAVE_LOAD_FILE .. "\n")
  1330. file:write(savestate_slot .. "\n")
  1331. file:write(pool.generation)
  1332. file:close()
  1333. end
  1334.  
  1335. function readlatestgen() --used to retrieve the latest generation from a file
  1336. local file = io.open('backups/latestgen', "r")
  1337. SAVE_LOAD_FILE = file:read("*line")
  1338. savestate_slot = file:read("*number")
  1339. SavestateObj = savestate.object(savestate_slot)
  1340. local latestgen = file:read("*number")
  1341. file:close()
  1342. local name = 'backups/backup.'.. latestgen .. '.' .. SAVE_LOAD_FILE
  1343. print('loaded: '.. name)
  1344. return name
  1345. end
  1346.  
  1347. function keyboardinput()
  1348. local keyboard = input.get()
  1349. if keyboard['N'] and keyflag == false then
  1350. if neural_net_disp then
  1351. neural_net_disp = false
  1352. else
  1353. neural_net_disp = true
  1354. end
  1355. keyflag = true
  1356. end
  1357. if keyboard['M'] and keyflag == false then
  1358. if mutation_rates_disp then
  1359. mutation_rates_disp = false
  1360. else
  1361. mutation_rates_disp = true
  1362. end
  1363. keyflag = true
  1364. end
  1365. if keyboard['L'] and keyflag == false then
  1366. if not nopopup then
  1367. local loadyn = input.popup('Are you sure you want to load the last saved generation?')
  1368. end
  1369. if loadyn == 'yes' or nopopup then
  1370. name = readlatestgen()
  1371. loadFile(name)
  1372. end
  1373. keyflag = true
  1374. end
  1375. if keyboard['B'] and keyflag == false then
  1376. if not nopopup then
  1377. local loadyn = input.popup('Are you sure you want to show the topFitness genome?')
  1378. end
  1379. if loadyn == 'yes' or nopopup then
  1380. playTop()
  1381. end
  1382. end
  1383. if not (keyboard['N'] or keyboard['L'] or keyboard['M'] or keyboard['T'] or keyboard['B']) and keyflag == true then
  1384. keyflag = false
  1385. end
  1386. if keyboard['T'] and keyflag == false then
  1387. if turbo then
  1388. emu.speedmode("normal")
  1389. turbo = false
  1390. print('stop turbo')
  1391. elseif not turbo and not restoreturbo then
  1392. emu.speedmode("turbo")
  1393. turbo = true
  1394. elseif restoreturbo then
  1395. restoreturbo = false
  1396. end
  1397. keyflag = true
  1398. end
  1399. end
  1400.  
  1401. if savedpool then
  1402. loadFile(savedpool)
  1403. end
  1404.  
  1405. writeFile("temp.pool")
  1406.  
  1407.  
  1408.  
  1409. while true do
  1410. keyboardinput()
  1411. local backgroundColor = toRGBA(0xD0FFFFFF)
  1412. gui.drawbox(0, 0, 260, 30, backgroundColor, backgroundColor)
  1413.  
  1414. if pool.maxcounter == nil then
  1415. pool.maxcounter = 0
  1416. emu.print("caught missing variable")
  1417. end
  1418.  
  1419. local species = pool.species[pool.currentSpecies]
  1420. local genome = species.genomes[pool.currentGenome]
  1421.  
  1422. if neural_net_disp then
  1423. displayGenome(genome)
  1424. end
  1425. if pool.generation == 0 and pool.currentSpecies ~= 1 and not turbo and AllowAutoTurbo and FirstGenSpeedup then
  1426. emu.speedmode("turbo")
  1427. turbo = true
  1428. -- elseif pool.generation == 2 and turbo and AllowAutoTurbo and FirstGenSpeedup then
  1429. -- emu.speedmode("normal")
  1430. -- turbo = false
  1431. end
  1432. if pool.currentFrame%5 == 0 then
  1433. evaluateCurrent()
  1434. end
  1435.  
  1436. joypad.set(player, controller)
  1437.  
  1438. getPositions()
  1439.  
  1440. if mariostate == 2 or mariostate == 3 then --detects when mario enters a pipe
  1441. marioPipeEnter = true
  1442. end
  1443. if (CurrentWorld == 3 and CurrentLevel == 4) or (CurrentWorld == 6 and CurrentLevel == 4) or (CurrentWorld == 7 and CurrentLevel == 3) then
  1444. if currentTracker == currentscreen or currentTracker == nextscreen and not mariopipe then --follows the progress of mario
  1445. currentTracker = nextscreen
  1446. elseif currentTracker > nextscreen and not mariopipe and not marioPipeEnter and mariostate ~= 7 then --if mario suddenly goes back trigger loop detection
  1447. loop = true
  1448. elseif currentscreen == 0 and nextscreen == 0 and mariopipe and mariostate ~= 7 then -- if mario enters a piperoom negate loop detection
  1449. loop = false
  1450. currentTracker = nextscreen
  1451. elseif nextscreen >= (currentTracker - 1) and mariopipe and mariostate ~= 7 then -- if mario goes forward in the level by entering a pipe negate loop detection
  1452. loop = false
  1453. currentTracker = nextscreen
  1454. pipeexit = true
  1455. else --if nothing is true then we are in a loop
  1456. if not pipeexit then --prevents the extra one frame flicker of the tracker
  1457. loop = true
  1458. else
  1459. pipeexit = false
  1460. end
  1461. end
  1462. end
  1463. if marioPipeEnter == true and mariostate == 7 then --set mariopipe when exiting the pipe
  1464. mariopipe = true
  1465. end
  1466. if mariopipe == true and mariostate ~= 7 and rightmost > marioX and not mariohole then --calculate offset
  1467. offset = rightmost - marioX
  1468. mariopipe = false
  1469. marioPipeEnter = false
  1470. elseif mariopipe == true and mariostate ~= 7 and rightmost <= marioX and not mariohole then --if the exit coordinates are higher than rightmost after exiting a pipe set offset to 0
  1471. offset = 0
  1472. mariopipe = false
  1473. marioPipeEnter = false
  1474. end
  1475.  
  1476. if marioY + memory.readbyte(0xB5)*255 > 512 then --added the fallen into a hole variable that will activate when mario goes below screen
  1477. mariohole = true
  1478. end
  1479. if marioX + offset > rightmost and mariohole == false and loop == false and marioPipeEnter == false and not killtrigger then
  1480. rightmost = marioX + offset
  1481. --if TimeoutConstant - timeout > 20 then --this is required for 4-4 so walking left will not give a big penalty
  1482. timebonus = timebonus + (TimeoutConstant - timeout) * 11/20
  1483. --end
  1484. timeout = TimeoutConstant
  1485. end
  1486. if marioPipeEnter == true then --freeze fitness and timer when mario enters a pipe
  1487. timeout = timeout + 1
  1488. timebonus = timebonus + 2/3
  1489. end
  1490.  
  1491. if mariostate == 4 or endlevel then
  1492. timeout = timeout +1
  1493. timebonus = timebonus + 2/3
  1494. endlevel = true
  1495. end
  1496. if endlevel and mariostate == 8 then
  1497. SAVE_LOAD_FILE = "SMB".. CurrentWorld+1 .."-".. CurrentLevel ..".state.pool"
  1498. savestate_slot = 3
  1499. Savestatebackup = savestate.object(3)
  1500. savestate.save(Savestatebackup)
  1501. savestate.persist(Savestatebackup)
  1502. savestate.save(SavestateObj)
  1503. endlevel = false
  1504. preparenext = true
  1505. end
  1506.  
  1507. if CurrentWorld == 0 and CurrentLevel == 0 and demoruncheck == 0 then
  1508. savestate.load(SavestateObj)
  1509. end
  1510.  
  1511. timeout = timeout - 1
  1512. if preparenext then
  1513. preparenext = false
  1514. GenerateNewKeepBest(genome)
  1515. end
  1516. if rightmost + offset > maxright*0.85 then
  1517. maxright = rightmost + offset
  1518. if turbo and maxright > 200 and AllowSlowdownNearMax then
  1519. emu.speedmode("normal")
  1520. turbo = false
  1521. restoreturbo = true
  1522. end
  1523. elseif restoreturbo then
  1524. emu.speedmode("turbo")
  1525. turbo = true
  1526. restoreturbo = false
  1527. end
  1528. local timeoutBonus = pool.currentFrame / 4
  1529. if (mariostate == 11 or mariohole or math.floor(rightmost - (pool.currentFrame) / 2 + - (timeout + timeoutBonus)*2/3 +20 + timebonus) < -50) and timeout > 40-timeoutBonus then
  1530. local temptimeout = timeout
  1531. timeout = 30 - timeoutBonus
  1532. local difference = temptimeout - timeout
  1533. pool.currentFrame = pool.currentFrame + difference
  1534. killtrigger = true
  1535. end
  1536. local timeoutBonus = pool.currentFrame / 4
  1537. if timeout + timeoutBonus <= 0 then
  1538. local fitness = rightmost - pool.currentFrame / 2 +20 + timebonus
  1539. if fitness == 0 then
  1540. fitness = -1
  1541. end
  1542.  
  1543. genome.fitness = fitness
  1544.  
  1545.  
  1546. if fitness > pool.maxFitness then
  1547. pool.maxFitness = fitness
  1548. fitnesstracker()
  1549. savePool()
  1550. end
  1551.  
  1552. if fitness > pool.maxFitness - 30 then
  1553. pool.maxcounter = pool.maxcounter + 1
  1554. end
  1555.  
  1556. emu.print("Gen " .. pool.generation .. " species " .. pool.currentSpecies .. " genome " .. pool.currentGenome .. " fitness: " .. fitness)
  1557. pool.currentSpecies = 1
  1558. pool.currentGenome = 1
  1559. while fitnessAlreadyMeasured() do
  1560. offset = 0 --offset in x coordinates for pipe
  1561. timebonus = 0 --used to freeze fitness
  1562. mariohole = false --reset the fallen into hole variable
  1563. mariopipe = false --is mario exiting a pipe or just teleporting
  1564. loop = false --is this a loop?
  1565. marioPipeEnter = false --has mario entered a pipe
  1566. currentTracker = 1 --tracker for the loop
  1567. killtrigger = false
  1568. nextGenome()
  1569. end
  1570. initializeRun()
  1571. end
  1572.  
  1573. local measured = 0
  1574. local total = 0
  1575. for _,species in pairs(pool.species) do
  1576. for _,genome in pairs(species.genomes) do
  1577. total = total + 1
  1578. if genome.fitness ~= 0 then
  1579. measured = measured + 1
  1580. end
  1581. end
  1582. end
  1583. gui.opacity(1) --made the top banner opaque. you can change this if you want a slightly transparent banner
  1584. if turbo then
  1585. gui.drawtext(250, 11, "T", toRGBA(0xFFFF0000), 0x0)
  1586. end
  1587. gui.drawtext(5, 11, "Gen " .. pool.generation .. " species " .. pool.currentSpecies .. " genome " .. pool.currentGenome .. " (" .. math.floor(measured/total*100) .. "%) ".. "Pop: ".. Population, toRGBA(0xFF000000), 0x0)
  1588. gui.drawtext(5, 20, "Fitness: " .. math.floor(rightmost - (pool.currentFrame) / 2 + - (timeout + timeoutBonus)*2/3 +20 + timebonus), toRGBA(0xFF000000), 0x0) --the (timeout + timeoutBonus)*2/3 makes sure that the displayed fitness remains stable for the viewers pleasure
  1589. gui.drawtext(80, 20, "Max Fitness:" .. math.floor(pool.maxFitness) .. " (".. math.floor(secondbest) .. ")".. " ".. "(" .. math.ceil(pool.maxcounter/Population*100) .. "%)", toRGBA(0xFF000000), 0x0)
  1590.  
  1591. pool.currentFrame = pool.currentFrame + 1
  1592.  
  1593. emu.frameadvance();
  1594. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement