-
Notifications
You must be signed in to change notification settings - Fork 170
/
Copy pathAntPlot.py
275 lines (231 loc) · 6.53 KB
/
AntPlot.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
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
import random
import time
import math
import matplotlib.pyplot as plt
class Ant:
"""A single Ant"""
def __init__(self, model):
self.model = model
self.x = 0
self.y = 0
self.has_food = 0
def next_left(self):
"""The (x, y) position of the Location the Ant
would move to if it moved forward left.
"""
if not self.has_food:
return (self.x, self.y + 1)
else:
return (self.x, self.y - 1)
def next_right(self):
"""The (x, y) position of the Location the Ant
would move to if it moved forward right.
"""
if not self.has_food:
return (self.x + 1, self.y)
else:
return (self.x - 1, self.y)
def left_pheromone(self):
"""The amount of pheromone in the Location that
the Ant would move into if it moved forward left.
"""
return self.model.get_pheromone(self.next_left())
def right_pheromone(self):
"""The amount of pheromone in the Location that
the Ant would move into if it moved forward right.
"""
return self.model.get_pheromone(self.next_right())
def will_move(self):
"""Whether or not this Ant will move this turn."""
if self.model.at_capacity(self.next_left()) and \
self.model.at_capacity(self.next_right()):
return False
p_l = self.left_pheromone()
p_r = self.right_pheromone()
prob_move = 0.5 + 0.5*math.tanh((p_l + p_r) / 100.0 - 1)
return random.random() < prob_move
def will_go_right(self):
"""Whether or not this Ant will move forward right
this turn.
"""
p_l = self.left_pheromone()
p_r = self.right_pheromone()
if self.model.at_capacity(self.next_right()):
return False
if self.model.at_capacity(self.next_left()):
return True
prob_right = (1 - (5 + p_l)**2 /
float((5 + p_l)**2 + (5 + p_r)**2))
return random.random() < prob_right
def move(self):
"""Moves this Ant."""
if not self.will_move():
return
if self.will_go_right():
(self.x, self.y) = self.next_right()
else:
(self.x, self.y) = self.next_left()
self.lay_pheromone()
pos = (self.x, self.y)
if pos == (0, 0):
self.has_food = False
else:
if self.model.has_food(pos) and not self.has_food:
self.model.remove_food(pos)
self.has_food = True
def lay_pheromone(self):
"""This Ant lays pheromone in its current Location."""
pos = (self.x, self.y)
current = self.model.get_pheromone(pos)
if not self.has_food:
limit = 1000
amount = 1
else:
limit = 300
amount = 10
if current >= limit:
return
new_amount = min(current + amount, limit)
self.model.set_pheromone(pos, new_amount)
class Location:
"""The grid recording the food and pheromone."""
def __init__(self):
self.food = 0
self.pheromone = 0
def place_food(self, p):
"""Place food with probability p into this Location."""
if random.random() < p:
self.food = 1
def has_food(self):
"""Returns True if this Location has at least 1 food in it,
False otherwise.
"""
return self.food > 0
def remove_food(self):
"""Remove one food from this Location. Crashes if there is
no food in this Location.
"""
assert(self.has_food)
self.food -= 1
def add_pheromone(self, amount=1):
"""Add pheromone to this Location."""
self.pheromone += amount
def set_pheromone(self, amount):
"""Set the pheromone in this Location to amount."""
self.pheromone = amount
def get_pheromone(self):
"""Returns the amount of pheromone in this Location."""
return self.pheromone
def evaporate_pheromone(self):
"""Evaporates 1/30 of the pheromone in this Location."""
self.pheromone -= self.pheromone * (1.0 / 30)
class Model:
"""Class that represents the room the robot ants live in """
MAX_ANTS = 200
def __init__(self):
self.ants = {}
self.locations = {}
self.p_food = 0
def place_food(self, p):
"""Place food in all Locations with probability p."""
self.p_food = p
for point in self.locations:
point.place_food(p)
def remove_food(self, pos):
"""Remove one unit of food from the Location at pos."""
self.locations[pos].remove_food();
def has_food(self, pos):
"""Returns true if the Location at pos has at least one unit
of food, false otherwise.
"""
return self.get_location(pos).has_food();
def add_ants(self, n):
"""Add n ants to the nest. Each ant starts at (0,0)"""
for i in range(n):
ant = Ant(self)
pos = (ant.x, ant.y)
if pos in self.ants:
self.ants[pos].append(ant)
else:
self.ants[pos] = [ant]
def __repr__(self):
"""Return a string representation of this room."""
return str(self.ants)
def move_ants(self):
"""Iterate through and move all the Ants in the room."""
ants = []
for pos, antlist in self.ants.items():
for ant in antlist:
ant.move()
ants.append(ant)
self.evaporate_pheromone()
d = {}
for ant in ants:
pos = (ant.x, ant.y)
if pos in d:
d[pos].append(ant)
else:
d[pos] = [ant]
self.ants = d
def get_location(self, pos):
"""Returns the Location at pos, creating it if it doesn't
already exist.
"""
if pos not in self.locations:
loc = Location()
self.locations[pos] = loc
if self.p_food > 0:
loc.place_food(self.p_food)
else:
loc = self.locations[pos]
return loc
def add_pheromone(self, pos, amount=1):
"""Adds amount pheromone to the Location at pos."""
self.get_location(pos).add_pheromone(amount)
def get_pheromone(self, pos):
"""Returns the amount of pheromone in the Location at pos."""
return self.get_location(pos).get_pheromone();
def set_pheromone(self, pos, amount):
"""Sets the amount of pheromone in the Location at pos to
amount.
"""
self.get_location(pos).set_pheromone(amount)
def evaporate_pheromone(self):
"""Evaporates pheromone from all existing Locations."""
for pos, loc in self.locations.items():
loc.evaporate_pheromone()
def num_ants(self, pos):
"""Returns the number of Ants at pos."""
if pos in self.ants:
return len(self.ants[pos])
else: return 0
def at_capacity(self, pos):
"""Returns True if the Location at pos is full with Ants,
False otherwise.
"""
return self.num_ants(pos) >= Model.MAX_ANTS
if __name__ == "__main__":
model = Model()
model.place_food(0.5)
timesteps = 600
for i in range(timesteps):
model.add_ants(4)
model.move_ants()
positions = model.ants
xdata = []
ydata = []
plt.show()
axes = plt.gca()
axes.set_xlim(0, 30)
axes.set_ylim(0, 30)
line, = axes.plot(xdata, ydata, 'gx') # A green mark for an ant
for pos in positions:
x, y = pos
xdata.append(x)
ydata.append(y)
line.set_xdata(xdata)
line.set_ydata(ydata)
plt.draw()
plt.pause(1e-17)
time.sleep(0.005)
plt.show()