Advertisement
Guest User

Untitled

a guest
Oct 14th, 2019
79
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.44 KB | None | 0 0
  1. import pygame, sys, os
  2. from enum import Enum
  3. import heapq
  4. import numpy as np
  5.  
  6. pygame.init()
  7.  
  8. size = width, height = 640, 480
  9.  
  10. screen = pygame.display.set_mode(size)
  11.  
  12.  
  13. class TileType(Enum):
  14. EMPTY = -1
  15. GROUND = 0
  16. WALL = 1
  17.  
  18.  
  19. TILE_TYPE_DICT = {
  20. TileType.EMPTY: " ",
  21. TileType.GROUND: "-SE",
  22. TileType.WALL: "#"
  23. }
  24.  
  25. TILE_COLOR_DICT = {
  26. TileType.EMPTY: (10, 10, 10),
  27. TileType.GROUND: (42, 86, 98),
  28. TileType.WALL: (7, 44, 54)
  29. }
  30.  
  31. # Added code
  32.  
  33. PLAYER_COLOR = (48, 117, 69)
  34. NPC_COLOR = (158, 77, 64)
  35.  
  36.  
  37. # TODO: Create classes, functions, and variables for pathfinding
  38. def is_position_empty(pos, grid, occupied_spaces=None):
  39. # Is index inside the grid, is the tile ground, and is there anyone standing there already?
  40. return 0 <= pos[0] < grid.shape[1] and 0 <= pos[1] < grid.shape[0]\
  41. and grid[pos[1], pos[0]] == TileType.GROUND.value\
  42. and (occupied_spaces is None or pos not in occupied_spaces)
  43.  
  44.  
  45. def is_valid_move(pos, move, grid, occupied_spaces=None):
  46. return move == (0,0) or is_position_empty((pos[0] + move[0], pos[1] + move[1]), grid, occupied_spaces)
  47.  
  48.  
  49. # Neighbors of a tile
  50. NEIGHBORS = [(-1, -1), (0, -1), (1, -1),
  51. (-1, 0), (0, 0), (1, 0),
  52. (-1, 1), (0, 1), (1, 1)]
  53.  
  54.  
  55. def get_neighbors(pos, grid, occupied_spaces=None):
  56. x, y = pos
  57. return [(x + direction[0], y + direction[1]) for direction in NEIGHBORS if is_valid_move(pos, direction, grid, occupied_spaces)]
  58.  
  59.  
  60. def dist(a, b):
  61. # easy distance for two tuples so we don't have to type this so much.
  62. return np.linalg.norm((a[0] - b[0], a[1] - b[1]))
  63.  
  64.  
  65. # Priority Queue class, needed for A*
  66. class PriorityQueue:
  67.  
  68. def __init__(self):
  69. self.elements = []
  70.  
  71. def empty(self):
  72. return len(self.elements) == 0
  73.  
  74. def put(self, item, priority):
  75. heapq.heappush(self.elements, (priority, item))
  76.  
  77. def get(self):
  78. return heapq.heappop(self.elements)[1]
  79.  
  80.  
  81. def find_path(pos, goal, grid, occupied_spaces=None):
  82.  
  83. search_queue = PriorityQueue()
  84. search_queue.put(pos, 0)
  85. node_paths = {pos: None}
  86. node_costs = {pos: 0}
  87.  
  88. # We'll use the closest we managed to get if no path exists.
  89. closest_distance = dist(pos, goal)
  90. closest_point = pos
  91.  
  92. while not search_queue.empty():
  93. current_pos = search_queue.get()
  94.  
  95. if current_pos == goal:
  96. # we did it
  97. break
  98. neighbors = get_neighbors(current_pos, grid, occupied_spaces)
  99. for position in neighbors:
  100. position_cost = node_costs[current_pos] + 1
  101. # Every position costs 1 to move to.
  102. if position not in node_costs or position_cost < node_costs[position]:
  103. node_costs[position] = position_cost
  104. distance_score = dist(position, goal)
  105. if distance_score < closest_distance:
  106. closest_distance = distance_score
  107. closest_point = position
  108. position_priority = position_cost + distance_score
  109. search_queue.put(position, position_priority)
  110. node_paths[position] = current_pos
  111.  
  112. # Start path from the closest point found.
  113. next_move = closest_point
  114.  
  115. path_points = [next_move]
  116. while node_paths[next_move] is not None and node_paths[next_move] != pos:
  117. next_move = node_paths[next_move]
  118. path_points.append(next_move)
  119.  
  120. return next_move, path_points
  121.  
  122.  
  123. # Player and NPC classes
  124. class Character:
  125.  
  126. def __init__(self, position):
  127. self.pos = position
  128. self.goal = None
  129. self.path_points = []
  130.  
  131. # each turn, a character will pathfind towards its goal. The player's goals will be set by user input, NPCs will
  132. # have a function that decides the AI's goal.
  133. def turn(self, grid, occupied_spaces=None):
  134. if self.goal is not None:
  135. # TODO: If character has a goal, find path from position to goal, then move to the next point in the path.
  136.  
  137. next_move, self.path_points = find_path(self.pos, self.goal, grid, occupied_spaces)
  138.  
  139. self.pos = next_move
  140.  
  141. # Once turn is taken, check if goal was reached.
  142. if self.pos == self.goal:
  143. # goal reached, reset goal to None.
  144. self.goal = None
  145.  
  146.  
  147. class Npc(Character):
  148.  
  149. def __init__(self, position):
  150. super().__init__(position)
  151. # TODO: Add variables for tracking and changing AI states.
  152.  
  153. # Set the goals of the NPC's AI
  154. def set_goals(self, grid, target=None, occupied_spaces=None):
  155. # TODO: Write code to choose AI states
  156. self.goal = target
  157.  
  158.  
  159. def load_map(filename):
  160. map_height, map_width = 10, 10
  161.  
  162. # Default character position, we'll update while loading the map
  163. player = Character((1, 1))
  164.  
  165. # list of npcs
  166. npcs = []
  167.  
  168. try:
  169. file = open(filename)
  170. lines = [line.rstrip() for line in file.readlines()]
  171.  
  172. print("File opened")
  173.  
  174. # If the file doesn't have any lines it's not a valid map_grid.
  175. assert len(lines) > 0
  176.  
  177. # Updating map_width and map_height based on what we read from the file.
  178. map_width = max([len(line) for line in lines])
  179. map_height = len(lines)
  180.  
  181. map_grid = np.full((map_height, map_width), TileType.EMPTY.value)
  182.  
  183. player_start_found = False
  184.  
  185. for row, line in enumerate(lines):
  186. for col, char in enumerate(line):
  187. # Add if statements to check for player start and enemy positions.
  188. if not player_start_found and char == "S":
  189. # Move player position
  190. player.pos = (col, row)
  191. player_start_found = True
  192. if char == "E":
  193. new_npc = Npc((col, row))
  194. npcs.append(new_npc)
  195. for type in TileType:
  196. if char in TILE_TYPE_DICT[type]:
  197. map_grid[row, col] = type.value
  198. break
  199.  
  200. print("Valid")
  201. # update return value to return player and npc list
  202. return map_grid, player, npcs
  203.  
  204. except (OSError, AssertionError):
  205. print("Map file not found or invalid, using default grid.")
  206. # update return value to return player and npc list
  207. npcs.append(Npc((8, 8)))
  208. return np.full((map_height, map_width), TileType.GROUND.value), player, npcs
  209.  
  210.  
  211. # Pixel size of a tile, used for tile/pixel conversions
  212. tile_size = 16
  213.  
  214. def tile_position(pixel):
  215. tile_x, tile_y = pixel
  216. tile_x //= tile_size
  217. tile_y //= tile_size
  218. return tile_x, tile_y
  219.  
  220.  
  221. def pixel_position(tile, center=False):
  222. pixel_x, pixel_y = tile
  223. pixel_x *= tile_size
  224. pixel_y *= tile_size
  225. if center:
  226. pixel_x += tile_size // 2
  227. pixel_y += tile_size // 2
  228. return pixel_x, pixel_y
  229.  
  230.  
  231. def tile_rect(tile):
  232. pixel_pos = pixel_position(tile)
  233. return pygame.Rect(pixel_pos, (tile_size, tile_size))
  234.  
  235.  
  236. ##
  237. # Test Map
  238. # #################
  239. # #------------#--#
  240. # #--#######---#--########
  241. # #--#-----#---#---------#
  242. # #--#--S--#-E-#--#--##--###
  243. # #--#-----#---#--#--#E--#E#
  244. # ######-#######--#--##--###
  245. # #----------------------#
  246. # ########################
  247.  
  248. #-#
  249. #P#
  250. #-#
  251. ##
  252.  
  253. # Set up game variables
  254.  
  255.  
  256. # Update map loading to get player and npc lists
  257. loaded_map, player, npcs = load_map("./one-room.txt")
  258.  
  259. map_height, map_width = loaded_map.shape
  260.  
  261. clock = pygame.time.Clock()
  262.  
  263. # How many milliseconds between turns. Larger numbers will slow down the turn speed so it's easier to see movement.
  264. turn_milliseconds = 250
  265. # counter to track how long since last turn.
  266. turn_timer = 0
  267.  
  268. # Game loop
  269. while True:
  270.  
  271. # Increase turn timer with number of milliseconds since last frame.
  272. turn_timer += clock.tick(60)
  273. for event in pygame.event.get():
  274. if event.type == pygame.QUIT:
  275. sys.exit()
  276. # TODO: Set player goals with user input.
  277. if event.type == pygame.MOUSEBUTTONDOWN:
  278. # if mouse pressed, set goal to the tile position based on mouse click.:
  279. player.goal = tile_position(event.pos)
  280. print(player.goal)
  281.  
  282. # AI chooses a goal:
  283. for npc in npcs:
  284. npc.set_goals(loaded_map, player.pos)
  285.  
  286. # If the player has a goal (which means they want to take a turn) and turn timer is high enough, take a turn
  287. if turn_timer >= turn_milliseconds and player.goal is not None:
  288. turn_timer = 0
  289.  
  290. # Debug print to test turn speed. Delete once you've got actual code here!
  291. print("Turn taken")
  292. # TODO: add code to take npc and player turns.
  293. player.turn(loaded_map, occupied_spaces=[npc.pos for npc in npcs] + [player.pos])
  294.  
  295. for npc in npcs:
  296. npc.turn(loaded_map, occupied_spaces=[npc.pos for npc in npcs] + [player.pos])
  297.  
  298. screen.fill((0, 0, 0))
  299. for row, col in np.ndindex(loaded_map.shape):
  300. color = TILE_COLOR_DICT[TileType(loaded_map[row, col])]
  301. rect = tile_rect((col, row))
  302. screen.fill(color, rect=rect)
  303.  
  304. # Draw player on screen
  305. screen.fill(PLAYER_COLOR, rect=tile_rect(player.pos))
  306. # TODO: Draw lines for player pathfinding paths.
  307. point_list = [pixel_position(pos, True) for pos in player.path_points]
  308.  
  309. if len(point_list) >= 2:
  310. pygame.draw.lines(screen, PLAYER_COLOR, False, point_list, 3)
  311.  
  312. # Draw each npc on screen
  313. for npc in npcs:
  314. screen.fill(NPC_COLOR, rect=tile_rect(npc.pos))
  315. # TODO: Draw lines for npc pathfinding paths.
  316. point_list = [pixel_position(pos, True) for pos in npc.path_points]
  317.  
  318. if len(point_list) >= 2:
  319. pygame.draw.lines(screen, NPC_COLOR, False, point_list, 3)
  320.  
  321. pygame.display.flip()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement