From 8ecbf13eae822864d5fdb24e62d86e724ff07374 Mon Sep 17 00:00:00 2001 From: eichhornchen Date: Fri, 18 Dec 2020 15:31:23 +0100 Subject: [PATCH] Being able to calculate paths is now a property of fighting entities. --- squirrelbattle/entities/player.py | 52 ---------------------------- squirrelbattle/interfaces.py | 57 ++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/squirrelbattle/entities/player.py b/squirrelbattle/entities/player.py index 36b497f..5f389cf 100644 --- a/squirrelbattle/entities/player.py +++ b/squirrelbattle/entities/player.py @@ -1,8 +1,6 @@ # Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse # SPDX-License-Identifier: GPL-3.0-or-later -from functools import reduce -from queue import PriorityQueue from random import randint from typing import Dict, Tuple @@ -15,7 +13,6 @@ class Player(InventoryHolder, FightingEntity): """ current_xp: int = 0 max_xp: int = 10 - paths: Dict[Tuple[int, int], Tuple[int, int]] def __init__(self, name: str = "player", maxhealth: int = 20, strength: int = 5, intelligence: int = 1, charisma: int = 1, @@ -87,55 +84,6 @@ class Player(InventoryHolder, FightingEntity): entity.hold(self) return super().check_move(y, x, move_if_possible) - def recalculate_paths(self, max_distance: int = 8) -> None: - """ - Uses Dijkstra algorithm to calculate best paths for monsters to go to - the player. - """ - distances = [] - predecessors = [] - # four Dijkstras, one for each adjacent tile - for dir_y, dir_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: - queue = PriorityQueue() - new_y, new_x = self.y + dir_y, self.x + dir_x - if not 0 <= new_y < self.map.height or \ - not 0 <= new_x < self.map.width or \ - not self.map.tiles[new_y][new_x].can_walk(): - continue - queue.put(((1, 0), (new_y, new_x))) - visited = [(self.y, self.x)] - distances.append({(self.y, self.x): (0, 0), (new_y, new_x): (1, 0)}) - predecessors.append({(new_y, new_x): (self.y, self.x)}) - while not queue.empty(): - dist, (y, x) = queue.get() - if dist[0] >= max_distance or (y, x) in visited: - continue - visited.append((y, x)) - for diff_y, diff_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: - new_y, new_x = y + diff_y, x + diff_x - if not 0 <= new_y < self.map.height or \ - not 0 <= new_x < self.map.width or \ - not self.map.tiles[new_y][new_x].can_walk(): - continue - new_distance = (dist[0] + 1, - dist[1] + (not self.map.is_free(y, x))) - if not (new_y, new_x) in distances[-1] or \ - distances[-1][(new_y, new_x)] > new_distance: - predecessors[-1][(new_y, new_x)] = (y, x) - distances[-1][(new_y, new_x)] = new_distance - queue.put((new_distance, (new_y, new_x))) - # For each tile that is reached by at least one Dijkstra, sort the - # different paths by distance to the player. For the technical bits : - # The reduce function is a fold starting on the first element of the - # iterable, and we associate the points to their distance, sort - # along the distance, then only keep the points. - self.paths = {} - for y, x in reduce(set.union, - [set(p.keys()) for p in predecessors], set()): - self.paths[(y, x)] = [p for d, p in sorted( - [(distances[i][(y, x)], predecessors[i][(y, x)]) - for i in range(len(distances)) if (y, x) in predecessors[i]])] - def save_state(self) -> dict: """ Saves the state of the entity into a dictionary diff --git a/squirrelbattle/interfaces.py b/squirrelbattle/interfaces.py index 71f70ad..afe4c44 100644 --- a/squirrelbattle/interfaces.py +++ b/squirrelbattle/interfaces.py @@ -4,7 +4,9 @@ from enum import Enum, auto from math import sqrt from random import choice, randint -from typing import List, Optional, Any +from typing import List, Optional, Any, Dict, Tuple +from queue import PriorityQueue +from functools import reduce from .display.texturepack import TexturePack from .translations import gettext as _ @@ -237,6 +239,7 @@ class Entity: x: int name: str map: Map + paths: Dict[Tuple[int, int], Tuple[int, int]] # noinspection PyShadowingBuiltins def __init__(self, y: int = 0, x: int = 0, name: Optional[str] = None, @@ -245,6 +248,7 @@ class Entity: self.x = x self.name = name self.map = map + self.paths = None def check_move(self, y: int, x: int, move_if_possible: bool = False)\ -> bool: @@ -292,6 +296,57 @@ class Entity: return self.move(self.y, self.x + 1) if force else \ self.check_move(self.y, self.x + 1, True) + def recalculate_paths(self, max_distance: int = 8) -> None: + """ + Uses Dijkstra algorithm to calculate best paths for other entities to + go to this entity. If self.paths is None, does nothing. + """ + if self.paths == None : + return + distances = [] + predecessors = [] + # four Dijkstras, one for each adjacent tile + for dir_y, dir_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: + queue = PriorityQueue() + new_y, new_x = self.y + dir_y, self.x + dir_x + if not 0 <= new_y < self.map.height or \ + not 0 <= new_x < self.map.width or \ + not self.map.tiles[new_y][new_x].can_walk(): + continue + queue.put(((1, 0), (new_y, new_x))) + visited = [(self.y, self.x)] + distances.append({(self.y, self.x): (0, 0), (new_y, new_x): (1, 0)}) + predecessors.append({(new_y, new_x): (self.y, self.x)}) + while not queue.empty(): + dist, (y, x) = queue.get() + if dist[0] >= max_distance or (y, x) in visited: + continue + visited.append((y, x)) + for diff_y, diff_x in [(1, 0), (-1, 0), (0, 1), (0, -1)]: + new_y, new_x = y + diff_y, x + diff_x + if not 0 <= new_y < self.map.height or \ + not 0 <= new_x < self.map.width or \ + not self.map.tiles[new_y][new_x].can_walk(): + continue + new_distance = (dist[0] + 1, + dist[1] + (not self.map.is_free(y, x))) + if not (new_y, new_x) in distances[-1] or \ + distances[-1][(new_y, new_x)] > new_distance: + predecessors[-1][(new_y, new_x)] = (y, x) + distances[-1][(new_y, new_x)] = new_distance + queue.put((new_distance, (new_y, new_x))) + # For each tile that is reached by at least one Dijkstra, sort the + # different paths by distance to the player. For the technical bits : + # The reduce function is a fold starting on the first element of the + # iterable, and we associate the points to their distance, sort + # along the distance, then only keep the points. + self.paths = {} + for y, x in reduce(set.union, + [set(p.keys()) for p in predecessors], set()): + self.paths[(y, x)] = [p for d, p in sorted( + [(distances[i][(y, x)], predecessors[i][(y, x)]) + for i in range(len(distances)) if (y, x) in predecessors[i]])] + def act(self, m: Map) -> None: """ Defines the action the entity will do at each tick.