-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathagent.py
More file actions
376 lines (316 loc) · 13.7 KB
/
agent.py
File metadata and controls
376 lines (316 loc) · 13.7 KB
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
'''
Created on 2010-04-21
@author: rv
'''
import random
from itertools import product
class Agent:
'''
classdocs
'''
costForMatingRange = 0,0 #10,40
tagsProbability = 0.55
def __init__(self, env, x = 0, y = 0, metabolism = 4, vision = 6, endowment = 25, maxAge = 100, sexe = 0, fertility = (12,50), tags = (0, 11)):
'''
Constructor
'''
self.env = env
self.x = x
self.y = y
self.metabolism = metabolism
self.vision = vision
self.maxAge = maxAge
self.age = 0
self.sexe = sexe
self.fertility = fertility
self.setInitialEndowment(endowment)
self.setTags(tags)
'''
get / set section
'''
def getEnv(self):
return self.env
def setLocation(self, (x, y)):
self.x = x
self.y = y
def getLocation(self):
return (self.x, self.y)
def setMetabolism(self, metabolism):
self.metabolism = metabolism
def getMetabolism(self):
return self.metabolism
def setVision(self, vision):
self.vision = vision
def getVision(self):
return self.vision
def setInitialEndowment(self, endowment):
self.endowment = endowment
self.sugar = endowment
if self.costForMatingRange == (0,0):
self.costForMating = endowment
else:
self.costForMating = random.randint(self.costForMatingRange[0], self.costForMatingRange[1])
def getSugar(self):
return self.sugar
def setAge(self, maxAge):
self.maxAge = maxAge
self.age = 0
def getAge(self):
return self.age
def setSexe(self, sexe):
self.sexe = sexe
def getSexe(self):
return self.sexe
def setFertility(self, fertility):
self.fertility = fertility
def setTags(self, (tags, tagsLength)):
self.tags = tags
self.tagsLength = tagsLength
self.tribe = round(float(bin(tags).count('1')) / float(tagsLength))
def getTags(self):
return self.tags
def getTagsLength(self):
return self.tagsLength
def getTribe(self):
return self.tribe
'''
build common lists
'''
# build a list of available food locations
def getFood(self):
food = [(x, self.y) for x in range(self.x - self.vision, self.x + self.vision + 2)
if self.env.isLocationValid((x, self.y))
and self.env.isLocationFree((x, self.y))]
food.extend([(self.x, y) for y in range (self.y - self.vision, self.y + self.vision + 2)
if self.env.isLocationValid((self.x, y))
and self.env.isLocationFree((self.x, y))])
return food
# build a list of possible neighbours for in neighbourhood
def getNeighbourhood(self):
neighbourhood = [self.env.getAgent((x,self.y)) for x in range(self.x - 1, self.x + 2)
if self.env.isLocationValid((x, self.y))
and not self.env.isLocationFree((x, self.y))
and x != self.x]
neighbourhood.extend([self.env.getAgent((self.x,y)) for y in range(self.y - 1, self.y + 2)
if self.env.isLocationValid((self.x, y))
and not self.env.isLocationFree((self.x, y))
and y != self.y])
return neighbourhood
# build a list of possible preys around
def getPreys(self):
preys = [self.env.getAgent((x, self.y)) for x in range(self.x - self.vision, self.x + self.vision + 2)
if self.env.isLocationValid((x, self.y))
and not self.env.isLocationFree((x, self.y))
and self.sugar > self.env.getAgent((x, self.y)).getSugar()
and self.env.getAgent((x, self.y)).getTribe() != self.tribe]
preys.extend([self.env.getAgent((self.x, y)) for y in range(self.y - self.vision, self.y + self.vision + 2)
if self.env.isLocationValid((self.x, y))
and not self.env.isLocationFree((self.x, y))
and self.sugar > self.env.getAgent((self.x, y)).getSugar()
and self.env.getAgent((self.x, y)).getTribe() != self.tribe])
return preys
'''
rules
'''
# TRANSMIT
def transmit(self):
# build a list of possible neighbours for in neighbourhood
neighbourhood = self.getNeighbourhood()
# tag-flipping with neighbours
for neighbour in neighbourhood:
mask = 1 << random.randint(0, self.tagsLength - 1)
neighbourTags = neighbour.getTags()
if (self.tags & mask) != (neighbourTags & mask):
# flip neighbour's tag
neighbourTags ^= mask
# transmit new tag
neighbour.setTags((neighbourTags, self.tagsLength))
# AGEING
# If age > maxAge Then the agent is dead (return False)
def incAge(self):
self.age += 1
return (max(self.maxAge - self.age, 0))
# FERTILITY
# First, to have offspring, agents must be of childbearing age.
# Second, children born with literally no initial endowment of sugar would instantly die.
# We therefore require that parents give their children some initial endowment.
# Each newborn's endowment is the sum of the (usually unequal) contributions of mother and father.
# Dad contributes an amount equal to one half of whatever his initial endowment had been, and likewise for mom.
# To be parents, agents must have amassed at least the amount of sugar which they were endowed at birth.
def isFertile(self):
return (self.age >= self.fertility[0] and self.age <= self.fertility[1] and self.sugar >= self.costForMating)
# MATE (Generator):
# Select a neighboring agent at random.
# If the neighbor is fertile and of the opposite sex and at least one of the agent has an empty neighboring site, then a child is born
# Repeat for all neighbors.
def mate(self):
# build a list of possible partners in neighbourhood
neighbourhood = self.getNeighbourhood()
# randomize
random.shuffle(neighbourhood)
# mate with (all) possible partners
for neighbour in neighbourhood:
# partner selection
if neighbour.getSexe() == self.sexe or not neighbour.isFertile():
continue
# find a free location around the agent for the baby
freeLocation = self.findFreeLocationAround(self.x, self.y)
if not freeLocation:
# or find around the partner
freeLocation = self.findFreeLocationAround(neighbour.x, neighbour.y)
# then, give birth if a location has been found
if freeLocation:
yield self.createChild(neighbour, freeLocation)
# Find a free location around x,y (for baby)
def findFreeLocationAround(self, x, y):
'''locations = [(i, j) for i,j in product(range(x - 1, x + 2), range(y - 1, y + 2))
if self.env.isLocationValid((i, j))
and self.env.isLocationFree((i, j))]'''
locations = [(i, y) for i in range(x - 1, x + 2) if self.env.isLocationValid((i, y)) and self.env.isLocationFree((i, y))]
locations.extend([(x, j) for j in range(y - 1, y + 2) if self.env.isLocationValid((x, j)) and self.env.isLocationFree((x, j))])
length = len(locations)
if length:
return locations[random.randint(0, length - 1)]
return None
# Cross-over offspring from two parents: parent1 and parent2
def createChild(self, parent, (x, y)):
# cross-over parents genetics
genitors = [self, parent]
metabolism = genitors[random.randint(0,1)].metabolism
vision = genitors[random.randint(0,1)].vision
endowment = 0.5 * (genitors[0].endowment + genitors[1].endowment)
genitors[0].sugar = max(genitors[0].sugar - 0.5 * genitors[0].endowment, 0)
genitors[1].sugar = max(genitors[1].sugar - 0.5 * genitors[1].endowment, 0)
ageMax = genitors[random.randint(0,1)].maxAge
sexe = random.randint(0,1)
fertility = genitors[random.randint(0,1)].fertility
# build cultural tags from parents genetics
mask = 1
childTags = 0
for tag in range(self.tagsLength):
tag1 = self.tags & mask
tag2 = parent.getTags() & mask
if tag1 == tag2 or random.random() < self.tagsProbability:
childTags |= tag1
else:
childTags |= tag2
mask <<= 1
# create child agent
child = Agent(self.env, x, y, metabolism, vision, endowment, ageMax, sexe, fertility, (childTags, self.tagsLength))
self.env.setAgent((x, y), child)
return child
# MOVE:
# Look out as far as vision permits in the four principal lattice directions and identify the unoccupied site(s) having the most sugar.
# If the greatest sugar value appears on multiple sites then select the nearest one.
# Move to this site.
# Collect all the sugar at this new position.
# Increment the agent's accumulated sugar wealth by the sugar collected and decrement by the agent's metabolic rate.
def move(self):
# build a list of available food locations
food = self.getFood()
# randomize food locations
random.shuffle(food)
# find best food location
# much faster than sorting.
move = False
newx = self.x
newy = self.y
best = self.env.getCapacity((self.x, self.y))
minDistance = 0
for (x,y) in food:
capacity = self.env.getCapacity((x, y))
distance = abs(x - self.x + y - self.y) # Manhattan distance enough due to no diagonal
if capacity > best or (capacity == best and distance < minDistance):
best = capacity
minDistance = distance
move = True
newx = x
newy = y
# move to new location if any
if move:
self.env.setAgent((self.x, self.y), None)
self.env.setAgent((newx, newy), self)
self.x = newx
self.y = newy
# collect, eat and consume
self.sugar = max(self.sugar + best - self.metabolism, 0)
self.env.setCapacity((self.x, self.y), 0)
'''
# sort implementation
# (for the record: actually was slower so not used)
def compareLocations(self, (x1, y1), (x2, y2)):
c1 = self.env.getCapacity((x1, y1))
c2 = self.env.getCapacity((x2, y2))
if c1 - c2:
# bigger is better
return cmp(c1, c2)
else:
# Manhattan distance enough due to no diagonal
d1 = abs(x1 - self.x + y1 - self.y)
d2 = abs(x2 - self.x + y2 - self.y)
# shorter is better
return cmp(d2, d1)
def moveSort(self):
# build a list of available food locations
food = getFood()
# randomize food locations
random.shuffle(food)
if len(food):
# add existing location (self.x, self.y) => currently not in food
food.extend([(self.x, self.y)])
# randomize food locations
random.shuffle(food)
# sort by capacity and distance
food.sort(cmp = self.compareLocations, reverse = True)
# move to best location
(newx, newy) = food[0]
self.env.setAgent((self.x, self.y), None)
self.env.setAgent((newx, newy), self)
self.x = newx
self.y = newy
# collect, eat and consume
self.sugar = max(self.sugar + self.env.getCapacity(self.x, self.y) - self.metabolism, 0)
self.env.setCapacity((self.x, self.y), 0)'''
# COMBAT
def combat(self, alpha):
# build a list of available unoccupied food locations
food = self.getFood()
# build a list of potential preys
preys = self.getPreys()
# append to food safe preys (retaliation condition)
C0 = self.sugar - self.metabolism
food.extend([preyA.getLocation() for preyA, preyB in product(preys, preys)
if preyA != preyB
and preyB.getSugar() < (C0 + self.env.getCapacity(preyA.getLocation()) + min(alpha, preyA.getSugar()))])
# randomize food locations
random.shuffle(food)
# find best food location
move = False
newx = self.x
newy = self.y
best = self.env.getCapacity((self.x, self.y))
minDistance = 0
for (x,y) in food:
capacity = self.env.getCapacity((x, y))
agent = self.env.getAgent((x, y))
if agent :
capacity += min(alpha, agent.getSugar())
distance = abs(x - self.x + y - self.y) # Manhattan distance enough due to no diagonal
if capacity > best or (capacity == best and distance < minDistance):
best = capacity
minDistance = distance
move = True
newx = x
newy = y
# move to new location if any, and kill if occupied
killed = None
if move:
killed = self.env.getAgent((newx, newy))
self.env.setAgent((self.x, self.y), None)
self.env.setAgent((newx, newy), self)
self.x = newx
self.y = newy
# collect, eat and consume
self.sugar = max(self.sugar + best - self.metabolism, 0)
self.env.setCapacity((self.x, self.y), 0)
return killed