Being able to calculate paths is now a property of fighting entities.
This commit is contained in:
		@@ -1,8 +1,6 @@
 | 
				
			|||||||
# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
					# Copyright (C) 2020 by ÿnérant, eichhornchen, nicomarg, charlse
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from functools import reduce
 | 
					 | 
				
			||||||
from queue import PriorityQueue
 | 
					 | 
				
			||||||
from random import randint
 | 
					from random import randint
 | 
				
			||||||
from typing import Dict, Tuple
 | 
					from typing import Dict, Tuple
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -15,7 +13,6 @@ class Player(InventoryHolder, FightingEntity):
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
    current_xp: int = 0
 | 
					    current_xp: int = 0
 | 
				
			||||||
    max_xp: int = 10
 | 
					    max_xp: int = 10
 | 
				
			||||||
    paths: Dict[Tuple[int, int], Tuple[int, int]]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, name: str = "player", maxhealth: int = 20,
 | 
					    def __init__(self, name: str = "player", maxhealth: int = 20,
 | 
				
			||||||
                 strength: int = 5, intelligence: int = 1, charisma: int = 1,
 | 
					                 strength: int = 5, intelligence: int = 1, charisma: int = 1,
 | 
				
			||||||
@@ -87,55 +84,6 @@ class Player(InventoryHolder, FightingEntity):
 | 
				
			|||||||
                    entity.hold(self)
 | 
					                    entity.hold(self)
 | 
				
			||||||
        return super().check_move(y, x, move_if_possible)
 | 
					        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:
 | 
					    def save_state(self) -> dict:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Saves the state of the entity into a dictionary
 | 
					        Saves the state of the entity into a dictionary
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,9 @@
 | 
				
			|||||||
from enum import Enum, auto
 | 
					from enum import Enum, auto
 | 
				
			||||||
from math import sqrt
 | 
					from math import sqrt
 | 
				
			||||||
from random import choice, randint
 | 
					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 .display.texturepack import TexturePack
 | 
				
			||||||
from .translations import gettext as _
 | 
					from .translations import gettext as _
 | 
				
			||||||
@@ -237,6 +239,7 @@ class Entity:
 | 
				
			|||||||
    x: int
 | 
					    x: int
 | 
				
			||||||
    name: str
 | 
					    name: str
 | 
				
			||||||
    map: Map
 | 
					    map: Map
 | 
				
			||||||
 | 
					    paths: Dict[Tuple[int, int], Tuple[int, int]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # noinspection PyShadowingBuiltins
 | 
					    # noinspection PyShadowingBuiltins
 | 
				
			||||||
    def __init__(self, y: int = 0, x: int = 0, name: Optional[str] = None,
 | 
					    def __init__(self, y: int = 0, x: int = 0, name: Optional[str] = None,
 | 
				
			||||||
@@ -245,6 +248,7 @@ class Entity:
 | 
				
			|||||||
        self.x = x
 | 
					        self.x = x
 | 
				
			||||||
        self.name = name
 | 
					        self.name = name
 | 
				
			||||||
        self.map = map
 | 
					        self.map = map
 | 
				
			||||||
 | 
					        self.paths = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def check_move(self, y: int, x: int, move_if_possible: bool = False)\
 | 
					    def check_move(self, y: int, x: int, move_if_possible: bool = False)\
 | 
				
			||||||
            -> bool:
 | 
					            -> bool:
 | 
				
			||||||
@@ -292,6 +296,57 @@ class Entity:
 | 
				
			|||||||
        return self.move(self.y, self.x + 1) if force else \
 | 
					        return self.move(self.y, self.x + 1) if force else \
 | 
				
			||||||
            self.check_move(self.y, self.x + 1, True)
 | 
					            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:
 | 
					    def act(self, m: Map) -> None:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Defines the action the entity will do at each tick.
 | 
					        Defines the action the entity will do at each tick.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user