1 import time
2 import threading
3 import thread
4 import random
5
6 from pyglet.event import EventDispatcher
7 from cocos.layer import *
8 from cocos.text import * \
9
10 from cocos.scene import Scene
11
12 from constants import *
13 from research import RESEARCH
14 from models import *
15 from game_layers import TransTimer
16 from utils import *
17 from imageLayer import *
18 from controller import *
19
20 from utils import *
21
22
23 -class Player(EventDispatcher, Layer):
24
25 - def __init__(self, ID, gameMap, colman, color, special=None):
26 super(Player, self).__init__()
27 self.map = gameMap
28 self.cm = colman
29
30 self.ID = ID
31 self.color = PLAYER_COLOR[self.ID]
32 self.special = None
33
34 self.troops = []
35
36 self.buildings = []
37
38
39 self.underConstruction = []
40
41
42
43 self.completedResearch = 1
44
45 self.availableResearch = []
46
47 self.allResearch = []
48
49 self.startingLevel = 1
50
51 self.visibleAS = set()
52
53 self.availableTroops = []
54 self.idleCPUs = []
55 self.busyCPUs = []
56
57 self.unitActionLists = DEFAULT_ACTIONS
58
59 self.has_server = True
60
61 self.numServers = 1
62
63 self.aiPlayerIDs = self.map.AIPlayers
64
66
67 toRemove = []
68 for unit in self.underConstruction:
69 unit.health += unit.maxHealth * (dt / unit.buildTime)
70 if not self.ID in self.aiPlayerIDs:
71 try:
72 unit.opacity = (unit.health / unit.maxHealth) * (
73 255 - UNIT_STARTING_OPACITY) + 255
74 except ZeroDivisionError:
75 unit.opacity = 255
76 if unit.health >= unit.maxHealth:
77 if not self.ID in self.aiPlayerIDs:
78 unit.opacity = 255
79 if issubclass(type(unit), Troop):
80 self.troops.append(unit)
81 elif issubclass(type(unit), Building):
82 self.buildings.append(unit)
83 self.cm.add(unit)
84 toRemove.append(unit)
85 for unit in toRemove:
86 self.underConstruction.remove(unit)
87 if not self.ID in self.aiPlayerIDs:
88 unit.curVertex.set_visibility_old(1, self.cm, self.map.minimap)
89
90 pass
91
93
94 for r in self.map.startingResearch[self.ID]:
95 self.completedResearch *= RESEARCH[r].uid
96 for r in self.map.availResearch:
97 if self.completedResearch % RESEARCH[r].dependencies == 0:
98 self.availableResearch.append(r)
99 self.allResearch = self.map.availResearch
100 for t in self.map.startingUnits[self.ID]:
101 self.availableTroops.append(t)
102 for vertID in self.map.startingUnits[self.ID]:
103 curVert = self.map.get_vertex(vertID)
104 unitsInVert = self.map.startingUnits[self.ID][vertID]
105 for strUnitType in unitsInVert:
106 unitType = eval(strUnitType)
107 if issubclass(unitType, Building):
108 self.add_building(unitType, vertID)
109 elif issubclass(unitType, Troop):
110 self.add_troop(unitType, vertID)
111 if type(curVert) == Vertex:
112 self.visibleAS.add(curVert.asID)
113 if self.ID not in self.aiPlayerIDs:
114 self.setup_visibility()
115
116
118 for v in self.map.vertices.values():
119 if v.asID in self.visibleAS:
120 v.set_visibility_old(HALF_VISIBLE, self.cm,self.map.minimap)
121
122 - def add_troop(self, troopType, vid, oldTroop = None):
123 curVertex = self.map.get_vertex(vid)
124 if oldTroop != None:
125
126 newTroop = EncryptedTroop(curVertex = oldTroop.curVertex, position = oldTroop.curVertex.position, health = oldTroop.health, pid = self.ID, speed = oldTroop.speed, originalType = type(oldTroop))
127 else:
128
129 newTroop = troopType(curVertex,pid=self.ID)
130 if not self.ID in self.aiPlayerIDs:
131 newTroop.opacity = UNIT_STARTING_OPACITY
132 else:
133 newTroop.opacity = 0
134 newTroop.color = self.color
135 newTroop.pid = self.ID
136 newTroop.push_handlers(self.on_destruction)
137 self.map.add(newTroop, TROOP_Z)
138 self.underConstruction.append(newTroop)
139 self.dispatch_event("player_add_troop", troopType)
140 return newTroop
141
143 curVertex = self.map.get_vertex(vid)
144 if curVertex.building is None or buildingType == CPU:
145 if buildingType == RSA:
146 curVertex.color = (0,0,0)
147 self.map.remove(curVertex)
148 self.map.add(curVertex, z = RSA_Z)
149 newBuilding = buildingType(curVertex,player=self,pid=self.ID)
150 else:
151 newBuilding = buildingType(curVertex,pid=self.ID)
152
153 if not self.ID in self.aiPlayerIDs:
154 newBuilding.opacity = UNIT_STARTING_OPACITY
155 else:
156 newBuilding.opacity = 0
157 newBuilding.color = self.color
158 newBuilding.pid = self.ID
159 self.map.add(newBuilding, z=BUILDING_Z)
160 newBuilding.push_handlers(self.on_destruction)
161 self.underConstruction.append(newBuilding)
162 if buildingType == CPU:
163 self.idleCPUs.append(newBuilding)
164 return newBuilding
165
166
167
168
169
170
171
173 builder.opacity = 200
174 cores = vertex.adjacentCores
175 if cores:
176 curCore = cores.pop()
177 newCPU = self.add_building(CPU, curCore.vid)
178 timer, action = self.start_cpu(newCPU.buildTime, newCPU)
179 action += CallFunc(self.busyCPUs.remove, newCPU)
180 action += CallFunc(self.on_destruction, builder)
181 timer.do(action)
182 else:
183 pass
184
185
186
188 if buildingName == "CPU":
189
190
191 self.execute_build_cpu(unit.curVertex, unit)
192
193
194 if len(self.idleCPUs) > 0 and unit.curVertex.building == None:
195
196 unit.opacity = 200
197
198
199
200 buildingType = ALL_UNITS[buildingName]
201 cpu = self.idleCPUs.pop()
202 building = self.add_building(buildingType, unit.curVertex.vid)
203
204 timer, action = self.start_cpu(building.buildTime, cpu)
205 action += CallFunc(self.stop_cpu, cpu)
206 action += CallFunc(self.on_destruction, unit)
207 self.dispatch_event("player_add_building", buildingType)
208 cpu.action = action
209 timer.do(action)
210 return True
211 else:
212
213 return False
214
216
217 if len(self.idleCPUs) > 0 and None in unit.curVertex.troops:
218 troopType = ALL_UNITS[troopName]
219 cpu = self.idleCPUs.pop()
220 troop = self.add_troop(troopType, unit.curVertex.vid)
221 timer, action = self.start_cpu(troop.buildTime, cpu)
222 action += CallFunc(self.stop_cpu, cpu)
223 timer.do(action)
224 return True
225 else:
226 print "Building already in vertex OR no idle CPUs available"
227 return False
228
241
243
244 if researchFacility.isSelected != False:
245 menuUpdate = CallFunc(researchFacility.clear_action_menu, self.map, cm, self)
246 menuUpdate += CallFunc(researchFacility.display_action_menu, self.map, cm, self)
247 self.do(menuUpdate)
248
250 self.busyCPUs.append(cpu)
251 transTimer = TransTimer(sec, pos=cpu.position)
252 cpu.transTimer = transTimer
253 moveAction = transTimer.get_move_action(action)
254 self.map.add(transTimer, z=TIMER_Z)
255 self.unitActionLists["CPU"].append("CANCEL")
256 return transTimer, moveAction
257
259 if cpu in self.busyCPUs:
260 cpu.action.stop()
261 cpu.transTimer.kill()
262 self.stop_cpu(cpu)
263
265 if cpu in self.busyCPUs:
266 self.unitActionLists["CPU"].remove("CANCEL")
267 self.busyCPUs.remove(cpu)
268 self.idleCPUs.append(cpu)
269
271
272 attacker = attackers.pop()
273 target = targets.pop()
274 if issubclass(type(attacker), Unit) and issubclass(type(target), Unit):
275 path = self.map.get_path(attacker.curVertex, target.curVertex)
276 if len(path) <= attacker.range:
277 attacker.attack(target, path, self.map)
278 self.dispath_event("player_unit_attack", attacker)
279 return True
280 else:
281 print "path too long"
282 return False
283
285
286
287 if not unit.isDestroyed:
288 if issubclass(type(unit), Troop):
289 unit.curVertex.remove_troop(unit)
290 self.troops.remove(unit)
291 else:
292 unit.curVertex.building = None
293 self.buildings.remove(unit)
294 self.map.remove(unit)
295
296 if cmRemove:
297 self.cm.remove_tricky(unit)
298 unit.isDestroyed = True
299
300 if issubclass(type(unit), Server):
301 self.numServers -= 1
302
303
305 if len(self.troops) == 0 and len(self.buildings) == 0:
306 self.dispatch_event("on_loss", self)
307
309
310 for unit in units:
311 if unit is not None and ((issubclass(type(unit), Troop) and not unit.are_actions_running()) or issubclass(type(unit), Building)) and unit.pid == self.ID:
312 unit.set_is_selected(not unit.isSelected, self.map, self.cm, self)
313 if unit.isSelected:
314 selectedUnits.append(unit)
315 else:
316 selectedUnits.remove(unit)
317
318 '''MARK: Research Methods'''
319
323
327
340
342 self.dispatch_event('on_building_complete')
343
344 Player.register_event_type('on_loss')
345 Player.register_event_type('player_add_troop')
346 Player.register_event_type('player_unit_attack')
347 Player.register_event_type('player_add_building')
348
349
351 '''
352 The premise of this AI is to use a FINITE STATE MACHINE to react "intelligently" to a human player.
353 The states of this machine are defined below. An "N-Gram" will be used to collect data about player moves.
354 The "A-Star" pathfinding algorithm will be employed to find the most efficient way to attack.
355 '''
356 - def __init__(self, ID, gameMap, cm, color, enemy):
357 super(ComputerPlayer, self).__init__(ID, gameMap, cm, color)
358 self.aiRunning = False
359 self.theMap = gameMap
360 self.aiID = ID
361 self.ai_levels = ["Easy", "Medium", "Hard", "Godlike"]
362 self.ai_level = "Easy"
363 self.ai_states = ["Initial", "Waiting", "Scanning", "Researching", "Attacking", "Building", "Defending"]
364 self.max_move_per_move = 4
365 self.max_attack_per_move = 5
366 self.enemy = enemy
367 self.color = (70, 0, 180)
368 self.ai_cur_state = ""
369 self.ai_troops = 0
370 self.human_troops = 0
371 self.all_troops = 0
372 self.human_cpus = 1
373 self.board_research_level = 1
374 self.discovered = False
375 self.health = 1000
376 self.human_health = 1000
377 self.adjacent_free_slots = 0
378
382
383
384
386
387
388 for vertex in self.theMap.vertices.values():
389 for troop in vertex.troopSlots.values():
390 self.all_troops += 1
391
392
393
394 self.human_troops = 0
395 self.ai_troops = 0
396
397
398
399
400
401
403 thread.start_new_thread(self.ai_loop, ())
404
406
407 while True:
408 if self.ai_cur_state == "":
409
410 self.ai_cur_state = "Initial"
411 if self.ai_cur_state == "Initial":
412
413 self.scan_all_vertices()
414 else:
415 pass
416
417
418 time.sleep(4)
419 '''
420 while True:
421 time.sleep(3)
422 units_left = self.max_move_per_move
423 attack_left = self.max_attack_per_move
424 for unit in self.troops:
425 if unit.power > 0:
426 target = self.select_target(unit)
427 if target is not None:
428 if attack_left == 0:
429 break
430 path = self.map.get_path(
431 unit.curVertex, target.curVertex)
432 unit.attack(target, path, self.map)
433 attack_left -= 1
434 else:
435 if units_left == 0:
436 break
437 units_left -= 1
438 if not self.enemy.troops and not self.enemy.buildings:
439 return
440 target = random.choice(
441 self.enemy.troops + self.enemy.buildings)
442 unit.move(target.curVertex, self.map, self.cm)
443 '''
445 for vertex in unit.curVertex.adjacentVertices + [unit.curVertex]:
446 for troop in vertex.troops:
447
448
449 if troop is not None and troop.pid != self.ID:
450 return troop
451 if vertex.building != None:
452 return vertex.building
453 return None
454