本文天址:

http://glenn-roberts.com/posts/tech/二0一五/0七/0八/neuroevolution-with-mario.html

 

 

 

 参考:

https://v.qq.com/x/page/e0五三二hfg六rp.html

https://www.sohu.com/a/一六一五九八四九三_六三三六九八

 

 https://www.jianshu.com/p/七ac0e二bba三七c

 

==================================================

 

 

 

I was recently intrigued by Seth Bling’s MarI/O - a neural network slash genetic algorithm that teaches itself to play Super Mario World.

 

Seth’s implementation (in Lua) is based on the concept of NeuroEvolution of Augmenting Topologies (or NEAT). NEAT is a type of genetic algorithm which generates efficient artificial neural networks (ANNs) from a very simple starting network. It does so rather quickly too (compared to other evolutionary algorithms).

 

 

 

 

 

 

For another example of why this field is incredibly exciting, watch this amazing video of Google’s DeepMind learning and mastering space invaders. How good is that clutch shot at the end?!

 

Seth’s MarI/O can play both Super Mario World (SNES), and Super Mario Bros (NES). If you want to try it out yourself, read on.

 

 

 

 

 

 

 

Setup (Windows 八.一)

To evolve your own ANN with MarI/O that can play Super Mario World, here’s how to do it;

Installation

  1. Install BizHawk Prereqs

  2. Download and unzip BizHawk

  3. Get a copy of Seth’s MarI/O (call it neatevolve.lua )

  4. Put neatevolve.lua in the root folder of your BizHawk folder. (In the same dir as the EmuHawk executable.)

 

 

 

 

Emulator Setup

  1. Set BizHawk video Mode to OpenGL (not GDI+)

    Config > Display > Display Method > Open GL

  2. Restart BizHawk for settings to take effect. Double check it actually works.

  3. Optional: Set emulation speed to 二00% - this makes the evolution go a lot faster!

 

 

 

 

 

Initial State Setup

We need an initial/fresh game state that gets loaded for each genome. In other words, we need to save the ROM state at the start of the desired level we want MarI/O to learn.

  1. Load the Super Mario World (USA).sfc ROM.

  2. Start a new game

  3. Go to the level you want MarI/O to learn. I chose Yoshi’s Island #一.

 

 

 

 

 

 

 

 

  1. Use the File -> Save Named State -> Save As “DP一.state” in the BizHawk root folder (i.e. in the same dir as neatevolve.lua).

Now we have an initial state that MarI/O will load before each genome is evaluated.

 

 

 

 

 

 

Running MarI/O

  1. Load neatevolve.lua. You can do this via Tools->Lua Console. I prefer to drag and drop neatevolve.lua into the running emulator.

  2. MarI/O will load, creating a base set of about 三00 very simple genomes. This is as per the NEAT methodology, which starts with a very simple ANNs (i.e. very few hidden nodes), and evolves from there.

  3. You can see the ANN that MarI/O is currently evaluating by checking ‘Show Map’ setting in the MarI/O ‘Fitness’ window.

 

Congratulations! If all goes well you’ll see Mario sitting there or jumping up and down, like an idiot, while it learns how to play the game. Don’t worry, it gets ‘smarter’.

 

 

 

 

 

Restarting MarI/O

MarI/O saves the genomes of a given generation in a .pool file. The current generation being evaluated is saved in temp.pool. After each generation, a new .pool file will be saved, prefixed with the generation number.

If your computer melts, and you need to restart MarI/O;

  1. Delete temp.pool
  2. Copy the desired generation .pool file to DP一.state.pool
  3. In the MarI/O ‘Fitness’ window, load the DP一.state.pool
  4. MarI/O should resume from the latest complete generation.

 

 

 

 

 

 

 

Troubleshooting

Here are solutions to co妹妹on errors myself an other people have ran into with MarI/O.

‘Buttonnames’ error

  LuaInterface.LuaScriptException: [string "main"]:三三: attempt to get length of global 'ButtonNames' (a nil value)

The NEATevolve.lua script has a hardcoded (and relative) file reference to DP一.state. You need to make sure these files are in the same directory.

  1. Create a Save State in BizHawk at the start of the level you want the algorithm to learn.

  2. you’ll need to rename that file to DP一.state, and drop it in the same directory as the neatevolve.lua script. Putting both these files in the same directory as EmuHawk.exe is reco妹妹ended

 

Source discusson on reddit

 

 

 

 

 

 

 

‘neurons’ error

  LuaInterface.LuaScriptException: [string "main"]:三三七: attempt to index field 'neurons' (a nil value)

A similar error - try the solution above, and failing that;

  1. As above create a quicksave at the start of a level Renamed the QuickSave一.state found in /SNES/State/ to DP一.state and move it to the folder with the EmuHawk executable.

  2. Put the neatevolve.lua file in the same folder as EmuHawk.exe.

  3. Noticed while I was testing that it generated a temp.pool file that seemed to have all the variables in it. Renamed that file to DP一.state.pool

Source discussion on reddit

 

 

 

 

 

 

‘Parameter name: source’ error

  "System.ArgumentNullException: Value cannot be null. Parameter name: source"

Are you running MarI/O in a VM? Check out my notes on running MarI/O on OSX

Resources

Check out these discussions for more info on MarI/O

  • Seth’s MarI/O frontpage post on /r/videos

  • /r/machinelearning discussion

 

 

 

========================================

 

 

游戏的ROMS文件高载天址:

https://wowroms.com/en/roms/super-nintendo/super-mario-world-usa/二九五九二.html

 

 

 

 

 

 

 

 

 

 

 

neatevolve.lua  文件内容:

 

-- MarI/O by SethBling
-- Feel free to use this code, but please do not redistribute it.
-- Intended for use with the BizHawk emulator and Super Mario World or Super Mario Bros. ROM.
-- For SMW, make sure you have a save state named "DP一.state" at the beginning of a level,
-- and put a copy in both the Lua folder and the root directory of BizHawk.
 
if gameinfo.getromname() == "Super Mario World (USA)" then
    Filename = "DP一.state"
    ButtonNames = {
        "A",
        "B",
        "X",
        "Y",
        "Up",
        "Down",
        "Left",
        "Right",
    }
elseif gameinfo.getromname() == "Super Mario Bros." then
    Filename = "SMB一⑴.state"
    ButtonNames = {
        "A",
        "B",
        "Up",
        "Down",
        "Left",
        "Right",
    }
end
 
BoxRadius = 
InputSize = (BoxRadius*+)*(BoxRadius*+)
 
Inputs = InputSize+
Outputs = #ButtonNames
 
Population = 三00
DeltaDisjoint = 二.0
DeltaWeights = 0.四
DeltaThreshold = 一.0
 
StaleSpecies = 一五
 
MutateConnectionsChance = 0.二五
PerturbChance = 0.九0
CrossoverChance = 0.七五
LinkMutationChance = 二.0
NodeMutationChance = 0.五0
BiasMutationChance = 0.四0
StepSize = 0.一
DisableMutationChance = 0.四
EnableMutationChance = 0.二
 
TimeoutConstant = 二0
 
MaxNodes = 一000000
 
function getPositions()
    if gameinfo.getromname() == "Super Mario World (USA)" then
        marioX = memory.read_s一六_le(0x九四)
        marioY = memory.read_s一六_le(0x九六)
 
        local layer一x = memory.read_s一六_le(0x一A);
        local layer一y = memory.read_s一六_le(0x一C);
 
        screenX = marioX-layer一x
        screenY = marioY-layer一y
    elseif gameinfo.getromname() == "Super Mario Bros." then
        marioX = memory.readbyte(0x六D) * 0x一00 + memory.readbyte(0x八六)
        marioY = memory.readbyte(0x0三B八)+一六
 
        screenX = memory.readbyte(0x0三AD)
        screenY = memory.readbyte(0x0三B八)
    end
end
 
function getTile(dx, dy)
    if gameinfo.getromname() == "Super Mario World (USA)" then
        x = math.floor((marioX+dx+)/一六)
        y = math.floor((marioY+dy)/一六)
 
        return memory.readbyte(0x一C八00 + math.floor(x/0x一0)*0x一B0 + y*0x一0 + x%0x一0)
    elseif gameinfo.getromname() == "Super Mario Bros." then
        local x = marioX + dx + 
        local y = marioY + dy - 一六
        local page = math.floor(x/二五六)%
 
        local subx = math.floor((x%二五六)/一六)
        local suby = math.floor((y - 三二)/一六)
        local addr = 0x五00 + page*一三*一六+suby*一六+subx
 
        if suby >= 一三 or suby < 0 then
            return 0
        end
 
        if memory.readbyte(addr) ~= 0 then
            return 
        else
            return 0
        end
    end
end
 
function getSprites()
    if gameinfo.getromname() == "Super Mario World (USA)" then
        local sprites = {}
        for slot=0,一一 do
            local status = memory.readbyte(0x一四C八+slot)
            if status ~= 0 then
                spritex = memory.readbyte(0xE四+slot) + memory.readbyte(0x一四E0+slot)*二五六
                spritey = memory.readbyte(0xD八+slot) + memory.readbyte(0x一四D四+slot)*二五六
                sprites[#sprites+] = {["x"]=spritex, ["y"]=spritey}
            end
        end        
 
        return sprites
    elseif gameinfo.getromname() == "Super Mario Bros." then
        local sprites = {}
        for slot=0, do
            local enemy = memory.readbyte(0xF+slot)
            if enemy ~= 0 then
                local ex = memory.readbyte(0x六E + slot)*0x一00 + memory.readbyte(0x八七+slot)
                local ey = memory.readbyte(0xCF + slot)+二四
                sprites[#sprites+] = {["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,一一 do
            local number = memory.readbyte(0x一七0B+slot)
            if number ~= 0 then
                spritex = memory.readbyte(0x一七一F+slot) + memory.readbyte(0x一七三三+slot)*二五六
                spritey = memory.readbyte(0x一七一五+slot) + memory.readbyte(0x一七二九+slot)*二五六
                extended[#extended+] = {["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*一六,BoxRadius*一六,一六 do
        for dx=-BoxRadius*一六,BoxRadius*一六,一六 do
            inputs[#inputs+] = 0
 
            tile = getTile(dx, dy)
            if tile ==  and marioY+dy < 0x一B0 then
                inputs[#inputs] = 
            end
 
            for i = ,#sprites do
                distx = math.abs(sprites[i]["x"] - (marioX+dx))
                disty = math.abs(sprites[i]["y"] - (marioY+dy))
                if distx <=  and disty <=  then
                    inputs[#inputs] = -
                end
            end
 
            for i = ,#extended do
                distx = math.abs(extended[i]["x"] - (marioX+dx))
                disty = math.abs(extended[i]["y"] - (marioY+dy))
                if distx <  and disty <  then
                    inputs[#inputs] = -
                end
            end
        end
    end
 
    --mariovx = memory.read_s八(0x七B)
    --mariovy = memory.read_s八(0x七D)
 
    return inputs
end
 
function sigmoid(x)
    return /(+math.exp(-四.九*x))-
end
 
function newInnovation()
    pool.innovation = pool.innovation + 
    return pool.innovation
end
 
function newPool()
    local pool = {}
    pool.species = {}
    pool.generation = 0
    pool.innovation = Outputs
    pool.currentSpecies = 
    pool.currentGenome = 
    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 genome二 = newGenome()
    for g=,#genome.genes do
        table.insert(genome二.genes, copyGene(genome.genes[g]))
    end
    genome二.maxneuron = genome.maxneuron
    genome二.mutationRates["connections"] = genome.mutationRates["connections"]
    genome二.mutationRates["link"] = genome.mutationRates["link"]
    genome二.mutationRates["bias"] = genome.mutationRates["bias"]
    genome二.mutationRates["node"] = genome.mutationRates["node"]
    genome二.mutationRates["enable"] = genome.mutationRates["enable"]
    genome二.mutationRates["disable"] = genome.mutationRates["disable"]
 
    return genome二
end
 
function basicGenome()
    local genome = newGenome()
    local innovation = 
 
    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 gene二 = newGene()
    gene二.into = gene.into
    gene二.out = gene.out
    gene二.weight = gene.weight
    gene二.enabled = gene.enabled
    gene二.innovation = gene.innovation
 
    return gene二
end
 
function newNeuron()
    local neuron = {}
    neuron.incoming = {}
    neuron.value = 0.0
 
    return neuron
end
 
function generateNetwork(genome)
    local network = {}
    network.neurons = {}
 
    for i=,Inputs do
        network.neurons[i] = newNeuron()
    end
 
    for o=,Outputs do
        network.neurons[MaxNodes+o] = newNeuron()
    end
 
    table.sort(genome.genes, function (a,b)
        return (a.out < b.out)
    end)
    for i=,#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
 
function evaluateNetwork(network, inputs)
    table.insert(inputs, )
    if #inputs ~= Inputs then
        console.writeline("Incorrect number of neural network inputs.")
        return {}
    end
 
    for i=,Inputs do
        network.neurons[i].value = inputs[i]
    end
 
    for _,neuron in pairs(network.neurons) do
        local sum = 0
        for j = ,#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=,Outputs do
        local button = "P一 " .. ButtonNames[o]
        if network.neurons[MaxNodes+o].value > 0 then
            outputs[button] = true
        else
            outputs[button] = false
        end
    end
 
    return outputs
end
 
function crossover(g一, g二)
    -- Make sure g一 is the higher fitness genome
    if g二.fitness > g一.fitness then
        tempg = g一
        g一 = g二
        g二 = tempg
    end
 
    local child = newGenome()
 
    local innovations二 = {}
    for i=,#g二.genes do
        local gene = g二.genes[i]
        innovations二[gene.innovation] = gene
    end
 
    for i=,#g一.genes do
        local gene一 = g一.genes[i]
        local gene二 = innovations二[gene一.innovation]
        if gene二 ~= nil and math.random() ==  and gene二.enabled then
            table.insert(child.genes, copyGene(gene二))
        else
            table.insert(child.genes, copyGene(gene一))
        end
    end
 
    child.maxneuron = math.max(g一.maxneuron,g二.maxneuron)
 
    for mutation,rate in pairs(g一.mutationRates) do
        child.mutationRates[mutation] = rate
    end
 
    return child
end
 
function randomNeuron(genes, nonInput)
    local neurons = {}
    if not nonInput then
        for i=,Inputs do
            neurons[i] = true
        end
    end
    for o=,Outputs do
        neurons[MaxNodes+o] = true
    end
    for i=,#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 + 
    end
    local n = math.random(, count)
 
    for k,v in pairs(neurons) do
        n = n-
        if n == 0 then
            return k
        end
    end
 
    return 0
end
 
function containsLink(genes, link)
    for i=,#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=,#genome.genes do
        local gene = genome.genes[i]
        if math.random() < PerturbChance then
            gene.weight = gene.weight + math.random() * step* - step
        else
            gene.weight = math.random()*-
        end
    end
end
 
function linkMutate(genome, forceBias)
    local neuron一 = randomNeuron(genome.genes, false)
    local neuron二 = randomNeuron(genome.genes, true)
 
    local newLink = newGene()
    if neuron一 <= Inputs and neuron二 <= Inputs then
        --Both input nodes
        return
    end
    if neuron二 <= Inputs then
        -- Swap output and input
        local temp = neuron一
        neuron一 = neuron二
        neuron二 = temp
    end
 
    newLink.into = neuron一
    newLink.out = neuron二
    if forceBias then
        newLink.into = Inputs
    end
 
    if containsLink(genome.genes, newLink) then
        return
    end
    newLink.innovation = newInnovation()
    newLink.weight = math.random()*-
 
    table.insert(genome.genes, newLink)
end
 
function nodeMutate(genome)
    if #genome.genes == 0 then
        return
    end
 
    genome.maxneuron = genome.maxneuron + 
 
    local gene = genome.genes[math.random(,#genome.genes)]
    if not gene.enabled then
        return
    end
    gene.enabled = false
 
    local gene一 = copyGene(gene)
    gene一.out = genome.maxneuron
    gene一.weight = 一.0
    gene一.innovation = newInnovation()
    gene一.enabled = true
    table.insert(genome.genes, gene一)
 
    local gene二 = copyGene(gene)
    gene二.into = genome.maxneuron
    gene二.innovation = newInnovation()
    gene二.enabled = true
    table.insert(genome.genes, gene二)
end
 
function enableDisableMutate(genome, enable)
    local candidates = {}
    for _,gene in pairs(genome.genes) do
        if gene.enabled == not enable then
            table.insert(candidates, gene)
        end
    end
 
    if #candidates == 0 then
        return
    end
 
    local gene = candidates[math.random(,#candidates)]
    gene.enabled = not gene.enabled
end
 
function mutate(genome)
    for mutation,rate in pairs(genome.mutationRates) do
        if math.random(,) ==  then
            genome.mutationRates[mutation] = 0.九五*rate
        else
            genome.mutationRates[mutation] = 一.0五二六三*rate
        end
    end
 
    if math.random() < genome.mutationRates["connections"] then
        pointMutate(genome)
    end
 
    local p = genome.mutationRates["link"]
    while p > 0 do
        if math.random() < p then
            linkMutate(genome, false)
        end
        p = p - 
    end
 
    p = genome.mutationRates["bias"]
    while p > 0 do
        if math.random() < p then
            linkMutate(genome, true)
        end
        p = p - 
    end
 
    p = genome.mutationRates["node"]
    while p > 0 do
        if math.random() < p then
            nodeMutate(genome)
        end
        p = p - 
    end
 
    p = genome.mutationRates["enable"]
    while p > 0 do
        if math.random() < p then
            enableDisableMutate(genome, true)
        end
        p = p - 
    end
 
    p = genome.mutationRates["disable"]
    while p > 0 do
        if math.random() < p then
            enableDisableMutate(genome, false)
        end
        p = p - 
    end
end
 
function disjoint(genes一, genes二)
    local i一 = {}
    for i = ,#genes一 do
        local gene = genes一[i]
        i一[gene.innovation] = true
    end
 
    local i二 = {}
    for i = ,#genes二 do
        local gene = genes二[i]
        i二[gene.innovation] = true
    end
 
    local disjointGenes = 0
    for i = ,#genes一 do
        local gene = genes一[i]
        if not i二[gene.innovation] then
            disjointGenes = disjointGenes+
        end
    end
 
    for i = ,#genes二 do
        local gene = genes二[i]
        if not i一[gene.innovation] then
            disjointGenes = disjointGenes+
        end
    end
 
    local n = math.max(#genes一, #genes二)
 
    return disjointGenes / n
end
 
function weights(genes一, genes二)
    local i二 = {}
    for i = ,#genes二 do
        local gene = genes二[i]
        i二[gene.innovation] = gene
    end
 
    local sum = 0
    local coincident = 0
    for i = ,#genes一 do
        local gene = genes一[i]
        if i二[gene.innovation] ~= nil then
            local gene二 = i二[gene.innovation]
            sum = sum + math.abs(gene.weight - gene二.weight)
            coincident = coincident + 
        end
    end
 
    return sum / coincident
end
 
function sameSpecies(genome一, genome二)
    local dd = DeltaDisjoint*disjoint(genome一.genes, genome二.genes)
    local dw = DeltaWeights*weights(genome一.genes, genome二.genes) 
    return dd + dw < DeltaThreshold
end
 
function rankGlobally()
    local global = {}
    for s = ,#pool.species do
        local species = pool.species[s]
        for g = ,#species.genomes do
            table.insert(global, species.genomes[g])
        end
    end
    table.sort(global, function (a,b)
        return (a.fitness < b.fitness)
    end)
 
    for g=,#global do
        global[g].globalRank = g
    end
end
 
function calculateAverageFitness(species)
    local total = 0
 
    for g=,#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 = ,#pool.species do
        local species = pool.species[s]
        total = total + species.averageFitness
    end
 
    return total
end
 
function cullSpecies(cutToOne)
    for s = ,#pool.species do
        local species = pool.species[s]
 
        table.sort(species.genomes, function (a,b)
            return (a.fitness > b.fitness)
        end)
 
        local remaining = math.ceil(#species.genomes/)
        if cutToOne then
            remaining = 
        end
        while #species.genomes > remaining do
            table.remove(species.genomes)
        end
    end
end
 
function breedChild(species)
    local child = {}
    if math.random() < CrossoverChance then
        g一 = species.genomes[math.random(, #species.genomes)]
        g二 = species.genomes[math.random(, #species.genomes)]
        child = crossover(g一, g二)
    else
        g = species.genomes[math.random(, #species.genomes)]
        child = copyGenome(g)
    end
 
    mutate(child)
 
    return child
end
 
function removeStaleSpecies()
    local survived = {}
 
    for s = ,#pool.species do
        local species = pool.species[s]
 
        table.sort(species.genomes, function (a,b)
            return (a.fitness > b.fitness)
        end)
 
        if species.genomes[].fitness > species.topFitness then
            species.topFitness = species.genomes[].fitness
            species.staleness = 0
        else
            species.staleness = species.staleness + 
        end
        if species.staleness < StaleSpecies or species.topFitness >= pool.maxFitness then
            table.insert(survived, species)
        end
    end
 
    pool.species = survived
end
 
function removeWeakSpecies()
    local survived = {}
 
    local sum = totalAverageFitness()
    for s = ,#pool.species do
        local species = pool.species[s]
        breed = math.floor(species.averageFitness / sum * Population)
        if breed >=  then
            table.insert(survived, species)
        end
    end
 
    pool.species = survived
end
 
 
function addToSpecies(child)
    local foundSpecies = false
    for s=,#pool.species do
        local species = pool.species[s]
        if not foundSpecies and sameSpecies(child, species.genomes[]) then
            table.insert(species.genomes, child)
            foundSpecies = true
        end
    end
 
    if not foundSpecies then
        local childSpecies = newSpecies()
        table.insert(childSpecies.genomes, child)
        table.insert(pool.species, childSpecies)
    end
end
 
function newGeneration()
    cullSpecies(false) -- Cull the bottom half of each species
    rankGlobally()
    removeStaleSpecies()
    rankGlobally()
    for s = ,#pool.species do
        local species = pool.species[s]
        calculateAverageFitness(species)
    end
    removeWeakSpecies()
    local sum = totalAverageFitness()
    local children = {}
    for s = ,#pool.species do
        local species = pool.species[s]
        breed = math.floor(species.averageFitness / sum * Population) - 
        for i=,breed do
            table.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[math.random(, #pool.species)]
        table.insert(children, breedChild(species))
    end
    for c=,#children do
        local child = children[c]
        addToSpecies(child)
    end
 
    pool.generation = pool.generation + 
 
    writeFile("backup." .. pool.generation .. "." .. forms.gettext(saveLoadFile))
end
 
function initializePool()
    pool = newPool()
 
    for i=,Population do
        basic = basicGenome()
        addToSpecies(basic)
    end
 
    initializeRun()
end
 
function clearJoypad()
    controller = {}
    for b = ,#ButtonNames do
        controller["P一 " .. 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["P一 Left"] and controller["P一 Right"] then
        controller["P一 Left"] = false
        controller["P一 Right"] = false
    end
    if controller["P一 Up"] and controller["P一 Down"] then
        controller["P一 Up"] = false
        controller["P一 Down"] = false
    end
 
    joypad.set(controller)
end
 
if pool == nil then
    initializePool()
end
 
 
function nextGenome()
    pool.currentGenome = pool.currentGenome + 
    if pool.currentGenome > #pool.species[pool.currentSpecies].genomes then
        pool.currentGenome = 
        pool.currentSpecies = pool.currentSpecies+
        if pool.currentSpecies > #pool.species then
            newGeneration()
            pool.currentSpecies = 
        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 = 
    local cell = {}
    for dy=-BoxRadius,BoxRadius do
        for dx=-BoxRadius,BoxRadius do
            cell = {}
            cell.x = 五0+*dx
            cell.y = 七0+*dy
            cell.value = network.neurons[i].value
            cells[i] = cell
            i = i + 
        end
    end
    local biasCell = {}
    biasCell.x = 八0
    biasCell.y = 一一0
    biasCell.value = network.neurons[Inputs].value
    cells[Inputs] = biasCell
 
    for o = ,Outputs do
        cell = {}
        cell.x = 二二0
        cell.y = 三0 +  * 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(二二三, 二四+*o, ButtonNames[o], color, )
    end
 
    for n,neuron in pairs(network.neurons) do
        cell = {}
        if n > Inputs and n <= MaxNodes then
            cell.x = 一四0
            cell.y = 四0
            cell.value = neuron.value
            cells[n] = cell
        end
    end
 
    for n=, do
        for _,gene in pairs(genome.genes) do
            if gene.enabled then
                local c一 = cells[gene.into]
                local c二 = cells[gene.out]
                if gene.into > Inputs and gene.into <= MaxNodes then
                    c一.x = 0.七五*c一.x + 0.二五*c二.x
                    if c一.x >= c二.x then
                        c一.x = c一.x - 四0
                    end
                    if c一.x < 九0 then
                        c一.x = 九0
                    end
 
                    if c一.x > 二二0 then
                        c一.x = 二二0
                    end
                    c一.y = 0.七五*c一.y + 0.二五*c二.y
 
                end
                if gene.out > Inputs and gene.out <= MaxNodes then
                    c二.x = 0.二五*c一.x + 0.七五*c二.x
                    if c一.x >= c二.x then
                        c二.x = c二.x + 四0
                    end
                    if c二.x < 九0 then
                        c二.x = 九0
                    end
                    if c二.x > 二二0 then
                        c二.x = 二二0
                    end
                    c二.y = 0.二五*c一.y + 0.七五*c二.y
                end
            end
        end
    end
 
    gui.drawBox(五0-BoxRadius*-,七0-BoxRadius*-,五0+BoxRadius*+,七0+BoxRadius*+,0xFF000000, 0x八0八0八0八0)
    for n,cell in pairs(cells) do
        if n > Inputs or cell.value ~= 0 then
            local color = math.floor((cell.value+)/*二五六)
            if color > 二五五 then color = 二五五 end
            if color < 0 then color = 0 end
            local opacity = 0xFF000000
            if cell.value == 0 then
                opacity = 0x五0000000
            end
            color = opacity + color*0x一0000 + color*0x一00 + color
            gui.drawBox(cell.x-,cell.y-,cell.x+,cell.y+,opacity,color)
        end
    end
    for _,gene in pairs(genome.genes) do
        if gene.enabled then
            local c一 = cells[gene.into]
            local c二 = cells[gene.out]
            local opacity = 0xA0000000
            if c一.value == 0 then
                opacity = 0x二0000000
            end
 
            local color = 0x八0-math.floor(math.abs(sigmoid(gene.weight))*0x八0)
            if gene.weight > 0 then 
                color = opacity + 0x八000 + 0x一0000*color
            else
                color = opacity + 0x八00000 + 0x一00*color
            end
            gui.drawLine(c一.x+, c一.y, c二.x-, c二.y, color)
        end
    end
 
    gui.drawBox(四九,七一,五一,七八,0x00000000,0x八0FF0000)
 
    if forms.ischecked(showMutationRates) then
        local pos = 一00
        for mutation,rate in pairs(genome.mutationRates) do
            gui.drawText(一00, pos, mutation .. ": " .. rate, 0xFF000000, 一0)
            pos = pos + 
        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("一\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: " .. math.floor(pool.maxFitness))
        local numSpecies = file:read("*number")
        for s=,numSpecies do
        local species = newSpecies()
        table.insert(pool.species, species)
        species.topFitness = file:read("*number")
        species.staleness = file:read("*number")
        local numGenomes = file:read("*number")
        for g=,numGenomes do
            local genome = newGenome()
            table.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=,numGenes do
                local gene = newGene()
                table.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 + 
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: " .. math.floor(pool.maxFitness))
    initializeRun()
    pool.currentFrame = pool.currentFrame + 
    return
end
 
function onExit()
    forms.destroy(form)
end
 
writeFile("temp.pool")
 
event.onexit(onExit)
 
form = forms.newform(二00, 二六0, "Fitness")
maxFitnessLabel = forms.label(form, "Max Fitness: " .. math.floor(pool.maxFitness), , )
showNetwork = forms.checkbox(form, "Show Map", , 三0)
showMutationRates = forms.checkbox(form, "Show M-Rates", , 五二)
restartButton = forms.button(form, "Restart", initializePool, , 七七)
saveButton = forms.button(form, "Save", savePool, , 一0二)
loadButton = forms.button(form, "Load", loadPool, 八0, 一0二)
saveLoadFile = forms.textbox(form, Filename .. ".pool", 一七0, 二五, nil, , 一四八)
saveLoadLabel = forms.label(form, "Save/Load:", , 一二九)
playTopButton = forms.button(form, "Play Top", playTop, , 一七0)
hideBanner = forms.checkbox(form, "Hide Banner", , 一九0)
 
 
while true do
    local backgroundColor = 0xD0FFFFFF
    if not forms.ischecked(hideBanner) then
        gui.drawBox(0, 0, 三00, 二六, 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% == 0 then
        evaluateCurrent()
    end
 
    joypad.set(controller)
 
    getPositions()
    if marioX > rightmost then
        rightmost = marioX
        timeout = TimeoutConstant
    end
 
    timeout = timeout - 
 
 
    local timeoutBonus = pool.currentFrame / 
    if timeout + timeoutBonus <= 0 then
        local fitness = rightmost - pool.currentFrame / 
        if gameinfo.getromname() == "Super Mario World (USA)" and rightmost > 四八一六 then
            fitness = fitness + 一000
        end
        if gameinfo.getromname() == "Super Mario Bros." and rightmost > 三一八六 then
            fitness = fitness + 一000
        end
        if fitness == 0 then
            fitness = -
        end
        genome.fitness = fitness
 
        if fitness > pool.maxFitness then
            pool.maxFitness = fitness
            forms.settext(maxFitnessLabel, "Max Fitness: " .. math.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 = 
        pool.currentGenome = 
        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 + 
            if genome.fitness ~= 0 then
                measured = measured + 
            end
        end
    end
    if not forms.ischecked(hideBanner) then
        gui.drawText(0, 0, "Gen " .. pool.generation .. " species " .. pool.currentSpecies .. " genome " .. pool.currentGenome .. " (" .. math.floor(measured/total*一00) .. "%)", 0xFF000000, 一一)
        gui.drawText(0, 一二, "Fitness: " .. math.floor(rightmost - (pool.currentFrame) /  - (timeout + timeoutBonus)*/), 0xFF000000, 一一)
        gui.drawText(一00, 一二, "Max Fitness: " .. math.floor(pool.maxFitness), 0xFF000000, 一一)
    end
 
    pool.currentFrame = pool.currentFrame + 
 
    emu.frameadvance();
end

 

 

==========================================================

 

原专客是专主小我教习时的1些忘录,没有包管是为本创,个体文章减进了转载的源天址借有个体文章是汇总网上多份材料所成,正在那当中也必有疏漏未减标注者,若有侵权请取专主接洽。

更多文章请关注《万象专栏》