Promisify everything?
This commit is contained in:
parent
088c92113b
commit
c7819c98f2
10 changed files with 487 additions and 414 deletions
2
game.lua
2
game.lua
|
@ -6,7 +6,7 @@ local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
local mathFunctions = dofile(base.."/mathFunctions.lua")
|
local mathFunctions = dofile(base.."/mathFunctions.lua")
|
||||||
local config = dofile(base.."/config.lua")
|
local config = dofile(base.."/config.lua")
|
||||||
local spritelist = dofile(base.."/spritelist.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 mem = dofile(base.."/mem.lua")
|
||||||
local _M = {
|
local _M = {
|
||||||
leader = 0,
|
leader = 0,
|
||||||
|
|
|
@ -4,7 +4,6 @@ local gui = gui
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
|
|
||||||
local pool = dofile(base.."/pool.lua")
|
local pool = dofile(base.."/pool.lua")
|
||||||
local util = dofile(base.."/util.lua")
|
|
||||||
|
|
||||||
local statusLine = nil
|
local statusLine = nil
|
||||||
local statusColor = 0x0000ff00
|
local statusColor = 0x0000ff00
|
||||||
|
@ -30,4 +29,10 @@ pool.onRenderForm(function(form)
|
||||||
gui.text(-500, guiHeight - 20, statusLine, 0x00000000)
|
gui.text(-500, guiHeight - 20, statusLine, 0x00000000)
|
||||||
end
|
end
|
||||||
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
114
pool.lua
|
@ -1,7 +1,17 @@
|
||||||
|
local callback, set_timer_timeout = callback, set_timer_timeout
|
||||||
|
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
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 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 serpent = dofile(base.."/serpent.lua")
|
||||||
local libDeflate = dofile(base.."/LibDeflate.lua")
|
local libDeflate = dofile(base.."/LibDeflate.lua")
|
||||||
|
|
||||||
|
@ -386,15 +396,17 @@ local function addToSpecies(child)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function initializePool(after)
|
local function initializePool()
|
||||||
|
local promise = Promise.new()
|
||||||
|
promise:resolve()
|
||||||
|
return promise:next(function()
|
||||||
pool = newPool()
|
pool = newPool()
|
||||||
|
|
||||||
for i=1,config.NeatConfig.Population do
|
for i=1,config.NeatConfig.Population do
|
||||||
basic = basicGenome()
|
local basic = basicGenome()
|
||||||
addToSpecies(basic)
|
addToSpecies(basic)
|
||||||
end
|
end
|
||||||
|
end)
|
||||||
after()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bytes(x)
|
local function bytes(x)
|
||||||
|
@ -406,6 +418,9 @@ local function bytes(x)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function writeFile(filename)
|
local function writeFile(filename)
|
||||||
|
local promise = Promise.new()
|
||||||
|
promise:resolve()
|
||||||
|
return promise:next(function ()
|
||||||
local file = io.open(filename, "w")
|
local file = io.open(filename, "w")
|
||||||
local dump = serpent.dump(pool)
|
local dump = serpent.dump(pool)
|
||||||
local zlib = libDeflate:CompressDeflate(dump)
|
local zlib = libDeflate:CompressDeflate(dump)
|
||||||
|
@ -414,11 +429,14 @@ local function writeFile(filename)
|
||||||
file:write(string.char(0,0,0,0))
|
file:write(string.char(0,0,0,0))
|
||||||
file:write(bytes(#dump % (2^32)))
|
file:write(bytes(#dump % (2^32)))
|
||||||
file:close()
|
file:close()
|
||||||
return
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- FIXME Save/load mechanism has to be rethought with items running in parallel
|
-- FIXME This isn't technically asynchronous. Probably can't be though.
|
||||||
local function loadFile(filename, after)
|
local function loadFile(filename)
|
||||||
|
local promise = Promise.new()
|
||||||
|
promise:resolve()
|
||||||
|
return promise:next(function()
|
||||||
message("Loading pool from " .. filename, 0x00999900)
|
message("Loading pool from " .. filename, 0x00999900)
|
||||||
local file = io.open(filename, "r")
|
local file = io.open(filename, "r")
|
||||||
if file == nil then
|
if file == nil then
|
||||||
|
@ -433,6 +451,7 @@ local function loadFile(filename, after)
|
||||||
end
|
end
|
||||||
|
|
||||||
pool = obj
|
pool = obj
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function savePool()
|
local function savePool()
|
||||||
|
@ -441,9 +460,8 @@ local function savePool()
|
||||||
message(string.format("Saved \"%s\"!", filename:sub(#filename - 50)), 0x00009900)
|
message(string.format("Saved \"%s\"!", filename:sub(#filename - 50)), 0x00009900)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function loadPool(after)
|
local function loadPool()
|
||||||
loadFile(_M.saveLoadFile, after)
|
return loadFile(_M.saveLoadFile)
|
||||||
after()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processRenderForm(form)
|
local function processRenderForm(form)
|
||||||
|
@ -471,7 +489,7 @@ end
|
||||||
local function crossover(g1, g2)
|
local function crossover(g1, g2)
|
||||||
-- Make sure g1 is the higher fitness genome
|
-- Make sure g1 is the higher fitness genome
|
||||||
if g2.fitness > g1.fitness then
|
if g2.fitness > g1.fitness then
|
||||||
tempg = g1
|
local tempg = g1
|
||||||
g1 = g2
|
g1 = g2
|
||||||
g2 = tempg
|
g2 = tempg
|
||||||
end
|
end
|
||||||
|
@ -562,11 +580,11 @@ end
|
||||||
local function breedChild(species)
|
local function breedChild(species)
|
||||||
local child = {}
|
local child = {}
|
||||||
if math.random() < config.NeatConfig.CrossoverChance then
|
if math.random() < config.NeatConfig.CrossoverChance then
|
||||||
g1 = species.genomes[math.random(1, #species.genomes)]
|
local g1 = species.genomes[math.random(1, #species.genomes)]
|
||||||
g2 = species.genomes[math.random(1, #species.genomes)]
|
local g2 = species.genomes[math.random(1, #species.genomes)]
|
||||||
child = crossover(g1, g2)
|
child = crossover(g1, g2)
|
||||||
else
|
else
|
||||||
g = species.genomes[math.random(1, #species.genomes)]
|
local g = species.genomes[math.random(1, #species.genomes)]
|
||||||
child = copyGenome(g)
|
child = copyGenome(g)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -605,7 +623,7 @@ local function removeWeakSpecies()
|
||||||
local sum = totalAverageFitness()
|
local sum = totalAverageFitness()
|
||||||
for s = 1,#pool.species do
|
for s = 1,#pool.species do
|
||||||
local species = pool.species[s]
|
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
|
if breed >= 1 then
|
||||||
table.insert(survived, species)
|
table.insert(survived, species)
|
||||||
end
|
end
|
||||||
|
@ -628,7 +646,7 @@ local function newGeneration()
|
||||||
local children = {}
|
local children = {}
|
||||||
for s = 1,#pool.species do
|
for s = 1,#pool.species do
|
||||||
local species = pool.species[s]
|
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
|
for i=1,breed do
|
||||||
table.insert(children, breedChild(species))
|
table.insert(children, breedChild(species))
|
||||||
end
|
end
|
||||||
|
@ -652,7 +670,7 @@ local function newGeneration()
|
||||||
writeFile(_M.saveLoadFile .. ".gen" .. pool.generation .. ".pool")
|
writeFile(_M.saveLoadFile .. ".gen" .. pool.generation .. ".pool")
|
||||||
end
|
end
|
||||||
|
|
||||||
local runner = Runner()
|
local runner = Runner(Promise)
|
||||||
runner.onMessage(function(msg, color)
|
runner.onMessage(function(msg, color)
|
||||||
message(msg, color)
|
message(msg, color)
|
||||||
end)
|
end)
|
||||||
|
@ -671,11 +689,20 @@ local topRequested = false
|
||||||
|
|
||||||
local loadRequested = false
|
local loadRequested = false
|
||||||
local saveRequested = 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
|
if loadRequested then
|
||||||
loadRequested = false
|
loadRequested = false
|
||||||
loadPool(mainLoop)
|
currentSpecies = nil
|
||||||
return
|
-- FIXME
|
||||||
|
return loadPool()
|
||||||
end
|
end
|
||||||
|
|
||||||
if saveRequested then
|
if saveRequested then
|
||||||
|
@ -685,19 +712,13 @@ local function mainLoop(currentSpecies)
|
||||||
|
|
||||||
if topRequested then
|
if topRequested then
|
||||||
topRequested = false
|
topRequested = false
|
||||||
playTop()
|
return playTop()
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not config.Running then
|
if not config.Running then
|
||||||
-- FIXME Tick?
|
-- FIXME Tick?
|
||||||
end
|
end
|
||||||
|
|
||||||
if currentSpecies == nil then
|
|
||||||
currentSpecies = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local slice = pool.species[currentSpecies]
|
|
||||||
if hasThreads then
|
if hasThreads then
|
||||||
slice = {}
|
slice = {}
|
||||||
for i=currentSpecies, currentSpecies + config.NeatConfig.Threads - 1, 1 do
|
for i=currentSpecies, currentSpecies + config.NeatConfig.Threads - 1, 1 do
|
||||||
|
@ -709,18 +730,16 @@ local function mainLoop(currentSpecies)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local finished = 0
|
local finished = 0
|
||||||
runner.run(
|
|
||||||
|
return runner.run(
|
||||||
slice,
|
slice,
|
||||||
pool.generation,
|
pool.generation,
|
||||||
function()
|
function()
|
||||||
-- Genome callback
|
-- Genome callback
|
||||||
end,
|
-- FIXME Should we do something here??? What was your plan, past me?
|
||||||
function()
|
|
||||||
if hasThreads then
|
|
||||||
finished = finished + 1
|
|
||||||
if finished ~= #slice then
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
):next(function()
|
||||||
|
if hasThreads then
|
||||||
currentSpecies = currentSpecies + #slice
|
currentSpecies = currentSpecies + #slice
|
||||||
else
|
else
|
||||||
currentSpecies = currentSpecies + 1
|
currentSpecies = currentSpecies + 1
|
||||||
|
@ -730,9 +749,12 @@ local function mainLoop(currentSpecies)
|
||||||
newGeneration()
|
newGeneration()
|
||||||
currentSpecies = 1
|
currentSpecies = 1
|
||||||
end
|
end
|
||||||
mainLoop(currentSpecies)
|
end)
|
||||||
|
end):next(function ()
|
||||||
|
if topGenome == nil then
|
||||||
|
return mainLoop(currentSpecies)
|
||||||
end
|
end
|
||||||
)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
playTop = function()
|
playTop = function()
|
||||||
|
@ -749,7 +771,7 @@ playTop = function()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- FIXME genome
|
-- FIXME genome
|
||||||
mainLoop(maxs)
|
return mainLoop(maxs, maxg)
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.requestLoad(filename)
|
function _M.requestLoad(filename)
|
||||||
|
@ -775,15 +797,19 @@ function _M.requestTop()
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.run(reset)
|
function _M.run(reset)
|
||||||
|
local promise = nil
|
||||||
if pool == nil or reset == true then
|
if pool == nil or reset == true then
|
||||||
initializePool(function()
|
promise = initializePool()
|
||||||
writeFile(config.PoolDir.."temp.pool")
|
|
||||||
mainLoop()
|
|
||||||
end)
|
|
||||||
else
|
else
|
||||||
writeFile(config.PoolDir.."temp.pool")
|
promise = Promise.new()
|
||||||
mainLoop()
|
promise:resolve()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return promise:next(function()
|
||||||
|
return writeFile(config.PoolDir.."temp.pool")
|
||||||
|
end):next(function ()
|
||||||
|
return mainLoop()
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
|
|
@ -78,7 +78,6 @@ transition = function(promise, state, value)
|
||||||
if promise.state == state
|
if promise.state == state
|
||||||
or promise.state ~= State.PENDING
|
or promise.state ~= State.PENDING
|
||||||
or ( state ~= State.FULFILLED and state ~= State.REJECTED )
|
or ( state ~= State.FULFILLED and state ~= State.REJECTED )
|
||||||
or value == nil
|
|
||||||
then
|
then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -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 base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
|
|
||||||
local Promise = dofile(base.."/promise.lua")
|
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 Runner = dofile(base.."/runner.lua")
|
||||||
local serpent = dofile(base.."/serpent.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 inputFilePath = os.getenv("RUNNER_INPUT_FILE")
|
||||||
local outputFilePath = os.getenv("RUNNER_OUTPUT_FILE")
|
local outputFilePath = os.getenv("RUNNER_OUTPUT_FILE")
|
||||||
|
|
||||||
local first = false
|
|
||||||
|
|
||||||
local outContents = {}
|
local outContents = {}
|
||||||
|
|
||||||
local statusLine = nil
|
local statusLine = nil
|
||||||
|
@ -22,7 +26,7 @@ local species = nil
|
||||||
local speciesId = -1
|
local speciesId = -1
|
||||||
local generationIndex = nil
|
local generationIndex = nil
|
||||||
|
|
||||||
local runner = Runner()
|
local runner = Runner(Promise)
|
||||||
runner.onMessage(function(msg, color)
|
runner.onMessage(function(msg, color)
|
||||||
statusLine = msg
|
statusLine = msg
|
||||||
statusColor = color
|
statusColor = color
|
||||||
|
@ -78,32 +82,7 @@ runner.onLoad(function(filename)
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local waiter = nil
|
|
||||||
if not util.isWin then
|
|
||||||
waiter = util.startWaiting(inputFilePath)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function waitLoop()
|
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 inputData = nil
|
||||||
local ok = false
|
local ok = false
|
||||||
while not ok or inputData == nil or speciesId == inputData[1].id do
|
while not ok or inputData == nil or speciesId == inputData[1].id do
|
||||||
|
@ -128,7 +107,7 @@ local function waitLoop()
|
||||||
|
|
||||||
print('Running')
|
print('Running')
|
||||||
|
|
||||||
runner.run(
|
return runner.run(
|
||||||
species,
|
species,
|
||||||
generationIndex,
|
generationIndex,
|
||||||
function(genome, index)
|
function(genome, index)
|
||||||
|
@ -141,8 +120,8 @@ local function waitLoop()
|
||||||
speciesId = speciesId,
|
speciesId = speciesId,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
end,
|
end
|
||||||
function()
|
):next(function()
|
||||||
table.insert(
|
table.insert(
|
||||||
outContents,
|
outContents,
|
||||||
serpent.dump({
|
serpent.dump({
|
||||||
|
@ -156,8 +135,12 @@ local function waitLoop()
|
||||||
local inputFile = io.open(inputFilePath, "w")
|
local inputFile = io.open(inputFilePath, "w")
|
||||||
inputFile:close()
|
inputFile:close()
|
||||||
|
|
||||||
if not util.isWin then
|
local waiter = nil
|
||||||
waiter = util.startWaiting(inputFilePath)
|
if util.isWin then
|
||||||
|
waiter = Promise.new()
|
||||||
|
waiter:resolve()
|
||||||
|
else
|
||||||
|
waiter = util.waitForFiles(inputFilePath)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Write the result
|
-- Write the result
|
||||||
|
@ -165,9 +148,27 @@ local function waitLoop()
|
||||||
outFile:write(table.concat(outContents, "\n"))
|
outFile:write(table.concat(outContents, "\n"))
|
||||||
outFile:close()
|
outFile:close()
|
||||||
|
|
||||||
waitLoop()
|
return waiter
|
||||||
end
|
end):next(waitLoop)
|
||||||
)
|
|
||||||
end
|
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)
|
|
@ -2,7 +2,9 @@ local random = random
|
||||||
|
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
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 config = dofile(base.."/config.lua")
|
||||||
local serpent = dofile(base.."/serpent.lua")
|
local serpent = dofile(base.."/serpent.lua")
|
||||||
local temps = {
|
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)))..
|
||||||
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)
|
local function message(_M, msg, color)
|
||||||
if color == nil then
|
if color == nil then
|
||||||
color = 0x00009900
|
color = 0x00009900
|
||||||
|
@ -59,7 +64,46 @@ local function onMessage(_M, handler)
|
||||||
table.insert(_M.onMessageHandler, handler)
|
table.insert(_M.onMessageHandler, handler)
|
||||||
end
|
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
|
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.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)
|
util.unzip(base..'/watchexec.zip', base)
|
||||||
|
@ -102,12 +146,12 @@ return function()
|
||||||
onLoad(_M, handler)
|
onLoad(_M, handler)
|
||||||
end
|
end
|
||||||
|
|
||||||
_M.run = function(species, generationIdx, genomeCallback, finishCallback)
|
_M.run = function(speciesSlice, generationIdx, genomeCallback)
|
||||||
local inputPrefix = tmpFileName..'_input_'
|
local promise = Promise.new()
|
||||||
local outputPrefix = tmpFileName..'_output_'
|
promise:resolve()
|
||||||
|
return promise:next(function()
|
||||||
-- Create the input files and output files
|
-- Create the input files and output files
|
||||||
for i=1,#species,1 do
|
for i=1,#speciesSlice,1 do
|
||||||
local inputFileName = inputPrefix..i
|
local inputFileName = inputPrefix..i
|
||||||
local inputFile = io.open(inputFileName, 'a')
|
local inputFile = io.open(inputFileName, 'a')
|
||||||
inputFile:close()
|
inputFile:close()
|
||||||
|
@ -117,58 +161,33 @@ return function()
|
||||||
outputFile:close()
|
outputFile:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
while #_M.poppets < #species do
|
return launchChildren(_M, #speciesSlice)
|
||||||
local i = #_M.poppets+1
|
end):next(function()
|
||||||
local outputFileName = outputPrefix..i
|
local outputFileNames = {}
|
||||||
local inputFileName = inputPrefix..i
|
for i=1,#speciesSlice,1 do
|
||||||
|
table.insert(outputFileNames, outputPrefix..i)
|
||||||
message(_M, _M.hostProcess)
|
|
||||||
|
|
||||||
local settingsDir = nil
|
|
||||||
if isWin then
|
|
||||||
settingsDir = tempDir.."/donk_runner_settings_"..i
|
|
||||||
util.mkdir(settingsDir)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local envs = {
|
local waiter = util.waitForFiles(outputFileNames, nil, tmpFileName.."_output_*")
|
||||||
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_*")
|
|
||||||
|
|
||||||
message(_M, 'Setting up child processes')
|
message(_M, 'Setting up child processes')
|
||||||
|
|
||||||
for i=1,#species,1 do
|
for i=1,#speciesSlice,1 do
|
||||||
|
|
||||||
local inputFileName = tmpFileName.."_input_"..i
|
local inputFileName = tmpFileName.."_input_"..i
|
||||||
local inputFile = io.open(inputFileName, 'w')
|
local inputFile = io.open(inputFileName, 'w')
|
||||||
inputFile:write(serpent.dump({species[i], generationIdx}))
|
inputFile:write(serpent.dump({speciesSlice[i], generationIdx}))
|
||||||
inputFile:close()
|
inputFile:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
message(_M, 'Waiting for child processes to finish')
|
message(_M, 'Waiting for child processes to finish')
|
||||||
|
|
||||||
util.finishWaiting(waiter, #waiters)
|
return waiter
|
||||||
|
end):next(function()
|
||||||
message(_M, 'Child processes finished')
|
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)
|
message(_M, "Processing output "..i)
|
||||||
local outputFileName = tmpFileName..'_output_'..i
|
local outputFileName = tmpFileName..'_output_'..i
|
||||||
local outputFile = io.open(outputFileName, "r")
|
local outputFile = io.open(outputFileName, "r")
|
||||||
|
@ -190,8 +209,8 @@ return function()
|
||||||
elseif obj.type == 'onSave' then
|
elseif obj.type == 'onSave' then
|
||||||
save(_M, obj.filename)
|
save(_M, obj.filename)
|
||||||
elseif obj.type == 'onGenome' then
|
elseif obj.type == 'onGenome' then
|
||||||
for i=1,#species,1 do
|
for i=1,#speciesSlice,1 do
|
||||||
local s = species[i]
|
local s = speciesSlice[i]
|
||||||
if s.id == obj.speciesId then
|
if s.id == obj.speciesId then
|
||||||
s.genomes[obj.genomeIndex] = obj.genome
|
s.genomes[obj.genomeIndex] = obj.genome
|
||||||
break
|
break
|
||||||
|
@ -199,14 +218,19 @@ return function()
|
||||||
end
|
end
|
||||||
genomeCallback(obj.genome, obj.index)
|
genomeCallback(obj.genome, obj.index)
|
||||||
elseif obj.type == 'onFinish' then
|
elseif obj.type == 'onFinish' then
|
||||||
finishCallback()
|
finished = finished + 1
|
||||||
|
if finished == #speciesSlice then
|
||||||
|
outputFile:close()
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
::continue::
|
::continue::
|
||||||
line = outputFile:read()
|
line = outputFile:read()
|
||||||
until(line == "" or line == nil)
|
until(line == "" or line == nil)
|
||||||
outputFile:close()
|
|
||||||
end
|
end
|
||||||
|
error(string.format("Some processes never finished? Saw %d terminations.", finished))
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
|
253
runner.lua
253
runner.lua
|
@ -12,6 +12,8 @@ local Outputs = #config.ButtonNames
|
||||||
local guiWidth = 0
|
local guiWidth = 0
|
||||||
local guiHeight = 0
|
local guiHeight = 0
|
||||||
|
|
||||||
|
local Promise = nil
|
||||||
|
|
||||||
local function message(_M, msg, color)
|
local function message(_M, msg, color)
|
||||||
if color == nil then
|
if color == nil then
|
||||||
color = 0x00009900
|
color = 0x00009900
|
||||||
|
@ -213,19 +215,21 @@ local function displayGenome(genome)
|
||||||
gui.renderctx.setnull()
|
gui.renderctx.setnull()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function advanceFrame(_M, after)
|
local function advanceFrame(_M)
|
||||||
table.insert(_M.onFrameAdvancedHandler, after)
|
local promise = Promise.new()
|
||||||
|
table.insert(_M.onFrameAdvancedHandler, promise)
|
||||||
|
return promise
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processFrameAdvanced(_M)
|
local function processFrameAdvanced(_M)
|
||||||
for i=#_M.onFrameAdvancedHandler,1,-1 do
|
for i=#_M.onFrameAdvancedHandler,1,-1 do
|
||||||
table.remove(_M.onFrameAdvancedHandler, i)()
|
table.remove(_M.onFrameAdvancedHandler, i):resolve()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local buttons = nil
|
local buttons = nil
|
||||||
local buttonCtx = gui.renderctx.new(500, 70)
|
local buttonCtx = gui.renderctx.new(500, 70)
|
||||||
function displayButtons()
|
local function displayButtons(_M)
|
||||||
buttonCtx:set()
|
buttonCtx:set()
|
||||||
buttonCtx:clear()
|
buttonCtx:clear()
|
||||||
|
|
||||||
|
@ -249,7 +253,7 @@ function displayButtons()
|
||||||
|
|
||||||
local insert = ""
|
local insert = ""
|
||||||
local confirm = "[Tab] Type in filename"
|
local confirm = "[Tab] Type in filename"
|
||||||
if inputmode then
|
if _M.inputmode then
|
||||||
insert = "_"
|
insert = "_"
|
||||||
confirm = "[Tab] Confirm filename"
|
confirm = "[Tab] Confirm filename"
|
||||||
end
|
end
|
||||||
|
@ -264,7 +268,7 @@ end
|
||||||
|
|
||||||
local formCtx = nil
|
local formCtx = nil
|
||||||
local form = nil
|
local form = nil
|
||||||
function displayForm(_M)
|
local function displayForm(_M)
|
||||||
if #_M.onRenderFormHandler == 0 then
|
if #_M.onRenderFormHandler == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
@ -303,7 +307,7 @@ function displayForm(_M)
|
||||||
gui.text(320, 65, string.format("Current Area: %04x", _M.currentArea))
|
gui.text(320, 65, string.format("Current Area: %04x", _M.currentArea))
|
||||||
gui.text(320, 80, "Rightmost: "..rightmost)
|
gui.text(320, 80, "Rightmost: "..rightmost)
|
||||||
|
|
||||||
displayButtons()
|
displayButtons(_M)
|
||||||
formCtx:set()
|
formCtx:set()
|
||||||
buttons:draw(5, 130)
|
buttons:draw(5, 130)
|
||||||
|
|
||||||
|
@ -373,11 +377,12 @@ local function evaluateNetwork(_M, network, inputs, inputDeltas)
|
||||||
return outputs
|
return outputs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local controller = nil
|
||||||
local function evaluateCurrent(_M)
|
local function evaluateCurrent(_M)
|
||||||
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
|
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
|
||||||
|
|
||||||
local inputDeltas = {}
|
local inputDeltas = {}
|
||||||
inputs, inputDeltas = game.getInputs()
|
local inputs, inputDeltas = game.getInputs()
|
||||||
|
|
||||||
controller = evaluateNetwork(_M, genome.network, inputs, inputDeltas)
|
controller = evaluateNetwork(_M, genome.network, inputs, inputDeltas)
|
||||||
|
|
||||||
|
@ -405,9 +410,70 @@ local function fitnessAlreadyMeasured(_M)
|
||||||
return genome.fitness ~= 0
|
return genome.fitness ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local rewinds = {}
|
||||||
local rew = movie.to_rewind(config.NeatConfig.Filename)
|
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))
|
message(_M, string.format("Total Genomes: %d", #_M.currentSpecies.genomes))
|
||||||
|
|
||||||
settings.set_speed("turbo")
|
settings.set_speed("turbo")
|
||||||
|
@ -419,12 +485,46 @@ local function initializeRun(_M, after)
|
||||||
end
|
end
|
||||||
exec('enable-sound '..enableSound)
|
exec('enable-sound '..enableSound)
|
||||||
gui.subframe_update(false)
|
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
|
end
|
||||||
|
|
||||||
|
local frame = 0
|
||||||
|
|
||||||
local function mainLoop(_M, genome)
|
local function mainLoop(_M, genome)
|
||||||
advanceFrame(_M, function()
|
return advanceFrame(_M):next(function()
|
||||||
if genome ~= nil then
|
if genome ~= nil then
|
||||||
_M.currentFrame = _M.currentFrame + 1
|
_M.currentFrame = _M.currentFrame + 1
|
||||||
end
|
end
|
||||||
|
@ -432,11 +532,7 @@ local function mainLoop(_M, genome)
|
||||||
genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
|
genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
|
||||||
|
|
||||||
if _M.drawFrame % 10 == 0 then
|
if _M.drawFrame % 10 == 0 then
|
||||||
--if not pcall(function()
|
|
||||||
displayGenome(genome)
|
displayGenome(genome)
|
||||||
--end) then
|
|
||||||
--message(_M, "Could not render genome graph", 0x00990000)
|
|
||||||
--end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if _M.currentFrame%5 == 0 then
|
if _M.currentFrame%5 == 0 then
|
||||||
|
@ -529,8 +625,7 @@ local function mainLoop(_M, genome)
|
||||||
-- Continue if we haven't timed out
|
-- Continue if we haven't timed out
|
||||||
local timeoutBonus = _M.currentFrame / 4
|
local timeoutBonus = _M.currentFrame / 4
|
||||||
if _M.timeout + timeoutBonus > 0 then
|
if _M.timeout + timeoutBonus > 0 then
|
||||||
mainLoop(_M, genome)
|
return mainLoop(_M, genome)
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Timeout calculations beyond this point
|
-- Timeout calculations beyond this point
|
||||||
|
@ -609,103 +704,16 @@ local function mainLoop(_M, genome)
|
||||||
input.keyhook("9", false)
|
input.keyhook("9", false)
|
||||||
input.keyhook("tab", false)
|
input.keyhook("tab", false)
|
||||||
|
|
||||||
_M.finishCallback()
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
initializeRun(_M, function()
|
|
||||||
mainLoop(_M, genome)
|
return initializeRun(_M):next(function()
|
||||||
|
return mainLoop(_M, genome)
|
||||||
end)
|
end)
|
||||||
end)
|
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)
|
local function register(_M, name, func)
|
||||||
callback.register(name, func)
|
callback.register(name, func)
|
||||||
_M.dereg[#_M.dereg+1] = { name, func }
|
_M.dereg[#_M.dereg+1] = { name, func }
|
||||||
|
@ -740,7 +748,7 @@ local function keyhook (_M, key, state)
|
||||||
if key == "tab" then
|
if key == "tab" then
|
||||||
_M.inputmode = not _M.inputmode
|
_M.inputmode = not _M.inputmode
|
||||||
_M.helddown = key
|
_M.helddown = key
|
||||||
elseif inputmode then
|
elseif _M.inputmode then
|
||||||
return
|
return
|
||||||
elseif key == "1" then
|
elseif key == "1" then
|
||||||
_M.helddown = key
|
_M.helddown = key
|
||||||
|
@ -761,13 +769,13 @@ local function keyhook (_M, key, state)
|
||||||
pool.run(true)
|
pool.run(true)
|
||||||
end
|
end
|
||||||
elseif state.value == 0 then
|
elseif state.value == 0 then
|
||||||
helddown = nil
|
_M.helddown = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function saveLoadInput(_M)
|
local function saveLoadInput(_M)
|
||||||
local inputs = input.raw()
|
local inputs = input.raw()
|
||||||
if not inputmode then
|
if not _M.inputmode then
|
||||||
-- FIXME
|
-- FIXME
|
||||||
_M.saveLoadFile = config.NeatConfig.SaveFile
|
_M.saveLoadFile = config.NeatConfig.SaveFile
|
||||||
return
|
return
|
||||||
|
@ -829,30 +837,25 @@ local function saveLoadInput(_M)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function run(_M, species, generationIdx, genomeCallback, finishCallback)
|
local function run(_M, species, generationIdx, genomeCallback)
|
||||||
game.registerHandlers()
|
game.registerHandlers()
|
||||||
|
|
||||||
_M.currentGenerationIndex = generationIdx
|
_M.currentGenerationIndex = generationIdx
|
||||||
_M.currentSpecies = species
|
_M.currentSpecies = species
|
||||||
_M.currentGenomeIndex = 1
|
_M.currentGenomeIndex = 1
|
||||||
_M.genomeCallback = genomeCallback
|
_M.genomeCallback = genomeCallback
|
||||||
_M.finishCallback = finishCallback
|
|
||||||
register(_M, 'paint', function()
|
register(_M, 'paint', function()
|
||||||
painting(_M)
|
painting(_M)
|
||||||
end)
|
end)
|
||||||
register(_M, 'input', function()
|
register(_M, 'input', function()
|
||||||
|
frame = frame + 1
|
||||||
processFrameAdvanced(_M)
|
processFrameAdvanced(_M)
|
||||||
end)
|
|
||||||
register(_M, 'input', function()
|
|
||||||
saveLoadInput(_M)
|
saveLoadInput(_M)
|
||||||
end)
|
end)
|
||||||
register(_M, 'keyhook', function(key, state)
|
register(_M, 'keyhook', function(key, state)
|
||||||
keyhook(_M, key, state)
|
keyhook(_M, key, state)
|
||||||
end)
|
end)
|
||||||
register(_M, 'post_rewind', rewound)
|
register(_M, 'post_rewind', rewound)
|
||||||
register(_M, 'timer', function()
|
|
||||||
elapsed(_M)
|
|
||||||
end)
|
|
||||||
|
|
||||||
input.keyhook("1", true)
|
input.keyhook("1", true)
|
||||||
input.keyhook("4", true)
|
input.keyhook("4", true)
|
||||||
|
@ -861,8 +864,8 @@ local function run(_M, species, generationIdx, genomeCallback, finishCallback)
|
||||||
input.keyhook("9", true)
|
input.keyhook("9", true)
|
||||||
input.keyhook("tab", true)
|
input.keyhook("tab", true)
|
||||||
|
|
||||||
initializeRun(_M, function()
|
return initializeRun(_M):next(function()
|
||||||
mainLoop(_M)
|
return mainLoop(_M)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -874,11 +877,11 @@ local function onRenderForm(_M, handler)
|
||||||
table.insert(_M.onRenderFormHandler, handler)
|
table.insert(_M.onRenderFormHandler, handler)
|
||||||
end
|
end
|
||||||
|
|
||||||
return function()
|
return function(promise)
|
||||||
|
Promise = promise
|
||||||
local _M = {
|
local _M = {
|
||||||
currentGenerationIndex = 1,
|
currentGenerationIndex = 1,
|
||||||
currentSpecies = nil,
|
currentSpecies = nil,
|
||||||
finishCallback = nil,
|
|
||||||
genomeCallback = nil,
|
genomeCallback = nil,
|
||||||
currentGenomeIndex = 1,
|
currentGenomeIndex = 1,
|
||||||
currentFrame = 0,
|
currentFrame = 0,
|
||||||
|
@ -907,8 +910,6 @@ return function()
|
||||||
upmost = {},
|
upmost = {},
|
||||||
lastBoth = 0,
|
lastBoth = 0,
|
||||||
|
|
||||||
runInitialized = {},
|
|
||||||
|
|
||||||
onMessageHandler = {},
|
onMessageHandler = {},
|
||||||
onSaveHandler = {},
|
onSaveHandler = {},
|
||||||
onLoadHandler = {},
|
onLoadHandler = {},
|
||||||
|
@ -933,8 +934,8 @@ return function()
|
||||||
onLoad(_M, handler)
|
onLoad(_M, handler)
|
||||||
end
|
end
|
||||||
|
|
||||||
_M.run = function(species, generationIdx, genomeCallback, finishCallback)
|
_M.run = function(species, generationIdx, genomeCallback)
|
||||||
run(_M, species, generationIdx, genomeCallback, finishCallback)
|
return run(_M, species, generationIdx, genomeCallback)
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1").."/.."
|
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 config = dofile(base.."/config.lua")
|
||||||
local mem = dofile(base.."/mem.lua")
|
local mem = dofile(base.."/mem.lua")
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ local warn = '========== The ROM must be running before running this script'
|
||||||
io.stderr:write(warn)
|
io.stderr:write(warn)
|
||||||
print(warn)
|
print(warn)
|
||||||
|
|
||||||
local util = dofile(base.."/util.lua")
|
local util = dofile(base.."/util.lua")()
|
||||||
local mem = dofile(base.."/mem.lua")
|
local mem = dofile(base.."/mem.lua")
|
||||||
local spritelist = dofile(base.."/spritelist.lua")
|
local spritelist = dofile(base.."/spritelist.lua")
|
||||||
local game = dofile(base.."/game.lua")
|
local game = dofile(base.."/game.lua")
|
||||||
|
@ -436,3 +436,17 @@ input.keyhook("9", true)
|
||||||
input.keyhook("0", true)
|
input.keyhook("0", true)
|
||||||
|
|
||||||
set_timer_timeout(100 * 1000)
|
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
|
51
util.lua
51
util.lua
|
@ -2,6 +2,8 @@ local utime = utime
|
||||||
|
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
|
|
||||||
|
local Promise = nil
|
||||||
|
|
||||||
local _M = {}
|
local _M = {}
|
||||||
|
|
||||||
_M.isWin = package.config:sub(1, 1) == '\\'
|
_M.isWin = package.config:sub(1, 1) == '\\'
|
||||||
|
@ -102,27 +104,10 @@ function _M.closeCmd(handle)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.finishWaiting(waiter, count)
|
function _M.waitForFiles(filenames, count, wild)
|
||||||
if count == nil then
|
local promise = Promise.new()
|
||||||
count = 1
|
promise:resolve()
|
||||||
end
|
|
||||||
|
|
||||||
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 type(filenames) == 'string' then
|
||||||
if wild == nil then
|
if wild == nil then
|
||||||
wild = filenames
|
wild = filenames
|
||||||
|
@ -135,16 +120,15 @@ function _M.startWaiting(filenames, count, wild)
|
||||||
count = #filenames
|
count = #filenames
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local poppet = nil
|
||||||
if _M.isWin then
|
if _M.isWin then
|
||||||
local sec, usec = utime()
|
local sec, usec = utime()
|
||||||
print(string.format('Starting watching file at %d', sec * 1000000 + usec))
|
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 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")
|
poppet:read("*l")
|
||||||
|
|
||||||
return poppet
|
|
||||||
else
|
else
|
||||||
local watchCmd = ''
|
local watchCmd = ''
|
||||||
if count == 1 then
|
if count == 1 then
|
||||||
|
@ -167,8 +151,24 @@ done ) &
|
||||||
wait
|
wait
|
||||||
EOF]]
|
EOF]]
|
||||||
end
|
end
|
||||||
return _M.popenCmd(watchCmd)
|
poppet = _M.popenCmd(watchCmd)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
function _M.table_to_string(tbl)
|
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
|
if f~=nil then io.close(f) return true else return false end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return function(promise)
|
||||||
|
Promise = promise
|
||||||
return _M
|
return _M
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue