Find the exits by moving the characters and camera in a grid pattern
This commit is contained in:
parent
529f5dc67a
commit
52bdebfc81
10 changed files with 365 additions and 185 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
*.lsmv
|
||||||
|
!pool/*.lsmv
|
||||||
*.log
|
*.log
|
||||||
catchem/
|
catchem/
|
||||||
state/
|
state/
|
||||||
|
|
|
@ -54,6 +54,7 @@ Located at [tools/bsnes-launcher.lua](tools/bsnes-launcher.lua), this script giv
|
||||||
* [Serpent](https://github.com/pkulchenko/serpent)
|
* [Serpent](https://github.com/pkulchenko/serpent)
|
||||||
* [LibDeflate](https://github.com/SafeteeWoW/LibDeflate)
|
* [LibDeflate](https://github.com/SafeteeWoW/LibDeflate)
|
||||||
* [watchexec](https://github.com/watchexec/watchexec/blob/main/LICENSE)
|
* [watchexec](https://github.com/watchexec/watchexec/blob/main/LICENSE)
|
||||||
|
* [Billiam's Promise library](https://github.com/Billiam/promise.lua)
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
|
171
game.lua
171
game.lua
|
@ -1,12 +1,14 @@
|
||||||
--Notes here
|
--Notes here
|
||||||
local memory, bit, memory2, input = memory, bit, memory2, input
|
local memory, bit, memory2, input, callback, movie = memory, bit, memory2, input, callback, movie
|
||||||
|
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
|
|
||||||
|
local Promise = nil
|
||||||
|
|
||||||
|
local util = nil
|
||||||
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 mem = dofile(base.."/mem.lua")
|
local mem = dofile(base.."/mem.lua")
|
||||||
local _M = {
|
local _M = {
|
||||||
leader = 0,
|
leader = 0,
|
||||||
|
@ -39,6 +41,27 @@ function _M.getPositions()
|
||||||
_M.screenY = (_M.partyY-256-_M.cameraY)*2
|
_M.screenY = (_M.partyY-256-_M.cameraY)*2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function _M.setPartyPosition(x, y)
|
||||||
|
memory.writeword(mem.addr.partyX, x)
|
||||||
|
memory.writeword(mem.addr.partyY, y)
|
||||||
|
_M.setSpritePosition(0, x, y)
|
||||||
|
_M.setSpritePosition(1, x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.setCameraPosition(x, y)
|
||||||
|
memory.writeword(mem.addr.cameraX, x)
|
||||||
|
memory.writeword(mem.addr.cameraY, y)
|
||||||
|
memory.writeword(mem.addr.cameraX2, x)
|
||||||
|
memory.writeword(mem.addr.cameraY2, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.setSpritePosition(index, x, y)
|
||||||
|
local offsets = mem.offset.sprite
|
||||||
|
local spriteBase = mem.addr.spriteBase + index * mem.size.sprite
|
||||||
|
memory.writeword(spriteBase + offsets.x, x)
|
||||||
|
memory.writeword(spriteBase + offsets.y, y)
|
||||||
|
end
|
||||||
|
|
||||||
function _M.getBananas()
|
function _M.getBananas()
|
||||||
local bananas = memory.readword(0x7e08bc)
|
local bananas = memory.readword(0x7e08bc)
|
||||||
return bananas
|
return bananas
|
||||||
|
@ -54,6 +77,126 @@ function _M.getKremCoins()
|
||||||
return krem
|
return krem
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function _M.getAreaWidth()
|
||||||
|
return memory.readword(mem.addr.areaWidth) + 256
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.getAreaHeight()
|
||||||
|
return memory.readword(mem.addr.areaHeight)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.getAreaLength()
|
||||||
|
return memory.readword(mem.addr.areaLength)
|
||||||
|
end
|
||||||
|
|
||||||
|
local onFrameAdvancedQueue = {}
|
||||||
|
function _M.advanceFrame()
|
||||||
|
local promise = Promise.new()
|
||||||
|
table.insert(onFrameAdvancedQueue, promise)
|
||||||
|
return promise
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processFrameAdvanced()
|
||||||
|
for i=#onFrameAdvancedQueue,1,-1 do
|
||||||
|
table.remove(onFrameAdvancedQueue, i):resolve()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local onSetRewindQueue = {}
|
||||||
|
function _M.setRewindPoint()
|
||||||
|
local promise = Promise.new()
|
||||||
|
table.insert(onSetRewindQueue, promise)
|
||||||
|
movie.unsafe_rewind()
|
||||||
|
return promise
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processSetRewind(state)
|
||||||
|
for i=#onSetRewindQueue,1,-1 do
|
||||||
|
table.remove(onSetRewindQueue, i):resolve(state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local onRewindQueue = {}
|
||||||
|
function _M.rewind(rew)
|
||||||
|
local promise = Promise.new()
|
||||||
|
movie.unsafe_rewind(rew)
|
||||||
|
table.insert(onRewindQueue, promise)
|
||||||
|
return promise
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processRewind()
|
||||||
|
for i=#onRewindQueue,1,-1 do
|
||||||
|
local promise = table.remove(onRewindQueue, i)
|
||||||
|
promise:resolve()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function findPreferredExitLoop(frame, searchX, searchY, found, uniqueExits)
|
||||||
|
return _M.advanceFrame():next(function()
|
||||||
|
frame = frame + 1
|
||||||
|
if frame % 2 ~=0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local areaWidth = _M.getAreaWidth()
|
||||||
|
memory.writebyte(0x7e19ce, 0x16)
|
||||||
|
memory.writebyte(0x7e0e12, 0x99)
|
||||||
|
memory.writebyte(0x7e0e70, 0x99)
|
||||||
|
local sprites = _M.getSprites()
|
||||||
|
for i=1,#sprites,1 do
|
||||||
|
local sprite = sprites[i]
|
||||||
|
local name = spritelist.SpriteNames[sprite.control]
|
||||||
|
if sprite.control == spritelist.GoodSprites.goalTarget or
|
||||||
|
sprite.control == spritelist.GoodSprites.areaExit then
|
||||||
|
found = true
|
||||||
|
uniqueExits[sprite.y * areaWidth + sprite.x] = sprite
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_M.setPartyPosition(searchX, searchY)
|
||||||
|
_M.setCameraPosition(searchX, searchY)
|
||||||
|
searchX = searchX + 0x100
|
||||||
|
|
||||||
|
if searchX > areaWidth then
|
||||||
|
searchX = 0
|
||||||
|
searchY = searchY + 0xe0
|
||||||
|
if searchY > _M.getAreaHeight() then
|
||||||
|
table.sort(uniqueExits, function(a, b)
|
||||||
|
return a.control < b.control
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Return upper right corner if we can't find anything
|
||||||
|
if found then
|
||||||
|
for id,sprite in pairs(uniqueExits) do
|
||||||
|
return { x = sprite.x, y = sprite.y }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return { x = areaWidth, y = 0}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end):next(function(ret)
|
||||||
|
if ret == nil then
|
||||||
|
return findPreferredExitLoop(frame, searchX, searchY, found, uniqueExits)
|
||||||
|
else
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _M.findPreferredExit()
|
||||||
|
local point = nil
|
||||||
|
local result = nil
|
||||||
|
return _M.setRewindPoint():next(function(p)
|
||||||
|
point = p
|
||||||
|
return findPreferredExitLoop(0, 0, 0, false, {})
|
||||||
|
end):next(function(r)
|
||||||
|
result = r
|
||||||
|
return _M.rewind(point)
|
||||||
|
end):next(function()
|
||||||
|
return result
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
function _M.getGoalHit()
|
function _M.getGoalHit()
|
||||||
local sprites = _M.getSprites()
|
local sprites = _M.getSprites()
|
||||||
for i=1,#sprites,1 do
|
for i=1,#sprites,1 do
|
||||||
|
@ -454,7 +597,16 @@ local function registerHandler(space, regname, addr, callback)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local inputHandler = nil
|
||||||
|
local setRewindHandler = nil
|
||||||
|
local rewindHandler = nil
|
||||||
function _M.unregisterHandlers()
|
function _M.unregisterHandlers()
|
||||||
|
callback.unregister('input', inputHandler)
|
||||||
|
callback.unregister('set_rewind', setRewindHandler)
|
||||||
|
callback.unregister('post_rewind', rewindHandler)
|
||||||
|
inputHandler = nil
|
||||||
|
setRewindHandler = nil
|
||||||
|
rewindHandler = nil
|
||||||
for i=#handlers,1,-1 do
|
for i=#handlers,1,-1 do
|
||||||
local handler = table.remove(handlers, i)
|
local handler = table.remove(handlers, i)
|
||||||
handler.unregisterFn(handler.space, handler.addr, handler.fn)
|
handler.unregisterFn(handler.space, handler.addr, handler.fn)
|
||||||
|
@ -462,6 +614,13 @@ function _M.unregisterHandlers()
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.registerHandlers()
|
function _M.registerHandlers()
|
||||||
|
if inputHandler ~= nil then
|
||||||
|
error("Only call register handlers once")
|
||||||
|
end
|
||||||
|
|
||||||
|
inputHandler = callback.register('input', processFrameAdvanced)
|
||||||
|
setRewindHandler = callback.register('set_rewind', processSetRewind)
|
||||||
|
rewindHandler = callback.register('post_rewind', processRewind)
|
||||||
registerHandler(memory2.BUS, 'registerwrite', 0xb517b2, processAreaLoad)
|
registerHandler(memory2.BUS, 'registerwrite', 0xb517b2, processAreaLoad)
|
||||||
registerHandler(memory2.WRAM, 'registerread', 0x06b1, processMapLoad)
|
registerHandler(memory2.WRAM, 'registerread', 0x06b1, processMapLoad)
|
||||||
for i=2,22,1 do
|
for i=2,22,1 do
|
||||||
|
@ -469,4 +628,10 @@ function _M.registerHandlers()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return function(promise)
|
||||||
|
Promise = promise
|
||||||
|
if util == nil then
|
||||||
|
util = dofile(base.."/util.lua")(Promise)
|
||||||
|
end
|
||||||
|
return _M
|
||||||
|
end
|
8
mem.lua
8
mem.lua
|
@ -6,8 +6,16 @@ local _M = {
|
||||||
verticalPointer = 0xc414,
|
verticalPointer = 0xc414,
|
||||||
tiledataPointer = 0x7e0098,
|
tiledataPointer = 0x7e0098,
|
||||||
haveBoth = 0x7e08c2,
|
haveBoth = 0x7e08c2,
|
||||||
|
---Height in game units for vertical levels, width for horizontal
|
||||||
|
areaLength = 0x7e17b4,
|
||||||
|
---This is always the traditional width no matter the level type
|
||||||
|
areaWidth = 0x7e0afc,
|
||||||
|
---This is always the traditional height no matter the level type
|
||||||
|
areaHeight = 0x7e0afe,
|
||||||
cameraX = 0x7e17ba,
|
cameraX = 0x7e17ba,
|
||||||
cameraY = 0x7e17c0,
|
cameraY = 0x7e17c0,
|
||||||
|
cameraX2 = 0x7e0ad7,
|
||||||
|
cameraY2 = 0x7e0adb,
|
||||||
leadChar = 0x7e08a4,
|
leadChar = 0x7e08a4,
|
||||||
partyX = 0x7e0a2a,
|
partyX = 0x7e0a2a,
|
||||||
partyY = 0x7e0a2c,
|
partyY = 0x7e0a2c,
|
||||||
|
|
|
@ -169,5 +169,6 @@ outFile:close()
|
||||||
print(string.format('Wrote init to output at %d', ts))
|
print(string.format('Wrote init to output at %d', ts))
|
||||||
|
|
||||||
waiter:next(waitLoop):catch(function(error)
|
waiter:next(waitLoop):catch(function(error)
|
||||||
print('ERROR: '..error)
|
print('Runner process error: '..error)
|
||||||
|
io.stderr:write('Runner process error: '..error..'\n')
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -102,7 +102,9 @@ end
|
||||||
return function(promise)
|
return function(promise)
|
||||||
-- FIXME Should this be a global???
|
-- FIXME Should this be a global???
|
||||||
Promise = promise
|
Promise = promise
|
||||||
util = dofile(base.."/util.lua")(Promise)
|
if util == nil then
|
||||||
|
util = dofile(base.."/util.lua")(Promise)
|
||||||
|
end
|
||||||
-- FIXME Maybe don't do this in the "constructor"?
|
-- 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')
|
||||||
|
|
149
runner.lua
149
runner.lua
|
@ -5,7 +5,7 @@ local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
local Promise = nil
|
local Promise = nil
|
||||||
|
|
||||||
local config = dofile(base.."/config.lua")
|
local config = dofile(base.."/config.lua")
|
||||||
local game = dofile(base.."/game.lua")
|
local game = nil
|
||||||
local mathFunctions = dofile(base.."/mathFunctions.lua")
|
local mathFunctions = dofile(base.."/mathFunctions.lua")
|
||||||
local util = dofile(base.."/util.lua")()
|
local util = dofile(base.."/util.lua")()
|
||||||
|
|
||||||
|
@ -216,18 +216,6 @@ local function displayGenome(genome)
|
||||||
gui.renderctx.setnull()
|
gui.renderctx.setnull()
|
||||||
end
|
end
|
||||||
|
|
||||||
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):resolve()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local buttons = nil
|
local buttons = nil
|
||||||
local buttonCtx = gui.renderctx.new(500, 70)
|
local buttonCtx = gui.renderctx.new(500, 70)
|
||||||
local function displayButtons(_M)
|
local function displayButtons(_M)
|
||||||
|
@ -286,10 +274,11 @@ local function displayForm(_M)
|
||||||
formCtx:clear()
|
formCtx:clear()
|
||||||
gui.rectangle(0, 0, 500, guiHeight, 1, 0x00ffffff, 0xbb000000)
|
gui.rectangle(0, 0, 500, guiHeight, 1, 0x00ffffff, 0xbb000000)
|
||||||
--gui.circle(game.screenX-84, game.screenY-84, 192 / 2, 1, 0x50000000)
|
--gui.circle(game.screenX-84, game.screenY-84, 192 / 2, 1, 0x50000000)
|
||||||
|
|
||||||
local rightmost = _M.rightmost[_M.currentArea]
|
local areaInfo = _M.areaInfo[_M.currentArea]
|
||||||
if rightmost == nil then
|
local distanceTraversed = 0
|
||||||
rightmost = 0
|
if areaInfo ~= nil then
|
||||||
|
distanceTraversed = areaInfo.startDistance - areaInfo.shortest
|
||||||
end
|
end
|
||||||
|
|
||||||
gui.text(5, 30, "Timeout: " .. _M.timeout)
|
gui.text(5, 30, "Timeout: " .. _M.timeout)
|
||||||
|
@ -307,7 +296,7 @@ local function displayForm(_M)
|
||||||
gui.text(230, 65, "Damage: " .. _M.partyHitCounter)
|
gui.text(230, 65, "Damage: " .. _M.partyHitCounter)
|
||||||
gui.text(230, 80, "PowerUp: " .. _M.powerUpCounter)
|
gui.text(230, 80, "PowerUp: " .. _M.powerUpCounter)
|
||||||
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, string.format("Traveled: %d", distanceTraversed))
|
||||||
|
|
||||||
displayButtons(_M)
|
displayButtons(_M)
|
||||||
formCtx:set()
|
formCtx:set()
|
||||||
|
@ -417,24 +406,6 @@ 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 function rewind()
|
|
||||||
local promise = Promise.new()
|
|
||||||
movie.unsafe_rewind(rew)
|
|
||||||
table.insert(rewinds, promise)
|
|
||||||
return promise
|
|
||||||
end
|
|
||||||
|
|
||||||
local function rewound()
|
|
||||||
frame = 0
|
|
||||||
lastFrame = 0
|
|
||||||
for i=#rewinds,1,-1 do
|
|
||||||
local promise = table.remove(rewinds, i)
|
|
||||||
promise:resolve()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function newNeuron()
|
local function newNeuron()
|
||||||
local neuron = {}
|
local neuron = {}
|
||||||
neuron.incoming = {}
|
neuron.incoming = {}
|
||||||
|
@ -481,6 +452,13 @@ local function generateNetwork(genome)
|
||||||
genome.network = network
|
genome.network = network
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local rew = movie.to_rewind(config.NeatConfig.Filename)
|
||||||
|
local function rewind()
|
||||||
|
return game.rewind(rew):next(function()
|
||||||
|
frame = 0
|
||||||
|
lastFrame = 0
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
local function initializeRun(_M)
|
local function initializeRun(_M)
|
||||||
settings.set_speed("turbo")
|
settings.set_speed("turbo")
|
||||||
|
@ -493,7 +471,7 @@ local function initializeRun(_M)
|
||||||
exec('enable-sound '..enableSound)
|
exec('enable-sound '..enableSound)
|
||||||
gui.subframe_update(false)
|
gui.subframe_update(false)
|
||||||
|
|
||||||
return rewind():next(function()
|
return rewind():next(function(preferredExit)
|
||||||
if config.StartPowerup ~= nil then
|
if config.StartPowerup ~= nil then
|
||||||
game.writePowerup(config.StartPowerup)
|
game.writePowerup(config.StartPowerup)
|
||||||
end
|
end
|
||||||
|
@ -520,18 +498,49 @@ local function initializeRun(_M)
|
||||||
_M.powerUpBefore = game.getBoth()
|
_M.powerUpBefore = game.getBoth()
|
||||||
_M.currentArea = game.getCurrentArea()
|
_M.currentArea = game.getCurrentArea()
|
||||||
_M.lastArea = _M.currentArea
|
_M.lastArea = _M.currentArea
|
||||||
_M.rightmost = { [_M.currentArea] = 0 }
|
|
||||||
_M.upmost = { [_M.currentArea] = 0 }
|
for areaId,areaInfo in pairs(_M.areaInfo) do
|
||||||
|
areaInfo.shortest = areaInfo.startDistance
|
||||||
|
end
|
||||||
|
|
||||||
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
|
local genome = _M.currentSpecies.genomes[_M.currentGenomeIndex]
|
||||||
generateNetwork(genome)
|
generateNetwork(genome)
|
||||||
evaluateCurrent(_M)
|
evaluateCurrent(_M)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function getDistanceTraversed(areaInfo)
|
||||||
|
local distanceTraversed = 0
|
||||||
|
for areaId,areaInfo in pairs(areaInfo) do
|
||||||
|
distanceTraversed = areaInfo.startDistance - areaInfo.shortest
|
||||||
|
end
|
||||||
|
return distanceTraversed
|
||||||
|
end
|
||||||
|
|
||||||
local function mainLoop(_M, genome)
|
local function mainLoop(_M, genome)
|
||||||
return advanceFrame(_M):next(function()
|
return game.advanceFrame():next(function()
|
||||||
|
local nextArea = game.getCurrentArea()
|
||||||
|
if nextArea ~= _M.lastArea then
|
||||||
|
_M.lastArea = nextArea
|
||||||
|
game.onceAreaLoaded(function()
|
||||||
|
_M.timeout = _M.timeout + 60 * 5
|
||||||
|
_M.currentArea = nextArea
|
||||||
|
_M.lastArea = _M.currentArea
|
||||||
|
end)
|
||||||
|
elseif _M.currentArea == _M.lastArea and _M.areaInfo[_M.currentArea] == nil then
|
||||||
|
message(_M, 'Searching for the main exit in this area')
|
||||||
|
return game.findPreferredExit():next(function(preferredExit)
|
||||||
|
local startDistance = math.floor(math.sqrt((preferredExit.y - game.partyY) ^ 2 + (preferredExit.x - game.partyX) ^ 2))
|
||||||
|
_M.areaInfo[_M.currentArea] = {
|
||||||
|
startDistance = startDistance,
|
||||||
|
preferredExit = preferredExit,
|
||||||
|
shortest = startDistance,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end):next(function()
|
||||||
if lastFrame + 1 ~= frame then
|
if lastFrame + 1 ~= frame then
|
||||||
message(_M, string.format("We missed %d frames", frame - lastFrame), 0x00990000)
|
message(_M, string.format("We missed %d frames", frame - lastFrame), 0x00ff0000)
|
||||||
end
|
end
|
||||||
lastFrame = frame
|
lastFrame = frame
|
||||||
|
|
||||||
|
@ -566,37 +575,16 @@ local function mainLoop(_M, genome)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local nextArea = game.getCurrentArea()
|
local areaInfo = _M.areaInfo[_M.currentArea]
|
||||||
if nextArea ~= _M.lastArea then
|
if areaInfo ~= nil then
|
||||||
_M.lastArea = nextArea
|
local exitDist = math.floor(math.sqrt((areaInfo.preferredExit.y - game.partyY) ^ 2 + (areaInfo.preferredExit.x - game.partyX) ^ 2))
|
||||||
game.onceAreaLoaded(function()
|
if exitDist < areaInfo.shortest then
|
||||||
_M.timeout = _M.timeout + 60 * 5
|
areaInfo.shortest = exitDist
|
||||||
_M.currentArea = nextArea
|
|
||||||
_M.lastArea = _M.currentArea
|
|
||||||
if _M.rightmost[_M.currentArea] == nil then
|
|
||||||
_M.rightmost[_M.currentArea] = 0
|
|
||||||
_M.upmost[_M.currentArea] = 0
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not game.vertical then
|
|
||||||
if game.partyX > _M.rightmost[_M.currentArea] then
|
|
||||||
_M.rightmost[_M.currentArea] = game.partyX
|
|
||||||
if _M.timeout < timeoutConst then
|
|
||||||
_M.timeout = timeoutConst
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if game.partyY > _M.upmost[_M.currentArea] then
|
|
||||||
_M.upmost[_M.currentArea] = game.partyY
|
|
||||||
if _M.timeout < timeoutConst then
|
if _M.timeout < timeoutConst then
|
||||||
_M.timeout = timeoutConst
|
_M.timeout = timeoutConst
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- FIXME Measure distance to target / area exit
|
|
||||||
-- We might not always be horizontal
|
|
||||||
|
|
||||||
local hitTimer = game.getHitTimer(_M.lastBoth)
|
local hitTimer = game.getHitTimer(_M.lastBoth)
|
||||||
|
|
||||||
|
@ -651,20 +639,9 @@ local function mainLoop(_M, genome)
|
||||||
local bumpPenalty = _M.bumps * 100
|
local bumpPenalty = _M.bumps * 100
|
||||||
local powerUpBonus = _M.powerUpCounter * 100
|
local powerUpBonus = _M.powerUpCounter * 100
|
||||||
|
|
||||||
local most = 0
|
local distanceTraversed = getDistanceTraversed(_M.areaInfo) - _M.currentFrame / 2
|
||||||
if not game.vertical then
|
|
||||||
for k,v in pairs(_M.rightmost) do
|
local fitness = bananaCoinsFitness - bumpPenalty - hitPenalty + powerUpBonus + distanceTraversed + game.getJumpHeight() / 100
|
||||||
most = most + v
|
|
||||||
end
|
|
||||||
most = most - _M.currentFrame / 2
|
|
||||||
else
|
|
||||||
for k,v in pairs(_M.upmost) do
|
|
||||||
most = most + v
|
|
||||||
end
|
|
||||||
most = most - _M.currentFrame / 2
|
|
||||||
end
|
|
||||||
|
|
||||||
local fitness = bananaCoinsFitness - bumpPenalty - hitPenalty + powerUpBonus + most + game.getJumpHeight() / 100
|
|
||||||
|
|
||||||
local lives = game.getLives()
|
local lives = game.getLives()
|
||||||
|
|
||||||
|
@ -855,13 +832,11 @@ local function run(_M, species, generationIdx, genomeCallback)
|
||||||
register(_M, 'input', function()
|
register(_M, 'input', function()
|
||||||
frame = frame + 1
|
frame = frame + 1
|
||||||
updateController()
|
updateController()
|
||||||
processFrameAdvanced(_M)
|
|
||||||
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)
|
|
||||||
|
|
||||||
input.keyhook("1", true)
|
input.keyhook("1", true)
|
||||||
input.keyhook("4", true)
|
input.keyhook("4", true)
|
||||||
|
@ -885,6 +860,9 @@ end
|
||||||
|
|
||||||
return function(promise)
|
return function(promise)
|
||||||
Promise = promise
|
Promise = promise
|
||||||
|
if game == nil then
|
||||||
|
game = dofile(base.."/game.lua")(Promise)
|
||||||
|
end
|
||||||
local _M = {
|
local _M = {
|
||||||
currentGenerationIndex = 1,
|
currentGenerationIndex = 1,
|
||||||
currentSpecies = nil,
|
currentSpecies = nil,
|
||||||
|
@ -912,16 +890,13 @@ return function(promise)
|
||||||
powerUpBefore = 0,
|
powerUpBefore = 0,
|
||||||
currentArea = 0,
|
currentArea = 0,
|
||||||
lastArea = 0,
|
lastArea = 0,
|
||||||
rightmost = {},
|
areaInfo = {},
|
||||||
upmost = {},
|
|
||||||
lastBoth = 0,
|
lastBoth = 0,
|
||||||
|
|
||||||
onMessageHandler = {},
|
onMessageHandler = {},
|
||||||
onSaveHandler = {},
|
onSaveHandler = {},
|
||||||
onLoadHandler = {},
|
onLoadHandler = {},
|
||||||
onRenderFormHandler = {},
|
onRenderFormHandler = {},
|
||||||
onFrameAdvancedHandler = {},
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_M.onRenderForm = function(handler)
|
_M.onRenderForm = function(handler)
|
||||||
|
|
188
spritelist.lua
188
spritelist.lua
|
@ -4,100 +4,114 @@ _M.Sprites = {}
|
||||||
|
|
||||||
-- Make sure this list is sorted before initialization.
|
-- Make sure this list is sorted before initialization.
|
||||||
_M.NeutralSprites = {
|
_M.NeutralSprites = {
|
||||||
0x0020, -- Krow egg fragments
|
krowEggFragments = 0x0020,
|
||||||
0x0060, -- Barrel fragments
|
barrelFragments = 0x0060,
|
||||||
0x0064, -- Barrel fragments
|
barrelFragments2 = 0x0064,
|
||||||
|
|
||||||
-- Our heroes
|
-- Our heroes
|
||||||
0x00e4, -- Diddy
|
diddy = 0x00e4,
|
||||||
0x00e8, -- Dixie
|
dixie = 0x00e8,
|
||||||
0x0100, -- Stars
|
stars = 0x0100,
|
||||||
|
|
||||||
-- Items that require too much interaction
|
-- Items that require too much interaction
|
||||||
0x01a4, -- Barrel
|
barrel = 0x01a4, -- Barrel
|
||||||
0x01b0, -- Cannonball (immobile)
|
cannonball = 0x01b0,
|
||||||
0x01c0, -- Chest
|
chest = 0x01c0,
|
||||||
0x01bc, -- Small crate
|
smallCrate = 0x01bc,
|
||||||
0x011c, -- Barrel
|
barrel2 = 0x011c,
|
||||||
0x013c, -- Cannon
|
cannon = 0x013c,
|
||||||
0x014c, -- Hook
|
hook = 0x014c,
|
||||||
0x01b8, -- TNT
|
tnt = 0x01b8,
|
||||||
|
|
||||||
-- Inert
|
-- Inert
|
||||||
0x0168, -- Goal pole
|
goalPole = 0x0168,
|
||||||
0x016c, -- Goal roulette
|
goalroulette = 0x016c,
|
||||||
0x0160, -- Goal base
|
goalBase = 0x0160,
|
||||||
0x0164, -- Goal barrel
|
goalBarrel = 0x0164,
|
||||||
|
|
||||||
0x0238, -- Pow
|
pow = 0x0238,
|
||||||
0x023c, -- Exploding crate
|
explodingCrate = 0x023c,
|
||||||
0x0258, -- No Animals Sign
|
noAnimalsSign = 0x0258,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Make sure this list is sorted before initialization.
|
-- Make sure this list is sorted before initialization.
|
||||||
_M.GoodSprites = {
|
_M.GoodSprites = {
|
||||||
-- Destinations
|
-- Destinations
|
||||||
0x0094, -- Area exit
|
areaExit = 0x0094,
|
||||||
0x00b0, -- Goal target
|
goalTarget = 0x00b0,
|
||||||
|
|
||||||
0x0120, -- Bonus barrel
|
bonusBarrel = 0x0120,
|
||||||
0x0128, -- Hot air balloon
|
hotAirBalloon = 0x0128,
|
||||||
0x0140, -- Launch barrel
|
launchBarrel = 0x0140,
|
||||||
0x0148, -- Animal crate
|
animalCrate = 0x0148,
|
||||||
0x0150, -- Invincibility barrel
|
invincibilityBarrel = 0x0150,
|
||||||
0x0154, -- Midpoint
|
midpoint = 0x0154,
|
||||||
0x015c, -- Banana Coin/Kremkoin/DK Coin
|
allCoins = 0x015c, -- Banana Coin/Kremkoin/DK Coin
|
||||||
0x0170, -- Banana bunch
|
bananaBunch = 0x0170,
|
||||||
0x0174, -- KONG letters
|
kongLetter = 0x0174,
|
||||||
0x0178, -- xUP balloon
|
upBalloon = 0x0178, -- xUP balloon
|
||||||
|
|
||||||
-- Animals
|
-- Animals
|
||||||
0x0190, -- Squitter
|
squitter = 0x0190,
|
||||||
0x0194, -- Rattly
|
rattly = 0x0194,
|
||||||
0x0198, -- Squawks
|
squawks = 0x0198,
|
||||||
0x019c, -- Rambi
|
rambi = 0x019c,
|
||||||
0x0304, -- Clapper
|
clapper = 0x0304,
|
||||||
|
|
||||||
0x01a8, -- DK Barrel label
|
dkBarrelLabel = 0x01a8,
|
||||||
|
|
||||||
0x01b4, -- Krow's eggs
|
krowEgg = 0x01b4,
|
||||||
|
|
||||||
0x0220, -- Flitter (used as unavoidable platforms in some levels)
|
flitter = 0x0220,
|
||||||
0x02d4, -- Krochead (red and green)
|
krocheadAllColors = 0x02d4,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Currently not used.
|
-- Currently not used.
|
||||||
_M.BadSprites = {
|
_M.BadSprites = {
|
||||||
-- Baddies
|
-- Baddies
|
||||||
0x006c, -- Kannon
|
kannon = 0x006c,
|
||||||
0x01ac, -- Klobber (yellow and green)
|
klobberAllColors = 0x01ac,
|
||||||
0x01d0, -- Kannon's fodder (Ball/barrel)
|
kannonFodder = 0x01d0,
|
||||||
0x01d8, -- Krusha
|
krusha = 0x01d8,
|
||||||
0x01dc, -- Click-Clack
|
clickClack = 0x01dc,
|
||||||
0x01e4, -- Neek
|
neek = 0x01e4,
|
||||||
0x01ec, -- Klomp
|
klomp = 0x01ec,
|
||||||
0x01e8, -- Klobber (awake)
|
klobberAwake = 0x01e8,
|
||||||
0x01f0, -- Klampon
|
klampon = 0x01f0,
|
||||||
0x01f8, -- Flotsam
|
flotsam = 0x01f8,
|
||||||
0x0200, -- Klinger
|
klinger = 0x0200,
|
||||||
0x0208, -- Puftup
|
puftup = 0x0208,
|
||||||
0x0218, -- Zinger (red and yellow)
|
zingerAllColors = 0x0218,
|
||||||
0x0214, -- Mini-Necky
|
miniNecky = 0x0214,
|
||||||
0x020c, -- Lockjaw
|
lockjaw = 0x020c,
|
||||||
0x021c, -- Kaboing
|
kaboing = 0x021c,
|
||||||
0x0224, -- Krow (Boss)
|
krow = 0x0224, -- Boss
|
||||||
0x025c, -- Krook (very large)
|
krook = 0x025c,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_M.SpriteNames = {}
|
||||||
|
|
||||||
|
function _M.InitSpriteNames()
|
||||||
|
for v,k in pairs(_M.GoodSprites) do
|
||||||
|
_M.SpriteNames[k] = v
|
||||||
|
end
|
||||||
|
for v,k in pairs(_M.BadSprites) do
|
||||||
|
_M.SpriteNames[k] = v
|
||||||
|
end
|
||||||
|
for v,k in pairs(_M.NeutralSprites) do
|
||||||
|
_M.SpriteNames[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function _M.InitSpriteList()
|
function _M.InitSpriteList()
|
||||||
for i=1,#_M.GoodSprites,1 do
|
for k,v in pairs(_M.GoodSprites) do
|
||||||
_M.Sprites[_M.GoodSprites[i]] = 1
|
_M.extSprites[v] = 1
|
||||||
end
|
end
|
||||||
for i=1,#_M.BadSprites,1 do
|
for k,v in pairs(_M.BadSprites) do
|
||||||
_M.Sprites[_M.BadSprites[i]] = -1
|
_M.extSprites[v] = -1
|
||||||
end
|
end
|
||||||
for i=1,#_M.NeutralSprites,1 do
|
for k,v in pairs(_M.NeutralSprites) do
|
||||||
_M.Sprites[_M.NeutralSprites[i]] = 0
|
_M.extSprites[v] = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,21 +122,21 @@ _M.ExtNeutralSprites = {
|
||||||
}
|
}
|
||||||
|
|
||||||
_M.ExtGoodSprites = {
|
_M.ExtGoodSprites = {
|
||||||
0xe0, -- banana
|
banana01 = 0xe0, -- banana
|
||||||
0xe1, -- banana
|
banana02 = 0xe1, -- banana
|
||||||
0xe2, -- banana
|
banana03 = 0xe2, -- banana
|
||||||
0xe3, -- banana
|
banana04 = 0xe3, -- banana
|
||||||
0xe4, -- banana
|
banana05 = 0xe4, -- banana
|
||||||
0xe5, -- banana
|
banana06 = 0xe5, -- banana
|
||||||
0xe6, -- banana
|
banana07 = 0xe6, -- banana
|
||||||
0xe7, -- banana
|
banana08 = 0xe7, -- banana
|
||||||
0xe8, -- banana
|
banana09 = 0xe8, -- banana
|
||||||
0xe9, -- banana
|
banana10 = 0xe9, -- banana
|
||||||
0xea, -- banana
|
banana11 = 0xea, -- banana
|
||||||
0xeb, -- banana
|
banana12 = 0xeb, -- banana
|
||||||
0xec, -- banana
|
banana13 = 0xec, -- banana
|
||||||
0xed, -- banana
|
banana14 = 0xed, -- banana
|
||||||
0xee, -- banana
|
banana15 = 0xee, -- banana
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Currently not used.
|
-- Currently not used.
|
||||||
|
@ -130,14 +144,14 @@ _M.ExtBadSprites = {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _M.InitExtSpriteList()
|
function _M.InitExtSpriteList()
|
||||||
for i=1,#_M.ExtGoodSprites,1 do
|
for k,v in pairs(_M.ExtGoodSprites) do
|
||||||
_M.extSprites[_M.ExtGoodSprites[i]] = 1
|
_M.extSprites[v] = 1
|
||||||
end
|
end
|
||||||
for i=1,#_M.ExtBadSprites,1 do
|
for k,v in pairs(_M.ExtBadSprites) do
|
||||||
_M.extSprites[_M.ExtBadSprites[i]] = -1
|
_M.extSprites[v] = -1
|
||||||
end
|
end
|
||||||
for i=1,#_M.ExtNeutralSprites,1 do
|
for k,v in pairs(_M.ExtNeutralSprites) do
|
||||||
_M.extSprites[_M.ExtNeutralSprites[i]] = 0
|
_M.extSprites[v] = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
print(string.hex(bit.compose(0xef, 0xbe)))
|
local memory, movie, utime, callback, set_timer_timeout = memory, movie, utime, callback, set_timer_timeout
|
||||||
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
|
||||||
local game = dofile(base.."/game.lua")
|
|
||||||
|
|
||||||
function on_input()
|
local base = string.gsub(@@LUA_SCRIPT_FILENAME@@, "(.*[/\\])(.*)", "%1")
|
||||||
end
|
local Promise = dofile(base.."/promise.lua")
|
||||||
|
callback.register('timer', function()
|
||||||
|
Promise.update()
|
||||||
|
set_timer_timeout(1)
|
||||||
|
end)
|
||||||
|
set_timer_timeout(1)
|
||||||
|
local game = dofile(base.."/game.lua")(Promise)
|
||||||
|
local util = dofile(base.."/util.lua")(Promise)
|
||||||
|
|
||||||
|
game.registerHandlers()
|
||||||
|
|
||||||
|
game.findPreferredExit():next(function(exit)
|
||||||
|
io.stderr:write(util.table_to_string(exit))
|
||||||
|
io.stderr:write('\n')
|
||||||
|
end)
|
|
@ -9,7 +9,7 @@ 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")()
|
||||||
local config = dofile(base.."/config.lua")
|
local config = dofile(base.."/config.lua")
|
||||||
|
|
||||||
spritelist.InitSpriteList()
|
spritelist.InitSpriteList()
|
||||||
|
|
Loading…
Add table
Reference in a new issue