Skip to main content

A Python engine for MTG-like card games.

Project description

PTG — Python The Gathering

A library for building MTG-like card games. Define your cards in YAML, assemble decks, and drive the game through a small set of pure, testable actions — no side-effects, no global state.


Table of Contents

  1. Quick Start
  2. Core Concepts
  3. Card Definition Reference
  4. Mana System
  5. Abilities & Effects
  6. Triggers
  7. Combat System
  8. Loading Cards & Building Decks
  9. Public API Reference
  10. Appendix: Complete Card Examples

1. Quick Start

Minimum working example — two players, hard‑coded cards, one turn.

from ptg.api import (
    Game, Player, CardDefinition,
    CardType, ManaType, ManaPool, ManaRequirement,
    PlayCardAction, DeclareAttackAction,
)
from ptg.engine.types import CardAbility, AlterationData
from ptg.effects.draw import DrawCardEffect

# ── Card definitions ──────────────────────────────────────────
goblin    = CardDefinition("gob_01", "Goblin Warrior", "Fast.",
                           CardType.CREATURE,
                           mana={ManaType.FIRE: 1},
                           attack=2, defense=2)

fire_mana = CardDefinition("fire_01", "Fire Mana", "Gives 1 fire.",
                           CardType.MANA,
                           mana={ManaType.FIRE: 1})

# ── Players ───────────────────────────────────────────────────
alice = Player("Alice", [fire_mana] * 3 + [goblin] * 3, hand_size=3)
bob   = Player("Bob",   [fire_mana] * 3 + [goblin] * 3, hand_size=3)

game = Game([alice, bob])
game.start()
game.advance_phase()                                      # DRAW → MAIN

# Play mana, then a creature
for _ in range(3):
    cid = next(id for id in game.state.hands[alice.uuid]
               if game.state.all_cards[id].name == "Fire Mana")
    game.apply_action(PlayCardAction(), player_id=alice.uuid, card_id=cid)

goblin_id = next(id for id in game.state.hands[alice.uuid]
                 if game.state.all_cards[id].name == "Goblin Warrior")
game.apply_action(PlayCardAction(), player_id=alice.uuid, card_id=goblin_id)

# Attack and end turn (advances through ATTACK, DEFENSE, COMBAT, POSTCOMBAT, END → DRAW)
attacker_id = next(id for id in game.state.battlefield[alice.uuid]
                   if game.state.all_cards[id].name == "Goblin Warrior")
game.apply_action(DeclareAttackAction(), player_id=alice.uuid,
                  attacker_id=attacker_id)
game.advance_phase()   # MAIN → ATTACK
game.advance_phase()   # ATTACK → DEFENSE
game.advance_phase()   # DEFENSE → COMBAT (auto-resolve)
game.advance_phase()   # COMBAT → MAIN_POSTCOMBAT
game.advance_phase()   # MAIN_POSTCOMBAT → END → next player DRAW

print(f"Bob HP: {game.state.players[bob.uuid].health}")   # 18

2. Core Concepts

flowchart LR
    subgraph Setup
        YAML[Card YAML files] -->|yaml_loader| Def[CardDefinition]
        Def -->|deck_builder| Deck[Deck list]
    end

    subgraph Runtime
        Deck -->|Player.initialize| GS[GameState]
        GS --> Actions[Play / Attack / Defend / Activate]
        Actions -->|returns new| GS2[GameState]
        GS2 -->|check_victory| Winner
    end

Card

A card has two representations:

  • CardDefinition — the blueprint. Immutable. Lives in YAML or Python. Contains card_id, name, type, mana cost, health, attack, defense, and optional abilities.
  • CardInstance — one copy in play. Has a unique uuid, current health, attack, defense, and a reference back to its CardDefinition.

Player

Each player owns four zones:

Zone Contents Notes
deck Cards not yet drawn Shuffled at game start.
hand Cards available to play Hidden from opponent.
battlefield Creatures in play Public.
graveyard Discarded / destroyed cards Public.

A player also has health, a mana_pool, and a maximum hand size.

Game

The Game object owns the GameState and the EventBus. It provides methods to advance turns, apply actions, resolve combat, and check victory.

GameState

The entire game at one point in time — cards, zones, players, pending combats. Every action returns a new GameState; the old one is never modified.


3. Card Definition Reference

Fields

Field Type Required Default Notes
card_id str yes Unique identifier.
name str yes Display name.
description str no "" Flavour or tooltip text.
type CardType yes See table below.
mana dict[ManaType, int] no {} Cost to play OR mana provided (for mana cards).
attack int no 0 Damage dealt when attacking.
defense int no 0 Creature toughness — its current life. Regenerates each turn.
abilities list[CardAbility] no None See Abilities & Effects.

CardType

Value Meaning
creature Stays on the battlefield. Can attack and block. Its defense is its current life; it regenerates each turn.
spell Resolves immediately, then goes to graveyard.
mana Stays on the battlefield as a permanent. Provides mana every turn. Free to play.

Examples

# A simple creature
card_id: goblin_01
name: Goblin Warrior
type: creature
mana:
  fire: 1
attack: 2
defense: 2
# A mana card (always free to play)
card_id: fire_mana_01
name: Fire Mana
type: mana
mana:
  fire: 1
# A spell that deals 3 damage
card_id: fireball_01
name: Fireball
type: spell
mana:
  fire: 2
abilities:
  - name: Explosion
    description: Deal 3 damage.
    ability_type: activated
    mana_requirement: {}
    alterations:
      - effect: damage_card
        start_trigger: null
        params:
          amount: 3

4. Mana System

ManaType

Type Colour / flavour
fire Red
earth Green
water Blue
air White
any Generic — paid with leftover mana of any type

Playing mana

Mana cards are free to play and stay on the battlefield as permanents. Playing a Fire Mana adds 1 fire to your pool and to your mana_sources. At the start of each of your turns, your pool is refilled from your sources.

You may only play one mana card per turn. Effects that generate mana (e.g. gain_mana) do not count toward this limit.

Hand limit

Your maximum hand size is 7 cards. At the end of your turn, if you have more than 7 cards, you discard down to 7 at random.

First turn

The player who goes first skips their draw step on the first turn.

The ANY type

When a requirement includes any, specific types are paid first, then the any shortfall is covered from the remaining pool:

Pool:  {fire: 2, earth: 1}
Cost:  {fire: 1, any: 1}

1. Pay fire:  pool → {fire: 1, earth: 1}
2. Cover any: pool → {fire: 0, earth: 1}

Cost fields in YAML

Field Where Meaning
mana Top-level on a card Cost to play the card (free for mana cards).
mana_requirement Inside an ability Cost to activate that ability.

5. Abilities & Effects

An ability is a named power on a card. An ability contains one or more alterations; each alteration links an effect to a target with an optional trigger.

AbilityType

Value Behaviour
triggered Fires automatically when its start_trigger event occurs. No mana cost at trigger time.
activated The player chooses when to use it and pays its mana_requirement.

Alteration structure

alterations:
  - effect: <registered effect name>   # required
    target: <TargetSpec | null>        # default: null (see PendingTrigger below)
    start_trigger: <EventType | null>  # default: null
    end_trigger: <EventType | null>    # default: null
    trigger_self: <bool>               # default: true
    turn_duration: <int | null>        # default: null
    params:                            # depends on the effect
      ...

TargetSpec

Value Resolves to
self The card that owns the ability.
owner The player who controls the card.
opponent The other player.
choose The player must choose a target manually (see PendingTrigger).
null Same as choose — the player must choose.

trigger_self

Controls whether a triggered ability only fires for events originating from the owning card itself:

Value Behaviour
true (default) The ability fires only when the event's source matches the owning card_id. Events with source=None (turn events) are never filtered.
false The ability fires for any event of the matching type, regardless of source. Useful for global-scope triggers like "whenever a creature dies".

end_trigger

When an alteration specifies both start_trigger and end_trigger, the engine automatically registers a second listener that reverses the effect by negating numeric params when the end event fires:

# Temporary +1/+1 until end of turn:
alterations:
  - effect: buff_card
    start_trigger: on_turn_start
    end_trigger: on_turn_end
    target: self
    params:
      attack_increase: 1
      defense_increase: 1

At on_turn_end, the engine applies buff_card(attack_increase=-1, defense_increase=-1). This works for any effect with numeric params (damage_card, heal_player, draw_card, etc.).

Effect catalogue

Effect name Target type Parameters Description
damage_card Card amount: int Deals damage to a creature, reducing its defense.
damage_player Player amount: int Deals damage to a player.
heal_player Player amount: int Restores health to a player.
buff_card Card attack_increase, defense_increase (default 0) Increases a creature's stats.
buff_all Player attack_increase, defense_increase (default 0) Buffs every creature on that player's battlefield.
draw_card Player count: int (default 1) Draws cards from the top of the deck.
destroy_card Card Sets a creature's health to 0.
spend_mana Player mana_requirement: dict Deducts mana from the player's pool.
gain_mana Player amounts: dict[ManaType, int] Adds temporary mana to a player's pool (does not increase mana_sources).
spawn_creature Player template: CardDefinition + count: int (default 1) Creates copies of a creature on the battlefield.
recruit Player card_definition_id: str, from_zone: ZoneType Moves a matching card from hand / deck / graveyard to the battlefield.
return_to_hand Player card_uuid: str Removes a card from the battlefield and puts it back in the owner's hand.
mill Player count: int (default 1) Moves cards from the top of the deck directly into the graveyard.
random_discard Player count: int (default 1) Randomly discards cards from the player's hand into their graveyard.
set_stats Card attack: int, defense: int Sets a creature's attack and/or defense to absolute values.
recycle_graveyard Player Shuffles all cards from the graveyard back into the deck.

Example: triggered ability

abilities:
  - name: Healing Aura
    description: When this enters, heal your hero for 3.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: heal_player
        target: owner
        start_trigger: on_card_enter_battlefield
        params:
          amount: 3

Example: activated ability

abilities:
  - name: Fire Blast
    description: Pay 2 fire → deal 2 damage to opponent.
    ability_type: activated
    mana_requirement:
      fire: 2
    alterations:
      - effect: damage_player
        target: opponent
        params:
          amount: 2

Dynamic parameter references

Params can reference the source card's current stats at runtime using:

Syntax Resolves to
$attack The source card's current attack value.
$defense The source card's current defense value.
abilities:
  - name: Life Drain
    description: When this enters, heal equal to its attack.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: heal_player
        target: owner
        start_trigger: on_card_enter
        params:
          amount: $attack

PendingTrigger — player-chosen targets

When a triggered ability has target: null or target: choose, the engine cannot resolve the target automatically. Instead of dropping the trigger, it defers it by adding a PendingTrigger to GameState.pending_triggers.

The game server must:

  1. After each action, check if state.pending_triggers is non-empty.
  2. Prompt the player to select a target (creature or player).
  3. Call game.resolve_pending_trigger(trigger_index, chosen_target) to apply the effect and register the end_trigger listener (if present).

The engine handles the full lifecycle — start, resolution, and end reversal — automatically once the target is chosen.

# Server-side flow
state = game.apply_action(PlayCardAction(), player_id=p1, card_id=card)

if state.pending_triggers:
    # Send trigger info to frontend for target selection
    trigger = state.pending_triggers[0]
    emit("choose_target", {
        "source_card_id": trigger.source_card_id,
        "trigger_index": 0,
    })

# After player picks target "g02":
state = game.resolve_pending_trigger(0, chosen_target=CardID("g02"))
# Engine applies effect and registers end_trigger listener automatically

Example: spawn tokens

# Using an inline template
- effect: spawn_creature
  target: owner
  start_trigger: on_card_enter_battlefield
  params:
    template:
      card_id: token_wasp
      name: Wasp
      type: creature
      defense: 1
      attack: 1
    count: 2

# Using a catalog reference (requires loading with a catalog)
- effect: spawn_creature
  target: owner
  start_trigger: on_card_death
  params:
    template_card_id: goblin_01
    count: 1

Example: recruit from graveyard

- effect: recruit
  target: owner
  start_trigger: on_card_enter_battlefield
  params:
    card_definition_id: goblin_01
    from_zone: graveyard

6. Triggers

Triggered abilities fire when a matching event is emitted. The table below lists every event in the engine.

EventType (YAML value) Emitted when …
on_card_enter_battlefield A creature is placed on the battlefield.
on_card_death A creature's health drops to 0 (it is moved to the graveyard).
on_card_receive_damage A creature takes damage.
on_card_attack A creature is declared as an attacker.
on_card_defense A creature is declared as a blocker.
on_card_healed A creature is healed.
on_card_draw A player draws one or more cards.
on_card_enter_graveyard Any card enters the graveyard (creatures, spells).
on_card_leave_battlefield A card is removed from the battlefield for any reason (death, bounce).
on_card_leave_graveyard A card is removed from the graveyard (e.g., recruited to the battlefield).
on_player_receive_damage A player takes damage.
on_player_death A player's health drops to 0.
on_player_mana_spent A player spends mana.
on_player_mana_gained A player gains mana.
on_player_healed A player is healed.
on_turn_start A player's turn begins (after flags are reset).
on_turn_end A player's turn ends.
on_attack_declared An attack is declared (before blockers are chosen).
on_defense_declared A blocker is assigned to an attacker.
on_combat_resolved All pending combats are resolved for the turn.
on_ability_activated A player activates an activated ability.

Event order during an attack

sequenceDiagram
    participant Attacker
    participant Blocker
    participant Engine

    Attacker->>Engine: DeclareAttackAction
    Engine-->>Engine: emit ON_ATTACK_DECLARED
    Blocker->>Engine: DeclareDefenseAction
    Engine-->>Engine: emit ON_DEFENSE_DECLARED
    Engine->>Engine: resolve_combat()
    Engine-->>Engine: emit ON_CARD_RECEIVE_DAMAGE
    Engine-->>Engine: emit ON_CARD_LEAVE_BATTLEFIELD (if any)
    Engine-->>Engine: emit ON_CARD_DEATH (if any)
    Engine-->>Engine: emit ON_PLAYER_RECEIVE_DAMAGE (spillover)
    Engine-->>Engine: emit ON_COMBAT_RESOLVED

7. Combat System

Turn structure

flowchart TD
    Start([start]) --> DRAW[DRAW: auto-refresh mana, draw card]
    DRAW --> MAIN[MAIN: play cards, activate abilities]
    MAIN --> ATTACK[ATTACK: declare attackers]
    ATTACK --> DEFENSE[DEFENSE: declare blockers]
    DEFENSE --> COMBAT[COMBAT: auto-resolve damage]
    COMBAT --> MAIN2[MAIN_POSTCOMBAT: play cards]
    MAIN2 --> END[END: auto-switch player]
    END --> DRAW

Direct attack (no blockers)

When no blockers are assigned, the attacker's full attack value hits the defending player directly.

game.apply_action(DeclareAttackAction(),
    player_id=alice.uuid, attacker_id=goblin_id)
game.resolve_combat()
# Bob loses attack value in health

Blocked attack (damage exchange)

Both the attacker and the blocker deal damage to each other simultaneously. Damage is applied directly to each card's defense. A card dies when its defense reaches 0.

game.apply_action(DeclareDefenseAction(),
    player_id=bob.uuid, blocker_id=giant_id, combat_index=0)
game.resolve_combat()

Multi-blocking

When multiple blockers are assigned to one attacker, the attacker distributes its damage among them. The engine assigns minimum lethal damage to each blocker in declaration order until no attack remains.

Spillover (trample)

If the attacker survives and all of its blockers die, any leftover damage hits the defending player.

Death & graveyard

Creatures that reach 0 health are moved from the battlefield to their owner's graveyard, and an on_card_death event is emitted.

Attack / block restriction

A creature that attacked during its controller's turn cannot block until the beginning of that controller's next turn. The attacked_this_turn flag is set by DeclareAttackAction and reset by _handle_draw at turn start. DeclareDefenseAction rejects any blocker with attacked_this_turn=True.


8. Loading Cards & Building Decks

Card YAML files

Place one .yaml file per card in a directory:

cards/
  goblin.yaml
  fireball.yaml
  fire_mana.yaml
  ...

Deck YAML files (optional)

# decks/aggro.yaml
cards:
  goblin_01: 3
  fireball_01: 2
  fire_mana_01: 5

Loading workflow

from ptg.api import (
    load_cards_from_dir, build_catalog, load_deck, build_deck,
    Player, Game,
)

# 1. Load every card definition
all_cards = load_cards_from_dir("cards/")

# 2. Index by card_id for fast lookup
catalog = build_catalog(all_cards)

# 3. Build decks
alice_deck = load_deck(catalog, "decks/alice_aggro.yaml")
bob_deck   = build_deck(catalog, {"giant_01": 3, "fire_mana_01": 5, "heal_01": 2})

# 4. Create players
alice = Player("Alice", alice_deck, initial_health=20, hand_size=4)
bob   = Player("Bob",   bob_deck,   initial_health=20, hand_size=4)

game = Game([alice, bob])
game.start()

Using a catalog for template_card_id

When a card references another card via template_card_id, pass the catalog to the loader:

# cards/hive_mind.yaml uses template_card_id: wasp_token_01
catalog = build_catalog(load_cards_from_dir("cards/"))
hive = load_card("cards/hive_mind.yaml", catalog=catalog)

9. Public API Reference

from ptg.api import (
    # ── Core types ───────────────────────────────────────────
    CardType, ManaType, EventType, AbilityType, TargetSpec,
    ManaRequirement, ManaPool,
    # ── Engine ───────────────────────────────────────────────
    Game, GameState, Player,
    CardDefinition, CardAbility, AlterationData,
    PendingTrigger,
    # ── Actions ──────────────────────────────────────────────
    PlayCardAction, DeclareAttackAction,
    DeclareDefenseAction, ActivateAbilityAction,
    # ── I/O ──────────────────────────────────────────────────
    load_card, load_cards_from_dir,
    build_catalog, build_deck, load_deck,
)

Game methods

The recommended way to control a turn is advance_phase(). begin_turn() and end_turn() are available for fine-grained manual control.

Method Returns Description
start() GameState Initialises both players, draws starting hands, sets phase to DRAW.
advance_phase() GameState Moves to the next phase. Executes automatic logic for DRAW / COMBAT / END.
begin_turn() GameState Manual jump to DRAW phase (legacy).
end_turn() GameState Manual jump to END phase (legacy).
apply_action(action, **kwargs) GameState Runs any action through the engine.
resolve_combat() GameState Processes all pending combats (called automatically in COMBAT phase).
resolve_pending_trigger(index, chosen_target) GameState Applies a deferred triggered ability to the player's chosen target and registers its end_trigger listener.
check_victory() Player | None Returns the winner, or None if the game continues.
Property Type Description
current_phase PhaseType | None Current phase of the active player's turn.
playable_phases tuple[PhaseType, ...] Phases where the active player may play cards or activate abilities.

Action parameters

Action kwargs
PlayCardAction player_id, card_id
DeclareAttackAction player_id, attacker_id
DeclareDefenseAction player_id, blocker_id, combat_index
ActivateAbilityAction player_id, card_id, ability_index, target_id (optional)

I/O functions

Function Signature
load_card (path, catalog=None) → CardDefinition
load_cards_from_dir (dir_path, catalog=None) → list[CardDefinition]
build_catalog (cards) → dict[str, CardDefinition]
build_deck (catalog, spec) → list[CardDefinition]
load_deck (catalog, path) → list[CardDefinition]

10. Appendix: Complete Card Examples

Creature with no abilities

card_id: giant_01
name: Clumsy Giant
type: creature
mana:
  earth: 2
defense: 5
attack: 3
defense: 1

Creature with a triggered ability

card_id: priest_01
name: Healing Priest
description: When this enters, heal your hero for 3.
type: creature
mana:
  any: 2
defense: 3
attack: 1
defense: 1
abilities:
  - name: Healing Aura
    description: Heal your hero for 3.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: heal_player
        target: owner
        start_trigger: on_card_enter_battlefield
        params:
          amount: 3

Creature with an activated ability

card_id: pyromancer_01
name: Pyromancer
type: creature
mana:
  fire: 2
defense: 3
attack: 2
defense: 0
abilities:
  - name: Fire Blast
    description: Pay 2 fire → deal 2 damage to opponent.
    ability_type: activated
    mana_requirement:
      fire: 2
    alterations:
      - effect: damage_player
        target: opponent
        params:
          amount: 2

Creature that spawns tokens (inline template)

card_id: hive_mind_01
name: Hive Mind
description: When this enters, create two 1/1 Wasps.
type: creature
mana:
  any: 3
defense: 2
attack: 1
defense: 0
abilities:
  - name: Swarm
    description: Spawn two Wasp tokens.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: spawn_creature
        target: owner
        start_trigger: on_card_enter_battlefield
        params:
          template:
            card_id: token_wasp
            name: Wasp
            type: creature
            defense: 1
            attack: 1
          count: 2

Creature that recruits from graveyard

card_id: necromancer_01
name: Necromancer
description: When this enters, return a Goblin Warrior from your
             graveyard to the battlefield.
type: creature
mana:
  any: 4
defense: 3
attack: 2
defense: 1
abilities:
  - name: Dark Ritual
    description: Recruit a Goblin Warrior.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: recruit
        target: owner
        start_trigger: on_card_enter_battlefield
        params:
          card_definition_id: goblin_01
          from_zone: graveyard

Creature that mills the opponent

card_id: mill_imp_01
name: Mill Imp
description: When this enters, mill 3 cards from your opponent's deck.
type: creature
mana:
  any: 2
defense: 2
attack: 1
defense: 0
abilities:
  - name: Mind Rot
    description: Mill 3 cards.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: mill
        target: opponent
        start_trigger: on_card_enter_battlefield
        params:
          count: 3

Creature that reacts to cards leaving the graveyard

card_id: graveyard_watcher_01
name: Graveyard Watcher
description: Whenever a card leaves your graveyard, draw a card.
type: creature
mana:
  any: 3
defense: 3
attack: 2
defense: 1
abilities:
  - name: Soul Drain
    description: Draw a card.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: draw_card
        target: owner
        start_trigger: on_card_leave_graveyard
        params:
          count: 1

Spell

card_id: fireball_01
name: Fireball
description: Deal 3 damage to any target.
type: spell
mana:
  fire: 2
abilities:
  - name: Explosion
    description: Deal 3 damage.
    ability_type: activated
    mana_requirement: {}
    alterations:
      - effect: damage_card
        params:
          amount: 3

Creature that generates mana

card_id: dark_ritualist_01
name: Dark Ritualist
description: When this enters, gain 2 fire mana until end of turn.
type: creature
mana:
  any: 2
defense: 2
attack: 1
defense: 0
abilities:
  - name: Dark Ritual
    description: Gain 2 fire mana this turn.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: gain_mana
        target: owner
        start_trigger: on_card_enter
        params:
          amounts:
            fire: 2

Creature that sets a creature's stats

card_id: polymorphist_01
name: Polymorphist
description: When this enters, set another creature's stats to 1/1.
type: creature
mana:
  any: 3
defense: 2
attack: 2
defense: 1
abilities:
  - name: Polymorph
    description: Set target creature to 1/1.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: set_stats
        target: null
        start_trigger: on_card_enter
        params:
          attack: 1
          defense: 0

Creature that discards from opponent's hand

card_id: mind_ripper_01
name: Mind Ripper
description: When this enters, your opponent discards a card at random.
type: creature
mana:
  any: 3
defense: 3
attack: 2
defense: 1
abilities:
  - name: Mind Rip
    description: Opponent discards a card at random.
    ability_type: triggered
    mana_requirement: {}
    alterations:
      - effect: random_discard
        target: opponent
        start_trigger: on_card_enter
        params:
          count: 1

Spell that recycles the graveyard

card_id: recycle_01
name: Recycle
description: Shuffle your graveyard into your deck.
type: spell
mana:
  any: 2
abilities:
  - name: Recycle
    description: Shuffle graveyard into deck.
    ability_type: activated
    mana_requirement: {}
    alterations:
      - effect: recycle_graveyard
        target: owner

Mana card

card_id: fire_mana_01
name: Fire Mana
description: Provides 1 fire mana.
type: mana
mana:
  fire: 1

Deck YAML

# decks/midrange.yaml
cards:
  goblin_01: 3
  giant_01: 2
  fireball_01: 2
  pyromancer_01: 2
  fire_mana_01: 5
  earth_mana_01: 6

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ptg-0.1.15.tar.gz (48.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ptg-0.1.15-py3-none-any.whl (46.5 kB view details)

Uploaded Python 3

File details

Details for the file ptg-0.1.15.tar.gz.

File metadata

  • Download URL: ptg-0.1.15.tar.gz
  • Upload date:
  • Size: 48.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for ptg-0.1.15.tar.gz
Algorithm Hash digest
SHA256 a5655bd6cb116726cf43e83771f28d2f86ee590986afcf125ce38af303264c48
MD5 a6cf820f6cb5d86ecadda69131d9a792
BLAKE2b-256 70c8f5feb19ae6ddbf80e9d4fd658955a0d551a4df1f58f5e3dbfeb557c5fd10

See more details on using hashes here.

File details

Details for the file ptg-0.1.15-py3-none-any.whl.

File metadata

  • Download URL: ptg-0.1.15-py3-none-any.whl
  • Upload date:
  • Size: 46.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for ptg-0.1.15-py3-none-any.whl
Algorithm Hash digest
SHA256 96504c0aba5c3c0a43b97e14134039e39366c512dff8bbed2da745fc5541421b
MD5 592eddb9989028291f2e269cf6fed96c
BLAKE2b-256 321545c0dc2c9ffd332fb68ffa64cf930dc03e7fe171ff0767b2b5637ed1ba9d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page