-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenetic_algorithm.lua
180 lines (138 loc) · 5.94 KB
/
genetic_algorithm.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
population = {} --contenedor de poblacion
backgroundColor = 0x2C3D72000 -- color rojo
FRAME_COUNT = 3500 --frames para pasar el juego
POPULATION_SIZE = 10 --tamaño de individuos en la generacion
TOURNAMENT_SIZE = 3 --tamaño de individuos en el torneo para reproducirse
JUMP_WEIGHT = 0.95 -- probabilidad de saltar
RIGHT_WEIGHT = 0.9 --probabilidad de moverse a la derecha
MUTATION_RATE_0 = 0.05 --probabilidad de no saltar
MUTATION_RATE_1 = 0.15 --probabilidad de saltar
FILE_NAME = "SMB1-1.state" --archivo de juego guardado iniciando el nivel
ROM_NAME = "mario_rom.nes"
function generateIndividualDNA() --generar DNA de un invividuo random
local individual = {}
individual.bytes = {}
bytes = "_"
for count=0, FRAME_COUNT do
if math.random() < JUMP_WEIGHT then
individual.bytes[#individual.bytes+1] = 1 -- probabilidad JUMP_WEIGHT (93%) de saltar
else
individual.bytes[#individual.bytes+1] = 0 -- probabilidad 1 - JUMP_WEIGHT (7%) de no saltar
end
bytes = bytes .. individual.bytes[#individual.bytes] --concatenar string con nuevo movimiento
end
return individual --regresar el DNA del individuo
end
function generateRandomPopulation() --generar primera poblacion random
for count=0,POPULATION_SIZE do
population[count] = generateIndividualDNA()
end
end
function tournamentSelection() --tomar individuos de la poblacion al azar y regresar los mejores
highestIndex = 0
highestFit = 0
highestSpeed = 3500
for i=0, TOURNAMENT_SIZE do
index = math.random(#population)
-- elegir el mejor o si hay un empate elegir el mas rapido
if population[index].fitness > highestFit or (population[index].fitness == highestFit and population[index].frameNumber < highestSpeed) then
highestIndex = index
highestSpeed = population[index].frameNumber
highestFit = population[index].fitness
end
end
return population[highestIndex]
end
function crossover(indiv1, indiv2) --tomar dos individuos y combinarlos (reproducirlos)
newIndiv = {}
newIndiv.bytes = {}
index = math.random(#indiv1.bytes) --random numero de bytes que el primer padre
for i=1, index do --copiar elementos del primer padre
newIndiv.bytes[#newIndiv.bytes+1] = indiv1.bytes[i]
end
for i=index, #indiv1. bytes do --copiar elementos del segundo padre
newIndiv.bytes[#newIndiv.bytes+1] = indiv2.bytes[i]
end
return newIndiv --regresar el hijo
end
function mutate(indiv) --cambiar un poco el DNA d un individuo
newIndiv = {}
newIndiv.bytes = {}
for i=1, #indiv.bytes do
if indiv.bytes[i] == 1 and math.random() < MUTATION_RATE_1 then
newIndiv.bytes[#newIndiv.bytes+1] = 0 --convertir 1 en 0
else if indiv.bytes[i] == 0 and math.random() < MUTATION_RATE_0 then
newIndiv.bytes[#newIndiv.bytes+1] = 1 --convertir 1 en 0
else --caso inutil
newIndiv.bytes[#newIndiv.bytes+1] = indiv.bytes[i]
end
end
end
return newIndiv
end
highestFit = 0 --iniciar la mejor finess a 0
highestSpeed = 3500 --iniciar la mejor velocidad (menor es mejor)
function evolvePopulation()
local newPopulation = {}
--keeping the highest fitness
highestIndex = 0
highestFit = 0
highestSpeed = 3500
for i=1, #population do
if population[i].fitness > highestFit or (population[i].fitness == highestFit and population[i].frameNumber < highestSpeed) then
highestIndex = i
highestSpeed = population[i].frameNumber
highestFit = population[i].fitness
end
end
newPopulation[#newPopulation+1] = population[highestIndex]
for i=2, POPULATION_SIZE do
indiv1 = tournamentSelection() --elegir individuo 1 en base a torneo
indiv2 = tournamentSelection() --elegir individuo 2 en base a torneo
newPopulation[#newPopulation+1] = crossover(indiv1, indiv2) --hijo entren in1 y in2
end
for i=2, POPULATION_SIZE do
newPopulation[i] = mutate(newPopulation[i])
end
population = newPopulation --remplazando nueva poblacion con una nueva
end
console.writeline("Generando poblacion random.")
generateRandomPopulation()
console.writeline("Listo. Jugando con primera generacion.")
generation = 0
while true do --Hacer para siempre
generation = generation + 1
for index=1, #population do --por cada individuo en la poblacion
local moveIndex = 0
savestate.load(FILE_NAME); --cargar el inicio del nivel
population[index].frameNumber = 0
while true do --play with the individual
population[index].fitness = memory.readbyte(0x6D) * 0x100 + memory.readbyte(0x86) --mario X position in level
population[index].fitness = population[index].fitness / 20 --normalizando dividiendo por 50
population[index].fitness = math.floor(population[index].fitness) --redondeando a un int
population[index].frameNumber = population[index].frameNumber + 1 --sumando ++1 al frame
--dibujar GUI Heads Up display
gui.drawBox(0, 0, 300, 35, backgroundColor, backgroundColor)
gui.drawText(0, 0, "Generacion No." .. generation .. "- Individuo No." .. index, 0xFFFFFFFF, 8)
gui.drawText(0, 10, "Fitness = " .. population[index].fitness .. " en " .. population[index].frameNumber .. " frames", 0xFFFFFFFF, 8)
gui.drawText(0, 20, "Top Fitness = " .. highestFit .. " en " .. highestSpeed .. " frames", 0xFFFFFFFF, 8)
controller = {}
if population[index].bytes[moveIndex] == 0 then
controller["P1 B"] = true --true si el DNA dice 0
else
controller["P1 A"] = true -- population[index].bytes[moveIndex] == 1 --true si el DNA dice 1
end
moveIndex = moveIndex + 1 --moveindex++
controller["P1 Right"] = true --presionar A para saltar en P1
joypad.set(controller) --api para presionar usar el control
emu.frameadvance(); --Api avanzar frame en el juego
if memory.readbyte(0x000E) == 0x06 then --si mario muere reiniciar juego con siguiente individuo
break
end
end
console.writeline("Fitness reached: " .. index .. "> " .. population[index].fitness) --log finess alcanzado
end
console.writeline("")
console.writeline("Evolucionando nueva generation." .. "(" .. generation + 1 .. ")")
evolvePopulation()
end