-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindiv.py
120 lines (94 loc) · 3.79 KB
/
indiv.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
import numpy as np
edge_angle = (2 * np.pi) / 3
small_angle = np.pi / 24
wide_angle = np.pi / 4
# Individual in the environment
class Indiv:
start_energy = 300 # Initial energy
move_speed = 1.0 # Movement speed
angular_speed = 1.0 # Rotation speed
sight_range = 15 # View distance in world
def __init__(self, x, y, angle, net):
self.x = x
self.y = y
self.angle = angle
self.net = net
self.speed = 0
self.age = 0
self.score = 0
self.energy = self.start_energy
def normalized_inputs(self, env):
const = 1
vision = self._get_normalized_vision(env.foods)
return const, vision[0], vision[1], vision[2], vision[3], vision[4]
def step(self, env):
self.age += 1
# If the individual is out of food, don't do anything
if self.energy <= 0:
self.energy = 0
return
# Activate brain
inputs = self.normalized_inputs(env)
actions = self.net.activate(inputs)
# Add input to state
speed = actions[0] * self.move_speed
angle = self.angle + actions[1] * self.angular_speed
new_x = self.x + speed * np.cos(angle)
new_y = self.y + speed * np.sin(angle)
# Wrap around angle
if angle > np.pi:
angle -= np.pi * 2
elif angle < -np.pi:
angle += np.pi * 2
# Clamp position
self.x = min(max(0, new_x), env.grid_size - 1)
self.y = min(max(0, new_y), env.grid_size - 1)
self.energy -= 1
self.speed = speed
self.angle = angle
self.energy -= speed ** 2
def _find_nearest(self, values):
nodes = np.asarray(values)
dist_2 = np.sum((nodes - (self.x, self.y)) ** 2, axis=1)
if dist_2.size == 0:
return 0
nearest = np.argmin(dist_2)
x, y = nodes[nearest]
min_dist = np.sqrt(dist_2[nearest])
# Return if the nearest entity is outside of view distance
if min_dist > self.sight_range:
return 0
# Calculate angle
x -= self.x
y -= self.y
angle = np.arctan2(y, x)
return self.angle - angle
def _get_normalized_vision(self, values):
range_squared = self.sight_range ** 2
squared_min_dist = [range_squared, range_squared, range_squared, range_squared, range_squared]
if values.size == 0:
return [0, 0, 0, 0, 0]
for value in values:
angle = self.angle - np.arctan2(value[1] - self.y, value[0] - self.x)
if abs(angle) < edge_angle:
dist_squared = (value[0] - self.x) ** 2 + (value[1] - self.y) ** 2
if dist_squared < range_squared:
if angle < (-wide_angle):
if dist_squared < squared_min_dist[0]:
squared_min_dist[0] = dist_squared
elif angle < (-small_angle):
if dist_squared < squared_min_dist[1]:
squared_min_dist[1] = dist_squared
elif angle < small_angle:
if dist_squared < squared_min_dist[2]:
squared_min_dist[2] = dist_squared
elif angle < wide_angle:
if dist_squared < squared_min_dist[3]:
squared_min_dist[3] = dist_squared
else:
if dist_squared < squared_min_dist[4]:
squared_min_dist[4] = dist_squared
impulses = [0, 0, 0, 0, 0]
for i in range(5):
impulses[i] = (self.sight_range - np.sqrt(squared_min_dist[i])) / self.sight_range
return impulses