diff --git a/config.lua b/config.lua index 1cdb80d..d3663a0 100644 --- a/config.lua +++ b/config.lua @@ -25,9 +25,10 @@ _M.State = { -- W1.2 Mainbrace Mayhem "MainbraceMayhem.lsmv", -- [4] "MainbraceMayhemBonus.lsmv", -- [5] + "MainbraceMayhemTopOfRope.lsmv", -- [6] } -_M.Filename = _M.PoolDir .. _M.State[4] +_M.Filename = _M.PoolDir .. _M.State[1] --[[ Start game with specific powerup. @@ -61,7 +62,7 @@ BiasMutationChance = 0.40, StepSize = 0.1, DisableMutationChance = 0.4, EnableMutationChance = 0.2, -TimeoutConstant = 20, +TimeoutConstant = 30, MaxNodes = 1000000, } diff --git a/game.lua b/game.lua index f99e85a..95cbe95 100644 --- a/game.lua +++ b/game.lua @@ -1,5 +1,5 @@ --Notes here -local memory, bit, memory2, input, callback, movie = memory, bit, memory2, input, callback, movie +local memory, bit, memory2, input, callback, movie, utime = memory, bit, memory2, input, callback, movie, utime local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1") @@ -265,11 +265,10 @@ function _M.getWaypoints(startX, startY) return collisions end -function _M.getGoalHit() - local sprites = _M.getSprites() +function _M.getGoalHit(sprites) for i=1,#sprites,1 do local sprite = sprites[i] - if sprite.control ~= 0x0164 then + if sprite.control ~= spritelist.GoodSprites.goalBarrel then goto continue end -- Check if the goal barrel is moving up @@ -517,9 +516,8 @@ end -- Currently only for single bananas since they don't -- count as regular computed sprites -function _M.getExtendedSprites() +function _M.getExtendedSprites(sprites) local oam = memory2.OAM:readregion(0x00, 0x220) - local sprites = _M.getSprites() local extended = {} for idx=0,0x200/4-1,1 do @@ -562,21 +560,21 @@ function _M.getExtendedSprites() end function _M.getInputs() - _M.getPositions() - - local sprites = _M.getSprites() - local extended = _M.getExtendedSprites() - - local inputs = {} - local inputDeltaDistance = {} - + _M.getPositions() + + local sprites = _M.getSprites() + local extended = _M.getExtendedSprites(sprites) + + local inputs = {} + local inputDeltaDistance = {} + for dy = -config.BoxRadius, config.BoxRadius, 1 do for dx = -config.BoxRadius, config.BoxRadius, 1 do - inputs[#inputs+1] = 0 - inputDeltaDistance[#inputDeltaDistance+1] = 1 - - local tile = _M.getTile(dx, dy) - if tile == 1 then + inputs[#inputs+1] = 0 + inputDeltaDistance[#inputDeltaDistance+1] = 1 + + local tile = _M.getTile(dx, dy) + if tile == 1 then if inputs[#inputs-config.BoxRadius*2-1] == -1 then inputs[#inputs] = -1 else @@ -601,42 +599,43 @@ function _M.getInputs() inputs[#inputs] = -1 end end - end - - for i = 1,#sprites do + end + + for i = 1,#sprites do local sprite = sprites[i] - local distx = math.abs(sprite.x - (_M.partyX+dx*mem.size.tile)) - local disty = math.abs(sprite.y - (_M.partyY+dy*mem.size.tile)) + if sprite.good == 0 then + goto continue + end + + local distx = math.abs(sprite.x - (_M.partyX+dx*mem.size.tile)) + local disty = math.abs(sprite.y - (_M.partyY+dy*mem.size.tile)) local dist = math.sqrt((distx * distx) + (disty * disty)) - if dist <= mem.size.tile * 1.25 then - if sprite.good == 0 then - goto continue + if dist <= mem.size.tile * 1.25 then + inputs[#inputs] = sprite.good + + if dist > mem.size.tile then + inputDeltaDistance[#inputDeltaDistance] = mathFunctions.squashDistance(dist) end - inputs[#inputs] = sprite.good - - if dist > mem.size.tile then - inputDeltaDistance[#inputDeltaDistance] = mathFunctions.squashDistance(dist) - end - end + end ::continue:: - end + end - for i = 1,#extended do - local distx = math.abs(extended[i]["x"]+_M.cameraX - (_M.partyX+dx*mem.size.tile)) - local disty = math.abs(extended[i]["y"]+_M.cameraY - (_M.partyY+dy*mem.size.tile)) - if distx < mem.size.tile / 2 and disty < mem.size.tile / 2 then - - inputs[#inputs] = extended[i]["good"] - local dist = math.sqrt((distx * distx) + (disty * disty)) - if dist > mem.size.tile / 2 then - inputDeltaDistance[#inputDeltaDistance] = mathFunctions.squashDistance(dist) - end - end - end - end - end + for i = 1,#extended do + local distx = math.abs(extended[i]["x"]+_M.cameraX - (_M.partyX+dx*mem.size.tile)) + local disty = math.abs(extended[i]["y"]+_M.cameraY - (_M.partyY+dy*mem.size.tile)) + if distx < mem.size.tile / 2 and disty < mem.size.tile / 2 then + + inputs[#inputs] = extended[i]["good"] + local dist = math.sqrt((distx * distx) + (disty * disty)) + if dist > mem.size.tile / 2 then + inputDeltaDistance[#inputDeltaDistance] = mathFunctions.squashDistance(dist) + end + end + end + end + end - return inputs, inputDeltaDistance + return inputs, inputDeltaDistance end function _M.getClimbing() diff --git a/runner.lua b/runner.lua index 3e2b2ca..95c11aa 100644 --- a/runner.lua +++ b/runner.lua @@ -579,7 +579,8 @@ local function mainLoop(_M, genome) end if _M.currentFrame % 5 == 0 then - local inputs, inputDeltas = game.getInputs() + local sprites = game.getSprites() + local inputs, inputDeltas = game.getInputs(sprites) if game.bonusScreenDisplayed(inputs) and _M.timeout > -1000 and _M.timeout < timeoutConst then _M.timeout = timeoutConst end @@ -677,10 +678,13 @@ local function mainLoop(_M, genome) message(_M, "Extra live bonus added " .. extraLiveBonus) end - if game.getGoalHit() then + local sprites = game.getSprites() + -- FIXME We should test this before we time out + if game.getGoalHit(sprites) then fitness = fitness + 1000 message(_M, string.format("LEVEL WON! Fitness: %d", fitness), 0x0000ff00) end + if fitness == 0 then fitness = -1 end diff --git a/util.lua b/util.lua index 822330d..e8de00b 100644 --- a/util.lua +++ b/util.lua @@ -18,6 +18,44 @@ function _M.promiseWrap(next, value) return promise:next(next) end +local times = {} + +--- Track how long a function takes +---@param toCall function Function that executes synchronously +---@param name string What to print in the finish text +---@return any any The original value +function _M.time(toCall, name) + if name == nil then + name = 'function' + end + + local sec, usec = utime() + local startTime = sec * 1000000 + usec + local finishTime = 0 + local ret = toCall() + sec, usec = utime() + finishTime = sec * 1000000 + usec + local t = times[name] + if t == nil then + t = {} + times[name] = t + end + + if #t > 50 then + table.remove(t, 1) + end + + t[#t+1] = finishTime - startTime + local sum = 0 + for i=1,#t,1 do + sum = sum + t[i] + end + + print(name..' is averaging '..math.floor(sum / #t)) + + return ret +end + --- Wait for a specified amount of time. Note that this is dependent on the --- timer timeout getting set elsewhere in the code, probably in the Promise --- handler setup @@ -41,6 +79,30 @@ function _M.delay(delayUsec) end) end +function _M.saveState(filename) + return Promise.new(function(res, rej) + local unSave = nil + local unErrSave = nil + local function errSave(f) + if f == filename then + callback.unregister('post_save', unSave) + callback.unregister('err_save', unErrSave) + rej() + end + end + local function postSave(f) + if f == filename then + callback.unregister('post_save', unSave) + callback.unregister('err_save', unErrSave) + res() + end + end + unSave = callback.register('post_save', postSave) + unErrSave = callback.register('err_save', errSave) + exec('save-state '..filename) + end) +end + function _M.loadAndStart(romFile) return Promise.new(function(res, rej) local unPaint = nil