1 import time
2 import threading
3 import thread
4 import random
5
6 from random import choice, randint
7 from pyglet.event import EventDispatcher
8 from cocos.layer import *
9 from cocos.text import *
10 from cocos.scene import Scene
11 from cocos.actions.interval_actions import Delay
12 from constants import *
13 from research import RESEARCH
14 from models import *
15 from maps import Core
16 from game_layers import TransTimer
17 from utils import *
18 from imageLayer import *
19 from player_new import Player
20 from utils import *
21 import objects
22
23 '''
24 AI TODO: Non Gratum Anus Rodentum: Yasin Dara
25
26 - [DONE] Need to prevent crashing by preventing "move" and "attack" from being called too rapidly. (check if action is being performed, cocos method)
27 - [DONE] Need to figure out how to make the AI detect TYPES of troops/units so that we don't try to attack with a server or something stupid like that.
28 - [DONE] Also need to figure out how to read from the map file directly to "cheat" and make the AI knowledgeable about all starting locations of Servers.
29 - [DONE (Fix Swarm)] Heatmap: AI needs to prioritize attacking groups of four human units that are on a single vertex.
30 - [DEFERRED] N-Gram
31 - [DONE] Yasin just discovered the need for spoof.
32 - [DONE] Build a function called "Swarm" that does exactly what you think it does. }:-)
33 - [DONE] Trigger AI only after 60 seconds or when AI AS is invaded.
34 - [DONE] Randomly attack troops and buildings with free range AI troops to add variety to AI movement and gameplay.
35 - [DEFERRED] Allow the AI to use different attack strategies for different types of troops and units.
36
37 .o. ooooo
38 .888. `888'
39 .8"888. 888
40 .8' `888. 888
41 .88ooo8888. 888
42 .8' `888. 888
43 o88o o8888o o888o
44 , _ _
45 /|/ \ | |
46 | | __ _|_ _ __ ,_ | | ,
47 | | / \_| |/ | | |_/ \_/ | |/_) / \_
48 | |_/\__/ |_/|__/ \/ \/ \__/ |_/| \_/ \/
49
50 (C) Carleton College 2013
51
52 '''
53
54
56 '''
57 The premise of this AI is to use a FINITE STATE MACHINE to react "intelligently" to a human player.
58 The states of this machine are defined below. An "N-Gram" will be used to collect data about player moves.
59 The "A-Star" pathfinding algorithm will be employed to find the most efficient way to attack. The AI will
60 prioritize vertices containing human-built troops based on a "heatmap", which allows the AI to prioritize
61 resources and its own troops.
62 '''
64 super(ComputerPlayer, self).__init__(ID)
65 self.server = objects.Objects.get_controller()
66 self.totalTime = 0
67 self.aiRunning = False
68 self.theMap = self.server.map
69 self.aiID = ID
70 self.aiLevels = ["Easy", "Medium", "Hard", "Godlike"]
71 self.aiLevel = "Easy"
72 self.aiStates = ["Initial", "Waiting", "Scanning", "Researching", "Attacking", "Building", "Defending",
73 "Determining"]
74 self.maxMovePerMove = 4
75 self.maxAttackPerMove = 5
76 self.enemy = self.server.players
77 self.color = (70, 0, 180)
78 self.ai_cur_state = ""
79 self.ai_prev_state = "Initial"
80 self.aiTroops = 0
81 self.humanTroops = 0
82 self.allTroops = 0
83 self.humanCpus = 1
84 self.boardResearchLevel = 1
85 self.discovered = False
86 self.health = 1000
87 self.humanHealth = 1000
88 self.adjacentFreeSlots = 0
89 self.genericCounter = 0
90 self.numUnitsToBuild = 0
91 self.enemyBuildingVertices = []
92 self.enemyTroopVertices = []
93 self.highPriorityEnemyVertices = []
94 self.aiTroopVertices = []
95 self.defensePriorityOneLocation = []
96 self.timeWaited = 0
97 self.swarmCompletion = 0
98 self.swarmVertices = []
99 self.swarmVertex = None
100 self.swarmNumber = 0
101 self.troopvertex = None
102 self.lastAttackTroop = None
103 self.troopsPOWMIA = []
104 self.troopsAtTheReady = []
105 self.troopsPoised = []
106 self.threatVertices = []
107 self.respondingTroops = []
108 self.myASes = []
109 self.aiVertices = []
110 self.buildLimit = 0
111 self.numTroopsBuilt = 0
112 self.triggerTime = 60
113
116
118
119 for player in self.server.players.values():
120 if player.pid != self.pid:
121 listOfUnits = player.units.values()
122 self.humanTroops = len(listOfUnits)
123 if player.pid == self.pid:
124 listOfUnits = player.units.values()
125 self.aiTroops = len(listOfUnits)
126
127
128 for unit in player.units.values():
129 if issubclass(type(unit), Building) == True:
130
131 if type(unit) == Server:
132
133
134 self.defensePriorityOneLocation = [unit, int(unit.curVertex.vid)]
135
136
137 self.enemyBuildingVertices = []
138 for vertex in self.server.map.vertices.values():
139 if type(vertex.building) == Server and vertex.building.pid != self.pid:
140
141 self.enemyBuildingVertices.append(int(vertex.vid))
142
143
144
145 self.aiTroopVertices = []
146 for unit in player.units.values():
147
148 if issubclass(type(unit), Troop) == True and unit.pid == self.pid:
149 if unit.power > 0:
150 self.aiTroopVertices.append([unit, unit.curVertex.vid])
151
152
153
154 self.enemyTroopVertices = []
155 for player in self.server.players.values():
156 if player.pid != self.pid:
157 for unit in player.units.values():
158 if issubclass(type(unit), Troop) == True:
159 self.enemyTroopVertices.append([unit, unit.curVertex.vid])
160
161
162
163
164
165 self.highPriorityEnemyVertices = []
166 for vertex in self.server.map.vertices.values():
167 priorityCounter = 0
168 for slot in vertex.troopSlots.values():
169 if issubclass(type(slot.troop), Troop) and slot.troop.pid != self.pid:
170 priorityCounter += 1
171 if priorityCounter > 3:
172 self.highPriorityEnemyVertices.append(vertex)
173
174
175
176 self.myASes = []
177 self.aiVertices = []
178 for vertex in self.server.map.vertices.values():
179 if vertex.building != None and vertex.building.pid == self.pid:
180 self.myASes.append(vertex.asID)
181 for myvertex in self.server.map.vertices.values():
182 if myvertex.asID in self.myASes:
183 self.aiVertices.append(myvertex)
184
185
186 self.buildLimit = 4 * len(self.aiVertices)
187
188
189 '''
190 # DISCARDED CODE ---------------------------------------------------------------------------------
191 #for slot in vertex.troopSlots.values():
192 #for slot in vertex.troopSlots.values():
193 ## /print "At vertex ", vertex.vid, " there is a ", vertex.building, " with this stuff ", slot.troop
194
195 # A GOOD SET OF TROOPS: DOS,2 DOS,2 DOS,18 DOS,18 Ping,31 DOS,17 Ping,35 DOS,14 DOS,14 DOS,14 DOS,14 Ping,35 Ping,35 Ping,35 Ping,33 Ping,33
196
197 #self.human_troops = 0
198 #self.ai_troops = 0
199
200 #print self.troops
201 #for vertex in self.enemy.availableTroops[0].curVertex.adjacentVertices:
202 #print vertex.troopSlots
203 '''
205 if len(self.aiVertices) > 0:
206 for vertex in self.aiVertices:
207 for slot in vertex.troopSlots.values():
208 if slot.troop != None:
209 if slot.troop.pid != self.pid:
210
211 return True
212 return False
213
214
216 randomInt = randint(1,100)
217 if randomInt <= boundary:
218 return True
219 else:
220 return False
221
222
224
225 attackingUnit = None
226 if len(self.troopsPoised) > 0:
227 for poisedTroop in self.troopsPoised:
228 self.perform_single_target_attack(poisedTroop[0],poisedTroop[1])
229
230 if poisedTroop in self.troopsPoised:
231 self.troopsPoised.remove(poisedTroop)
232
233
234 if len(self.troopsAtTheReady) == 0:
235 for unit in self.units.values():
236
237 if unit.curVertex != self.swarmVertex and unit.curVertex.vid not in self.swarmVertices and issubclass(type(unit), DOS) == True:
238
239 attackingUnit = unit
240 self.troopsAtTheReady.append(attackingUnit)
241
242 else:
243 for unit in self.troopsAtTheReady:
244 attackingUnit = unit
245
246
247 if attackingUnit == None:
248
249 self.reassign_troops()
250 return
251
252
253
254 if self.mathematical_probability(50) == True:
255
256 for vertex in self.server.map.vertices.values():
257 if vertex.building != None and vertex.building.pid != self.pid:
258 attackFrom = self.get_closest_safe_adjacent_vertex(vertex.vid, False)
259 if attackingUnit.isSelectable == True and attackFrom != None:
260
261 self.perform_single_target_random_move(attackFrom, attackingUnit)
262 self.troopsPoised.append([vertex.building, attackingUnit])
263 if attackingUnit in self.troopsAtTheReady:
264 self.troopsAtTheReady.remove(attackingUnit)
265
266 else:
267
268 for player in self.server.players.values():
269 if player.pid != self.pid:
270 for eunit in player.units.values():
271 if issubclass(type(eunit), Troop) == True:
272
273 attackFrom = self.get_closest_safe_adjacent_vertex(eunit.curVertex.vid, False)
274 if attackingUnit.isSelectable == True and attackFrom != None:
275
276 self.perform_single_target_random_move(attackFrom, attackingUnit)
277 self.troopsPoised.append([eunit, attackingUnit])
278 if attackingUnit in self.troopsAtTheReady:
279 self.troopsAtTheReady.remove(attackingUnit)
280
281
282
283
284
285
286
288 if swarmVertex == None:
289 self.swarmVertex = self.get_closest_empty_vertex()
290 returnVal = self.build_a_troop("DOS", self.swarmVertex.vid)
291 if returnVal != None:
292 self.swarmCompletion += 1
293 if self.swarmCompletion == 4:
294 self.swarmCompletion = 0
295 self.swarmVertices.append(returnVal.curVertex.vid)
296
297 self.swarmVertex = None
298 else:
299 return self.swarmCompletion
300 return self.swarmCompletion
301
302
303
305
306
307 if self.numTroopsBuilt > self.buildLimit:
308 returnVal = None
309 return returnVal
310 else:
311 returnVal = self.server.build_unit("DOS", self, locationVid)
312 self.numTroopsBuilt += 1
313 return returnVal
314
315
317 for vertex in self.server.map.vertices.values():
318
319 if not type(vertex) == Core and vertex.building == None and vertex.troopSlots.values() == []:
320 return vertex
321
322
325
326
327
329 numTroopsToActivate = 0
330 if swarmNumber != 0:
331 swarmNumber = self.swarmNumber
332 if len(self.swarmVertices) != 0 and len(self.highPriorityEnemyVertices) != 0:
333 marshallVertex = self.swarmVertices[swarmNumber]
334 attackersPosition = self.highPriorityEnemyVertices[swarmNumber]
335 attackFromPosition = self.get_closest_safe_adjacent_vertex(attackersPosition.vid, safe=False)
336
337
338
339
340
341 numTroopsToActivate = 4 - len(attackFromPosition.troopSlots.values())
342
343
344
345
346
347
348
349
350 for key in self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots.keys():
351 attackTroop = self.server.map.vertices[str(self.swarmVertices[swarmNumber])].troopSlots[key].troop
352 self.perform_single_target_random_move(attackFromPosition, attackTroop)
353
354
355
356
357
358
359
360
361
362 self.highPriorityEnemyVertices.pop(0)
363 self.swarmVertices.pop(0)
364
365 self.troopvertex = attackFromPosition
366 self.lastAttackTroop = attackTroop
367 return
368 else:
369
370 return 0
371
373
374 if self.lastAttackTroop.isSelectable == False:
375
376 return
377 else:
378 pass
379
380 attackTroops = []
381 victimTroops = []
382 for key in attackFromPosition.troopSlots.keys():
383 attackTroops.append(attackFromPosition.troopSlots[key].troop)
384 if len(self.highPriorityEnemyVertices) > 0:
385 vertexOfInterest = self.highPriorityEnemyVertices[0]
386 if vertexOfInterest == None:
387
388 return
389 for key in vertexOfInterest.troopSlots.keys():
390 victimTroops.append(vertexOfInterest.troopSlots[key].troop)
391 for i in range(4):
392 self.perform_single_target_attack(victimTroops[i], attackTroops[i])
393 self.troopvertex = None
394 self.highPriorityEnemyVertices.remove(vertexOfInterest)
395 else:
396
397 return
398
399
400
401
402
404 vertexOfInterest = self.server.map.vertices[str(vidOfInterest)]
405 if safe == False:
406 for vertex in self.server.map.vertices.values():
407 if vertexOfInterest in vertex.adjacentVertices:
408
409 return vertex
410 elif safe == True:
411 for vertex in self.server.map.vertices.values():
412 if vertexOfInterest in vertex.adjacentVertices:
413 if vertex.troopSlots.values() == []:
414
415 return vertex
416
418
419 return issubclass(type(attacker), Troop) and attacker.power > 0
420
438
459
460
461
462
463
465 for player in self.server.players.values():
466 if player.pid != self.pid:
467 enemyUnits = player.units.values()
468 for enemyUnit in enemyUnits:
469 for aiUnit in self.units.values():
470 if aiUnit.curVertex == enemyUnit.curVertex:
471
472 return True
473 return False
474
475
476
477
479
480 threateningTroops = []
481 serverVid = self.defensePriorityOneLocation[1]
482 serverUnit = self.defensePriorityOneLocation[0]
483 serverVertex = self.server.map.vertices[str(serverVid)]
484
485
486 for adjVertex in serverVertex.adjacentVertices:
487 if adjVertex not in self.threatVertices:
488 self.threatVertices.append(adjVertex)
489 for distantVertex in adjVertex.adjacentVertices:
490 if distantVertex not in self.threatVertices:
491 self.threatVertices.append(distantVertex)
492
493 for player in self.server.players.values():
494 if player.pid != self.pid:
495 enemyUnits = player.units.values()
496
497 for eUnit in enemyUnits:
498 for vertex in self.threatVertices:
499 if eUnit.curVertex == vertex:
500 threateningTroops.append(eUnit)
501
502
503
504 self.attack_with_list_of_troops(threateningTroops)
505
506
521
522
523
525
526 theAdjacentVertices = [passVertex]
527 theAttackingTroops = []
528 for adjVertex in passVertex.adjacentVertices:
529 theAdjacentVertices.append(adjVertex)
530 for distantVertex in adjVertex.adjacentVertices:
531 theAdjacentVertices.append(distantVertex)
532 for searchVertex in theAdjacentVertices:
533 for key in searchVertex.troopSlots.keys():
534 if searchVertex.troopSlots[key].troop.pid == self.pid and searchVertex.troopSlots[key].troop not in self.respondingTroops:
535 theAttackingTroops.append(searchVertex.troopSlots[key].troop)
536
537 return theAttackingTroops
538
540
541 for unit in self.units.values():
542 if issubclass(type(unit), DOS) == True and not unit.is_attacking:
543 if unit in self.respondingTroops:
544 self.respondingTroops.remove(unit)
545
546 if unit not in self.troopsAtTheReady:
547 self.troopsAtTheReady.append(unit)
548
549
550
572
573
574
576 self.totalTime += dt
577
578 if self.ai_cur_state == "":
579
580 print "[AI] is ON."
581 self.ai_cur_state = "Initial"
582
583 if self.ai_cur_state == "Initial":
584
585 self.scan_all_vertices()
586 self.ai_cur_state = "Waiting"
587
588 if self.ai_cur_state == "Waiting":
589
590 self.timeWaited += dt
591 if self.timeWaited < self.triggerTime and not self.trigger():
592
593 return
594 self.timeWaited = 0
595 self.genericCounter += 1
596 self.ai_cur_state = "Determining"
597
598 if self.ai_cur_state == "Researching":
599
600 pass
601
602 if self.ai_cur_state == "Scanning":
603
604 self.scan_all_vertices()
605 self.ai_prev_state = "Scanning"
606
607 if self.ai_cur_state == "Determining":
608
609 if self.ai_prev_state == "Initial":
610 self.ai_prev_state = "Building"
611
612 return
613
614
615
616
617 if self.ai_prev_state == "Building":
618 self.ai_cur_state = "Attacking"
619 if self.ai_prev_state == "Attacking":
620 self.ai_cur_state = "Defending"
621 if self.ai_prev_state == "Defending":
622 self.ai_cur_state = "Building"
623
624 if self.troopvertex != None:
625
626 self.swarm_attack(self.troopvertex)
627
628 if self.check_defenses() == True:
629
630 pass
631
632 if self.ai_cur_state == "Building":
633
634 if len(self.swarmVertices) > 2:
635 self.swarm_vertex(self.swarmCompletion, self.swarmVertex)
636 self.ai_prev_state = "Building"
637 self.ai_cur_state = "Determining"
638 return
639 else:
640 self.ai_prev_state = "Building"
641 self.ai_cur_state = "Determining"
642 return
643
644 if self.ai_cur_state == "Attacking":
645
646 self.random_search_and_destroy()
647 self.marshall_troops(self.swarmNumber)
648 self.reassign_troops()
649 self.ai_prev_state = "Attacking"
650 self.ai_cur_state = "Determining"
651 return
652
653 if self.ai_cur_state == "Defending":
654
655 self.defend_our_server()
656 self.ai_prev_state = "Defending"
657 self.ai_cur_state = "Determining"
658 return
659
660
662 for vertex in unit.curVertex.adjacentVertices + [unit.curVertex]:
663 for troop in vertex.troops:
664
665
666 if troop is not None and troop.pid != self.ID:
667 return troop
668 if vertex.building != None:
669 return vertex.building
670 return None
671