Added promise library, serpent.load, temp logic
This commit is contained in:
parent
36d945cf47
commit
c591641233
7 changed files with 362 additions and 22 deletions
|
@ -35,7 +35,8 @@ _M.Filename = _M.PoolDir .. _M.State[1]
|
|||
_M.StartPowerup = 0
|
||||
|
||||
_M.NeatConfig = {
|
||||
Threads = 8,
|
||||
Threads = 2,
|
||||
ThreadDontQuit = true,
|
||||
--Filename = "DP1.state",
|
||||
SaveFile = _M.Filename .. ".pool",
|
||||
Filename = _M.Filename,
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||
|
||||
local game = dofile(base.."/game.lua")
|
||||
local config = dofile(base.."/config.lua")
|
||||
local pool = dofile(base.."/pool.lua")
|
||||
local util = dofile(base.."/util.lua")
|
||||
|
||||
|
|
8
pool.lua
8
pool.lua
|
@ -414,13 +414,13 @@ local function loadFile(filename, after)
|
|||
return
|
||||
end
|
||||
local contents = file:read("*all")
|
||||
local obj, err = loadstring(libDeflate:DecompressDeflate(contents:sub(11, #contents - 8)))
|
||||
if err ~= nil then
|
||||
message(string.format("Error parsing: %s", err), 0x00990000)
|
||||
local ok, obj = serpent.load(libDeflate:DecompressDeflate(contents:sub(11, #contents - 8)))
|
||||
if not ok then
|
||||
message("Error parsing pool file", 0x00990000)
|
||||
return
|
||||
end
|
||||
|
||||
pool = obj()
|
||||
pool = obj
|
||||
end
|
||||
|
||||
local function savePool()
|
||||
|
|
299
promise.lua
Normal file
299
promise.lua
Normal file
|
@ -0,0 +1,299 @@
|
|||
--[[
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Colin Fein`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
-- Port of https://github.com/rhysbrettbowen/promise_impl/blob/master/promise.js
|
||||
-- and https://github.com/rhysbrettbowen/Aplus
|
||||
--
|
||||
local queue = {}
|
||||
|
||||
local State = {
|
||||
PENDING = 'pending',
|
||||
FULFILLED = 'fulfilled',
|
||||
REJECTED = 'rejected',
|
||||
}
|
||||
|
||||
local passthrough = function(x) return x end
|
||||
local errorthrough = function(x) error(x) end
|
||||
|
||||
local function callable_table(callback)
|
||||
local mt = getmetatable(callback)
|
||||
return type(mt) == 'table' and type(mt.__call) == 'function'
|
||||
end
|
||||
|
||||
local function is_callable(value)
|
||||
local t = type(value)
|
||||
return t == 'function' or (t == 'table' and callable_table(value))
|
||||
end
|
||||
|
||||
local transition, resolve, run
|
||||
|
||||
local Promise = {
|
||||
is_promise = true,
|
||||
state = State.PENDING
|
||||
}
|
||||
Promise.mt = { __index = Promise }
|
||||
|
||||
local do_async = function(callback)
|
||||
if Promise.async then
|
||||
Promise.async(callback)
|
||||
else
|
||||
table.insert(queue, callback)
|
||||
end
|
||||
end
|
||||
|
||||
local reject = function(promise, reason)
|
||||
transition(promise, State.REJECTED, reason)
|
||||
end
|
||||
|
||||
local fulfill = function(promise, value)
|
||||
transition(promise, State.FULFILLED, value)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
promise.state = state
|
||||
promise.value = value
|
||||
run(promise)
|
||||
end
|
||||
|
||||
function Promise:next(on_fulfilled, on_rejected)
|
||||
local promise = Promise.new()
|
||||
|
||||
table.insert(self.queue, {
|
||||
fulfill = is_callable(on_fulfilled) and on_fulfilled or nil,
|
||||
reject = is_callable(on_rejected) and on_rejected or nil,
|
||||
promise = promise
|
||||
})
|
||||
|
||||
run(self)
|
||||
|
||||
return promise
|
||||
end
|
||||
|
||||
resolve = function(promise, x)
|
||||
if promise == x then
|
||||
reject(promise, 'TypeError: cannot resolve a promise with itself')
|
||||
return
|
||||
end
|
||||
|
||||
local x_type = type(x)
|
||||
|
||||
if x_type ~= 'table' then
|
||||
fulfill(promise, x)
|
||||
return
|
||||
end
|
||||
|
||||
-- x is a promise in the current implementation
|
||||
if x.is_promise then
|
||||
-- 2.3.2.1 if x is pending, resolve or reject this promise after completion
|
||||
if x.state == State.PENDING then
|
||||
x:next(
|
||||
function(value)
|
||||
resolve(promise, value)
|
||||
end,
|
||||
function(reason)
|
||||
reject(promise, reason)
|
||||
end
|
||||
)
|
||||
return
|
||||
end
|
||||
-- if x is not pending, transition promise to x's state and value
|
||||
transition(promise, x.state, x.value)
|
||||
return
|
||||
end
|
||||
|
||||
local called = false
|
||||
-- 2.3.3.1. Catches errors thrown by __index metatable
|
||||
local success, reason = pcall(function()
|
||||
local next = x.next
|
||||
if is_callable(next) then
|
||||
next(
|
||||
x,
|
||||
function(y)
|
||||
if not called then
|
||||
resolve(promise, y)
|
||||
called = true
|
||||
end
|
||||
end,
|
||||
function(r)
|
||||
if not called then
|
||||
reject(promise, r)
|
||||
called = true
|
||||
end
|
||||
end
|
||||
)
|
||||
else
|
||||
fulfill(promise, x)
|
||||
end
|
||||
end)
|
||||
|
||||
if not success then
|
||||
if not called then
|
||||
reject(promise, reason)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
run = function(promise)
|
||||
if promise.state == State.PENDING then return end
|
||||
|
||||
do_async(function()
|
||||
-- drain promise.queue while allowing pushes from within callbacks
|
||||
local q = promise.queue
|
||||
local i = 0
|
||||
while i < #q do
|
||||
i = i + 1
|
||||
local obj = q[i]
|
||||
local success, result = pcall(function()
|
||||
local success = obj.fulfill or passthrough
|
||||
local failure = obj.reject or errorthrough
|
||||
local callback = promise.state == State.FULFILLED and success or failure
|
||||
return callback(promise.value)
|
||||
end)
|
||||
|
||||
if not success then
|
||||
reject(obj.promise, result)
|
||||
else
|
||||
resolve(obj.promise, result)
|
||||
end
|
||||
end
|
||||
for j = 1, i do
|
||||
q[j] = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function Promise.new(callback)
|
||||
local instance = {
|
||||
queue = {}
|
||||
}
|
||||
setmetatable(instance, Promise.mt)
|
||||
|
||||
if callback then
|
||||
callback(
|
||||
function(value)
|
||||
resolve(instance, value)
|
||||
end,
|
||||
function(reason)
|
||||
reject(instance, reason)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
return instance
|
||||
end
|
||||
|
||||
function Promise:catch(callback)
|
||||
return self:next(nil, callback)
|
||||
end
|
||||
|
||||
function Promise:resolve(value)
|
||||
fulfill(self, value)
|
||||
end
|
||||
|
||||
function Promise:reject(reason)
|
||||
reject(self, reason)
|
||||
end
|
||||
|
||||
function Promise.update()
|
||||
while true do
|
||||
local async = table.remove(queue, 1)
|
||||
|
||||
if not async then
|
||||
break
|
||||
end
|
||||
|
||||
async()
|
||||
end
|
||||
end
|
||||
|
||||
-- resolve when all promises complete
|
||||
function Promise.all(...)
|
||||
local promises = {...}
|
||||
local results = {}
|
||||
local state = State.FULFILLED
|
||||
local remaining = #promises
|
||||
|
||||
local promise = Promise.new()
|
||||
|
||||
local check_finished = function()
|
||||
if remaining > 0 then
|
||||
return
|
||||
end
|
||||
transition(promise, state, results)
|
||||
end
|
||||
|
||||
for i,p in ipairs(promises) do
|
||||
p:next(
|
||||
function(value)
|
||||
results[i] = value
|
||||
remaining = remaining - 1
|
||||
check_finished()
|
||||
end,
|
||||
function(value)
|
||||
results[i] = value
|
||||
remaining = remaining - 1
|
||||
state = State.REJECTED
|
||||
check_finished()
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
check_finished()
|
||||
|
||||
return promise
|
||||
end
|
||||
|
||||
-- resolve with first promise to complete
|
||||
function Promise.race(...)
|
||||
local promises = {...}
|
||||
local promise = Promise.new()
|
||||
|
||||
Promise.all(...):next(nil, function(value)
|
||||
reject(promise, value)
|
||||
end)
|
||||
|
||||
local success = function(value)
|
||||
fulfill(promise, value)
|
||||
end
|
||||
|
||||
for _,p in ipairs(promises) do
|
||||
p:next(success)
|
||||
end
|
||||
|
||||
return promise
|
||||
end
|
||||
|
||||
return Promise
|
|
@ -5,16 +5,14 @@ local serpent = dofile(base.."/serpent.lua")
|
|||
local util = dofile(base.."/util.lua")
|
||||
|
||||
local runnerDataFile = io.open(os.getenv("RUNNER_DATA"), 'r')
|
||||
local runnerData, err = loadstring(runnerDataFile:read('*a'))
|
||||
local ok, runnerData = serpent.load(runnerDataFile:read('*a'))
|
||||
runnerDataFile:close()
|
||||
|
||||
if err ~= nil then
|
||||
print(err)
|
||||
if not ok then
|
||||
print("Deserialization error")
|
||||
return
|
||||
end
|
||||
|
||||
runnerData = runnerData()
|
||||
|
||||
local species = runnerData[1]
|
||||
|
||||
local speciesId = species.id
|
||||
|
@ -110,6 +108,8 @@ runner.run(
|
|||
)
|
||||
outFile:write(table.concat(outContents, "\n"))
|
||||
outFile:close()
|
||||
exec('quit-emulator')
|
||||
if os.getenv("RUNNER_DONT_QUIT") == nil then
|
||||
exec('quit-emulator')
|
||||
end
|
||||
end
|
||||
)
|
||||
|
|
|
@ -1,8 +1,25 @@
|
|||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||
|
||||
local util = dofile(base.."/util.lua")
|
||||
local config = dofile(base.."/config.lua")
|
||||
local serpent = dofile(base.."/serpent.lua")
|
||||
local tmpFileName = "/tmp/donk_runner_"..tostring(math.floor(random.integer(0, 0xffffffffffffffff))):hex()
|
||||
local temps = {
|
||||
os.getenv("TMPDIR"),
|
||||
os.getenv("TEMP"),
|
||||
os.getenv("TEMPDIR"),
|
||||
os.getenv("TMP"),
|
||||
}
|
||||
|
||||
local tempDir = "/tmp"
|
||||
for i=1,#temps,1 do
|
||||
local temp = temps[i]
|
||||
if temp ~= nil and temp ~= "" then
|
||||
tempDir = temps[i]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local tmpFileName = tempDir.."donk_runner"
|
||||
|
||||
local function message(_M, msg, color)
|
||||
if color == nil then
|
||||
|
@ -70,13 +87,40 @@ return function()
|
|||
local outputFileName = tmpFileName..'_output_'..i
|
||||
|
||||
local inputFileName = tmpFileName.."_input_"..i
|
||||
print(inputFileName)
|
||||
local inputFile = io.open(inputFileName, 'w')
|
||||
inputFile:write(serpent.dump({species[i], generationIdx, outputFileName}))
|
||||
inputFile:close()
|
||||
|
||||
local cmd = "RUNNER_DATA=\""..inputFileName.."\" lsnes \"--rom="..config.ROM.."\" --unpause \"--lua="..base.."/runner-process.lua\""
|
||||
message(_M, cmd)
|
||||
local poppet = io.popen(cmd, 'r')
|
||||
local proc = "lsnes"
|
||||
if util.isWin then
|
||||
local checkParent = io.popen('powershell "(Get-WmiObject Win32_Process -Filter ProcessId=$((Get-WmiObject Win32_Process -Filter ProcessId=$((Get-WmiObject Win32_Process -Filter ProcessId=$PID).ParentProcessId)).ParentProcessId)").ExecutablePath')
|
||||
proc = checkParent:read("*l")
|
||||
checkParent:close()
|
||||
else
|
||||
-- FIXME Linux
|
||||
end
|
||||
print(proc)
|
||||
local cmd = "\""..proc.."\" \"--rom="..config.ROM.."\" --unpause \"--lua="..base.."/runner-process.lua\""
|
||||
local envs = {
|
||||
RUNNER_DATA = inputFileName
|
||||
}
|
||||
if config.NeatConfig.ThreadDontQuit then
|
||||
envs.RUNNER_DONT_QUIT = "1"
|
||||
end
|
||||
|
||||
local cmdParts = {}
|
||||
for k,v in pairs(envs) do
|
||||
if util.isWin then
|
||||
table.insert(cmdParts, string.format("set %s=%s &&", k, v))
|
||||
else
|
||||
table.insert(cmdParts, string.format("%s='%s'", k, v))
|
||||
end
|
||||
end
|
||||
table.insert(cmdParts, cmd)
|
||||
local fullCmd = table.concat(cmdParts, " ")
|
||||
message(_M, fullCmd)
|
||||
local poppet = io.popen(fullCmd, 'r')
|
||||
table.insert(poppets, poppet)
|
||||
end
|
||||
|
||||
|
@ -91,13 +135,11 @@ return function()
|
|||
local outputFile = io.open(outputFileName, "r")
|
||||
local line = ""
|
||||
repeat
|
||||
local obj, err = loadstring(line)
|
||||
if err ~= nil then
|
||||
local ok, obj = serpent.load(line)
|
||||
if not ok then
|
||||
goto continue
|
||||
end
|
||||
|
||||
obj = obj()
|
||||
|
||||
if obj == nil then
|
||||
goto continue
|
||||
end
|
||||
|
|
|
@ -557,7 +557,7 @@ local function mainLoop(_M, genome)
|
|||
_M.genomeCallback(genome, _M.currentGenomeIndex)
|
||||
end
|
||||
|
||||
message(_M, string.format("Gen %d species %d genome %d fitness: %d", _M.currentGenerationIndex, _M.currentSpecies.id, _M.currentGenomeIndex, fitness))
|
||||
message(_M, string.format("Gen %d species %d genome %d fitness: %d", _M.currentGenerationIndex, _M.currentSpecies.id, _M.currentGenomeIndex, math.floor(fitness)))
|
||||
_M.currentGenomeIndex = 1
|
||||
while fitnessAlreadyMeasured(_M) do
|
||||
_M.currentGenomeIndex = _M.currentGenomeIndex + 1
|
||||
|
|
Loading…
Add table
Reference in a new issue