Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ----------------------------------------------------------------------------------------------------------------------------------------------------
- --Super Mario Bros.
- --Neural Network Learning
- --by Michael Roberts
- --06/2015
- --
- --Use with FCEUX v2.2.2
- ----------------------------------------------------------------------------------------------------------------------------------------------------
- --RAM Addresses for variables
- ----------------------------------------------------------------------------------------------------------------------------------------------------
- saveState = savestate.object(1)
- savestate.save(saveState)
- RamNametableHi = 0x20
- RamNametableLow = 0x01
- RamNametableSize = 0x2BF
- RamNametableSolidStart = 0x01
- --Screen attributes
- RamObjectMapStart = 0x500
- MapTileNum = 208
- --Player attributes
- RamPlayerX = 0x86
- RamPlayerY = 0x3B8
- RamPlayerScreenX = 0x6D
- RamLives = 0x75A
- RamCoins = 0x75E
- --Game attributes
- RamWorld = 0x75F
- RamLevel = 0x760
- --Score
- RamScoreDigit1 = 0x7D8 --10^5
- RamScoreDigit2 = 0x7D9 --10^4
- RamScoreDigit3 = 0x7DA --10^3
- RamScoreDigit4 = 0x7DB --10^2
- RamScoreDigit5 = 0x7DC --10^1
- --Enemy attributes
- RamEnemyX = 0x87 --starting address in memory for multiple enemies
- RamEnemyY = 0xCF
- RamEnemyScreenX = 0x6E
- EnemyNumSlots = 5
- RamEnemyFlag = 0xF --In Zelda, it's enemy direction used as flag
- --Projectile attributes
- --RamProjectileX = 0x87 --starting address in memory for multiple enemies
- --RamProjectileY = 0xCF
- --RamProjectileScreenX = 0x6E
- --ProjectileNumSlots = 5
- --RamProjectileFlag = 0xF --In Zelda, it's enemy direction used as flag
- ------------------------------------------------------------------------------------------------------------------------------------------------------
- --timer = 40
- index = 1
- --AReady = true
- --BReady = true
- vblankFlag = 0
- vblankOff = 0
- --PPU2002 = {}
- generation = 1
- fitness = 0
- maxFitness = 0
- startFitness = 0
- timeStuck = 0
- maxStuckTime = 100 --220
- lastX = 0
- lastY = 0
- lastLives = 0
- timerHold = 0
- lastScreen = 0
- screenTimeStuck = 0
- maxScreenStuckTime = 350
- playerX = 0
- playerY = 0
- playerRoomX = 0
- playerRoomY = 0
- playerMapX = 0
- playerMapY = 0
- playerLives = 0
- room = {}
- map = {}
- for i=1,2*MapTileNum do
- map[i] = 0
- end
- enemyX = {}
- enemyY = {}
- enemyRoomX = {}
- enemyRoomY = {}
- enemyMapX = {}
- enemyMapY = {}
- enemyDir = {}
- projectileX = {}
- projectileY = {}
- projectileRoomX = {}
- projectileRoomY = {}
- drawRoom = false
- drawMap = false
- drawView = true
- drawCoord = false
- drawNeurons = true
- drawWeights = true
- drawController = true
- drawPopulation = true
- viewRadius = 2
- viewDiameter = 2*viewRadius+1
- viewBox = {}
- for i=1,viewDiameter*viewDiameter do
- viewBox[i] = 0
- end
- inputNum = viewDiameter*viewDiameter + 1
- outputNum = 6 --6 buttons on the controller
- layerNum = 2
- layerSize = {inputNum, 12, outputNum}
- --layerSize array has layerNum+1 entries. (inputNum layer is counted as layer 0)
- populationSize = 25
- populationFitness = {}
- for i=1, populationSize do
- populationFitness[i] = nil
- end
- currentChild = 1
- currentParent1 = 0
- currentParent2 = 0
- mutationProbability = .05
- meanFitness = 0
- stdvFitness = 0
- inputViewNum = MapTileNum --this is the number of inputs to X that are from the view or map
- --inputSigmoidStrength = 4.394/inputNum
- --hiddenSigmoidStrength = 4.394/layerSize
- outputStepStrength = 0 --layerSize/4
- --sigmoid strength = 4ln3/N --4ln3 = 4.394
- --where N=number of neurons input into current layer being calculated.
- --X[1][] = input
- --X[layerNum+1][] = output
- --Y = output bool
- --
- --S[l] = W[l]*X[l-1]
- --X[l] = f(S[l])
- --f is shaping function (tanh)
- Y = {}
- for i=1, outputNum do
- Y[i] = false
- end
- X = {}
- S = {}
- delta = {}
- for l=1,layerNum+1 do
- X[l] = {}
- S[l] = {}
- delta[l] = {}
- for i=1, layerSize[l] do
- X[l][i] = 0
- S[l][i] = 0
- delta[l][i] = 0
- end
- end
- W = {}
- memW = {}
- for p=1, populationSize do
- W[p] = {}
- for l=1,layerNum do
- W[p][l] = {}
- memW[l] = {}
- for i=1,layerSize[l+1] do
- W[p][l][i] = {}
- memW[l][i] = {}
- for j=1,layerSize[l] do
- W[p][l][i][j] = 2*math.random() - 1 --math.random(3)-2
- memW[l][i][j] = W[p][l][i][j]
- end
- end
- end
- end
- --Training parameters
- --trainingMode = 1 -> begin in training mode. 0 is game running mode
- geneticMode = true
- trainingMode = false
- recordMode = 1
- keyPressReady = true
- randomChangeSize = .1
- stepSize = .1
- sampleRate = 10
- sampleTimer = 0
- controllerInput = {}
- errorVal = 0
- Y_Train = {}
- for i=1, outputNum do
- Y_Train[i] = 0
- end
- --------------------------------------------------------------------------------------------------------------------------------------------
- --Functions
- --------------------------------------------------------------------------------------------------------------------------------------------
- function readPPU()
- memory.writebyte(0x2006, RamNametableHi)
- memory.writebyte(0x2006, RamNametableLow)
- j=1
- for i=0,RamNametableSize do
- local val = memory.readbyte(0x2007)
- room[i+1] = val
- if (math.floor(i/32))%2 == 0 then
- if i%2 == 0 then
- map[j] = val
- j = j+1
- end
- end
- --if val <= RamNametableSolidStart then
- -- room[i+1] = 0
- --else
- -- room[i+1] = 1
- --end
- end
- end
- function inputView()
- for i = -viewRadius, viewRadius do
- for j = -viewRadius, viewRadius do
- local x = playerMapX+i-1
- local y = playerMapY+j-1
- local page = math.floor(x/16)
- local xAddress = x - 16*page+1
- local yAddress = y + 13*(page%2)
- if xAddress >= 1 and xAddress < 32 and yAddress >= 1 and yAddress <= 25 then
- viewBox[(i+viewRadius+1)+viewDiameter*(j+viewRadius)] = map[xAddress + 16*yAddress]
- else
- viewBox[(i+viewRadius+1)+viewDiameter*(j+viewRadius)] = 0
- end
- end
- end
- end
- function inputMap()
- for i=1, 2*MapTileNum do
- if memory.readbyte(0x500 + i-1) ~= 0 then
- map[i] = 1
- else
- map[i] = 0
- end
- end
- for i=1, EnemyNumSlots do
- if memory.readbyte(RamEnemyFlag+(i-1)) ~= 0 then
- local page = math.floor(enemyMapX[i]/16)
- local xAddress = enemyMapX[i] - 16*page
- local yAddress = enemyMapY[i] - 1 + 13*(page%2)
- if xAddress >= 1 and xAddress < 32 and yAddress >= 1 and yAddress <= 25 then
- map[xAddress + 16*yAddress] = -1
- end
- end
- end
- end
- function inputPlayer()
- playerX = memory.readbyte(RamPlayerX) + memory.readbyte(RamPlayerScreenX)*0x100 + 4
- playerY = memory.readbyte(RamPlayerY) + 16
- playerRoomX = math.floor(playerX/8)+1
- playerRoomY = math.floor(playerY/7.5)-7
- playerMapX = math.floor((playerX%512)/16)+1
- playerMapY = math.floor((playerY-32)/16)+1
- end
- function inputEnemies()
- for i=1,EnemyNumSlots do
- if memory.readbyte(RamEnemyFlag+(i-1)) ~= 0 then
- enemyX[i] = memory.readbyte(RamEnemyX+(i-1)) + memory.readbyte(RamEnemyScreenX+(i-1))*0x100
- enemyY[i] = memory.readbyte(RamEnemyY+(i-1)) + 24
- else
- enemyX[i] = -1
- enemyY[i] = -1
- end
- enemyRoomX[i] = math.floor(enemyX[i]/8)+1
- enemyRoomY[i] = math.floor(enemyY[i]/7.5)-7
- enemyMapX[i] = math.floor((enemyX[i]%512)/16)+1
- enemyMapY[i] = math.floor((enemyY[i]-32)/16)
- end
- --for i=1,ProjectileNumSlots do
- -- if memory.readbyte(RamProjectileFlag-(i-1)) ~= 0 then
- -- projectileX[i] = memory.readbyte(RamProjectileX-(i-1))
- -- projectileY[i] = memory.readbyte(RamProjectileY-(i-1))
- -- else
- -- projectileX[i] = -1
- -- projectileY[i] = -1
- -- end
- -- projectileRoomX[i] = math.floor(projectileX[i]/8)
- -- projectileRoomY[i] = math.floor(projectileY[i]/7.5)
- --end
- end
- function inputViewToX(startPosition)
- for i=1,(viewDiameter)*(viewDiameter) do
- X[1][startPosition-1+i] = viewBox[i]
- end
- end
- function inputMapToX(startPosition)
- for i=1,MapTileNum do
- if map[i] <= RamNametableSolidStart then
- X[1][startPosition-1+i] = 0
- else
- X[1][startPosition-1+i] = 1
- end
- end
- end
- function inputCoordToX(startPosition)
- X[1][startPosition] = playerMapX/32
- X[1][startPosition+1] = playerMapY/13
- --X[1][startPosition+2] = playerHealth
- end
- function inputEnemyToX(startPosition)
- for i=0,EnemyNumSlots-1 do
- X[1][startPosition+2*i] = enemyMapX[i+1]/32
- X[1][startPosition+2*i+1] = enemyMapY[i+1]/13
- end
- --for i=0,ProjectileNumSlots-1 do
- -- X[1][startPosition+2*i+14] = projectileX[i+1]/256
- -- X[1][startPosition+2*i+15] = projectileY[i+1]/240
- --end
- end
- function inputNoise(val)
- for i=1,inputNum do
- X[1][i] = X[1][i] + val*(2*math.random()-1)
- if X[1][i] > 1 then
- X[1][i] = 1
- end
- if X[1][i] < -1 then
- X[1][i] = -1
- end
- end
- end
- function drawGui()
- local mapDrawX = -8
- local mapDrawY = 0
- local mapScale = 8
- local viewDrawX = 56
- local viewDrawY = 56
- local neuronDrawSpacing = 32
- local neuronVerticalSpacing = 4
- local controllerDrawX = 176
- local controllerDrawY = 48 --200
- local drawNeuronsX = 88
- if drawRoom == true then
- for i=0,RamNametableSize do
- local x = i%32
- local y = math.floor(i/32)
- if tunicColor%2 == 0 then
- gui.text(x*8, 64+y*8, room[i+1]%16)
- else
- gui.text(x*8, 64+y*8, math.floor(room[i+1]/16))
- end
- --if room[i+1] <= 0x77 then
- -- gui.box(mapDrawX+x*mapScale, mapDrawY+y*mapScale, mapDrawX+x*mapScale+mapScale, mapDrawY+y*mapScale+mapScale, "black")
- --else
- -- gui.box(mapDrawX+x*mapScale, mapDrawY+y*mapScale, mapDrawX+x*mapScale+mapScale, mapDrawY+y*mapScale+mapScale, "blue")
- --end
- end
- gui.box(mapDrawX+playerMapX*mapScale, mapDrawY+mapScale*(playerMapY-8), mapDrawX+mapScale*(playerMapX+1.5), mapDrawY+mapScale*(playerMapY-6.5), "green")
- for i=1,EnemyNumSlots do
- if enemyX[i] ~= -1 then
- gui.box(mapDrawX+enemyRoomX[i]*mapScale, mapDrawY+mapScale*(enemyRoomY[i]-8), mapDrawX+mapScale*(enemyRoomX[i]+1.5), mapDrawY+mapScale*(enemyRoomY[i]-6.5), "red")
- end
- end
- --for i=1,ProjectileNumSlots do
- -- if projectileX[i] ~= -1 then
- -- gui.box(mapDrawX+projectileRoomX[i]*mapScale, mapDrawY+mapScale*(projectileRoomY[i]-8), mapDrawX+mapScale*(projectileRoomX[i]+1), mapDrawY+mapScale*(projectileRoomY[i]-7), "red")
- -- end
- --end
- end
- if drawMap == true then
- --gui.box(0,0,256,240,"black")
- for i=0,2*MapTileNum-1 do
- local x = 16*math.floor(i/208) + i%16 + 1
- local y = math.floor(i/16) - 13*math.floor(i/208) + 1
- --local x = i%16 + 1 -- + 16*(i%208)
- --local y = math.floor(i/16) + 1 - 13*(i%208)
- --gui.text(mapDrawX+x*16, mapDrawY++y*16, map[i+1]%16)
- if map[i+1] == 0 then
- gui.box(mapDrawX+x*mapScale, mapDrawY+y*mapScale, mapDrawX+(x+1)*mapScale-1, mapDrawY+(y+1)*mapScale-1, "black")
- elseif map[i+1] == 1 then
- gui.box(mapDrawX+x*mapScale, mapDrawY+y*mapScale, mapDrawX+(x+1)*mapScale-1, mapDrawY+(y+1)*mapScale-1, "blue")
- elseif map[i+1] == -1 then
- gui.box(mapDrawX+x*mapScale, mapDrawY+y*mapScale, mapDrawX+(x+1)*mapScale-1, mapDrawY+(y+1)*mapScale-1, "red")
- end
- end
- gui.box(mapDrawX+playerMapX*mapScale, mapDrawY+mapScale*playerMapY, mapDrawX+mapScale*(playerMapX+1)-1, mapDrawY+mapScale*(playerMapY+1)-1, "green")
- gui.box(mapDrawX+(playerMapX-viewRadius)*mapScale, mapDrawY+mapScale*(playerMapY-viewRadius), mapDrawX+(playerMapX+viewRadius+1)*mapScale, mapDrawY+mapScale*(playerMapY+viewRadius+1), "clear", "white")
- --Draw enemies on map. Replaced by inputing enemies into map[i] as -1 values
- --for i=1,EnemyNumSlots do
- -- if enemyX[i] ~= -1 then
- -- gui.box(mapDrawX+enemyMapX[i]*mapScale, mapDrawY+mapScale*enemyMapY[i], mapDrawX+mapScale*(enemyMapX[i]+1)-1, mapDrawY+mapScale*(enemyMapY[i]+1)-1, "red")
- -- end
- --end
- --for i=1,4 do
- -- if projectileX[i] ~= -1 then
- -- gui.box(mapDrawX+projectileRoomX[i]*mapScale, mapDrawY+mapScale*(projectileRoomY[i]-8), mapDrawX+mapScale*(projectileRoomX[i]+1), mapDrawY+mapScale*(projectileRoomY[i]-7), "red")
- -- end
- --end
- end
- if drawView == true then
- for i=1,viewDiameter*viewDiameter do
- local x = (i-1)%viewDiameter - viewRadius
- local y = math.floor((i-1)/viewDiameter) - viewRadius
- if viewBox[i] == 1 then
- gui.box(viewDrawX+8*x, viewDrawY+8*y, viewDrawX+8*(x+1)-1, viewDrawY+8*(y+1)-1, "blue")
- elseif viewBox[i] == 0 then
- gui.box(viewDrawX+8*x, viewDrawY+8*y, viewDrawX+8*(x+1)-1, viewDrawY+8*(y+1)-1, "black")
- elseif viewBox[i] == -1 then
- gui.box(viewDrawX+8*x, viewDrawY+8*y, viewDrawX+8*(x+1)-1, viewDrawY+8*(y+1)-1, "red")
- end
- end
- gui.box(viewDrawX, viewDrawY, viewDrawX+8-1, viewDrawY+8-1, "green")
- end
- if drawCoord == true then
- gui.text(192,8,playerX)
- gui.text(224,8,playerY)
- gui.text(192,16,playerMapX)
- gui.text(224,16,playerMapY)
- --gui.text(168,48,playerHealth)
- end
- if drawNeurons == true then
- for l=1,layerNum+1 do
- local layerDrawSpacing = (neuronVerticalSpacing*inputNum) / layerSize[l]
- for i=1, layerSize[l] do
- if X[l][i] >= -0.5 and X[l][i] <= 0.5 then
- gui.box(drawNeuronsX+neuronDrawSpacing*(l-1), 10+(i-1)*layerDrawSpacing, drawNeuronsX+3+neuronDrawSpacing*(l-1), 10+i*layerDrawSpacing, "gray", "black")
- elseif X[l][i] > 0.5 then
- gui.box(drawNeuronsX+neuronDrawSpacing*(l-1), 10+(i-1)*layerDrawSpacing, drawNeuronsX+3+neuronDrawSpacing*(l-1), 10+i*layerDrawSpacing, "green", "black")
- elseif X[l][i] < -0.5 then
- gui.box(drawNeuronsX+neuronDrawSpacing*(l-1), 10+(i-1)*layerDrawSpacing, drawNeuronsX+3+neuronDrawSpacing*(l-1), 10+i*layerDrawSpacing, "red", "black")
- end
- end
- end
- end
- if drawController == true then
- gui.box(controllerDrawX-1, controllerDrawY-1, controllerDrawX+64, controllerDrawY+24, "black")
- --A
- if Y[1] == true then
- gui.box(controllerDrawX+56, controllerDrawY+8, controllerDrawX+63, controllerDrawY+15, "blue")
- else
- gui.box(controllerDrawX+56, controllerDrawY+8, controllerDrawX+63, controllerDrawY+15, "gray")
- end
- --up
- if Y[2] == true then
- gui.box(controllerDrawX+8, controllerDrawY, controllerDrawX+15, controllerDrawY+7, "blue")
- else
- gui.box(controllerDrawX+8, controllerDrawY, controllerDrawX+15, controllerDrawY+7, "gray")
- end
- --left
- if Y[3] == true then
- gui.box(controllerDrawX, controllerDrawY+8, controllerDrawX+7, controllerDrawY+15, "blue")
- else
- gui.box(controllerDrawX, controllerDrawY+8, controllerDrawX+7, controllerDrawY+15, "gray")
- end
- --B
- if Y[4] == true then
- gui.box(controllerDrawX+40, controllerDrawY+8, controllerDrawX+47, controllerDrawY+15, "blue")
- else
- gui.box(controllerDrawX+40, controllerDrawY+8, controllerDrawX+47, controllerDrawY+15, "gray")
- end
- --right
- if Y[5] == true then
- gui.box(controllerDrawX+16, controllerDrawY+8, controllerDrawX+23, controllerDrawY+15, "blue")
- else
- gui.box(controllerDrawX+16, controllerDrawY+8, controllerDrawX+23, controllerDrawY+15, "gray")
- end
- --down
- if Y[6] == true then
- gui.box(controllerDrawX+8, controllerDrawY+16, controllerDrawX+15, controllerDrawY+23, "blue")
- else
- gui.box(controllerDrawX+8, controllerDrawY+16, controllerDrawX+15, controllerDrawY+23, "gray")
- end
- end
- if drawWeights == true then
- for l=1,layerNum do
- local layerInSpacing = (neuronVerticalSpacing*inputNum) / layerSize[l]
- local layerOutSpacing = (neuronVerticalSpacing*inputNum) / layerSize[l+1]
- for i=1,layerSize[l+1] do
- for j=1,layerSize[l] do
- if W[currentChild][l][i][j] >= .6 then
- gui.line(drawNeuronsX+3+neuronDrawSpacing*(l-1), 10+(j-.5)*layerInSpacing, drawNeuronsX-1+neuronDrawSpacing*l, 10+(i-.5)*layerOutSpacing, {0,0,255,48})
- end
- if W[currentChild][l][i][j] <= -.6 then
- gui.line(drawNeuronsX+3+neuronDrawSpacing*(l-1), 10+(j-.5)*layerInSpacing, drawNeuronsX-1+neuronDrawSpacing*l, 10+(i-.5)*layerOutSpacing, {255,0,0,48})
- end
- end
- end
- end
- end
- if drawPopulation == true then
- if currentParent1 > 0 then
- gui.box(4, 8*currentParent1, 10, 8*(currentParent1+1)-2, "blue")
- end
- if currentParent2 > 0 then
- gui.box(4, 8*currentParent2, 10, 8*(currentParent2+1)-2, "blue")
- end
- gui.box(4, 8*currentChild, 10, 8*(currentChild+1)-2, "green")
- for i=1, populationSize do
- if populationFitness[i] ~= nil then
- gui.text(12, 8*i, populationFitness[i])
- end
- end
- end
- end
- function multiply(matrixIn, vectorIn, rows, collumns)
- local vectorOut = {}
- for i=1,rows do
- vectorOut[i] = 0
- for j=1,collumns do
- vectorOut[i] = vectorOut[i] + matrixIn[i][j]*vectorIn[j]
- end
- end
- return vectorOut
- end
- function sigmoid(inputVector, inputLength, strength)
- local vectorOut = {}
- for i=1,inputLength do
- vectorOut[i] = 2/(1+math.exp(-strength*inputVector[i])) - 1
- end
- return vectorOut
- end
- function tanh(inputVector, inputLength)
- local vectorOut = {}
- for i=1,inputLength do
- vectorOut[i] = (math.exp(inputVector[i]) - math.exp(-inputVector[i])) / (math.exp(inputVector[i]) + math.exp(-inputVector[i]))
- end
- return vectorOut
- end
- function stepFunction(inputVector, inputLength)
- local vectorOut = {}
- for i=1,inputLength do
- if inputVector[i] >= outputStepStrength then
- vectorOut[i] = true
- else
- vectorOut[i] = false
- end
- end
- return vectorOut
- end
- function forwardPropogate()
- X[1][1] = -1
- for l=1, layerNum do
- S[l+1] = multiply(W[currentChild][l], X[l], layerSize[l+1], layerSize[l])
- X[l+1] = tanh(S[l+1], layerSize[l+1])
- if l < layerNum then
- X[l+1][1] = -1
- end
- end
- Y = stepFunction(X[layerNum+1], outputNum)
- --make it so up/down etc. can't be hit at same time
- doublePressNegate(3,5)
- doublePressNegate(2,6)
- doublePressNegate(5,6)
- doublePressNegate(2,5)
- doublePressNegate(3,6)
- end
- function doublePressNegate(button1, button2)
- if Y[button1] == true and Y[button2] == true then
- if X[layerNum+1][button1] > X[layerNum+1][button2] then
- Y[button2] = false
- else
- Y[button1] = false
- end
- end
- end
- function outputToController()
- local controllerInput = {}
- controllerInput = {A=Y[1], up=Y[2], left=Y[3], B=Y[4], select=nil, right=Y[5], down=Y[6], start=nil}
- joypad.write(1, controllerInput)
- end
- function controllerOverride()
- local controllerInput = {}
- controllerInput = joypad.read(1)
- Y[1] = controllerInput["A"]
- Y[2] = controllerInput["up"]
- Y[3] = controllerInput["left"]
- Y[4] = controllerInput["B"]
- Y[5] = controllerInput["right"]
- Y[6] = controllerInput["down"]
- end
- function updateWeightsFromMem()
- for l=1,layerNum do
- for i=1,layerSize[l+1] do
- for j=1,layerSize[l] do
- --if math.random() < stepSize then
- -- W[l][i][j] = (memW[l][i][j] + 2)%3 - 1
- --else
- -- W[l][i][j] = memW[l][i][j]
- --end
- W[currentChild][l][i][j] = memW[l][i][j] + randomChangeSize*(2*math.random() - 1)
- --if W[i][j][k] > 1 then
- -- W[i][j][k] = 1
- --end
- --if W[i][j][k] < -1 then
- -- W[i][j][k] = -1
- --end
- end
- end
- end
- end
- function updateWeightsRandom()
- for l=1,layerNum do
- for i=1,layerSize[l+1] do
- for j=1,layerSize[l] do
- W[currentChild][l][i][j] = 2*math.random() - 1 --math.random(3)-2
- memW[l][i][j] = W[currentChild][l][i][j]
- end
- end
- end
- end
- function updateMemWeights()
- for l=1,layerNum do
- for i=1,layerSize[l+1] do
- for j=1,layerSize[l] do
- memW[l][i][j] = W[currentChild][l][i][j]
- end
- end
- end
- end
- function restartOld()
- if fitness > maxFitness then
- maxFitness = fitness
- updateMemWeights()
- end
- updateWeightsFromMem()
- if fitness <= minFitness then
- if maxFitness <= minFitness then
- updateWeightsRandom()
- end
- end
- generation = generation + 1
- fitness = 0
- timeStuck = 0
- savestate.load(saveState)
- inputPlayer()
- inputMap()
- inputView()
- end
- function inputController()
- --controllerInput = {A=true, up=false, left=false, B=false, select=nil, right=false, down=false, start=nil}
- controllerInput = joypad.read(1)
- for i=1, outputNum do
- Y_Train[i] = 0
- end
- if controllerInput["A"] == true then
- Y_Train[1] = 1
- end
- if controllerInput["up"] == true then
- Y_Train[2] = 1
- end
- if controllerInput["left"] == true then
- Y_Train[3] = 1
- end
- if controllerInput["B"] == true then
- Y_Train[4] = 1
- end
- if controllerInput["right"] == true then
- Y_Train[5] = 1
- end
- if controllerInput["down"] == true then
- Y_Train[6] = 1
- end
- if controllerInput["select"] == true then
- trainingMode = false
- timeStuck = 0
- screenTimeStuck = 0
- savestate.load(saveState)
- inputPlayer()
- inputEnemies()
- inputMap()
- inputView()
- fitness = playerX
- end
- if controllerInput["up"] == true and keyPressReady == true then
- recordMode = (recordMode+1)%2
- keyPressReady = false
- end
- if controllerInput["up"] == false then
- keyPressReady = true
- end
- end
- function backPropogate()
- for i=1, outputNum do
- delta[layerNum+1][i] = (1-X[layerNum+1][i]*X[layerNum+1][i]) * (X[layerNum+1][i] - Y_Train[i])
- end
- for l=0, layerNum-2 do
- for j=1, layerSize[layerNum-l] do
- local matrixProduct = 0
- for k=1, layerSize[layerNum-l+1] do
- matrixProduct = matrixProduct + W[layerNum-l][k][j] * delta[layerNum-l+1][k]
- end
- delta[layerNum-l][j] = (1 - X[layerNum-l][j]*X[layerNum-l][j]) * matrixProduct
- end
- end
- end
- function batchGradientDescent()
- for l=1, layerNum do
- for i=1, layerSize[l+1] do
- for j=1, layerSize[l] do
- --l = math.random(layerNum)
- --i = math.random(layerSize[l+1])
- --j = math.random(layerSize[l])
- W[currentChild][l][i][j] = W[currentChild][l][i][j] - stepSize * delta[l+1][i] * X[l][j]
- end
- end
- end
- end
- function restart()
- generation = generation + 1/populationSize
- if populationFitness[currentChild] == nil then
- populationFitness[currentChild] = fitness
- elseif fitness > populationFitness[currentChild] then
- populationFitness[currentChild] = fitness
- end
- if #populationFitness < populationSize then
- currentChild = currentChild + 1
- else
- currentChild = minimum(populationFitness)
- if math.random() <= .5 then
- currentParent1 = maximum(populationFitness)
- if currentParent1 == currentChild then
- currentParent1 = math.random(populationSize-1)
- if currentParent1 >= currentChild then
- currentParent1 = currentParent1+1
- end
- end
- else
- currentParent1 = math.random(populationSize-1)
- if currentParent1 >= currentChild then
- currentParent1 = currentParent1+1
- end
- end
- currentParent2 = math.random(populationSize-2)
- if currentParent2 >= currentChild then
- currentParent2 = currentParent2+1
- end
- if currentParent2 >= currentParent1 then
- currentParent2 = currentParent2+1
- end
- if currentParent2 == currentChild then
- currentParent2 = currentParent2+1
- end
- meanFitness = mean(populationFitness)
- stdvFitness = standardDev(populationFitness)
- if stdvFitness <= meanFitness/6 then
- uniformCrossover(mutationProbability*2)
- else
- uniformCrossover(mutationProbability)
- end
- end
- timeStuck = 0
- screenTimeStuck = 0
- savestate.load(saveState)
- inputPlayer()
- startFitness = playerX
- end
- function minimum(vectorIn)
- local val = vectorIn[1]
- local index = 1
- for i=1, #vectorIn do
- if vectorIn[i] < val then
- index = i
- val = vectorIn[i]
- end
- end
- return index
- end
- function maximum(vectorIn)
- local val = vectorIn[1]
- local index = 1
- for i=1, #vectorIn do
- if vectorIn[i] > val then
- index = i
- val = vectorIn[i]
- end
- end
- return index
- end
- function uniformCrossover(probability)
- for l=1, layerNum do
- for i=1, layerSize[l+1] do
- for j=1, layerSize[l] do
- if math.random() < probability then
- W[currentChild][l][i][j] = 2*math.random()-1
- else
- if math.random() <=0.5 then
- W[currentChild][l][i][j] = W[currentParent1][l][i][j]
- else
- W[currentChild][l][i][j] = W[currentParent2][l][i][j]
- end
- end
- end
- end
- end
- end
- function mean(arrayIn)
- local sum = 0
- --if #arrayIn > 0 then
- for i=1, #arrayIn do
- sum = sum + arrayIn[i]
- end
- return sum / #arrayIn
- --else
- -- return 0
- --end
- end
- function standardDev(arrayIn)
- local sum = 0
- local stdv = 0
- local meanVal = mean(arrayIn)
- --if #arrayIn > 1 then
- for i=1, #arrayIn do
- sum = sum + (arrayIn[i] - meanVal) * (arrayIn[i] - meanVal)
- end
- stdv = math.sqrt(sum / (#arrayIn-1))
- return stdv
- --else
- -- return 0
- --end
- end
- ------------------------------------------------------------------------------------------------------------------------------------------------------------
- --Main Code
- ------------------------------------------------------------------------------------------------------------------------------------------------------------
- inputPlayer()
- lastX = playerX
- lastY = playerY
- lastScreen = memory.readbyte(RamPlayerScreenX)
- startFitness = playerX
- playerLives = memory.readbyte(RamLives)
- lastLives = playerLives
- while (true) do
- --read PPU only when changing rooms
- --vblankFlag = memory.readbyte(0xE3)
- --if vblankFlag == 1 then
- -- vblankOff = 1
- --elseif vblankOff == 1 then
- -- readPPU()
- -- vblankOff = 0
- --end
- inputPlayer()
- inputEnemies()
- inputMap()
- inputView()
- index = 2 --first neuron slot is for constant X[1] = 1
- inputViewToX(index)
- index = index + viewDiameter*viewDiameter
- --inputCoordToX(index)
- --index = index + 2
- --inputEnemyToX(index)
- --index = index + 2*EnemyNumSlots
- --inputNoise(.1)
- forwardPropogate()
- if trainingMode == true then
- inputController()
- if recordMode == 1 then
- sampleTimer = sampleTimer + 1
- if sampleTimer >= sampleRate then
- sampleTimer = 0
- backPropogate()
- batchGradientDescent()
- errorVal = 0
- for i=1, 6 do
- errorVal = errorVal + ((X[layerNum+1][i] - Y_Train[i])*(X[layerNum+1][i] - Y_Train[i]))/2
- end
- end
- gui.text(0,8,errorVal)
- --outputToController()
- end
- else
- outputToController()
- --timeStuck = 0
- --screenTimeStuck = 0
- fitness = playerX - startFitness
- if timerHold == 0 then
- if math.abs(playerX - lastX) < 2 then
- timeStuck = timeStuck + 1
- else
- timeStuck = 0
- end
- if memory.readbyte(RamPlayerScreenX) == lastScreen then
- screenTimeStuck = screenTimeStuck + 1
- else
- screenTimeStuck = 0
- end
- end
- if (timeStuck >= maxStuckTime) or (screenTimeStuck >= maxScreenStuckTime) then
- if geneticMode == true then
- emu.print(fitness)
- restart()
- else
- restartOld()
- end
- end
- playerLives = memory.readbyte(RamLives)
- if lastLives ~= playerLives then
- --timerHold = 200
- --timeStuck = 0
- --screenTimeStuck = 0
- savestate.load(saveState)
- restart()
- end
- if timerHold > 0 then
- timerHold = timerHold - 1
- end
- --Cause seizures if he sits still for too long
- --if timeStuck >= maxStuckTime then
- -- inputNoise(.7)
- --end
- end
- drawGui()
- gui.text(220, 8, math.floor(generation))
- gui.text(48, 8, fitness)
- --gui.text(48, 16, startFitness)
- --gui.text(48, 24, timerHold)
- --gui.text(64, 24, timeStuck)
- --gui.text(48, 16, math.floor(meanFitness))
- --gui.text(48, 24, math.floor(stdvFitness))
- lastX = playerX
- lastY = playerY
- lastScreen = memory.readbyte(RamPlayerScreenX)
- lastLives = playerLives
- emu.frameadvance()
- end
- ------------------------------------------------------------------------------------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement