BizHawk
BizHawk copied to clipboard
Segfault running SMW with Lua
Summary
While running Super Mario World (USA) with SethBling's MarI/O script (see Repro), after about 2-3 hours the game crashes with a segfault.
Repro
Show code
-- Use locals to reduce global lookups
local read_s16_le = memory.read_s16_le
local readbyte = memory.readbyte
local random = math.random
local abs = math.abs
local ceil = math.ceil
local floor = math.floor
local max = math.max
local insert = table.insert
local remove = table.remove
local exp = math.exp
local sort = table.sort
if gameinfo.getromname() == "Super Mario World (USA)" then
Filename = "DP1.state"
ButtonNames = {
"A",
"B",
"X",
"Y",
"Up",
"Down",
"Left",
"Right",
}
elseif gameinfo.getromname() == "Super Mario Bros." then
Filename = "SMB1-1.state"
ButtonNames = {
"A",
"B",
"Up",
"Down",
"Left",
"Right",
}
end
BoxRadius = 6
InputSize = (BoxRadius*2+1)*(BoxRadius*2+1)
Inputs = InputSize+2
Outputs = #ButtonNames+1
Population = 300
DeltaDisjoint = 2.0
DeltaWeights = 0.4
DeltaThreshold = 1.0
StaleSpecies = 15
MutateConnectionsChance = 0.25
PerturbChance = 0.90
CrossoverChance = 0.75
LinkMutationChance = 2.0
NodeMutationChance = 0.50
BiasMutationChance = 0.40
StepSize = 0.1
DisableMutationChance = 0.4
EnableMutationChance = 0.2
TimeoutConstant = 20
MaxNodes = 10000
function getPositions()
marioX = read_s16_le(0x94)
marioY = read_s16_le(0x96)
local layer1x = read_s16_le(0x1A);
local layer1y = read_s16_le(0x1C);
screenX = marioX-layer1x
screenY = marioY-layer1y
end
function getTile(dx, dy)
if gameinfo.getromname() == "Super Mario World (USA)" then
x = floor((marioX+dx+8)/16)
y = floor((marioY+dy)/16)
return readbyte(0x1C800 + floor(x/0x10)*0x1B0 + y*0x10 + x%0x10)
elseif gameinfo.getromname() == "Super Mario Bros." then
local x = marioX + dx + 8
local y = marioY + dy - 16
local page = floor(x/256)%2
local subx = floor((x%256)/16)
local suby = floor((y - 32)/16)
local addr = 0x500 + page*13*16+suby*16+subx
if suby >= 13 or suby < 0 then
return 0
end
if readbyte(addr) ~= 0 then
return 1
else
return 0
end
end
end
function getSprites()
if gameinfo.getromname() == "Super Mario World (USA)" then
local sprites = {}
for slot=0,11 do
local status = readbyte(0x14C8+slot)
if status ~= 0 then
spritex = readbyte(0xE4+slot) + readbyte(0x14E0+slot)*256
spritey = readbyte(0xD8+slot) + readbyte(0x14D4+slot)*256
sprites[#sprites+1] = {["x"]=spritex, ["y"]=spritey}
end
end
return sprites
elseif gameinfo.getromname() == "Super Mario Bros." then
local sprites = {}
for slot=0,4 do
local enemy = readbyte(0xF+slot)
if enemy ~= 0 then
local ex = readbyte(0x6E + slot)*0x100 + readbyte(0x87+slot)
local ey = readbyte(0xCF + slot)+24
sprites[#sprites+1] = {["x"]=ex,["y"]=ey}
end
end
return sprites
end
end
function getExtendedSprites()
if gameinfo.getromname() == "Super Mario World (USA)" then
local extended = {}
for slot=0,11 do
local number = readbyte(0x170B+slot)
if number ~= 0 then
spritex = readbyte(0x171F+slot) + readbyte(0x1733+slot)*256
spritey = readbyte(0x1715+slot) + readbyte(0x1729+slot)*256
extended[#extended+1] = {["x"]=spritex, ["y"]=spritey}
end
end
return extended
elseif gameinfo.getromname() == "Super Mario Bros." then
return {}
end
end
function getInputs()
getPositions()
sprites = getSprites()
extended = getExtendedSprites()
local inputs = {}
for dy=-BoxRadius*16,BoxRadius*16,16 do
for dx=-BoxRadius*16,BoxRadius*16,16 do
inputs[#inputs+1] = 0
tile = getTile(dx, dy)
if tile == 1 and marioY+dy < 0x1B0 then
inputs[#inputs] = 1
end
for i = 1,#sprites do
distx = abs(sprites[i]["x"] - (marioX+dx))
disty = abs(sprites[i]["y"] - (marioY+dy))
if distx <= 8 and disty <= 8 then
inputs[#inputs] = -1
end
end
for i = 1,#extended do
distx = abs(extended[i]["x"] - (marioX+dx))
disty = abs(extended[i]["y"] - (marioY+dy))
if distx < 8 and disty < 8 then
inputs[#inputs] = -1
end
end
end
end
--mariovx = memory.read_s8(0x7B)
--mariovy = memory.read_s8(0x7D)
return inputs
end
function sigmoid(x)
return 2/(1+exp(-4.9*x))-1
end
function newInnovation()
pool.innovation = pool.innovation + 1
return pool.innovation
end
function newPool()
local pool = {}
pool.species = {}
pool.generation = 0
pool.innovation = Outputs
pool.currentSpecies = 1
pool.currentGenome = 1
pool.currentFrame = 0
pool.maxFitness = 0
return pool
end
function newSpecies()
local species = {}
species.topFitness = 0
species.staleness = 0
species.genomes = {}
species.averageFitness = 0
return species
end
function newGenome()
local genome = {}
genome.genes = {}
genome.fitness = 0
genome.adjustedFitness = 0
genome.network = {}
genome.maxneuron = 0
genome.globalRank = 0
genome.mutationRates = {}
genome.mutationRates["connections"] = MutateConnectionsChance
genome.mutationRates["link"] = LinkMutationChance
genome.mutationRates["bias"] = BiasMutationChance
genome.mutationRates["node"] = NodeMutationChance
genome.mutationRates["enable"] = EnableMutationChance
genome.mutationRates["disable"] = DisableMutationChance
genome.mutationRates["step"] = StepSize
return genome
end
function copyGenome(genome)
local genome2 = newGenome()
for g=1,#genome.genes do
insert(genome2.genes, copyGene(genome.genes[g]))
end
genome2.maxneuron = genome.maxneuron
genome2.mutationRates["connections"] = genome.mutationRates["connections"]
genome2.mutationRates["link"] = genome.mutationRates["link"]
genome2.mutationRates["bias"] = genome.mutationRates["bias"]
genome2.mutationRates["node"] = genome.mutationRates["node"]
genome2.mutationRates["enable"] = genome.mutationRates["enable"]
genome2.mutationRates["disable"] = genome.mutationRates["disable"]
return genome2
end
function basicGenome()
local genome = newGenome()
local innovation = 1
genome.maxneuron = Inputs
mutate(genome)
return genome
end
function newGene()
local gene = {}
gene.into = 0
gene.out = 0
gene.weight = 0.0
gene.enabled = true
gene.innovation = 0
return gene
end
function copyGene(gene)
local gene2 = newGene()
gene2.into = gene.into
gene2.out = gene.out
gene2.weight = gene.weight
gene2.enabled = gene.enabled
gene2.innovation = gene.innovation
return gene2
end
function newNeuron()
local neuron = {}
neuron.incoming = {}
neuron.value = 0.0
return neuron
end
function generateNetwork(genome)
local network = {}
network.neurons = {}
for i=1,Inputs do
network.neurons[i] = newNeuron()
end
for o=1,Outputs do
network.neurons[MaxNodes+o] = newNeuron()
end
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]
insert(neuron.incoming, gene)
if network.neurons[gene.into] == nil then
network.neurons[gene.into] = newNeuron()
end
end
end
genome.network = network
end
function evaluateNetwork(network, inputs)
insert(inputs, 1)
insert(inputs, network.lastOutput or 0)
if #inputs ~= Inputs then
console.writeline("Incorrect number of neural network inputs.")
return {}
end
for i=1,Inputs do
network.neurons[i].value = inputs[i]
end
for _,neuron in pairs(network.neurons) do
local sum = 0
for j = 1,#neuron.incoming do
local incoming = neuron.incoming[j]
local other = network.neurons[incoming.into]
sum = sum + incoming.weight * other.value
end
if #neuron.incoming > 0 then
neuron.value = sigmoid(sum)
end
end
local outputs = {}
for o=1,Outputs do
if o > #ButtonNames then
network.lastOutput = network.neurons[MaxNodes+o].value
else
local button = "P1 " .. ButtonNames[o]
if network.neurons[MaxNodes+o].value > 0 then
outputs[button] = true
else
outputs[button] = false
end
end
end
return outputs
end
function crossover(g1, g2)
-- Make sure g1 is the higher fitness genome
if g2.fitness > g1.fitness then
tempg = g1
g1 = g2
g2 = tempg
end
local child = newGenome()
local innovations2 = {}
for i=1,#g2.genes do
local gene = g2.genes[i]
innovations2[gene.innovation] = gene
end
for i=1,#g1.genes do
local gene1 = g1.genes[i]
local gene2 = innovations2[gene1.innovation]
if gene2 ~= nil and random(2) == 1 and gene2.enabled then
insert(child.genes, copyGene(gene2))
else
insert(child.genes, copyGene(gene1))
end
end
child.maxneuron = max(g1.maxneuron,g2.maxneuron)
for mutation,rate in pairs(g1.mutationRates) do
child.mutationRates[mutation] = rate
end
return child
end
function randomNeuron(genes, nonInput)
local neurons = {}
if not nonInput then
for i=1,Inputs do
neurons[i] = true
end
end
for o=1,Outputs do
neurons[MaxNodes+o] = true
end
for i=1,#genes do
if (not nonInput) or genes[i].into > Inputs then
neurons[genes[i].into] = true
end
if (not nonInput) or genes[i].out > Inputs then
neurons[genes[i].out] = true
end
end
local count = 0
for _,_ in pairs(neurons) do
count = count + 1
end
local n = random(1, count)
for k,v in pairs(neurons) do
n = n-1
if n == 0 then
return k
end
end
return 0
end
function containsLink(genes, link)
for i=1,#genes do
local gene = genes[i]
if gene.into == link.into and gene.out == link.out then
return true
end
end
end
function pointMutate(genome)
local step = genome.mutationRates["step"]
for i=1,#genome.genes do
local gene = genome.genes[i]
if random() < PerturbChance then
gene.weight = gene.weight + random() * step*2 - step
else
gene.weight = random()*4-2
end
end
end
function linkMutate(genome, forceBias)
local neuron1 = randomNeuron(genome.genes, false)
local neuron2 = randomNeuron(genome.genes, true)
local newLink = newGene()
if neuron1 <= Inputs and neuron2 <= Inputs then
--Both input nodes
return
end
if neuron2 <= Inputs then
-- Swap output and input
local temp = neuron1
neuron1 = neuron2
neuron2 = temp
end
newLink.into = neuron1
newLink.out = neuron2
if forceBias then
newLink.into = Inputs
end
if containsLink(genome.genes, newLink) then
return
end
newLink.innovation = newInnovation()
newLink.weight = random()*4-2
insert(genome.genes, newLink)
end
function nodeMutate(genome)
if #genome.genes == 0 then
return
end
genome.maxneuron = genome.maxneuron + 1
local gene = genome.genes[random(1,#genome.genes)]
if not gene.enabled then
return
end
gene.enabled = false
local gene1 = copyGene(gene)
gene1.out = genome.maxneuron
gene1.weight = 1.0
gene1.innovation = newInnovation()
gene1.enabled = true
insert(genome.genes, gene1)
local gene2 = copyGene(gene)
gene2.into = genome.maxneuron
gene2.innovation = newInnovation()
gene2.enabled = true
insert(genome.genes, gene2)
end
function enableDisableMutate(genome, enable)
local candidates = {}
for _,gene in pairs(genome.genes) do
if gene.enabled == not enable then
insert(candidates, gene)
end
end
if #candidates == 0 then
return
end
local gene = candidates[random(1,#candidates)]
gene.enabled = not gene.enabled
end
function mutate(genome)
for mutation,rate in pairs(genome.mutationRates) do
if random(1,2) == 1 then
genome.mutationRates[mutation] = 0.95*rate
else
genome.mutationRates[mutation] = 1.05263*rate
end
end
if random() < genome.mutationRates["connections"] then
pointMutate(genome)
end
local p = genome.mutationRates["link"]
while p > 0 do
if random() < p then
linkMutate(genome, false)
end
p = p - 1
end
p = genome.mutationRates["bias"]
while p > 0 do
if random() < p then
linkMutate(genome, true)
end
p = p - 1
end
p = genome.mutationRates["node"]
while p > 0 do
if random() < p then
nodeMutate(genome)
end
p = p - 1
end
p = genome.mutationRates["enable"]
while p > 0 do
if random() < p then
enableDisableMutate(genome, true)
end
p = p - 1
end
p = genome.mutationRates["disable"]
while p > 0 do
if random() < p then
enableDisableMutate(genome, false)
end
p = p - 1
end
end
function disjoint(genes1, genes2)
local i1 = {}
for i = 1,#genes1 do
local gene = genes1[i]
i1[gene.innovation] = true
end
local i2 = {}
for i = 1,#genes2 do
local gene = genes2[i]
i2[gene.innovation] = true
end
local disjointGenes = 0
for i = 1,#genes1 do
local gene = genes1[i]
if not i2[gene.innovation] then
disjointGenes = disjointGenes+1
end
end
for i = 1,#genes2 do
local gene = genes2[i]
if not i1[gene.innovation] then
disjointGenes = disjointGenes+1
end
end
local n = max(#genes1, #genes2)
return disjointGenes / n
end
function weights(genes1, genes2)
local i2 = {}
for i = 1,#genes2 do
local gene = genes2[i]
i2[gene.innovation] = gene
end
local sum = 0
local coincident = 0
for i = 1,#genes1 do
local gene = genes1[i]
if i2[gene.innovation] ~= nil then
local gene2 = i2[gene.innovation]
sum = sum + abs(gene.weight - gene2.weight)
coincident = coincident + 1
end
end
return sum / coincident
end
function sameSpecies(genome1, genome2)
local dd = DeltaDisjoint*disjoint(genome1.genes, genome2.genes)
local dw = DeltaWeights*weights(genome1.genes, genome2.genes)
return dd + dw < DeltaThreshold
end
function rankGlobally()
local global = {}
for s = 1,#pool.species do
local species = pool.species[s]
for g = 1,#species.genomes do
insert(global, species.genomes[g])
end
end
sort(global, function (a,b)
return (a.fitness < b.fitness)
end)
for g=1,#global do
global[g].globalRank = g
end
end
function calculateAverageFitness(species)
local total = 0
for g=1,#species.genomes do
local genome = species.genomes[g]
total = total + genome.globalRank
end
species.averageFitness = total / #species.genomes
end
function totalAverageFitness()
local total = 0
for s = 1,#pool.species do
local species = pool.species[s]
total = total + species.averageFitness
end
return total
end
function cullSpecies(cutToOne)
for s = 1,#pool.species do
local species = pool.species[s]
sort(species.genomes, function (a,b)
return (a.fitness > b.fitness)
end)
local remaining = ceil(#species.genomes/2)
if cutToOne then
remaining = 1
end
while #species.genomes > remaining do
remove(species.genomes)
end
end
end
function breedChild(species)
local child = {}
if random() < CrossoverChance then
g1 = species.genomes[random(1, #species.genomes)]
g2 = species.genomes[random(1, #species.genomes)]
child = crossover(g1, g2)
else
g = species.genomes[random(1, #species.genomes)]
child = copyGenome(g)
end
mutate(child)
return child
end
function removeStaleSpecies()
local survived = {}
for s = 1,#pool.species do
local species = pool.species[s]
sort(species.genomes, function (a,b)
return (a.fitness > b.fitness)
end)
if species.genomes[1].fitness > species.topFitness then
species.topFitness = species.genomes[1].fitness
species.staleness = 0
else
species.staleness = species.staleness + 1
end
if species.staleness < StaleSpecies or species.topFitness >= pool.maxFitness then
insert(survived, species)
end
end
pool.species = survived
end
function removeWeakSpecies()
local survived = {}
local sum = totalAverageFitness()
for s = 1,#pool.species do
local species = pool.species[s]
breed = floor(species.averageFitness / sum * Population)
if breed >= 1 then
insert(survived, species)
end
end
pool.species = survived
end
function addToSpecies(child)
local foundSpecies = false
for s=1,#pool.species do
local species = pool.species[s]
if not foundSpecies and sameSpecies(child, species.genomes[1]) then
insert(species.genomes, child)
foundSpecies = true
end
end
if not foundSpecies then
local childSpecies = newSpecies()
insert(childSpecies.genomes, child)
insert(pool.species, childSpecies)
end
end
function newGeneration()
cullSpecies(false) -- Cull the bottom half of each species
rankGlobally()
removeStaleSpecies()
rankGlobally()
for s = 1,#pool.species do
local species = pool.species[s]
calculateAverageFitness(species)
end
removeWeakSpecies()
local sum = totalAverageFitness()
local children = {}
for s = 1,#pool.species do
local species = pool.species[s]
breed = floor(species.averageFitness / sum * Population) - 1
for i=1,breed do
insert(children, breedChild(species))
end
end
cullSpecies(true) -- Cull all but the top member of each species
while #children + #pool.species < Population do
local species = pool.species[random(1, #pool.species)]
insert(children, breedChild(species))
end
for c=1,#children do
local child = children[c]
addToSpecies(child)
end
pool.generation = pool.generation + 1
writeFile("backup." .. pool.generation .. "." .. forms.gettext(saveLoadFile))
end
function initializePool()
pool = newPool()
for i=1,Population do
basic = basicGenome()
addToSpecies(basic)
end
initializeRun()
end
function clearJoypad()
controller = {}
for b = 1,#ButtonNames do
controller["P1 " .. ButtonNames[b]] = false
end
joypad.set(controller)
end
function initializeRun()
savestate.load(Filename);
rightmost = 0
pool.currentFrame = 0
timeout = TimeoutConstant
clearJoypad()
local species = pool.species[pool.currentSpecies]
local genome = species.genomes[pool.currentGenome]
generateNetwork(genome)
evaluateCurrent()
end
function evaluateCurrent()
local species = pool.species[pool.currentSpecies]
local genome = species.genomes[pool.currentGenome]
inputs = getInputs()
controller = evaluateNetwork(genome.network, inputs)
if controller["P1 Left"] and controller["P1 Right"] then
controller["P1 Left"] = false
controller["P1 Right"] = false
end
if controller["P1 Up"] and controller["P1 Down"] then
controller["P1 Up"] = false
controller["P1 Down"] = false
end
joypad.set(controller)
end
if pool == nil then
initializePool()
end
function nextGenome()
pool.currentGenome = pool.currentGenome + 1
if pool.currentGenome > #pool.species[pool.currentSpecies].genomes then
pool.currentGenome = 1
pool.currentSpecies = pool.currentSpecies+1
if pool.currentSpecies > #pool.species then
newGeneration()
pool.currentSpecies = 1
end
end
end
function fitnessAlreadyMeasured()
local species = pool.species[pool.currentSpecies]
local genome = species.genomes[pool.currentGenome]
return genome.fitness ~= 0
end
function displayGenome(genome)
local network = genome.network
local cells = {}
local i = 1
local cell = {}
for dy=-BoxRadius,BoxRadius do
for dx=-BoxRadius,BoxRadius do
cell = {}
cell.x = 50+5*dx
cell.y = 70+5*dy
cell.value = network.neurons[i].value
cells[i] = cell
i = i + 1
end
end
local biasCell = {}
biasCell.x = 80
biasCell.y = 110
biasCell.value = network.neurons[Inputs-1].value
cells[Inputs-1] = biasCell
local storageCell = {}
storageCell.x = 80
storageCell.y = 120
storageCell.value = network.neurons[Inputs].value
cells[Inputs] = storageCell
for o = 1,Outputs-1 do
cell = {}
cell.x = 220
cell.y = 30 + 8 * o
cell.value = network.neurons[MaxNodes + o].value
cells[MaxNodes+o] = cell
local color
if cell.value > 0 then
color = 0xFF0000FF
else
color = 0xFF000000
end
gui.drawText(223, 24+8*o, ButtonNames[o], color, 9)
end
local outputStorageCell = {}
outputStorageCell.x = 220
outputStorageCell.y = 102
outputStorageCell.value = network.neurons[MaxNodes + Outputs].value
cells[MaxNodes+Outputs] = outputStorageCell
for n,neuron in pairs(network.neurons) do
cell = {}
if n > Inputs and n <= MaxNodes then
cell.x = 140
cell.y = 40
cell.value = neuron.value
cells[n] = cell
end
end
for n=1,4 do
for _,gene in pairs(genome.genes) do
if gene.enabled then
local c1 = cells[gene.into]
local c2 = cells[gene.out]
if gene.into > Inputs and gene.into <= MaxNodes then
c1.x = 0.75*c1.x + 0.25*c2.x
if c1.x >= c2.x then
c1.x = c1.x - 40
end
if c1.x < 90 then
c1.x = 90
end
if c1.x > 220 then
c1.x = 220
end
c1.y = 0.75*c1.y + 0.25*c2.y
end
if gene.out > Inputs and gene.out <= MaxNodes then
c2.x = 0.25*c1.x + 0.75*c2.x
if c1.x >= c2.x then
c2.x = c2.x + 40
end
if c2.x < 90 then
c2.x = 90
end
if c2.x > 220 then
c2.x = 220
end
c2.y = 0.25*c1.y + 0.75*c2.y
end
end
end
end
gui.drawBox(50-BoxRadius*5-3,70-BoxRadius*5-3,50+BoxRadius*5+2,70+BoxRadius*5+2,0xFF000000, 0x80808080)
for n,cell in pairs(cells) do
if n > Inputs-1 or cell.value ~= 0 then
local color = floor((cell.value+1)/2*256)
if color > 255 then color = 255 end
if color < 0 then color = 0 end
local opacity = 0xFF000000
if cell.value == 0 then
opacity = 0x50000000
end
color = opacity + color*0x10000 + color*0x100 + color
gui.drawBox(cell.x-2,cell.y-2,cell.x+2,cell.y+2,opacity,color)
end
end
for _,gene in pairs(genome.genes) do
if gene.enabled then
local c1 = cells[gene.into]
local c2 = cells[gene.out]
local opacity = 0xA0000000
if c1.value == 0 then
opacity = 0x20000000
end
local color = 0x80-floor(abs(sigmoid(gene.weight))*0x80)
if gene.weight > 0 then
color = opacity + 0x8000 + 0x10000*color
else
color = opacity + 0x800000 + 0x100*color
end
gui.drawLine(c1.x+1, c1.y, c2.x-3, c2.y, color)
end
end
gui.drawBox(49,71,51,78,0x00000000,0x80FF0000)
if forms.ischecked(showMutationRates) then
local pos = 100
for mutation,rate in pairs(genome.mutationRates) do
gui.drawText(100, pos, mutation .. ": " .. rate, 0xFF000000, 10)
pos = pos + 8
end
end
end
function writeFile(filename)
local file = io.open(filename, "w")
file:write(pool.generation .. "\n")
file:write(pool.maxFitness .. "\n")
file:write(#pool.species .. "\n")
for n,species in pairs(pool.species) do
file:write(species.topFitness .. "\n")
file:write(species.staleness .. "\n")
file:write(#species.genomes .. "\n")
for m,genome in pairs(species.genomes) do
file:write(genome.fitness .. "\n")
file:write(genome.maxneuron .. "\n")
for mutation,rate in pairs(genome.mutationRates) do
file:write(mutation .. "\n")
file:write(rate .. "\n")
end
file:write("done\n")
file:write(#genome.genes .. "\n")
for l,gene in pairs(genome.genes) do
file:write(gene.into .. " ")
file:write(gene.out .. " ")
file:write(gene.weight .. " ")
file:write(gene.innovation .. " ")
if(gene.enabled) then
file:write("1\n")
else
file:write("0\n")
end
end
end
end
file:close()
end
function savePool()
local filename = forms.gettext(saveLoadFile)
writeFile(filename)
end
function loadFile(filename)
local file = io.open(filename, "r")
pool = newPool()
pool.generation = file:read("*number")
pool.maxFitness = file:read("*number")
forms.settext(maxFitnessLabel, "Max Fitness: " .. floor(pool.maxFitness))
local numSpecies = file:read("*number")
for s=1,numSpecies do
local species = newSpecies()
insert(pool.species, species)
species.topFitness = file:read("*number")
species.staleness = file:read("*number")
local numGenomes = file:read("*number")
for g=1,numGenomes do
local genome = newGenome()
insert(species.genomes, genome)
genome.fitness = file:read("*number")
genome.maxneuron = file:read("*number")
local line = file:read("*line")
while line ~= "done" do
genome.mutationRates[line] = file:read("*number")
line = file:read("*line")
end
local numGenes = file:read("*number")
for n=1,numGenes do
local gene = newGene()
insert(genome.genes, gene)
local enabled
gene.into, gene.out, gene.weight, gene.innovation, enabled = file:read("*number", "*number", "*number", "*number", "*number")
if enabled == 0 then
gene.enabled = false
else
gene.enabled = true
end
end
end
end
file:close()
while fitnessAlreadyMeasured() do
nextGenome()
end
initializeRun()
pool.currentFrame = pool.currentFrame + 1
end
function loadPool()
local filename = forms.gettext(saveLoadFile)
loadFile(filename)
end
function playTop()
local maxfitness = 0
local maxs, maxg
for s,species in pairs(pool.species) do
for g,genome in pairs(species.genomes) do
if genome.fitness > maxfitness then
maxfitness = genome.fitness
maxs = s
maxg = g
end
end
end
pool.currentSpecies = maxs
pool.currentGenome = maxg
pool.maxFitness = maxfitness
forms.settext(maxFitnessLabel, "Max Fitness: " .. floor(pool.maxFitness))
initializeRun()
pool.currentFrame = pool.currentFrame + 1
return
end
function onExit()
forms.destroy(form)
end
writeFile("temp.pool")
event.onexit(onExit)
form = forms.newform(200, 260, "Fitness")
maxFitnessLabel = forms.label(form, "Max Fitness: " .. floor(pool.maxFitness), 5, 8)
showNetwork = forms.checkbox(form, "Show Map", 5, 30)
showMutationRates = forms.checkbox(form, "Show M-Rates", 5, 52)
restartButton = forms.button(form, "Restart", initializePool, 5, 77)
saveButton = forms.button(form, "Save", savePool, 5, 102)
loadButton = forms.button(form, "Load", loadPool, 80, 102)
saveLoadFile = forms.textbox(form, Filename .. ".pool", 170, 25, nil, 5, 148)
saveLoadLabel = forms.label(form, "Save/Load:", 5, 129)
playTopButton = forms.button(form, "Play Top", playTop, 5, 170)
hideBanner = forms.checkbox(form, "Hide Banner", 5, 190)
while true do
local backgroundColor = 0xD0FFFFFF
if not forms.ischecked(hideBanner) then
gui.drawBox(0, 0, 300, 26, backgroundColor, backgroundColor)
end
local species = pool.species[pool.currentSpecies]
local genome = species.genomes[pool.currentGenome]
if forms.ischecked(showNetwork) then
displayGenome(genome)
end
if pool.currentFrame%5 == 0 then
evaluateCurrent()
end
joypad.set(controller)
getPositions()
if marioX > rightmost then
rightmost = marioX
timeout = TimeoutConstant
end
timeout = timeout - 1
local timeoutBonus = pool.currentFrame / 4
if timeout + timeoutBonus <= 0 then
local fitness = rightmost - pool.currentFrame / 2
if gameinfo.getromname() == "Super Mario World (USA)" and rightmost > 4816 then
fitness = fitness + 1000
end
if gameinfo.getromname() == "Super Mario Bros." and rightmost > 3186 then
fitness = fitness + 1000
end
if fitness == 0 then
fitness = -1
end
genome.fitness = fitness
if fitness > pool.maxFitness then
pool.maxFitness = fitness
forms.settext(maxFitnessLabel, "Max Fitness: " .. floor(pool.maxFitness))
writeFile("backup." .. pool.generation .. "." .. forms.gettext(saveLoadFile))
end
--console.writeline("Gen " .. pool.generation .. " species " .. pool.currentSpecies .. " genome " .. pool.currentGenome .. " fitness: " .. fitness)
pool.currentSpecies = 1
pool.currentGenome = 1
while fitnessAlreadyMeasured() do
nextGenome()
end
initializeRun()
end
local measured = 0
local total = 0
for _,species in pairs(pool.species) do
for _,genome in pairs(species.genomes) do
total = total + 1
if genome.fitness ~= 0 then
measured = measured + 1
end
end
end
if not forms.ischecked(hideBanner) then
gui.drawText(0, 0, "Gen " .. pool.generation .. " species " .. pool.currentSpecies .. " genome " .. pool.currentGenome .. " (" .. floor(measured/total*100) .. "%)", 0xFF000000, 11)
gui.drawText(0, 12, "Fitness: " .. floor(rightmost - (pool.currentFrame) / 2 - (timeout + timeoutBonus)*2/3), 0xFF000000, 11)
gui.drawText(100, 12, "Max Fitness: " .. floor(pool.maxFitness), 0xFF000000, 11)
end
pool.currentFrame = pool.currentFrame + 1
emu.frameadvance();
end
Output
From EmuHawkMono_laststdout.txt:
Show log
=================================================================
Native Crash Reporting
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
=================================================================
Native stacktrace:
=================================================================
0x55e002dd5e2b - mono :
0x55e002d9e9b7 - mono :
0x55e002dc5978 - mono :
0x7ffdfc3c1df0 - Unknown
=================================================================
Telemetry Dumper:
=================================================================
Pkilling 0x139632818529984x from 0x139633190637696x
Pkilling 0x139633091110592x from 0x139633190637696x
Pkilling 0x139632820631232x from 0x139633190637696x
Pkilling 0x139632319297216x from 0x139633190637696x
Pkilling 0x139633142953664x from 0x139633190637696x
Entering thread summarizer pause from 0x139633190637696x
Finished thread summarizer pause from 0x139633190637696x.
Failed to create breadcrumb file (null)/crash_hash_0x652abb048
Waiting for dumping threads to resume
=================================================================
Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x7ffdfc3c1df0):0x7ffdfc3c1de0 f0 01 6e d4 fe 7e 00 00 03 00 00 00 00 00 00 00 ..n..~..........
0x7ffdfc3c1df0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x7ffdfc3c1e00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 00 ............ ...
0x7ffdfc3c1e10 e0 1d 3c fc fd 7f 00 00 d0 01 6e d4 fe 7e 00 00 ..<.......n..~..
=================================================================
Managed Stacktrace:
=================================================================
at <unknown> <0xffffffff>
at System.Reflection.RuntimeMethodInfo:InternalInvoke <0x000ad>
at System.Reflection.RuntimeMethodInfo:Invoke <0x0010a>
at System.Reflection.MethodBase:Invoke <0x00047>
at NLua.Method.LuaMethodWrapper:Call <0x0153a>
at NLua.MetaFunctions:RunFunctionDelegate <0x0008a>
at NLua.MetaFunctions:RunFunctionDelegate <0x0004b>
at KopiLua.Lua:LuaDPreCall <0x00794>
at KopiLua.Lua:luaV_execute <0x017b7>
at KopiLua.Lua:Resume <0x0015b>
at KopiLua.Lua:LuaDRawRunProtected <0x000db>
at KopiLua.Lua:LuaResume <0x00163>
at NLua.Lua:Resume <0x00047>
at BizHawk.Client.EmuHawk.Win32LuaLibraries:ResumeScript <0x00087>
at <>c__DisplayClass61_1:<ResumeScripts>b__1 <0x0007b>
at BizHawk.Client.Common.EnvironmentSandbox:Sandbox <0x0002a>
at BizHawk.Client.Common.LuaSandbox:Sandbox <0x0008b>
at BizHawk.Client.Common.LuaSandbox:Sandbox <0x0004f>
at BizHawk.Client.EmuHawk.LuaConsole:ResumeScripts <0x005b3>
at BizHawk.Client.EmuHawk.LuaConsole:UpdateAfter <0x000b7>
at BizHawk.Client.EmuHawk.ToolFormBase:UpdateValues <0x00074>
at BizHawk.Client.EmuHawk.ToolManager:UpdateToolsAfter <0x000b1>
at BizHawk.Client.EmuHawk.MainForm:UpdateToolsAfter <0x00037>
at BizHawk.Client.EmuHawk.MainForm:StepRunLoop_Core <0x00bdf>
at BizHawk.Client.EmuHawk.MainForm:ProgramRunLoop <0x004eb>
at BizHawk.Client.EmuHawk.MainForm:ProgramRunLoop <0x00073>
at BizHawk.Client.EmuHawk.Program:SubMain <0x0117f>
at BizHawk.Client.EmuHawk.Program:Main <0x0002b>
at <Module>:runtime_invoke_int_object <0x00091>
=================================================================
Host env.
- BizHawk 2.8; Arch Linux; AMD/NVIDIA
Could you upload the script you're using as a file? Github seems to have mangled some *
and perhaps other characters; it doesn't execute for me at least.
Could you upload the script you're using as a file? Github seems to have mangled some
*
and perhaps other characters; it doesn't execute for me at least.
crash_repro.zip fyi I just click the "Edit" button to get the script since GitHub's UI was trash here.
@Martmists-GH What SNES core were you using anyways?
I used the default which was set to snes9x
Ran the script overnight and no crashing. The stack trace doesn't seem particularly helpful in this case either. All it really says is "lua function call crashed". Might be an internal lua call or a call to BizHawk code. Can't really tell in any case :/
I checked out 2.8 on my Linux machine to try and repro this, and while I don't think the script was working correctly, it didn't crash within an hour or so. My guess is a memory leak was the cause.