-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenetic_game.py
219 lines (169 loc) · 6.57 KB
/
genetic_game.py
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import time
import random
import os
import copy
import argparse
import sys
import world
import player
import random_player
import genetic_player
import assembler
import cpu
class GeneticGame(object):
def __init__(self, w, h, players):
self.w = w
self.h = h
self.players = players
for player in self.players:
player.dist = 0
player.survived = 0
player.kills = 0
def new_world(self):
self.world = world.World(w = self.w, h = self.h)
for player in self.players:
# Reset the hitpoints.
player.hitpoints = 2
self.world.add_player("Player %d" % (len(self.world.players) + 1), player)
def draw_world(self):
return self.world.draw()
def step(self):
self.world.step()
def run_game(self, count):
for i in xrange(count):
self.step()
for world_player in self.world.players:
# Copy the results down to the player itself.
world_player.player.dist += world_player.distance_travelled
world_player.player.survived += 1 if world_player.is_alive() else 0
world_player.player.kills += world_player.kills
def get_winners(self):
def find_winners(attr_func):
winners = []
curr_value = 0
for p in self.players:
value = attr_func(p)
if not value:
continue
if value > curr_value:
curr_value = value
winners = [p]
continue
if value == curr_value:
winners.append(p)
return winners
return find_winners(lambda p:p.dist), find_winners(lambda p:p.survived), find_winners(lambda p:p.kills)
#for i in range(len(self.players)):
# print "-" * 20
# print "Player %d" % i
# player = self.players[i]
# print "Distance: %d" % player.dist
# print "Survived: %d" % player.survived
# print "Kills: %d" % player.kills
def mutate_insert_opcode(chromo):
i = 0
if chromo.data:
# Get a list of 'safe' indexes we can insert at.
test_cpu = cpu.CPU(chromo.data)
indexes = test_cpu.index_mem()
# Insert it where?
i = orig_i = random.choice(indexes)
# Generate an opcode to insert
opcode = random.randint(0, len(cpu.instructionset) - 1)
chromo.data.insert(i, opcode)
# Generate any extra needed for this opcode.
for _ in range(cpu.instructionset[opcode].extradata_len):
i += 1
jumplength = random.randint(-128, 127)
chromo.data.insert(i, jumplength)
def mutate_remove_opcode(chromo):
# If the chromo is empty, do nothing.
if not chromo.data:
return
# Get a list of 'safe' indexes we can insert at.
test_cpu = cpu.CPU(chromo.data)
indexes = test_cpu.index_mem()
# Insert it where?
i = orig_i = random.choice(indexes)
opcode = chromo.data[i]
chromo.data.pop(i)
# Remove any extra data for this opcode.
for _ in range(cpu.instructionset[opcode].extradata_len):
chromo.data.pop(i)
# If its empty, reset the weight so it'll get eliminated someday.
if not chromo.data:
chromo.weight = 0
def mutate(chromo):
# 5% chance of mutation
chance = random.randint(1,100)
if chance <= 5:
# select a mutation
mutations = [ mutate_insert_opcode,
mutate_remove_opcode ]
func = random.choice(mutations)
func(chromo)
def mutate_order(chromosomes):
chance = random.randint(1,100)
if chance <= 2:
a = random.randint(0, len(chromosomes) -1)
b = random.randint(0, len(chromosomes) -1)
if a != b:
chromosomes[a], chromosomes[b] = chromosomes[b], chromosomes[a]
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--num_players", action='store', default=10)
parser.add_argument("--num_generations", action='store', default=10)
parser.add_argument("--num_games_per_generation", action='store', default=10)
parser.add_argument("--num_steps_per_game", action='store', default=100000)
args = parser.parse_args()
players = [genetic_player.GeneticPlayer(2) for _ in range(int(args.num_players))]
for generation in range(int(args.num_generations)):
print "Running generation %d" % generation
game = GeneticGame(15,15, players)
# Run 10 games
for i in range(int(args.num_games_per_generation)):
print "\tRunning game %d" % i
game.new_world()
game.run_game(int(args.num_steps_per_game))
# Write the players out for study.
out_path = "./generation_%d" % (generation, )
os.mkdir(out_path)
for i in range(len(game.world.players)):
players[i].write(out_path, "player_%d" % i)
# Get the winners
dist_winners, survival_winners, kill_winners = game.get_winners()
# Add weights for the better players
for p in dist_winners:
p.add_weight(2)
for p in kill_winners:
p.add_weight(3)
# From the winners generate 10 new children
new_players = []
for _ in range(10):
# Pick 2 winners
a = random.choice(players)
b = random.choice(players)
def random_weighted(a,b):
total = 1 + a.weight + b.weight
r = random.randint(1,total)
if r > a.weight:
return b
return a
# Copy the chromosome, so we get indiviually mutated ones. Otherwise,
# if we mutuate one, it would mutate all copys of that chromosome
new_chromosomes = [copy.copy(random_weighted(c[0], c[1])) for c in zip(a.chromosomes, b.chromosomes)]
if len(a.chromosomes) > len(new_chromosomes):
new_chromosomes += a.chromosomes[len(new_chromosomes):]
if len(b.chromosomes) > len(new_chromosomes):
new_chromosomes += b.chromosomes[len(new_chromosomes):]
# Now mutate individual chromosomes.
for chromo in new_chromosomes:
mutate(chromo)
# Mutate the order
mutate_order(new_chromosomes)
# Create the new player
new_players.append(genetic_player.GeneticPlayer(2, new_chromosomes))
players = new_players
# Get the code from the player
disassem = assembler.Disassembler()
print disassem.disassemble(players[0].cpu.memory)