Advertisement
SethBling

NearestEvolve.lua

Mar 20th, 2015
2,667
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.81 KB | None | 0 0
  1. filename = "YI2.state"
  2. boxRadius = 6
  3. buttonNames = {
  4. "A",
  5. "B",
  6. "X",
  7. "Y",
  8. "Up",
  9. "Down",
  10. "Left",
  11. "Right",
  12. }
  13.  
  14. inputSize = (boxRadius*2+1)*(boxRadius*2+1)
  15. snapshotSize = inputSize + #buttonNames
  16.  
  17.  
  18. function getTile(dx, dy)
  19. marioX = memory.read_s16_le(0x94)
  20. marioY = memory.read_s16_le(0x96)
  21.  
  22. x = math.floor((marioX+dx+8)/16)
  23. y = math.floor((marioY+dy)/16)
  24.  
  25. return memory.readbyte(0x1C800 + math.floor(x/0x10)*0x1B0 + y*0x10 + x%0x10)
  26. end
  27.  
  28. function getSprites()
  29. local sprites = {}
  30. for slot=0,11 do
  31. local status = memory.readbyte(0x14C8+slot)
  32. if status ~= 0 then
  33. spritex = memory.readbyte(0xE4+slot) + memory.readbyte(0x14E0+slot)*256
  34. spritey = memory.readbyte(0xD8+slot) + memory.readbyte(0x14D4+slot)*256
  35. sprites[#sprites+1] = {["x"]=spritex, ["y"]=spritey}
  36. end
  37. end
  38.  
  39. return sprites
  40. end
  41.  
  42. function getExtendedSprites()
  43. local extended = {}
  44. for slot=0,11 do
  45. local number = memory.readbyte(0x170B+slot)
  46. if number ~= 0 then
  47. spritex = memory.readbyte(0x171F+slot) + memory.readbyte(0x1733+slot)*256
  48. spritey = memory.readbyte(0x1715+slot) + memory.readbyte(0x1729+slot)*256
  49. extended[#extended+1] = {["x"]=spritex, ["y"]=spritey}
  50. end
  51. end
  52.  
  53. return extended
  54. end
  55.  
  56. function getInputs()
  57. marioX = memory.read_s16_le(0x94)
  58. marioY = memory.read_s16_le(0x96)
  59.  
  60. sprites = getSprites()
  61. extended = getExtendedSprites()
  62.  
  63. local inputs = {}
  64.  
  65. for dy=-boxRadius*16,boxRadius*16,16 do
  66. for dx=-boxRadius*16,boxRadius*16,16 do
  67. inputs[#inputs+1] = 0
  68.  
  69. tile = getTile(dx, dy)
  70. if tile == 1 and marioY+dy < 0x1B0 then
  71. inputs[#inputs] = 1
  72. end
  73.  
  74. for i = 1,#sprites do
  75. distx = math.abs(sprites[i]["x"] - (marioX+dx))
  76. disty = math.abs(sprites[i]["y"] - (marioY+dy))
  77. if distx < 8 and disty < 8 then
  78. inputs[#inputs] = -1
  79. end
  80. end
  81.  
  82. for i = 1,#extended do
  83. distx = math.abs(extended[i]["x"] - (marioX+dx))
  84. disty = math.abs(extended[i]["y"] - (marioY+dy))
  85. if distx < 8 and disty < 8 then
  86. inputs[#inputs] = -1
  87. end
  88. end
  89. end
  90. end
  91.  
  92. mariovx = memory.read_s8(0x7B)
  93. mariovy = memory.read_s8(0x7D)
  94.  
  95. return inputs
  96. end
  97.  
  98.  
  99. function evaluate(inputs, chromosome)
  100. local outputs = {}
  101. local minDist = 4*inputSize
  102.  
  103. for i=0,(math.floor(#chromosome/snapshotSize)-1) do
  104. local total = 0
  105. for j=1,inputSize do
  106. d = inputs[j] - chromosome[i*snapshotSize + j]
  107. total = total + d*d
  108. end
  109. if total < minDist then
  110. minDist = total
  111. for j=1,#buttonNames do
  112. outputs[j] = chromosome[i*snapshotSize+inputSize+j]
  113. end
  114. nearest = i*snapshotSize
  115. end
  116. end
  117.  
  118. return outputs
  119. end
  120.  
  121. function randomChromosome()
  122. local c = {}
  123.  
  124. for i=1,snapshotSize*100 do
  125. c[i] = math.random(-1, 1)
  126. end
  127.  
  128. return c
  129. end
  130.  
  131. function initializeRun()
  132. savestate.load(filename);
  133. rightmost = 0
  134. frame = 0
  135. timeout = 20
  136. clearJoypad()
  137. end
  138.  
  139. function crossover(c1, c2)
  140. local c = {["chromosome"] = {}, ["fitness"] = 0}
  141. local pick = true
  142. for i=1,#c1["chromosome"] do
  143. if math.random(#c1["chromosome"]/2) == 1 then
  144. pick = not pick
  145. end
  146. if pick then
  147. c["chromosome"][i] = c1["chromosome"][i]
  148. else
  149. c["chromosome"][i] = c2["chromosome"][i]
  150. end
  151. end
  152.  
  153. return c
  154. end
  155.  
  156. function mutate(c, rate)
  157. for i=1,#c["chromosome"] do
  158. if math.random(rate) == 1 then
  159. c["chromosome"][i] = math.random(-1, 1)
  160. end
  161. end
  162. end
  163.  
  164. function difference(c1, c2)
  165. total = 0
  166. for i=1,#c1 do
  167. d = c1[i] - c2[i]
  168. total = total + d*d
  169. end
  170.  
  171. return total / (#c1*4)
  172. end
  173.  
  174. function clone(c)
  175. cl = {["chromosome"] = {}, ["fitness"] = 0}
  176. for i=1,#c["chromosome"] do
  177. cl["chromosome"][i] = c["chromosome"][i]
  178. end
  179. return cl
  180. end
  181.  
  182. function createNewGeneration()
  183. shock = (generation % 60 == 59)
  184.  
  185. local top = pool[1]["fitness"]
  186. if not shock then
  187. pool[1]["fitness"] = 1000000
  188. end
  189.  
  190. table.sort(pool, function (a,b)
  191. return (a["fitness"] > b["fitness"])
  192. end)
  193.  
  194. pool[1]["fitness"] = top
  195.  
  196. if shock then
  197. for i=2,(#pool) do
  198. pool[i] = clone(pool[1])
  199. mutate(pool[i], 20)
  200. end
  201. else
  202. for i=((#pool)/2+1),(#pool) do
  203. c1 = pool[math.random(2, #pool/2)]
  204. c2 = pool[math.random(2, #pool/2)]
  205. pool[i] = crossover(c1, c2)
  206. mutate(pool[i], 400)
  207. end
  208. forms.settext(localFitness, "Local Fitness: " .. math.floor(pool[2]["fitness"]))
  209. end
  210.  
  211. generation = generation + 1
  212. end
  213.  
  214. function clearJoypad()
  215. local controller = {}
  216. for b = 1,#buttonNames do
  217. controller["P1 " .. buttonNames[b]] = false
  218. end
  219. joypad.set(controller)
  220. end
  221.  
  222. function showTop()
  223. clearJoypad()
  224. currentChromosome = 0
  225. initializeRun()
  226. end
  227.  
  228. function onExit()
  229. forms.destroy(form)
  230. end
  231. event.onexit(onExit)
  232.  
  233. function connectionCost(chromosome)
  234. local total = 0
  235. for i=1,#chromosome["chromosome"] do
  236. c = chromosome["chromosome"][i]
  237. total = total + c*c
  238. end
  239.  
  240. return total
  241. end
  242.  
  243. function outputChromosomes()
  244. local filename = forms.gettext(saveLoadFile)
  245. local file = io.open(filename, "w")
  246. file:write(generation .. "\n")
  247. file:write(maxfitness .. "\n")
  248. file:write(#pool .. "\n")
  249. for c=1,#pool do
  250. local size = #pool[c]["chromosome"]
  251. file:write(size .. "\n")
  252. for n=1,size do
  253. file:write(pool[c]["chromosome"][n] .. "\n")
  254. end
  255. end
  256. file:close()
  257. end
  258.  
  259. function inputChromosomes()
  260. initializeSimulation()
  261. currentChromosome=0
  262. local filename = forms.gettext(saveLoadFile)
  263. local file = io.open(filename, "r")
  264. generation = tonumber(file:read())
  265. maxfitness = tonumber(file:read())
  266. forms.settext(maxFitnessLabel, "Top Fitness: " .. math.floor(maxfitness))
  267. local poolsize = tonumber(file:read())
  268. for c=1,poolsize do
  269. local size = tonumber(file:read())
  270. for n=1,size do
  271. pool[c]["chromosome"][n] = tonumber(file:read())
  272. end
  273. end
  274. file:close()
  275. end
  276.  
  277. function initializeSimulation()
  278. pool = {}
  279. for i=1,20 do
  280. pool[i] = {["chromosome"] = randomChromosome(), ["fitness"] = 0}
  281. end
  282. currentChromosome = 1
  283. generation = 0
  284. maxfitness = -1000000
  285. initializeRun()
  286. end
  287.  
  288. form = forms.newform(200, 275, "Fitness")
  289. maxFitnessLabel = forms.label(form, "Top Fitness: ", 5, 8)
  290. goButton = forms.button(form, "Show Top", showTop, 5, 30)
  291. goButton = forms.button(form, "Restart", initializeSimulation, 80, 30)
  292. localFitness = forms.label(form, "Local Fitness: ", 5, 55)
  293. showUI = forms.checkbox(form, "Show Inputs", 5, 74)
  294. inputsLabel = forms.label(form, "Inputs", 5, 96)
  295. showChromosomes = forms.checkbox(form, "Show Map", 5, 118)
  296. saveButton = forms.button(form, "Save", outputChromosomes, 5, 142)
  297. loadButton = forms.button(form, "Load", inputChromosomes, 80, 142)
  298. saveLoadFile = forms.textbox(form, "nearest_chromosomes.txt", 170, 25, nil, 5, 188)
  299. saveLoadLabel = forms.label(form, "Save/Load:", 5, 169)
  300. showNearest = forms.checkbox(form, "Show Nearest", 5, 215)
  301.  
  302. if pool == nil then
  303. initializeSimulation()
  304. else
  305. forms.settext(maxFitnessLabel, "Top Fitness: " .. math.floor(maxfitness))
  306. end
  307.  
  308. while true do
  309. marioX = memory.read_s16_le(0x94)
  310. marioY = memory.read_s16_le(0x96)
  311.  
  312. timeoutBonus = frame / 4
  313. if timeout + timeoutBonus <= 0 then
  314. local fitness = 0
  315. if currentChromosome == 1 and generation ~= 0 then
  316. fitness = maxfitness
  317. elseif currentChromosome >= 1 then
  318. fitness = rightmost - frame / 2
  319. end
  320.  
  321. if currentChromosome >= 1 then
  322. pool[currentChromosome]["fitness"] = fitness
  323. end
  324.  
  325. if fitness > maxfitness then
  326. forms.settext(maxFitnessLabel, "Top Fitness: " .. math.floor(fitness))
  327. maxfitness = fitness
  328. end
  329.  
  330. console.writeline("Generation " .. generation .. " chromosome " .. currentChromosome .. " fitness: " .. math.floor(fitness))
  331. if currentChromosome == #pool then
  332. createNewGeneration()
  333. currentChromosome = #pool/2+1
  334. else
  335. if currentChromosome == 2 and pool[currentChromosome+1]["fitness"] ~= 0 then
  336. currentChromosome = (#pool)/2+1
  337. else
  338. currentChromosome = currentChromosome + 1
  339. end
  340. end
  341. initializeRun()
  342. end
  343.  
  344. if timeout + timeoutBonus > 2 and frame % 5 == 0 and currentChromosome >= 1 then
  345. inputs = getInputs()
  346. outputs = evaluate(inputs, pool[currentChromosome]["chromosome"])
  347.  
  348. controller = {}
  349. inputsString = ""
  350. for n = 1,#buttonNames do
  351. if outputs[n] > 0 then
  352. controller["P1 " .. buttonNames[n]] = true
  353. inputsString = inputsString .. buttonNames[n]
  354. else
  355. controller["P1 " .. buttonNames[n]] = false
  356. end
  357. end
  358.  
  359. forms.settext(inputsLabel, inputsString)
  360. end
  361. joypad.set(controller)
  362.  
  363. if timeout + timeoutBonus <= 2 then
  364. clearJoypad()
  365. end
  366.  
  367. if marioX > rightmost then
  368. timeout = 20
  369. rightmost = marioX
  370. end
  371.  
  372. timeout = timeout - 1
  373. frame = frame + 1
  374.  
  375.  
  376. if forms.ischecked(showUI) and inputs ~= nil then
  377. layer1x = memory.read_s16_le(0x1A);
  378. layer1y = memory.read_s16_le(0x1C);
  379.  
  380. for dy = 0,boxRadius*2 do
  381. for dx = 0,boxRadius*2 do
  382. input = inputs[dy*(boxRadius*2+1)+dx+1]
  383. local x = marioX+(dx-boxRadius)*16-layer1x
  384. local y = marioY+(dy-boxRadius)*16-layer1y
  385. if input == -1 then
  386. gui.drawBox(x, y, x+16, y+16, 0xFFFF0000, 0xA0FF0000)
  387. elseif input == 1 then
  388. gui.drawBox(x, y, x+16, y+16, 0xFF00FF00, 0xA000FF00)
  389. else
  390. gui.drawBox(x, y, x+16, y+16, 0xFFFFFF00, 0xA0FFFF00)
  391. end
  392. --gui.drawText(,,string.format("%i", input),0x80FFFFFF, 11)
  393. end
  394. end
  395.  
  396. local x = marioX-layer1x
  397. local y = marioY-layer1y
  398. gui.drawBox(x, y, x+16, y+32, 0xFF000000)
  399. end
  400.  
  401. if forms.ischecked(showNearest) and nearest ~= nil and currentChromosome >= 1 then
  402. layer1x = memory.read_s16_le(0x1A);
  403. layer1y = memory.read_s16_le(0x1C);
  404.  
  405. for dy = 0,boxRadius*2 do
  406. for dx = 0,boxRadius*2 do
  407. input = pool[currentChromosome]["chromosome"][nearest+dy*(boxRadius*2+1)+dx+1]
  408. local x = marioX+(dx-boxRadius)*16-layer1x
  409. local y = marioY+(dy-boxRadius)*16-layer1y
  410. if input == -1 then
  411. gui.drawBox(x, y, x+16, y+16, 0xFFFF0000, 0xA0FF0000)
  412. elseif input == 1 then
  413. gui.drawBox(x, y, x+16, y+16, 0xFF00FF00, 0xA000FF00)
  414. else
  415. gui.drawBox(x, y, x+16, y+16, 0xFFFFFF00, 0xA0FFFF00)
  416. end
  417. end
  418. end
  419.  
  420. local x = marioX-layer1x
  421. local y = marioY-layer1y
  422. gui.drawBox(x, y, x+16, y+32, 0xFF000000)
  423. end
  424.  
  425. if forms.ischecked(showChromosomes) then
  426. gui.drawBox(0, 3, 201, 3+#pool*3, 0xFFFFFFFF, 0xFFFFFFFF)
  427. for c=1,#pool do
  428. local y = 1+c*3
  429. local size = #pool[c]["chromosome"]
  430. for n=1,size do
  431. if n%math.floor(size/200) == 0 then
  432. local x = 1+n*200/#pool[c]["chromosome"]
  433. v = pool[c]["chromosome"][n]
  434. r = (1-v)/2
  435. g = (1+v)/2
  436. gui.drawLine(x, y, x, y+1, 0xFF000000 + math.floor(r*0xFF)*0x10000 + math.floor(g*0xFF)*0x100)
  437. end
  438. end
  439. end
  440. end
  441.  
  442. emu.frameadvance();
  443. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement