77
88logger = logging .getLogger (__name__ )
99
10+
11+ def distance (a , b ):
12+ d = ((b .x - a .x )** 2 + (b .y - a .y )** 2 )** (1 / 2 )
13+ return round (d ,2 )
14+
1015class Cycle :
1116 def __init__ (self , cycle , agents , foods ):
1217 self .cycle = cycle
@@ -22,15 +27,16 @@ def toJSON(obj):
2227 return result
2328
2429 def buildAgentState (self , agent ):
25- return AgentState (agent .id , agent .alive (), agent .x , agent .y , agent .health )
30+ return AgentState (agent .id , agent .alive (), agent .x , agent .y , agent .health , agent . category )
2631
2732class AgentState :
28- def __init__ (self , uuid , alive , x , y , health ):
33+ def __init__ (self , uuid , alive , x , y , health , category ):
2934 self .id = uuid
3035 self .alive = alive
3136 self .x = x
3237 self .y = y
3338 self .health = health
39+ self .category = category
3440 self .type = "AgentState"
3541
3642class Food :
@@ -39,6 +45,7 @@ def __init__(self, x, y):
3945 self .y = y
4046 self .id = uuid .uuid4 ().hex
4147 self .type = "Food"
48+
4249
4350class Agent (object ):
4451 def __init__ (self , environment ):
@@ -50,21 +57,26 @@ def __init__(self, environment):
5057 self .x = random .randint (0 , environment .size )
5158 self .y = random .randint (0 , environment .size )
5259 self .health = 10
53- self .state = AgentState (self .id , self .alive (), self .x , self .y , self .health )
5460 self .foods = []
5561 self .logger = logging .getLogger (__name__ )
62+ self .category = random .choice ([1 ,2 ,3 ])
63+ self .state = AgentState (self .id , self .alive (), self .x , self .y , self .health , self .category )
5664 self .type = "Agent"
65+ self .route = []
66+ self .seen = []
67+
5768
5869 # each step should perform various combinations of each behaviour
59- def step (self ):
70+ def step (self , foods ):
6071 if not self .alive ():
61- return self .state
72+ return self .foods
6273
63- foods , agents = self .see ()
64- self .move (foods )
74+ self .environment .foods = foods
75+ self .move ()
76+ self .eat ()
6577 self .health = self .health - self .environment .decayRate
66- self .state = AgentState (self .id , self .alive (), self .x , self .y , self .health )
67- return self .state
78+ self .state = AgentState (self .id , self .alive (), self .x , self .y , self .health , self . category )
79+ return self .foods
6880
6981 def size (self ):
7082 return len (self .foods )
@@ -74,64 +86,112 @@ def alive(self):
7486
7587 # Actions, should take a vector, actor accelerates in that direction
7688 # degrees are made up from combinations of [up right, down right, up left, down left]
77- def move (self , visibleFoods ):
89+ def move (self ):
7890 # need to avoid agents that are larger
7991 # need to eat the closest food
8092 # question, build a policy engine and router
8193 # use reinforcement learning to build policies?
82- for i in range (self .environment .velocity ):
83- visibleFoods = self .eat (visibleFoods )
94+ def travel (x , y ):
95+ if x < 0 :
96+ x = self .environment .width
97+ if x > self .environment .width :
98+ x = 0
99+ if y < 0 :
100+ y = self .environment .height
101+ if y > self .environment .height :
102+ y = 0
103+ return x , y
104+
105+ def findFoodAndDistance ():
106+ foods , agents = self .see ()
107+ foods = list ((sorted (foods , key = lambda food : abs (distance (self , food )))))
108+ food = foods [1 ]
109+ d = abs (int (distance (self , food )))
110+ return food , d
111+
112+ def route ():
113+ self .eat ()
114+ food , d = findFoodAndDistance ()
115+ if d == 0 :
116+ self .logger .warning ("NOPE ROUTE HERE %d %d" % (d , self .health ))
117+ self .eat ();
118+ self .logger .warning ("HEALTH %d " % self .health )
119+ food , d = findFoodAndDistance ()
120+
121+ x = self .x
122+ y = self .y
123+ sign = lambda n , n1 : 0 if n == n1 else (1 if n > n1 else - 1 )
124+ for i in range (d ):
125+ xs = sign (food .x , x )
126+ x = x + xs
127+ ys = sign (food .y , y )
128+ y = y + ys
129+ #tx, ty = travel(x, y)
130+ self .logger .warning ("%d %d" % (x , y ))
131+ #if (tx == x & ty == y):
132+ self .route .append ((xs , ys ))
133+
134+ self .logger .warning ("ROUTE LENGTH %d" % len (self .route ))
135+
136+ def routeTraverse ():
137+ if len (self .route ) > 0 :
138+ point = self .route .pop ()
139+ self .x = self .x + point [0 ]
140+ self .y = self .y + point [1 ]
141+ self .eat ()
142+
143+ if (self .category == 1 ):
84144 # move one space in a random direction
85145 rl = random .choice ([(1 ,0 ), (0 ,1 ), (- 1 ,0 ),(0 ,- 1 ),(2 , - 1 ), (- 1 , 2 ), (- 2 , 1 ), (1 , - 2 )])
86146 self .x = self .x + rl [0 ]
87147 self .y = self .y + rl [0 ]
88- if self .x < 0 :
89- self .x = self .environment .width
90- if self .x > self .environment .width :
91- self .x = 0
92- if self .y < 0 :
93- self .y = self .environment .height
94- if self .y > self .environment .height :
95- self .y = 0
148+ x , y = travel (self .x , self .y )
149+ self .x = x
150+ self .y = y
96151
152+ elif self .category == 2 | self .category == 3 :
153+ food , d = findFoodAndDistance ()
154+ if (len (self .route ) == 0 ):
155+ route ()
156+ routeTraverse ()
157+
158+ self .eat ()
97159
98160 # senses
99161 # get all objects that surround the agent
100162 # with each move the agent should update the in view list
101163 def see (self ):
102- def distance (a , b ):
103- return ((b .x - a .x )** 2 + (b .y - a .y )** 2 )** 1 / 2
104-
105164 currentEnvironment = self .environment
106- foods = list (filter (lambda food : distance (self , food ) <= currentEnvironment .visibility , currentEnvironment .foods ))
165+ foods = list (filter (lambda food : int ( distance (self , food ) ) <= currentEnvironment .visibility , currentEnvironment .foods ))
107166 agents = list (filter (lambda agent : currentEnvironment .agents , currentEnvironment .agents ))
167+ # see food diet, if I can see it, I can eat it
168+ self .seen = foods
108169 return foods , agents
109170
110171 # Rules/behaviours
111172 # each move should check whether rules
112173 # rules need to accept the environment
113- def eat (self , visibleFoods ):
114- onFood = list (filter (lambda food : self .x == food .x & self .y == food .y , visibleFoods ))
115- self .health = self .health + len (onFood )
116- self .foods .append (onFood )
117- if len (onFood ) > 0 :
118- self .logger .debug ("ATE FOOD" )
119- # would be better to be a dictionary and delete by id?
120- self .environment .foods = list (filter (lambda food : food not in onFood , self .environment .foods ))
121- visibleFoods = list (filter (lambda food : food not in onFood , visibleFoods ))
122- return visibleFoods
174+ def eat (self ):
175+ currentEnvironment = self .environment
176+ onFoods = [food for food in self .seen if (self .x == food .x ) & (self .y == food .y )]
177+ if len (onFoods ) == 0 :
178+ #self.logger.warning("NAH", self.x, self.y)
179+ return self .foods
180+
181+ for onFood in onFoods :
182+ self .health = self .health + 1
183+ self .foods .append (onFood )
184+ return self .foods
123185
124186# each AgarAgent needs access to the environment
125187@ray .remote
126188class Environment (object ):
127- def __init__ (self , size , agentVelocity , agentVisibility , foodSpawnRate , numberOfAgents , decayRate ):
189+ def __init__ (self , size , agentVisibility , foodSpawnRate , numberOfAgents , decayRate ):
128190 self .size = size
129191 self .width = size
130192 self .height = size
131193
132194 # constants
133- # max number of spaces an agent can move each turn
134- self .velocity = agentVelocity
135195 # minimum size difference an agent needs to be to eat another agent
136196 self .edibleThreshold = 5
137197 # radius that agents can see other objects
@@ -142,6 +202,7 @@ def __init__(self, size, agentVelocity, agentVisibility, foodSpawnRate, numberOf
142202 self .numberOfAgents = numberOfAgents
143203
144204 self .decayRate = decayRate
205+ self .lastFoods = []
145206 self .foods = []
146207 self .agents = []
147208 self .agents = [Agent (self ) for i in range (self .numberOfAgents )]
@@ -151,18 +212,22 @@ def __init__(self, size, agentVelocity, agentVisibility, foodSpawnRate, numberOf
151212 def printAlive (self ):
152213 point = lambda x : 1 if x .alive () else 0
153214 aliveCount = sum (map (point , self .agents ))
154- avgHealth = mean (map (lambda x : x .health , self .agents )) if len (self .agents ) > 0 else 0
215+ avgHealth = max (map (lambda x : x .health , self .agents )) if len (self .agents ) > 0 else 0
155216 self .logger .warning ("=== ALIVE c: %d h: %d ====" % (aliveCount , avgHealth ))
156217
157218 def run (self , itr ):
158219 self .logger .warning ("=== CYCLE %d ===" % itr )
159220 self .generateFood ()
160- [ agent . step () for agent in self .agents ]
221+ self . lastFoods = self .foods
161222 self .logger .warning ("=== FOODS %d ===" % len (self .foods ))
223+ for agent in self .agents :
224+ foods = agent .step (self .foods )
225+ self .foods = list (filter (lambda f : f .id not in list (map (lambda f1 : f1 .id , foods )), self .foods ))
226+
162227 self .printAlive ()
163228 self .logger .warning ("=== FOODS %d ===" % len (self .foods ))
164229 self .logger .warning ("\n " )
165- return self .agents , self .foods
230+ return self .agents , self .lastFoods
166231
167232 def occupiedSpot (self , x , y ):
168233 for food in self .foods :
@@ -179,22 +244,38 @@ def generateFood(self):
179244 originalFoodCount = len (self .foods )
180245 foodCount = len (self .foods )
181246 tryCount = 0
182- while foodCount < originalFoodCount + self .foodSpawnRate and tryCount < 5 :
247+ while ( foodCount < originalFoodCount + self .foodSpawnRate ) and tryCount < 5 :
183248 foodCount = len (self .foods )
184249 x = random .randint (0 , self .width )
185250 y = random .randint (0 , self .height )
186251 if not self .occupiedSpot (x , y ):
187252 food = Food (x , y )
188253 self .foods .append (food )
254+ tryCount = 0
189255 else :
190256 tryCount += 1
257+
191258
192259# information of each cycle should be sent over socket
193260async def run (ws ):
261+ logger = logging .getLogger (__name__ )
194262 if not ray .is_initialized ():
195263 ray .init ()
196- environment = Environment .remote (5 , 5 , 5 , 20 , 10 , 1 )
197- for i in range (50 ):
264+ environment = Environment .remote (10 , 20 , 10 , 5 , 1 )
265+ i = 0
266+ for i in range (1000 ):
198267 agents , foods = ray .get (environment .run .remote (i ))
199- await ws .send (json .dumps (Cycle (i , agents , foods ), default = Cycle .toJSON ))
268+ if len (list (filter (lambda x : x .alive (), agents ))) > 0 :
269+ await ws .send (json .dumps (Cycle (i , agents , foods ), default = Cycle .toJSON ))
270+ else :
271+ logger .warning ("=== CYCLES COMPLETED %d ===" % i )
272+
273+ if __name__ == "__main__" :
274+ if not ray .is_initialized ():
275+ ray .init ()
276+ environment = Environment (10 , 20 , 20 , 1 , 1 )
277+ for i in range (30 ):
278+ agents , foods = environment .run (i )
279+ if len (list (filter (lambda x : x .alive (), agents ))) == 0 :
280+ break
200281 #ray.shutdown()
0 commit comments