Promisify everything?

This commit is contained in:
Empathic Qubit 2021-05-01 19:39:35 -04:00
parent 088c92113b
commit c7819c98f2
10 changed files with 487 additions and 414 deletions

View file

@ -6,7 +6,7 @@ local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local mathFunctions = dofile(base.."/mathFunctions.lua")
local config = dofile(base.."/config.lua")
local spritelist = dofile(base.."/spritelist.lua")
local util = dofile(base.."/util.lua")
local util = dofile(base.."/util.lua")()
local mem = dofile(base.."/mem.lua")
local _M = {
leader = 0,

View file

@ -4,7 +4,6 @@ local gui = gui
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local pool = dofile(base.."/pool.lua")
local util = dofile(base.."/util.lua")
local statusLine = nil
local statusColor = 0x0000ff00
@ -30,4 +29,10 @@ pool.onRenderForm(function(form)
gui.text(-500, guiHeight - 20, statusLine, 0x00000000)
end
end)
pool.run()
pool.run():next(function()
print("The pool finished running!!!")
end):catch(function(error)
io.stderr:write(string.format("There was a problem running the pool: %s", error))
print(string.format("There was a problem running the pool: %s", error))
end)

114
pool.lua
View file

@ -1,7 +1,17 @@
local callback, set_timer_timeout = callback, set_timer_timeout
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local Promise = dofile(base.."/promise.lua")
-- Only the parent should manage ticks!
callback.register('timer', function()
Promise.update()
set_timer_timeout(1)
end)
set_timer_timeout(1)
local config = dofile(base.."/config.lua")
local util = dofile(base.."/util.lua")
local util = dofile(base.."/util.lua")(Promise)
local serpent = dofile(base.."/serpent.lua")
local libDeflate = dofile(base.."/LibDeflate.lua")
@ -386,15 +396,17 @@ local function addToSpecies(child)
end
end
local function initializePool(after)
local function initializePool()
local promise = Promise.new()
promise:resolve()
return promise:next(function()
pool = newPool()
for i=1,config.NeatConfig.Population do
basic = basicGenome()
local basic = basicGenome()
addToSpecies(basic)
end
after()
end)
end
local function bytes(x)
@ -406,6 +418,9 @@ local function bytes(x)
end
local function writeFile(filename)
local promise = Promise.new()
promise:resolve()
return promise:next(function ()
local file = io.open(filename, "w")
local dump = serpent.dump(pool)
local zlib = libDeflate:CompressDeflate(dump)
@ -414,11 +429,14 @@ local function writeFile(filename)
file:write(string.char(0,0,0,0))
file:write(bytes(#dump % (2^32)))
file:close()
return
end)
end
-- FIXME Save/load mechanism has to be rethought with items running in parallel
local function loadFile(filename, after)
-- FIXME This isn't technically asynchronous. Probably can't be though.
local function loadFile(filename)
local promise = Promise.new()
promise:resolve()
return promise:next(function()
message("Loading pool from " .. filename, 0x00999900)
local file = io.open(filename, "r")
if file == nil then
@ -433,6 +451,7 @@ local function loadFile(filename, after)
end
pool = obj
end)
end
local function savePool()
@ -441,9 +460,8 @@ local function savePool()
message(string.format("Saved \"%s\"!", filename:sub(#filename - 50)), 0x00009900)
end
local function loadPool(after)
loadFile(_M.saveLoadFile, after)
after()
local function loadPool()
return loadFile(_M.saveLoadFile)
end
local function processRenderForm(form)
@ -471,7 +489,7 @@ end
local function crossover(g1, g2)
-- Make sure g1 is the higher fitness genome
if g2.fitness > g1.fitness then
tempg = g1
local tempg = g1
g1 = g2
g2 = tempg
end
@ -562,11 +580,11 @@ end
local function breedChild(species)
local child = {}
if math.random() < config.NeatConfig.CrossoverChance then
g1 = species.genomes[math.random(1, #species.genomes)]
g2 = species.genomes[math.random(1, #species.genomes)]
local g1 = species.genomes[math.random(1, #species.genomes)]
local g2 = species.genomes[math.random(1, #species.genomes)]
child = crossover(g1, g2)
else
g = species.genomes[math.random(1, #species.genomes)]
local g = species.genomes[math.random(1, #species.genomes)]
child = copyGenome(g)
end
@ -605,7 +623,7 @@ local function removeWeakSpecies()
local sum = totalAverageFitness()
for s = 1,#pool.species do
local species = pool.species[s]
breed = math.floor(species.averageFitness / sum * config.NeatConfig.Population)
local breed = math.floor(species.averageFitness / sum * config.NeatConfig.Population)
if breed >= 1 then
table.insert(survived, species)
end
@ -628,7 +646,7 @@ local function newGeneration()
local children = {}
for s = 1,#pool.species do
local species = pool.species[s]
breed = math.floor(species.averageFitness / sum * config.NeatConfig.Population) - 1
local breed = math.floor(species.averageFitness / sum * config.NeatConfig.Population) - 1
for i=1,breed do
table.insert(children, breedChild(species))
end
@ -652,7 +670,7 @@ local function newGeneration()
writeFile(_M.saveLoadFile .. ".gen" .. pool.generation .. ".pool")
end
local runner = Runner()
local runner = Runner(Promise)
runner.onMessage(function(msg, color)
message(msg, color)
end)
@ -671,11 +689,20 @@ local topRequested = false
local loadRequested = false
local saveRequested = false
local function mainLoop(currentSpecies)
local function mainLoop(currentSpecies, topGenome)
if currentSpecies == nil then
currentSpecies = 1
end
local slice = pool.species[currentSpecies]
local promise = Promise.new()
promise:resolve()
return promise:next(function()
if loadRequested then
loadRequested = false
loadPool(mainLoop)
return
currentSpecies = nil
-- FIXME
return loadPool()
end
if saveRequested then
@ -685,19 +712,13 @@ local function mainLoop(currentSpecies)
if topRequested then
topRequested = false
playTop()
return
return playTop()
end
if not config.Running then
-- FIXME Tick?
end
if currentSpecies == nil then
currentSpecies = 1
end
local slice = pool.species[currentSpecies]
if hasThreads then
slice = {}
for i=currentSpecies, currentSpecies + config.NeatConfig.Threads - 1, 1 do
@ -709,18 +730,16 @@ local function mainLoop(currentSpecies)
end
end
local finished = 0
runner.run(
return runner.run(
slice,
pool.generation,
function()
-- Genome callback
end,
function()
if hasThreads then
finished = finished + 1
if finished ~= #slice then
return
-- FIXME Should we do something here??? What was your plan, past me?
end
):next(function()
if hasThreads then
currentSpecies = currentSpecies + #slice
else
currentSpecies = currentSpecies + 1
@ -730,9 +749,12 @@ local function mainLoop(currentSpecies)
newGeneration()
currentSpecies = 1
end
mainLoop(currentSpecies)
end)
end):next(function ()
if topGenome == nil then
return mainLoop(currentSpecies)
end
)
end)
end
playTop = function()
@ -749,7 +771,7 @@ playTop = function()
end
-- FIXME genome
mainLoop(maxs)
return mainLoop(maxs, maxg)
end
function _M.requestLoad(filename)
@ -775,15 +797,19 @@ function _M.requestTop()
end
function _M.run(reset)
local promise = nil
if pool == nil or reset == true then
initializePool(function()
writeFile(config.PoolDir.."temp.pool")
mainLoop()
end)
promise = initializePool()
else
writeFile(config.PoolDir.."temp.pool")
mainLoop()
promise = Promise.new()
promise:resolve()
end
return promise:next(function()
return writeFile(config.PoolDir.."temp.pool")
end):next(function ()
return mainLoop()
end)
end
return _M

View file

@ -78,7 +78,6 @@ transition = function(promise, state, value)
if promise.state == state
or promise.state ~= State.PENDING
or ( state ~= State.FULFILLED and state ~= State.REJECTED )
or value == nil
then
return
end

View file

@ -1,18 +1,22 @@
local gui, utime = gui, utime
local gui, utime, callback, set_timer_timeout = gui, utime, callback, set_timer_timeout
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local Promise = dofile(base.."/promise.lua")
-- Only the parent should manage ticks!
callback.register('timer', function()
Promise.update()
set_timer_timeout(1)
end)
set_timer_timeout(1)
local Runner = dofile(base.."/runner.lua")
local serpent = dofile(base.."/serpent.lua")
local util = dofile(base.."/util.lua")
local util = dofile(base.."/util.lua")(Promise)
local inputFilePath = os.getenv("RUNNER_INPUT_FILE")
local outputFilePath = os.getenv("RUNNER_OUTPUT_FILE")
local first = false
local outContents = {}
local statusLine = nil
@ -22,7 +26,7 @@ local species = nil
local speciesId = -1
local generationIndex = nil
local runner = Runner()
local runner = Runner(Promise)
runner.onMessage(function(msg, color)
statusLine = msg
statusColor = color
@ -78,32 +82,7 @@ runner.onLoad(function(filename)
)
end)
local waiter = nil
if not util.isWin then
waiter = util.startWaiting(inputFilePath)
end
local function waitLoop()
if not first then
local sec, usec = utime()
local ts = sec * 1000000 + usec
local outFile = io.open(outputFilePath, "w")
outFile:write(serpent.dump({ type = 'onInit', ts = ts }))
outFile:close()
print(string.format('Wrote init to output at %d', ts))
first = true
end
print('Waiting for input from master process')
if not util.isWin then
util.finishWaiting(waiter)
end
local inputData = nil
local ok = false
while not ok or inputData == nil or speciesId == inputData[1].id do
@ -128,7 +107,7 @@ local function waitLoop()
print('Running')
runner.run(
return runner.run(
species,
generationIndex,
function(genome, index)
@ -141,8 +120,8 @@ local function waitLoop()
speciesId = speciesId,
})
)
end,
function()
end
):next(function()
table.insert(
outContents,
serpent.dump({
@ -156,8 +135,12 @@ local function waitLoop()
local inputFile = io.open(inputFilePath, "w")
inputFile:close()
if not util.isWin then
waiter = util.startWaiting(inputFilePath)
local waiter = nil
if util.isWin then
waiter = Promise.new()
waiter:resolve()
else
waiter = util.waitForFiles(inputFilePath)
end
-- Write the result
@ -165,9 +148,27 @@ local function waitLoop()
outFile:write(table.concat(outContents, "\n"))
outFile:close()
waitLoop()
end
)
return waiter
end):next(waitLoop)
end
waitLoop()
local waiter = nil
if util.isWin then
waiter = Promise.new()
waiter:resolve()
else
waiter = util.waitForFiles(inputFilePath)
end
local sec, usec = utime()
local ts = sec * 1000000 + usec
local outFile = io.open(outputFilePath, "w")
outFile:write(serpent.dump({ type = 'onInit', ts = ts }))
outFile:close()
print(string.format('Wrote init to output at %d', ts))
waiter:next(waitLoop):catch(function(error)
print('ERROR: '..error)
end)

View file

@ -2,7 +2,9 @@ local random = random
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local util = dofile(base.."/util.lua")
local Promise = nil
local util = nil
local config = dofile(base.."/config.lua")
local serpent = dofile(base.."/serpent.lua")
local temps = {
@ -25,6 +27,9 @@ local tmpFileName = tempDir.."/donk_runner_"..
string.hex(math.floor(random.integer(0, 0xffffffff)))..
string.hex(math.floor(random.integer(0, 0xffffffff)))
local inputPrefix = tmpFileName..'_input_'
local outputPrefix = tmpFileName..'_output_'
local function message(_M, msg, color)
if color == nil then
color = 0x00009900
@ -59,7 +64,46 @@ local function onMessage(_M, handler)
table.insert(_M.onMessageHandler, handler)
end
return function()
--- Launches the child processes
---@param _M table The instance
---@param count integer Number of processes needed
---@return Promise Promise A promise that resolves when all the processes are ready
local function launchChildren(_M, count)
local children = {}
while #_M.poppets < count do
local i = #_M.poppets+1
local outputFileName = outputPrefix..i
local inputFileName = inputPrefix..i
local settingsDir = nil
if util.isWin then
settingsDir = tempDir.."/donk_runner_settings_"..i
util.mkdir(settingsDir)
end
local envs = {
RUNNER_INPUT_FILE = inputFileName,
RUNNER_OUTPUT_FILE = outputFileName,
APPDATA = settingsDir,
}
local child = util.waitForFiles(outputFileName)
local cmd = '"'.._M.hostProcess..'" "--rom='..config.ROM..'" --unpause "--lua='..base..'/runner-process.lua"'
local poppet = util.popenCmd(cmd, nil, envs)
table.insert(_M.poppets, poppet)
table.insert(children, child)
end
return Promise.all(table.unpack(children))
end
return function(promise)
-- FIXME Should this be a global???
Promise = promise
util = dofile(base.."/util.lua")(Promise)
-- FIXME Maybe don't do this in the "constructor"?
if util.isWin then
util.downloadFile('https://github.com/watchexec/watchexec/releases/download/1.13.1/watchexec-1.13.1-x86_64-pc-windows-gnu.zip', base..'/watchexec.zip')
util.unzip(base..'/watchexec.zip', base)
@ -102,12 +146,12 @@ return function()
onLoad(_M, handler)
end
_M.run = function(species, generationIdx, genomeCallback, finishCallback)
local inputPrefix = tmpFileName..'_input_'
local outputPrefix = tmpFileName..'_output_'
_M.run = function(speciesSlice, generationIdx, genomeCallback)
local promise = Promise.new()
promise:resolve()
return promise:next(function()
-- Create the input files and output files
for i=1,#species,1 do
for i=1,#speciesSlice,1 do
local inputFileName = inputPrefix..i
local inputFile = io.open(inputFileName, 'a')
inputFile:close()
@ -117,58 +161,33 @@ return function()
outputFile:close()
end
while #_M.poppets < #species do
local i = #_M.poppets+1
local outputFileName = outputPrefix..i
local inputFileName = inputPrefix..i
message(_M, _M.hostProcess)
local settingsDir = nil
if isWin then
settingsDir = tempDir.."/donk_runner_settings_"..i
util.mkdir(settingsDir)
return launchChildren(_M, #speciesSlice)
end):next(function()
local outputFileNames = {}
for i=1,#speciesSlice,1 do
table.insert(outputFileNames, outputPrefix..i)
end
local envs = {
RUNNER_INPUT_FILE = inputFileName,
RUNNER_OUTPUT_FILE = outputFileName,
APPDATA = settingsDir,
}
local waiter = util.startWaiting(outputFileName)
local cmd = '"'.._M.hostProcess..'" "--rom='..config.ROM..'" --unpause "--lua='..base..'/runner-process.lua"'
local poppet = util.popenCmd(cmd, nil, envs)
table.insert(_M.poppets, poppet)
util.finishWaiting(waiter)
end
local waiters = {}
for i=1,#species,1 do
table.insert(waiters, outputPrefix..i)
end
local waiter = util.startWaiting(waiters, nil, tmpFileName.."_output_*")
local waiter = util.waitForFiles(outputFileNames, nil, tmpFileName.."_output_*")
message(_M, 'Setting up child processes')
for i=1,#species,1 do
for i=1,#speciesSlice,1 do
local inputFileName = tmpFileName.."_input_"..i
local inputFile = io.open(inputFileName, 'w')
inputFile:write(serpent.dump({species[i], generationIdx}))
inputFile:write(serpent.dump({speciesSlice[i], generationIdx}))
inputFile:close()
end
message(_M, 'Waiting for child processes to finish')
util.finishWaiting(waiter, #waiters)
return waiter
end):next(function()
message(_M, 'Child processes finished')
for i=1,#species,1 do
local finished = 0
for i=1,#speciesSlice,1 do
message(_M, "Processing output "..i)
local outputFileName = tmpFileName..'_output_'..i
local outputFile = io.open(outputFileName, "r")
@ -190,8 +209,8 @@ return function()
elseif obj.type == 'onSave' then
save(_M, obj.filename)
elseif obj.type == 'onGenome' then
for i=1,#species,1 do
local s = species[i]
for i=1,#speciesSlice,1 do
local s = speciesSlice[i]
if s.id == obj.speciesId then
s.genomes[obj.genomeIndex] = obj.genome
break
@ -199,14 +218,19 @@ return function()
end
genomeCallback(obj.genome, obj.index)
elseif obj.type == 'onFinish' then
finishCallback()
finished = finished + 1
if finished == #speciesSlice then
outputFile:close()
return
end
end
::continue::
line = outputFile:read()
until(line == "" or line == nil)
outputFile:close()
end
error(string.format("Some processes never finished? Saw %d terminations.", finished))
end)
end
return _M

View file

@ -12,6 +12,8 @@ local Outputs = #config.ButtonNames
local guiWidth = 0
local guiHeight = 0
local Promise = nil
local function message(_M, msg, color)
if color == nil then
color = 0x00009900
@ -213,19 +215,21 @@ local function displayGenome(genome)
gui.renderctx.setnull()
end
local function advanceFrame(_M, after)
table.insert(_M.onFrameAdvancedHandler, after)
local function advanceFrame(_M)
local promise = Promise.new()
table.insert(_M.onFrameAdvancedHandler, promise)
return promise
end
local function processFrameAdvanced(_M)
for i=#_M.onFrameAdvancedHandler,1,-1 do
table.remove(_M.onFrameAdvancedHandler, i)()
table.remove(_M.onFrameAdvancedHandler, i):resolve()
end
end
local buttons = nil
local buttonCtx = gui.renderctx.new(500, 70)
function displayButtons()
local function displayButtons(_M)
buttonCtx:set()
buttonCtx:clear()
@ -249,7 +253,7 @@ function displayButtons()
local insert = ""
local confirm = "[Tab] Type in filename"
if inputmode then
if _M.inputmode then
insert = "_"
confirm = "[Tab] Confirm filename"
end
@ -264,7 +268,7 @@ end
local formCtx = nil
local form = nil
function displayForm(_M)
local function displayForm(_M)
if #_M.onRenderFormHandler == 0 then
return
end
@ -303,7 +307,7 @@ function displayForm(_M)
gui.text(320, 65, string.format("Current Area: %04x", _M.currentArea))
gui.text(320, 80, "Rightmost: "..rightmost)
displayButtons()
displayButtons(_M)
formCtx:set()
buttons:draw(5, 130)
@ -373,11 +377,12 @@ local function evaluateNetwork(_M, network, inputs, inputDeltas)
return outputs
end
local controller = nil
local function evaluateCurrent(_M)
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
local inputDeltas = {}
inputs, inputDeltas = game.getInputs()
local inputs, inputDeltas = game.getInputs()
controller = evaluateNetwork(_M, genome.network, inputs, inputDeltas)
@ -405,9 +410,70 @@ local function fitnessAlreadyMeasured(_M)
return genome.fitness ~= 0
end
local rewinds = {}
local rew = movie.to_rewind(config.NeatConfig.Filename)
local function rewind()
local promise = Promise.new()
movie.unsafe_rewind(rew)
table.insert(rewinds, promise)
return promise
end
local function initializeRun(_M, after)
local function rewound()
for i=#rewinds,1,-1 do
local promise = table.remove(rewinds, i)
promise:resolve()
end
end
local function newNeuron()
local neuron = {}
neuron.incoming = {}
neuron.value = 0.0
--neuron.dw = 1
return neuron
end
local function generateNetwork(genome)
local network = {}
network.neurons = {}
for i=1,Inputs do
network.neurons[i] = newNeuron()
end
for o=1,Outputs do
if o == 4 then
goto continue
end
network.neurons[config.NeatConfig.MaxNodes+o] = newNeuron()
::continue::
end
table.sort(genome.genes, function (a,b)
return (a.out < b.out)
end)
for i=1,#genome.genes do
local gene = genome.genes[i]
if gene.enabled then
if network.neurons[gene.out] == nil then
network.neurons[gene.out] = newNeuron()
end
local neuron = network.neurons[gene.out]
table.insert(neuron.incoming, gene)
if network.neurons[gene.into] == nil then
network.neurons[gene.into] = newNeuron()
end
end
end
genome.network = network
end
local function initializeRun(_M)
message(_M, string.format("Total Genomes: %d", #_M.currentSpecies.genomes))
settings.set_speed("turbo")
@ -419,12 +485,46 @@ local function initializeRun(_M, after)
end
exec('enable-sound '..enableSound)
gui.subframe_update(false)
table.insert(_M.runInitialized, after)
movie.unsafe_rewind(rew)
return rewind():next(function()
if config.StartPowerup ~= nil then
game.writePowerup(config.StartPowerup)
end
_M.currentFrame = 0
_M.timeout = config.NeatConfig.TimeoutConstant
-- Kill the run if we go back to the map screen
game.onceMapLoaded(function()
_M.timeout = -100000
end)
_M.bumps = 0
-- Penalize player for collisions that do not result in enemy deaths
game.onEmptyHit(function()
_M.bumps = _M.bumps + 1
end)
game.clearJoypad()
_M.startKong = game.getKong()
_M.startBananas = game.getBananas()
_M.startKrem = game.getKremCoins()
_M.lastKrem = _M.startKrem
_M.startCoins = game.getCoins()
_M.startLives = game.getLives()
_M.partyHitCounter = 0
_M.powerUpCounter = 0
_M.powerUpBefore = game.getBoth()
_M.currentArea = game.getCurrentArea()
_M.lastArea = _M.currentArea
_M.rightmost = { [_M.currentArea] = 0 }
_M.upmost = { [_M.currentArea] = 0 }
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
generateNetwork(genome)
evaluateCurrent(_M)
end)
end
local frame = 0
local function mainLoop(_M, genome)
advanceFrame(_M, function()
return advanceFrame(_M):next(function()
if genome ~= nil then
_M.currentFrame = _M.currentFrame + 1
end
@ -432,11 +532,7 @@ local function mainLoop(_M, genome)
genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
if _M.drawFrame % 10 == 0 then
--if not pcall(function()
displayGenome(genome)
--end) then
--message(_M, "Could not render genome graph", 0x00990000)
--end
end
if _M.currentFrame%5 == 0 then
@ -529,8 +625,7 @@ local function mainLoop(_M, genome)
-- Continue if we haven't timed out
local timeoutBonus = _M.currentFrame / 4
if _M.timeout + timeoutBonus > 0 then
mainLoop(_M, genome)
return
return mainLoop(_M, genome)
end
-- Timeout calculations beyond this point
@ -609,103 +704,16 @@ local function mainLoop(_M, genome)
input.keyhook("9", false)
input.keyhook("tab", false)
_M.finishCallback()
return
end
end
initializeRun(_M, function()
mainLoop(_M, genome)
return initializeRun(_M):next(function()
return mainLoop(_M, genome)
end)
end)
end
local function newNeuron()
local neuron = {}
neuron.incoming = {}
neuron.value = 0.0
--neuron.dw = 1
return neuron
end
local function generateNetwork(genome)
local network = {}
network.neurons = {}
for i=1,Inputs do
network.neurons[i] = newNeuron()
end
for o=1,Outputs do
if o == 4 then
goto continue
end
network.neurons[config.NeatConfig.MaxNodes+o] = newNeuron()
::continue::
end
table.sort(genome.genes, function (a,b)
return (a.out < b.out)
end)
for i=1,#genome.genes do
local gene = genome.genes[i]
if gene.enabled then
if network.neurons[gene.out] == nil then
network.neurons[gene.out] = newNeuron()
end
local neuron = network.neurons[gene.out]
table.insert(neuron.incoming, gene)
if network.neurons[gene.into] == nil then
network.neurons[gene.into] = newNeuron()
end
end
end
genome.network = network
end
local function elapsed(_M)
if config.StartPowerup ~= nil then
game.writePowerup(config.StartPowerup)
end
_M.currentFrame = 0
_M.timeout = config.NeatConfig.TimeoutConstant
-- Kill the run if we go back to the map screen
game.onceMapLoaded(function()
_M.timeout = -100000
end)
_M.bumps = 0
-- Penalize player for collisions that do not result in enemy deaths
game.onEmptyHit(function()
_M.bumps = _M.bumps + 1
end)
game.clearJoypad()
_M.startKong = game.getKong()
_M.startBananas = game.getBananas()
_M.startKrem = game.getKremCoins()
_M.lastKrem = _M.startKrem
_M.startCoins = game.getCoins()
_M.startLives = game.getLives()
_M.partyHitCounter = 0
_M.powerUpCounter = 0
_M.powerUpBefore = game.getBoth()
_M.currentArea = game.getCurrentArea()
_M.lastArea = _M.currentArea
_M.rightmost = { [_M.currentArea] = 0 }
_M.upmost = { [_M.currentArea] = 0 }
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
generateNetwork(genome)
evaluateCurrent(_M)
for i=#_M.runInitialized,1,-1 do
table.remove(_M.runInitialized, i)()
end
end
local function rewound()
set_timer_timeout(1)
end
local function register(_M, name, func)
callback.register(name, func)
_M.dereg[#_M.dereg+1] = { name, func }
@ -740,7 +748,7 @@ local function keyhook (_M, key, state)
if key == "tab" then
_M.inputmode = not _M.inputmode
_M.helddown = key
elseif inputmode then
elseif _M.inputmode then
return
elseif key == "1" then
_M.helddown = key
@ -761,13 +769,13 @@ local function keyhook (_M, key, state)
pool.run(true)
end
elseif state.value == 0 then
helddown = nil
_M.helddown = nil
end
end
local function saveLoadInput(_M)
local inputs = input.raw()
if not inputmode then
if not _M.inputmode then
-- FIXME
_M.saveLoadFile = config.NeatConfig.SaveFile
return
@ -829,30 +837,25 @@ local function saveLoadInput(_M)
end
end
local function run(_M, species, generationIdx, genomeCallback, finishCallback)
local function run(_M, species, generationIdx, genomeCallback)
game.registerHandlers()
_M.currentGenerationIndex = generationIdx
_M.currentSpecies = species
_M.currentGenomeIndex = 1
_M.genomeCallback = genomeCallback
_M.finishCallback = finishCallback
register(_M, 'paint', function()
painting(_M)
end)
register(_M, 'input', function()
frame = frame + 1
processFrameAdvanced(_M)
end)
register(_M, 'input', function()
saveLoadInput(_M)
end)
register(_M, 'keyhook', function(key, state)
keyhook(_M, key, state)
end)
register(_M, 'post_rewind', rewound)
register(_M, 'timer', function()
elapsed(_M)
end)
input.keyhook("1", true)
input.keyhook("4", true)
@ -861,8 +864,8 @@ local function run(_M, species, generationIdx, genomeCallback, finishCallback)
input.keyhook("9", true)
input.keyhook("tab", true)
initializeRun(_M, function()
mainLoop(_M)
return initializeRun(_M):next(function()
return mainLoop(_M)
end)
end
@ -874,11 +877,11 @@ local function onRenderForm(_M, handler)
table.insert(_M.onRenderFormHandler, handler)
end
return function()
return function(promise)
Promise = promise
local _M = {
currentGenerationIndex = 1,
currentSpecies = nil,
finishCallback = nil,
genomeCallback = nil,
currentGenomeIndex = 1,
currentFrame = 0,
@ -907,8 +910,6 @@ return function()
upmost = {},
lastBoth = 0,
runInitialized = {},
onMessageHandler = {},
onSaveHandler = {},
onLoadHandler = {},
@ -933,8 +934,8 @@ return function()
onLoad(_M, handler)
end
_M.run = function(species, generationIdx, genomeCallback, finishCallback)
run(_M, species, generationIdx, genomeCallback, finishCallback)
_M.run = function(species, generationIdx, genomeCallback)
return run(_M, species, generationIdx, genomeCallback)
end
return _M

View file

@ -1,6 +1,6 @@
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1").."/.."
local util = dofile(base.."/util.lua")
local util = dofile(base.."/util.lua")()
local config = dofile(base.."/config.lua")
local mem = dofile(base.."/mem.lua")

View file

@ -6,7 +6,7 @@ local warn = '========== The ROM must be running before running this script'
io.stderr:write(warn)
print(warn)
local util = dofile(base.."/util.lua")
local util = dofile(base.."/util.lua")()
local mem = dofile(base.."/mem.lua")
local spritelist = dofile(base.."/spritelist.lua")
local game = dofile(base.."/game.lua")
@ -436,3 +436,17 @@ input.keyhook("9", true)
input.keyhook("0", true)
set_timer_timeout(100 * 1000)
for i=0,22,1 do
memory2.BUS:registerwrite(mem.addr.spriteBase + mem.size.sprite * i + mem.offset.sprite.x, function(addr, val)
print(memory.getregister('pc'))
end)
end
-- fe0a58 crate: near bunch and klomp on barrels
-- fe0a58: Crate X position
-- fe0a60: Crate Y position
-- fe0a70 bunch: near crate and klomp on barrels
-- fe0a70: X position
-- fe0a72: Y position

View file

@ -2,6 +2,8 @@ local utime = utime
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
local Promise = nil
local _M = {}
_M.isWin = package.config:sub(1, 1) == '\\'
@ -102,27 +104,10 @@ function _M.closeCmd(handle)
end
end
function _M.finishWaiting(waiter, count)
if count == nil then
count = 1
end
function _M.waitForFiles(filenames, count, wild)
local promise = Promise.new()
promise:resolve()
if _M.isWin then
local i = 1
while i <= count do
local line = waiter:read("*l")
for chr in line:gmatch(";") do
i = i + 1
end
i = i + 1
end
else
waiter:read("*a")
_M.closeCmd(waiter)
end
end
function _M.startWaiting(filenames, count, wild)
if type(filenames) == 'string' then
if wild == nil then
wild = filenames
@ -135,16 +120,15 @@ function _M.startWaiting(filenames, count, wild)
count = #filenames
end
local poppet = nil
if _M.isWin then
local sec, usec = utime()
print(string.format('Starting watching file at %d', sec * 1000000 + usec))
local cmd = '"'..base..'/watchexec/watchexec.exe" "-w" "'..table.concat(filenames, '" "-w" "')..'" "echo" "%WATCHEXEC_WRITTEN_PATH%"'
local poppet = _M.popenCmd(cmd, base)
poppet = _M.popenCmd(cmd, base)
poppet:read("*l")
return poppet
else
local watchCmd = ''
if count == 1 then
@ -167,8 +151,24 @@ done ) &
wait
EOF]]
end
return _M.popenCmd(watchCmd)
poppet = _M.popenCmd(watchCmd)
end
return promise:next(function()
if _M.isWin then
local i = 1
while i <= count do
local line = poppet:read("*l")
for chr in line:gmatch(";") do
i = i + 1
end
i = i + 1
end
else
print(poppet:read("*a"))
_M.closeCmd(poppet)
end
end)
end
function _M.table_to_string(tbl)
@ -214,4 +214,7 @@ function _M.file_exists(name)
if f~=nil then io.close(f) return true else return false end
end
return function(promise)
Promise = promise
return _M
end