-
Notifications
You must be signed in to change notification settings - Fork 0
/
environment.py
157 lines (120 loc) · 4.96 KB
/
environment.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
import random
import numpy as np
import simulation_state
import turtle
import itertools
from environment_artist import EnvironmentArtist
from thing_artist import ThingArtist
class Thing:
COLOUR = "black"
CONSUMPTION_RATE = 0.1
ROBOT_FILL_RATE = 0.25
REGROW_RATE = 0.025
TOTAL_AMOUNT = 4
def __init__(self, gene, nx=None, ny=None):
self.amount_when_full = self.TOTAL_AMOUNT / gene.amount.value
self.amount_left = 0
self.radius = 20
self.smell_signature = gene.smell_signature.flatten()
if simulation_state.ENABLE_DRAWING:
self.artist = ThingArtist(self.radius)
self.reset()
if nx is not None and ny is not None:
self.position = np.array([nx, ny])
def reset(self):
self.amount_left = self.amount_when_full
self.position = np.array([
random.randrange(0, simulation_state.arena_width),
random.randrange(0, simulation_state.arena_width)
])
def draw(self):
self.artist\
.x_position(self.position[0])\
.y_position(self.position[1])\
.colour(self.COLOUR)\
.amount_left(self.amount_left)\
.smell_signature(self.smell_signature)\
.amount_full(self.amount_when_full)\
.amount_left(self.amount_left)
self.artist.draw()
def clear(self):
self.artist.clear()
def destroy(self):
self.artist.destroy()
def update(self):
if not self.is_gone():
self.amount_left += self.REGROW_RATE * simulation_state.timestep
if self.amount_left > self.amount_when_full:
self.amount_left = self.amount_when_full
def interact_with_robot(self, robot):
if not self.is_gone():
self.on_touched_by_robot(robot)
def on_touched_by_robot(self, robot):
# To be implemented by concrete subclasses.
pass
def is_gone(self):
return self.amount_left <= 0
class Food(Thing):
COLOUR = (255, 255, 0) # yellow in rgb
def on_touched_by_robot(self, robot):
self.amount_left -= self.CONSUMPTION_RATE * simulation_state.timestep
robot.food_battery += self.ROBOT_FILL_RATE * simulation_state.timestep
class Water(Thing):
COLOUR = (0, 0, 255) # blue in rgb
def on_touched_by_robot(self, robot):
self.amount_left -= self.CONSUMPTION_RATE * simulation_state.timestep
robot.water_battery += self.ROBOT_FILL_RATE * simulation_state.timestep
class Trap(Thing):
COLOUR = (255, 0, 0) # red in rgb
def on_touched_by_robot(self, robot):
robot.food_battery = 0.0
robot.water_battery = 0.0
robot.is_alive = False
class Environment:
def __init__(self, genome):
self.foods = []
self.waters = []
self.traps = []
self.thing_positions = None
for gene in genome.food_genes.list:
for _ in range(int(gene.amount.value)):
self.foods.append(Food(gene))
for gene in genome.water_genes.list:
for _ in range(int(gene.amount.value)):
self.waters.append(Water(gene))
# Ensure number of traps = number of (water + food)
trap_written_sum = sum([trap.amount.value for trap in genome.trap_genes.list])
trap_amount_multiplier = (len(self.foods) + len(self.waters)) / trap_written_sum
for gene in genome.trap_genes.list:
for _ in range(max(1, int(gene.amount.value * trap_amount_multiplier))):
self.traps.append(Trap(gene))
self.thing_signatures = np.array([thing.smell_signature for thing in self.everything()]).transpose()
# self.artist = EnvironmentArtist(1, foods, waters, traps)
self.thing_radii = np.array([thing.radius for thing in self.everything()])
self.thing_is_alive = np.vstack([1 for thing in self.everything()])
def everything(self):
return itertools.chain(self.foods, self.waters, self.traps)
def reset(self):
for thing in self.everything():
thing.reset()
self.thing_positions = np.array([thing.position for thing in self.everything()]).transpose()
def interact_with_robot(self, robot):
interaction_distances = robot.radius + self.thing_radii
distances = np.linalg.norm(self.thing_positions - robot.position, axis=0)
things_are_interacting = distances < interaction_distances
for thing, is_interacting in zip(self.everything(), things_are_interacting):
if is_interacting:
thing.interact_with_robot(robot)
def update(self):
for i, thing in enumerate(self.everything()):
thing.update()
self.thing_is_alive[i][0] = 0 if thing.is_gone() else 1
def draw(self):
for thing in self.everything():
thing.draw()
def clear(self):
for thing in self.everything():
thing.clear()
def destroy(self):
for thing in self.everything():
thing.destroy()