Advertisement
Guest User

Untitled

a guest
Aug 3rd, 2014
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.81 KB | None | 0 0
  1. import math
  2. import pygame
  3. import random
  4. import sys
  5. import subprocess
  6. import os
  7. from threading import Thread
  8. from Queue import Queue, Empty, Full
  9.  
  10. ON_POSIX = 'posix' in sys.builtin_module_names
  11.  
  12. def enqueue_output(out, queue):
  13. while True:
  14. for line in iter(out.readline, b''):
  15. queue.put(line)
  16.  
  17. def enqueue_input(inp, queue):
  18. while True:
  19. s = queue.get(block=True, timeout=None)
  20. inp.write(s)
  21. inp.flush()
  22. if s == 'q' or s == 'Q':
  23. break
  24.  
  25.  
  26. class Player(object):
  27. def __init__(self, name, command):
  28. self.name = name
  29. self.direction = North
  30. self.square = None
  31. self.process = subprocess.Popen(command, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=1, close_fds=ON_POSIX, cwd="bots/"+name+"/")
  32. self.in_queue = Queue()
  33. self.out_queue = Queue()
  34. self.out_thread = Thread(target=enqueue_output, args=(self.process.stdout, self.out_queue))
  35. self.in_thread = Thread(target=enqueue_input, args=(self.process.stdin, self.in_queue))
  36. self.in_thread.daemon = True
  37. self.out_thread.daemon = True
  38. self.in_thread.start()
  39. self.out_thread.start()
  40. self.turns_invincible = 0
  41. self.score = 0
  42. self.cur_invincible_bonus = 200
  43. self.is_ghost = False
  44. self.has_teleported = False
  45.  
  46. def start(self):
  47. maze_desc = ""
  48. for line in self.square.maze.grid:
  49. for square in line:
  50. maze_desc+=square.to_hex()
  51. self.send_message(maze_desc)
  52.  
  53. def move(self):
  54. letters = ['P' if len(self.square.players) > 1 else 'X']
  55. letters = [",".join([str(x) for x in self.square.coordinates])]
  56. if self.is_ghost:
  57. letters[0] += "G"
  58. elif len(self.square.players) >1:
  59. letters[0] += "P"
  60. else:
  61. letters[0] += "X"
  62. if self.direction:
  63. d_index = directions.index(self.direction)
  64. follow_directions = [d_index-1, d_index, d_index-3]
  65. else:
  66. follow_directions = range(0, 4)
  67. for d in follow_directions:
  68. cur_square = self.square
  69. next_square = cur_square.neighbors(wraps=True)[d]
  70. while cur_square.is_connected_to(next_square, wraps=True):
  71. letters.append(",".join([str(x) for x in next_square.coordinates])+next_square.letter())
  72. cur_square = next_square
  73. next_square = next_square.neighbors(wraps=True)[d]
  74.  
  75. for x in xrange(len(follow_directions)):
  76. cur_direction = follow_directions[x]
  77. prev_direction = follow_directions[x-1]
  78. if (not self.square.walls[cur_direction] and not self.square.neighbors(wraps=True)[cur_direction].walls[prev_direction])\
  79. or (not self.square.walls[prev_direction] and not self.square.neighbors(wraps=True)[prev_direction].walls[cur_direction]):
  80. corner = self.square.neighbors(wraps=True)[cur_direction].neighbors(wraps=True)[prev_direction]
  81. letters.append(",".join([str(x) for x in corner.coordinates])+corner.letter())
  82.  
  83. message = " ".join(letters)
  84. self.send_message(message)
  85. while True:
  86. move = str(self.get_response()).lower().strip()
  87. coord = move.split(",")
  88. if len(coord) != 2:
  89. break
  90. if coord[0].isdigit() and coord[1].isdigit():
  91. self.send_message(self.square.grid.get(coord).to_hex())
  92. if move=="n":
  93. direction = North
  94. elif move=="e":
  95. direction = East
  96. elif move=="w":
  97. direction = West
  98. elif move=="s":
  99. direction = South
  100. else:
  101. if move != 'x':
  102. self.send_message("Bad input:"+move)
  103. direction = None
  104.  
  105. index = directions.index(direction)
  106. if self.square.walls[index]:
  107. self.direction = None
  108. else:
  109. self.move_to(self.square.neighbors(wraps=True)[index])
  110.  
  111. def check_square(self):
  112. if self.square.ghosts:
  113. invincibles = [player for player in self.square.players if player.turns_invincible]
  114. if invincibles:
  115. for ghost in self.square.ghosts:
  116. ghost.teleport()
  117. for player in invincibles:
  118. player.score += player.cur_invincible_bonus
  119. player.cur_invincible_bonus *= 2
  120. else:
  121. self.is_ghost = True
  122. all_ghosts.append(self)
  123. bots.remove(self)
  124. self.square.players.remove(self)
  125. self.square.ghosts.append(self)
  126. return
  127. if len(self.square.players) > 1:
  128. self.square.contents = Nothing
  129. return
  130. if self.square.contents is Nothing:
  131. return
  132. if self.square.contents is Pellet:
  133. self.score += 10
  134. elif self.square.contents is PowerPellet:
  135. self.score += 50
  136. if not self.turns_invincible:
  137. self.cur_invincible_bonus = 200
  138. self.turns_invincible = 10
  139. elif self.square.contents is Fruit:
  140. self.score += 100
  141. self.square.contents = Nothing
  142.  
  143. def get_response(self):
  144. try:
  145. return self.out_queue.get(timeout=1)
  146. except Empty:
  147. self.remove()
  148. raise RuntimeError(self.name+" didn't accept a message")
  149.  
  150. def teleport(self):
  151. self.has_teleported = True
  152. self.move_to(self.square.maze.get((random.randrange(self.square.maze.side_length),random.randrange(self.square.maze.side_length))))
  153.  
  154.  
  155. def move_to(self, square):
  156. if self.is_ghost:
  157. if self.has_teleported:
  158. self.has_teleported = False
  159. return
  160. self.square.ghosts.remove(self)
  161. self.square = square
  162. self.square.ghosts.append(self)
  163. else:
  164. if self.turns_invincible:
  165. self.turns_invincible -= 1
  166. self.square.players.remove(self)
  167. self.square = square
  168. self.square.players.append(self)
  169.  
  170.  
  171. def send_message(self, message):
  172. try:
  173. self.in_queue.put(message+"\r\n", timeout=1)
  174. except Full:
  175. self.remove()
  176. raise RuntimeError(self.name+" didn't accept a message")
  177.  
  178. def remove(self):
  179. self.square.players.remove(self)
  180.  
  181. class Ghost(object):
  182. def __init__(self, start_square):
  183. self.duration = 10
  184. self.count = 0
  185. self.chasing = False
  186. self.square = start_square
  187. self.closest_player = None
  188. self.last_square = None
  189. self.has_teleported = False
  190.  
  191. def teleport(self):
  192. self.step(self.square.maze.get((random.randrange(self.square.maze.side_length),random.randrange(self.square.maze.side_length))))
  193. self.has_teleported = True
  194.  
  195. def move(self):
  196. self.count += 1
  197. if self.count is self.duration:
  198. self.chasing = not self.chasing
  199. self.count = 0
  200. if self.chasing:
  201. players = []
  202. distance = 100
  203. for x in xrange(-5, 6):
  204. for y in xrange(-5, 6):
  205. next = self.square.maze.get((self.square.x+x, self.square.y+y), wraps=True)
  206. if next.players:
  207. if abs(x)+abs(y) < distance:
  208. players = []
  209. distance = abs(x) + abs(y)
  210. if abs(x)+abs(y)==distance:
  211. players.extend(next.players)
  212. if players:
  213. self.closest_player = random.choice(players)
  214. self.switch()
  215. self.step_to(self.last_square.coordinates, can_reverse=True)
  216. return
  217. else:
  218. self.chasing = False
  219. if self.chasing:
  220. self.chase()
  221. else:
  222. self.scatter()
  223.  
  224. def scatter(self):
  225. neighbors = self.square.neighbors(connected=True, wraps=True)
  226. try:
  227. neighbors.remove(self.last_square)
  228. except ValueError:
  229. pass
  230. try:
  231. self.step(random.choice(neighbors))
  232. except IndexError:
  233. import pdb
  234. pdb.set_trace()
  235.  
  236. def chase(self):
  237. pass
  238.  
  239. def switch(self):
  240. pass
  241.  
  242. def step(self, to):
  243. if self.has_teleported:
  244. self.has_teleported = False
  245. return
  246. self.square.ghosts.remove(self)
  247. self.last_square = self.square
  248. to.ghosts.append(self)
  249. self.square = to
  250.  
  251.  
  252. def step_to(self, point, can_reverse=False):
  253. top_score = -50
  254. next_direction = None
  255. for index, direction in enumerate(directions):
  256. if self.last_square and direction + self.square == self.last_square.coordinates:
  257. continue
  258. if self.square.walls[index]:
  259. continue
  260. score = [(finish-start)*dir for start, finish, dir in zip(self.square.coordinates, point, direction.get_coordinates())]
  261. score = score[0] if score[0] else score[1]
  262. if score > 10:
  263. score -= self.square.maze.side_length
  264. elif score < -10:
  265. score += self.square.maze.side_length
  266. if score > top_score:
  267. top_score = score
  268. next_direction = direction
  269. if next_direction:
  270. self.step(self.square.maze.get((next_direction+self.square).get_coordinates(), wraps=True))
  271.  
  272.  
  273. class Pinky(Ghost):
  274. def chase(self):
  275. if self.closest_player.direction:
  276. next_square = [direction*4 + coordinate for direction, coordinate in zip(self.closest_player.direction.get_coordinates(), self.closest_player.square.coordinates)]
  277. else:
  278. next_square = self.closest_player.square.coordinates
  279. self.step_to(next_square)
  280.  
  281.  
  282. class Inky(Ghost):
  283. def __init__(self, start_square):
  284. Ghost.__init__(self, start_square)
  285. self.closest_ghost = None
  286.  
  287. def chase(self):
  288. if self.closest_player.direction:
  289. player_square = [direction*2 + coordinate for direction, coordinate in zip(self.closest_player.direction.get_coordinates(), self.closest_player.square.coordinates)]
  290. else:
  291. player_square = self.closest_player.square.coordinates
  292. next_square = [a*2-b for a, b in zip(self.closest_ghost.square.coordinates,self.square.coordinates)]
  293. self.step_to(next_square)
  294.  
  295.  
  296. def switch(self):
  297. ghosts = []
  298. distance = 100
  299. for x in xrange(-5, 6):
  300. for y in xrange(-5, 6):
  301. next = self.square.maze.get((self.square.x+x, self.square.y+y), wraps=True)
  302. if next.ghosts:
  303. if abs(x)+abs(y) < distance:
  304. ghosts = []
  305. distance = abs(x) + abs(y)
  306. if abs(x)+abs(y)==distance:
  307. ghosts.extend(next.ghosts)
  308. self.closest_ghost = random.choice(ghosts)
  309.  
  310.  
  311. class Blinky(Ghost):
  312. def chase(self):
  313. next_square = self.closest_player.square
  314. self.step_to(next_square.coordinates)
  315.  
  316.  
  317. class Clyde(Ghost):
  318. def __init__(self, start_square):
  319. Ghost.__init__(self, start_square)
  320. self.furthest_player = None
  321.  
  322. def chase(self):
  323. self.step_to(self.furthest_player.square.coordinates)
  324.  
  325. def switch(self):
  326. players = []
  327. distance = 0
  328. for x in xrange(-8, 9):
  329. for y in xrange(-8, 9):
  330. next = self.square.maze.get((self.square.x+x, self.square.y+y), wraps=True)
  331. if next.players:
  332. if abs(x)+abs(y) > distance:
  333. players = []
  334. distance = abs(x) + abs(y)
  335. if abs(x)+abs(y)==distance:
  336. players.extend(next.players)
  337. self.furthest_player = random.choice(players)
  338.  
  339. ghosts = Inky, Blinky, Clyde, Pinky
  340.  
  341.  
  342. class MazeGraphics:
  343. def __init__(self, maze):
  344. self.square_size = 15
  345. pygame.init()
  346. self.maze = maze
  347. self.bg_color = (0, 0, 0)
  348. self.fg_color = (0, 0, 255)
  349. self.dimensions = [maze.side_length*self.square_size]*2
  350. self.screen = pygame.display.set_mode(self.dimensions, 0, 32)
  351. pygame.display.set_caption("Maze")
  352. pygame.display.flip()
  353.  
  354. def draw_maze(self):
  355. for line in self.maze.grid:
  356. for square in line:
  357. self.draw_square(square)
  358. self.update()
  359.  
  360. def update(self):
  361. self.screen.blit(self.screen, (0, 0))
  362. pygame.display.update()
  363. for event in pygame.event.get():
  364. if event.type == pygame.QUIT:
  365. sys.exit(0)
  366.  
  367. def draw_square(self, square):
  368. square_coordinates = [a*self.square_size for a in square.coordinates]
  369. rect = pygame.Rect(square_coordinates, (self.square_size, self.square_size))
  370. self.screen.fill(self.bg_color, rect)
  371. for index, wall in enumerate(square.walls):
  372. if not wall:
  373. continue
  374. line_coordinate = [[[0, 0], [1, 0]],
  375. [[1, 0], [1, 1]],
  376. [[1, 1], [0, 1]],
  377. [[0, 1], [0, 0]]][index]
  378. offset_coordinate = []
  379. for point in line_coordinate:
  380. offset_coordinate.append([])
  381. for coordinate, offset in zip(point, square_coordinates):
  382. offset_coordinate[-1].append(coordinate*(self.square_size-1) + offset)
  383. pygame.draw.line(self.screen, self.fg_color, offset_coordinate[0], offset_coordinate[1], 1)
  384. circle_color = (((0, 0, 0), (255, 0, 0), (100, 200, 200), (100, 200, 200))[square.contents] if not square.ghosts else (0, 255, 0)) if not square.players else (255, 255, 0)
  385. circle_size = self.square_size/6 if square.contents is Pellet and not square.ghosts and not square.players else self.square_size/3
  386. circle_offset = [coordinate*self.square_size + self.square_size/2 for coordinate in square.coordinates]
  387. pygame.draw.circle(self.screen, circle_color, circle_offset, circle_size)
  388.  
  389.  
  390. class Direction(object):
  391.  
  392. def __init__(self, x, y):
  393. self.x = x
  394. self.y = y
  395.  
  396. def __add__(self, other):
  397. return Direction(self.x + other.x,self.y + other.y)
  398.  
  399. def get_opposite(self):
  400. return directions[directions.index(self)-2]
  401.  
  402. def get_coordinates(self):
  403. return self.x , self.y
  404.  
  405.  
  406. North = Direction(0, -1)
  407. East = Direction(1, 0)
  408. South = Direction(0, 1)
  409. West = Direction(-1, 0)
  410. directions = [North, East, South, West]
  411.  
  412.  
  413. class Square(object):
  414. def __init__(self, maze, coordinates):
  415. self.x, self.y = coordinates
  416. self.coordinates = coordinates
  417. self.maze = maze
  418. self.walls = [True]*4
  419. self.players = []
  420. self.ghosts = []
  421. self.contents = Nothing
  422.  
  423. def __str__(self):
  424. return str(self.x)+","+str(self.y)
  425.  
  426. def neighbors(self, connected=None, **kwargs):
  427. return [x for x in [self.maze.get((x+self).get_coordinates(), **kwargs) for x in directions] if connected==None or (self.is_connected_to(x, **kwargs)==connected)]
  428.  
  429. def is_connected_to(self, other, **kwargs):
  430. try:
  431. return not self.walls[self.neighbors(**kwargs).index(other)]
  432. except IndexError:
  433. return False
  434.  
  435. def connect_to(self, other, **kwargs):
  436. try:
  437. index = self.neighbors(**kwargs).index(other)
  438. self.walls[index] = False
  439. other.walls[index-2] = False
  440. except ValueError:
  441. pass
  442.  
  443. def to_hex(self):
  444. num = 0
  445. for index, wall in enumerate(self.walls):
  446. num += 2**index if wall else 0
  447. return hex(num)
  448.  
  449. def letter(self):
  450. if self.players:
  451. return 'P'
  452. elif self.ghosts:
  453. return 'G'
  454. else:
  455. return ['X','F','O','o'][self.contents]
  456.  
  457. Nothing, Fruit, PowerPellet, Pellet = range(4)
  458.  
  459.  
  460. class Maze(object):
  461. def __init__(self, num_players):
  462. self.num_players = max(num_players, 1)
  463. self.side_length = int(math.ceil(math.sqrt(self.num_players)*10))
  464. self.num_ghosts = 2*self.num_players
  465. self.num_power_pellets = 4*self.num_players
  466. self.num_fruit = 2*self.num_players
  467. self.grid = [[Square(self, (b, a)) for a in xrange(self.side_length)] for b in xrange(self.side_length)]
  468.  
  469. def get(self, coordinates, wraps=False):
  470. try:
  471. return self.grid[coordinates[0]][coordinates[1]]
  472. except IndexError:
  473. if not wraps:
  474. return None
  475. coordinates = list(coordinates)
  476. while coordinates[0] < 0:
  477. coordinates[0] += self.side_length
  478. while coordinates[0] >= self.side_length:
  479. coordinates[0] -= self.side_length
  480. while coordinates[1] < 0:
  481. coordinates[1] += self.side_length
  482. while coordinates[1] >= self.side_length:
  483. coordinates[1] -= self.side_length
  484. return self.grid[coordinates[0]][coordinates[1]]
  485.  
  486. def generate(self):
  487. start_square = self.get((random.randrange(self.side_length),
  488. random.randrange(self.side_length)))
  489. to_process = [start_square]
  490. while to_process:
  491. random.shuffle(to_process)
  492. next_square = to_process[-1]
  493. unconnected_neighbors = [x for x in next_square.neighbors(wraps=True) if x and all(x.walls)]
  494. if unconnected_neighbors:
  495. random.shuffle(unconnected_neighbors)
  496. connection = unconnected_neighbors.pop()
  497. next_square.connect_to(connection)
  498. if not unconnected_neighbors:
  499. to_process.pop()
  500. to_process.append(connection)
  501. else:
  502. to_process.pop()
  503. for line in self.grid:
  504. for square in line:
  505. if len(square.neighbors(connected=True, wraps=True)) == 1:
  506. connections = square.neighbors(wraps=True, connected=False)
  507. random.shuffle(connections)
  508. for connection in connections:
  509. if len(connection.neighbors(connected=True, wraps=True)) == 1:
  510. square.connect_to(connection, wraps=True)
  511. break
  512. else:
  513. if connections[0] in square.neighbors(connected=True, wraps=True):
  514. square.connect_to(connections[1], wraps=True)
  515. else:
  516. square.connect_to(connections[0], wraps=True)
  517. to_place = (PowerPellet, self.num_power_pellets), (Fruit, self.num_fruit)
  518. for item, amount in to_place:
  519. while amount > 0:
  520. square = self.get((random.randrange(self.side_length),random.randrange(self.side_length)))
  521. if square.contents is Nothing:
  522. square.contents = item
  523. amount -= 1
  524.  
  525. for line in self.grid:
  526. for square in line:
  527. if square.contents is Nothing:
  528. square.contents = Pellet
  529. for _ in xrange(self.num_ghosts):
  530. while True:
  531. square = self.get((random.randrange(self.side_length), random.randrange(self.side_length)))
  532. if square.contents is Pellet:
  533. square.contents = Nothing
  534. square.ghosts.append(random.sample(ghosts, 1)[0](square))
  535. all_ghosts.append(square.ghosts[-1])
  536. break
  537.  
  538. for bot in bots:
  539. while True:
  540. square = self.get((random.randrange(self.side_length),random.randrange(self.side_length)))
  541. if square.contents is not Pellet:
  542. continue
  543. for neighbor in square.neighbors(connected=True, wraps=True):
  544. if neighbor.contents is not Pellet:
  545. break
  546. else:
  547. square.contents = Nothing
  548. square.players.append(bot)
  549. bot.square = square
  550. break
  551.  
  552.  
  553. def run_programs():
  554. for bot in bots:
  555. bot.start()
  556. import time
  557. time.sleep(10)
  558. while True:
  559. if not bots:
  560. break
  561. for ghost in all_ghosts:
  562. ghost.move()
  563. for bot in bots[:]:
  564. bot.check_square()
  565. for bot in bots:
  566. bot.move()
  567. for bot in bots[:]:
  568. bot.check_square()
  569. graphics.draw_maze()
  570.  
  571.  
  572. def generate_maze():
  573. global bots
  574. maze = Maze(len(bots))
  575. maze.generate()
  576. return maze
  577.  
  578.  
  579. def read_bot_list():
  580. players = []
  581. for dir in os.listdir(r"bots/"):
  582. try:
  583. file = open("bots/"+dir+"/command.txt", 'r')
  584. except IOError:
  585. continue
  586. command = file.read()
  587. file.close()
  588. for x in xrange(5):
  589. players.append(Player(dir, command))
  590. return players
  591.  
  592. if __name__ == "__main__":
  593. random.seed()
  594. bots = read_bot_list()
  595. all_ghosts = []
  596. maze = generate_maze()
  597. graphics = MazeGraphics(maze)
  598. graphics.draw_maze()
  599. run_programs()
  600.  
  601. for bot in sorted([bot for bot in all_ghosts if isinstance(bot, Player)], key=lambda x: x.score, reverse=True):
  602. print bot.name + ": "+str(bot.score)+" points"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement