Giter VIP home page Giter VIP logo

Comments (2)

TStand90 avatar TStand90 commented on September 6, 2024

I agree on the class hierarchy.

For creating the Entities, what about a simple factory function for each type?

def create_orc(x: int, y: int):
    return Entity(x=x, y=y, char="o", color=(63, 127, 63), name="Orc", blocks_movement=True)


def create_player(x: int, y: int):
    return Entity(x=x, y=y, char="@", color=(255, 255, 255), name="Player", blocks_movement=True)


def create_troll(x: int, y: int):
    return Entity(x=x, y=y, char="T", color=(0, 127, 0), name="Troll", blocks_movement=True)

from tcod_tutorial_v2.

HexDecimal avatar HexDecimal commented on September 6, 2024

I was hoping to also reduce the complexity of defining them, so that the end result would be more tabulated.

I'd recommend using a factory function for procedurally generated monsters, but not for predefined ones.

functools.partial doesn't look that great either.

create_orc: Callable[[int, int], Entity] = functools.partial(
    Entity, char="o", color=(63, 127, 63), name="Orc", blocks_movement=True
)

It might be a little awkward to pass Callable[[int, int], Entity] around, but that's totally doable compared to some worse examples. It's also more convenient when GameMap is included in the spawning function.

I didn't give an example of how the spawn method would be implemented, but now I'll gave several examples to compare.

Based on your given example, behold the factory function factory:

def new_monster(
    char: str,
    color: Tuple[int, int, int],
    name: str,
    blocks_movement: bool = True,
) -> Callable[[GameMap, int, int], Entity]:
    """Define a new monster type"""

    def spawn(gamemap: GameMap, x: int, y: int) -> Entity:
        """Spawn a monster at the given location."""
        new_obj = Entity(
            x=x,
            y=y,
            char=char,
            color=color,
            name=name,
            blocks_movement=blocks_movement,
        )
        gamemap.entities.add(new_obj)
        return new_obj

    return spawn
 
spawn_orc = new_monster(
    char="o", color=(63, 127, 63), name="Orc",
)
spawn_orc(gamemap, x, y)

It's a little better but I don't think it will keep up with the subclassing of Entity. You'd need more factories for different kinds of entities. This could also work as a classmethod but it'll always need to be overridden by subclasses.

Tabulated data as a plain dictionary:

monsters = {
    "orc": {
        "char": "o",
        "color": (63, 127, 63),
        "name": "Orc",
        "blocks_movement": True,
    },
    "troll": dict(
        char="T",
        color=(0, 127, 0),
        name="Troll",
        blocks_movement=True,
    ),
}
new_orc = Entity(x=0, y=0, **monsters["orc"])
gamemap.entities.add(new_orc)

The idea is that you'd use dictionaries in Python the same way you'd use JSON in JavaScript. I'm not sure how well these are typed but any specific item should be checked where they're used. Any dictionaries generated from code or loaded from a file will break type hinting.

It probably only looks clean to me because I've used Python for so long. Using dict with keywords helps a little but I don't know how well that preserves types. There isn't a good system for tracking the types of dictionary items in general.

There's also no information on that types these dictionary items are bound to, so subclassing Entity will cause confusion.

Data class factory class:

import dataclasses


@dataclasses.dataclass
class MonsterType:
    char: str
    color: Tuple[int, int, int]
    name: str
    blocks_movement: bool = True
    
    def spawn(self, gamemap: GameMap, x: int, y: int) -> Entity:
        """Spawn this monster at the given location."""
        new_obj = Entity(
            x=x,
            y=y,
            char=self.char,
            color=self.color,
            name=self.name,
            blocks_movement=self.blocks_movement,
        )
        gamemap.entities.add(new_obj)
        return new_obj


orc_type = MonsterType(
    char="o",
    color=(63, 127, 63),
    name="Orc", 
)

orc = orc_type.spawn(dungeon, x, y)

The whole point of dataclasses is to avoid boilerplate and yet it always seems to turn into a huge mess anyway especially when type hinting is involved. dataclasses.asdict could be used but I doubt that type hinting will work correctly there. If you ignore the class itself then the data definitions look as good as the factory function factory, but I'd hate to update this class as things are extended.

I thought of using NamedTuple's but I know that passing parameters positionally would cause trouble later when somebody inevitably messes it up and doesn't think to check MyPy.

Prototype deepcopy method:

import copy
from typing import TypeVar

T = TypeVar('T')


class Entity:
    ...  # Change 'x, y' defaults to 0.

    def spawn(self: T, gamemap: GameMap, x: int, y: int) -> T:
        """Spawn a copy of this instance at the given location."""
        clone = copy.deepcopy(self)
        clone.x = x
        clone.y = y
        gamemap.entities.add(clone)
        return clone


orc = Entity(
    name="Orc",
    char="o",
    color=(63, 127, 63),
    blocks_movement=True,  # Actor class should imply 'blocks_movement=True' later.
)
new_orc = orc.spawn(gamemap, x, y)

I changed my mind on deepcopy, it will work fine and this will also work with subclasses without any extra code.

The best part is that this is DRY and nothing else needs to be updated separately when the Entity class or subclasses are changed. Most of the other examples were making copies of its type hints and attributes.

from tcod_tutorial_v2.

Related Issues (17)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.