Skip to content

Instantly share code, notes, and snippets.

@greg76
Last active January 25, 2026 22:14
Show Gist options
  • Select an option

  • Save greg76/163b7f1e73b47695c08453f73d74abf5 to your computer and use it in GitHub Desktop.

Select an option

Save greg76/163b7f1e73b47695c08453f73d74abf5 to your computer and use it in GitHub Desktop.
Simple example for AD&D classes implemented in Python using abstract classes and properties
from abc import ABC, abstractmethod
from dataclasses import asdict, dataclass
class Character(ABC):
@dataclass(slots=True, kw_only=True)
class Stats:
STR: int
DEX: int
CON: int
INT: int
WIS: int
CHA: int
def __str__(self) -> str:
return " ".join(f"{k}:{v}" for k, v in asdict(self).items())
@property
@abstractmethod
def STANDARD_ARRAY(self) -> Stats:
"""
Returns the standard array for a specific class.
Also adds +2/+1 for the primary abilities for higher initial bonuses.
Each character class must implement their own standard array.
"""
return Character.Stats(STR=15 + 1, DEX=14 + 2, CON=13, INT=12, WIS=10, CHA=8)
@property
@abstractmethod
def HIT_DIE(self) -> int:
"""
Specifies how many sided dice is used for calcualting the hit points for the class.
Each character class must implement and specify its hit die.
"""
return 8
def __init__(self, level=None) -> None:
super().__init__()
self.stats = self.STANDARD_ARRAY
self.level = level or 1
self.set_max_hp()
self.hp = self.max_hp
def set_max_hp(self) -> None:
mod = self.get_modifier(self.stats.CON)
self.max_hp = self.HIT_DIE + mod
if self.level > 1:
self.max_hp += round((self.level - 1) * ((1 + self.HIT_DIE) / 2 + mod))
def get_modifier(self, score: int) -> int:
return (score - 10) // 2
def __str__(self) -> str:
"""Provides a standard way of representing the characters stats in a human friendly way."""
description = f"""
Level {self.level} {self.__class__.__name__.lower()}
HP: {self.hp}/{self.max_hp} {self.stats}
""".strip()
return "\n".join(line.strip() for line in description.splitlines())
class Paladin(Character):
@property
def HIT_DIE(self) -> int:
return 10
@property
def STANDARD_ARRAY(self) -> Character.Stats:
return Character.Stats(STR=15 + 1, DEX=8, CON=13, INT=10, WIS=12, CHA=14 + 2)
class Ranger(Character):
@property
def HIT_DIE(self) -> int:
return 10
@property
def STANDARD_ARRAY(self) -> Character.Stats:
return Character.Stats(STR=8, DEX=15 + 1, CON=13, INT=12, WIS=14 + 2, CHA=10)
class Bard(Character):
@property
def HIT_DIE(self) -> int:
return 8
@property
def STANDARD_ARRAY(self) -> Character.Stats:
return Character.Stats(STR=8, DEX=14 + 2, CON=13, INT=10, WIS=12, CHA=15 + 1)
def main() -> None:
party = [Paladin(level=10), Bard(level=11), Ranger(level=9)]
for member in party:
print(member)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment