Skip to content

Commit

Permalink
I think I fixed most of the game code, to now support cells. interact…
Browse files Browse the repository at this point in the history
…ions are still broke, but the cell class is nice :)
  • Loading branch information
Alineman123456555 committed Apr 30, 2024
1 parent bc13509 commit 67ea858
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 150 deletions.
3 changes: 2 additions & 1 deletion python/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
DumbGhost,
EatModePlayer,
DynamicEntity,
Cell,
)
from python.world import World, Cell
from python.world import World
from python.game import Game
from python.coordinate import Coordinate
import python.config as config
Expand Down
125 changes: 91 additions & 34 deletions python/entity.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging

from abc import ABC, abstractmethod
from typing import Tuple, List
from python.coordinate import Coordinate
from typing import Set, Tuple, Dict

from python.coordinate import Coordinate
from python.direction import Direction

logger = logging.getLogger(__name__)
Expand All @@ -12,20 +13,84 @@ class Entity(ABC):
pass


class Cell:
"""Stores entities at each spot in the worlds 2d array
The entities are stored in a dictionary that maps
Class type to a set of entities that are that type.
This makes it easier to grab specific sets of entities in a
Cell when doing various interactions.
"""

def __init__(self):
self._entity_dict: Dict[type, Set[Entity]] = {}

def add_entity(self, entity: Entity) -> "Cell":
self._entity_dict.setdefault(entity.__class__, set())
self._entity_dict[entity.__class__].add(entity)
return self

def remove_entity(self, entity: Entity) -> "Cell":
try:
entity_set = self._entity_dict[entity.__class__]
entity_set.remove(entity)
if len(entity_set) == 0:
logger.debug(f"Empty entity_set removing class: {entity.__class__}")
self._entity_dict.pop(entity.__class__)
except KeyError:
# TODO: Don't know if this error should actually be returned
logger.error(
f"Tried deleting an entity that was not in this cell entity: {entity}"
f"_entity_dict: {self._entity_dict}"
)
return self

def get_all(self) -> Set[Entity]:
all_ents = set()
for ents in self._entity_dict.values():
all_ents.update(ents)
return all_ents

def is_empty(self) -> bool:
if self._entity_dict:
return False
return True

def has_class(self, class_type: type) -> bool:
if class_type in self._entity_dict.keys():
return True
return False

def has_subclass(self, class_type: type) -> bool:
for entity_class in self._entity_dict.keys():
if issubclass(entity_class, class_type):
return True
return False

def get_class_set(self, class_type: type) -> Set[Entity]:
"""Gets set of all entities that match class"""
try:
return self._entity_dict[class_type]
except KeyError:
# TODO: Don't know if this error should actually be returned
logger.error(f"No class_type: {class_type}, " f"stored in Cell: {self}")
return set()

def get_subclass_set(self, class_type: type) -> Set[Entity]:
"""Gets set of all entities and children type"""
full_entity_set = set()
for entity_class, entity_set in self._entity_dict.items():
if issubclass(entity_class, class_type):
full_entity_set.update(entity_set)
return full_entity_set


class DynamicEntity(Entity):
def __init__(self):
self.coords: Coordinate = None

@abstractmethod
def gen_move(
self,
surroundings: Tuple[
Entity,
Entity,
Entity,
Entity,
],
) -> Coordinate:
def gen_move(self, surroundings: Dict[Direction, "Cell"]) -> Coordinate:
"""Generates a move
Parameters
Expand All @@ -51,15 +116,7 @@ def __init__(self, coords: Coordinate = None):
# Maybe I should just init entities at (0, 0)
# or just add a coord to the init

def gen_move(
self,
surroundings: Tuple[
Entity,
Entity,
Entity,
Entity,
],
) -> Coordinate:
def gen_move(self, surroundings: Dict[Direction, "Cell"]) -> Coordinate:
# TODO: Figure out if you need to use surroundsing to generate the move.
return self.coords + self.direction.value

Expand Down Expand Up @@ -99,24 +156,24 @@ def __init__(self, direction: Direction = Direction.UP, coords: Coordinate = Non
self.direction: Direction = direction
self.coords: Coordinate = coords

def gen_move(
self,
surroundings: Tuple[
Entity,
Entity,
Entity,
Entity,
],
) -> Coordinate:
def gen_move(self, surroundings: Dict[Direction, "Cell"]) -> Coordinate:
# TODO: make logic check all 4 direction no matter what the starting direction is.
right, left, up, down = surroundings
if self.direction == Direction.RIGHT and isinstance(right, Wall):
# TODO: Fix formatting probably refactor this check to a function.
if self.direction == Direction.RIGHT and surroundings[
Direction.RIGHT
].has_class(Wall):
self.direction = Direction.DOWN
if self.direction == Direction.DOWN and isinstance(down, Wall):
if self.direction == Direction.DOWN and surroundings[Direction.DOWN].has_class(
Wall
):
self.direction = Direction.LEFT
if self.direction == Direction.LEFT and isinstance(left, Wall):
if self.direction == Direction.LEFT and surroundings[Direction.LEFT].has_class(
Wall
):
self.direction = Direction.UP
if self.direction == Direction.UP and isinstance(up, Wall):
if self.direction == Direction.UP and surroundings[Direction.UP].has_class(
Wall
):
self.direction = Direction.RIGHT

return self.coords + self.direction.value
Expand Down
55 changes: 21 additions & 34 deletions python/game.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
from typing import Dict, List, Set

from typing import Dict, List, Set

from python.entity import (
Entity,
Expand Down Expand Up @@ -51,38 +51,31 @@ def _tick(self, input) -> int:
logger.warning(f"Unknown Direction Keybind: {input}")

# All things move on this board first
tmp_board: List[List[Set[Entity]]] = World.gen_empty_board(
# TODO: Improve how this call works for generating a temp board :(
Coordinate(len(self._world.board), len(self._world.board[0])),
set().copy,
)
temp_world = World(self._world.size)

# Move all Entities
self._move_entities(tmp_board)
self._move_entities(temp_world)

# Perform actions for entities on same spot
self._interact_entities(tmp_board)
# self._interact_entities(temp_world)

# Update board
self._update_world(tmp_board)
self._update_world(temp_world)
if not self._player:
logger.error("No player!")
# TODO: Add respawn
return 1
return 0

def _move_entities(self, tmp_board: List[List[set[Entity]]]):
for old_coords, ent_set in World.enumerate(self._world.board):
for ent in ent_set:
# logger.debug(f"Ent {ent}")
new_coords = old_coords

if isinstance(ent, DynamicEntity):
new_coords = ent.gen_move(self._world.get_surroundings(old_coords))
if not self._is_valid_move(new_coords):
logger.info(f"Can't move entity, to {new_coords}")
new_coords = old_coords
tmp_board[new_coords.x][new_coords.y].add(ent)
def _move_entities(self, temp_world: World):
for old_coords, cell in self._world.enumerate():
dyent: DynamicEntity
for dyent in cell.get_subclass_set(DynamicEntity):
new_coords = dyent.gen_move(self._world.get_surroundings(old_coords))
if not self._is_valid_move(new_coords):
logger.info(f"Can't move entity, to {new_coords}")
new_coords = old_coords
temp_world.add_entity(dyent, new_coords)

def _update_player_velocity(self, direction: Direction):
self._player.direction = direction
Expand All @@ -94,13 +87,10 @@ def _is_valid_move(self, coord: Coordinate):
"""
The only type of entity you can't move onto is a Wall
"""
class_dict = self._space_to_class_entity_set_dict(
self._world.get_entities(coord)
)
if self._dict_has(class_dict, Wall):
cell = self._world.get_cell(coord)
if cell.has_class(Wall):
return False
else:
return True
return True

def _space_to_class_entity_set_dict(
self,
Expand Down Expand Up @@ -157,13 +147,10 @@ def _interact_entities(self, tmp_board: List[List[Set[Entity]]]):

# TODO: Add game logic for entity interactions

def _update_world(self, tmp_board: List[List[Set[Entity]]]):
def _update_world(self, temp_world: World):
# TODO: Figure out if there's a good way to
# Just update the world with a new board.
# self._world.board = tmp_board
for coords, entity_set in World.enumerate(tmp_board):
for entity in entity_set:
if isinstance(entity, DynamicEntity):
self._world.move_dynamic_entity(
entity, Coordinate(coords.x, coords.y)
)
for coords, cell in temp_world.enumerate():
for dyent in cell.get_subclass_set(DynamicEntity):
self._world.move_dynamic_entity(dyent, coords)
91 changes: 12 additions & 79 deletions python/world.py
Original file line number Diff line number Diff line change
@@ -1,85 +1,14 @@
import logging
from typing import List, Tuple, Set, Callable, Generator, Dict, Iterable
from python.coordinate import Coordinate

from python.entity import Entity, DynamicEntity, Wall, Player
from typing import List, Tuple, Callable, Generator, Dict, Iterable

from python.coordinate import Coordinate
from python.entity import Entity, DynamicEntity, Wall, Player, Cell
from python.direction import Direction

logger = logging.getLogger(__name__)


class Cell:
"""Stores entities at each spot in the worlds 2d array
The entities are stored in a dictionary that maps
Class type to a set of entities that are that type.
This makes it easier to grab specific sets of entities in a
Cell when doing various interactions.
"""

def __init__(self):
self._entity_dict: Dict[type, Set[Entity]] = {}

def add_entity(self, entity: Entity) -> "Cell":
self._entity_dict.setdefault(entity.__class__, set())
self._entity_dict[entity.__class__].add(entity)
return self

def remove_entity(self, entity: Entity) -> "Cell":
try:
entity_set = self._entity_dict[entity.__class__]
entity_set.remove(entity)
if len(entity_set) == 0:
logger.debug(f"Empty entity_set removing class: {entity.__class__}")
self._entity_dict.pop(entity.__class__)
except KeyError:
# TODO: Don't know if this error should actually be returned
logger.error(
f"Tried deleting an entity that was not in this cell entity: {entity}"
f"_entity_dict: {self._entity_dict}"
)
return self

def get_all(self) -> Set[Entity]:
all_ents = set()
for ents in self._entity_dict.values():
all_ents.update(ents)
return all_ents

def is_empty(self) -> bool:
if self._entity_dict:
return False
return True

def has_class(self, class_type: type) -> bool:
if class_type in self._entity_dict.keys():
return True
return False

def has_subclass(self, class_type: type) -> bool:
for entity_class in self._entity_dict.keys():
if issubclass(entity_class, class_type):
return True
return False

def get_class_set(self, class_type: type) -> Set[Entity]:
"""Gets set of all entities that match class"""
try:
return self._entity_dict[class_type]
except KeyError:
# TODO: Don't know if this error should actually be returned
logger.error(f"No class_type: {class_type}, " f"stored in Cell: {self}")
return set()

def get_subclass_set(self, class_type: type) -> Set[Entity]:
"""Gets set of all entities and children type"""
full_entity_set = set()
for entity_class, entity_set in self._entity_dict.items():
if issubclass(entity_class, class_type):
full_entity_set.update(entity_set)
return full_entity_set


class World:
"""This class could actually be called Board. That's basically what it is
A 2d board that has operations to help you place and move things.
Expand All @@ -91,6 +20,10 @@ def __init__(self, size: Coordinate = Coordinate(1, 1)):
"""
self.board: List[List[Cell]] = World.gen_empty_board(size)

@property
def size(self):
return Coordinate(len(self.board), len(self.board[0]))

def get_surroundings(self, coord: Coordinate) -> Dict[Direction, Cell]:
"""Returns cells at RIGHT, LEFT, UP, DOWN"""
return {
Expand Down Expand Up @@ -129,10 +62,10 @@ def move_dynamic_entity(self, entity: DynamicEntity, new_coords: Coordinate):

def find_player(self) -> Player:
"""Finds the first player in the board"""
for _, entity_list in World.enumerate(self.board):
for entity in entity_list:
if isinstance(entity, Player):
return entity
for _, cell in self.enumerate():
if cell.has_class(Player):
# TODO: Make find all players
return list(cell.get_class_set(Player))[0]

def enumerate(self) -> Generator[Tuple[Coordinate, Cell], None, None]:
for x, row in enumerate(self.board):
Expand Down

0 comments on commit 67ea858

Please sign in to comment.