Being able to calculate paths is now a property of fighting entities.
This commit is contained in:
parent
a3e059a97b
commit
8ecbf13eae
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue