/
robot.py
159 lines (140 loc) · 5.7 KB
/
robot.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
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal
import random
import copy
class Robot(object):
"""
Robot class keeps track of all the chemical values for an individual and processes the movement.
"""
def __init__(self,a,b,x,y,rd_params):
"""
Initializes the robot class
Arguments:
a (float): initial chemical of a concentration
b (float): initial b concentration
x (int): initial x position
y (int): initial y position
rd_params (list(float)): parameters for the robot diffusion
"""
self.a = a
self.b = b
self.ca, self.cb, self.a_add_rate, self.b_add_rate = rd_params
self.x = x
self.y = y
self.divA = 0
self.divB = 0
self.lastX = x
self.lastY = y
# Kernel for calculating divergence of chemicals
self.kernel = np.array([[.05, .2, .05],
[ .2, -1, .2],
[.05, .2, .05]])
# Kernel for calculating valid moves for a robot
self.matchKernel = np.array([[0,1,0],
[1,-10,1],
[0,1,0]])
# Kernel for detecting if an robot is on the edge
# A robot is on the edge if it has 5 or fewer robot neighbors
self.edgeKernel = np.array([[-1, -1,-1],
[-1, 6, -1],
[-1, -1,-1]])
# Bool to keep track of if the robot is on the edge or not
self.isOnEdge = False
# Bool to keep track of if the robot is dead or not
# A robot is dead if it has no robot neighbors
self.isDead = False
def getRobotNeighbors(self, attr=None, dtype=np.int):
"""
Returns a list of the neighbors that are robots, optionally with an attribute of them
Arguments:
attr (dtype type): attribute of the robot to return
dtype (type): type of attr
Returns:
np.array(dtype) A vector of either the robots or the attribute of the robots specified
"""
if attr is None:
vec = np.vectorize(lambda x: 1 if isinstance(x, Robot) else 0, otypes=[dtype])
else:
vec = np.vectorize(lambda x: getattr(x, attr) if isinstance(x, Robot) else 0, otypes=[dtype])
return vec(self.neighbors)
def detectEdge(self):
"""
Calculates whether the robots is on an edge, or completely isolated
Returns:
(bool) if robot is on edge or completely isolated
"""
adj_neighbors = self.getRobotNeighbors()
edgeSum = (adj_neighbors * self.edgeKernel).sum()
if edgeSum!= 6:
self.isDead = False
return edgeSum > 0
else:
self.isDead = True
return False
def isSatisfied(self):
"""
calculates whether the robot is satisfied (defined as being close to a robot with sufficient concentration of a chemical)
Returns:
(bool) is the robot satisfied
"""
for neighbor in self.neighbors.ravel():
if(neighbor != 0):
if(neighbor.b >= 0.05):
return True
return False
def move(self):
"""
Moves the robot one space along the edge, not going back to a space it just came from.
Returns:
(int,int) new position of the robot
"""
robot_neighbors = self.getRobotNeighbors()
robot_neighbors[1][1] = 0
# matched cells is a 3x3 where positive numbers are valid moves
matchedCells = scipy.signal.correlate2d(robot_neighbors, self.matchKernel, mode='same')
# current position is invalid (impossible to not move)
matchedCells[1][1] = -1
if (matchedCells > 0).sum() > 1:
# previous position is invalid and there are no other spots
matchedCells[(self.lastX-self.x)+1][(self.lastY-self.y)+1] = -1
matches = np.where(matchedCells > 0)
# Robot became an orphan, all of its neighbors left :(
if len(matches[0]) - 1 < 0:
self.isDead = True
return(self.x, self.y)
index = random.randint(0,len(matches[0])-1)
self.lastX = self.x
self.lastY = self.y
self.x = self.x+matches[0][index]-1
self.y = self.y+matches[1][index]-1
return (self.x,self.y)
def setNeighbors(self,neighbors):
"""
Sets the robot's list of neighbors to be the incoming list of neigbors
Arguments:
neighbors (np.array(Robot or int))):
"""
self.neighbors = neighbors
def setDivergence(self):
"""
Sets the divergence in each chemical according to the neighbor's concentrations.
"""
self.divA = 0
self.divB = 0
for kernelVal, neighbor in zip(self.kernel.flat, self.neighbors.flat):
if neighbor != 0:
self.divA += neighbor.a * kernelVal
self.divB += neighbor.b * kernelVal
else:
# If there is no robot, pretend that the value is the same as the current grid space
# This effectively prevents chemicals from diffusing out
self.divA += self.a * kernelVal
self.divB += self.b * kernelVal
def updateChemicals(self):
"""
Sets the new chemical concentrations based no the divergence and reaction
"""
reaction = self.a * self.b**2
self.a += self.divA * self.ca - reaction + self.a_add_rate * (1-self.a)
self.b += self.divB * self.cb + reaction + self.b_add_rate * self.b