Skip to content

Western Geomancy

A geomancy engine with the sixteen-figure deck and the shield-chart spread. Casting is RNG-driven: four Mother figures are generated from random points, then the Daughters, Nieces, two Witnesses, and the Judge follow by geomantic addition.

from fortune_telling_core import RandomRng, ReadingRequest
from fortune_telling_core.traditions.geomancy import GEOMANCY_DECK, SHIELD, build_engine

request = ReadingRequest(deck_id=GEOMANCY_DECK.id, spread_id=SHIELD.id)
reading = build_engine().read(request, rng=RandomRng(seed=42))

geomancy

GEOMANCY_DECK module-attribute

GEOMANCY_DECK = Deck(
    id="geomancy.deck.figures.v1",
    symbols=tuple(
        (
            Symbol(
                id=figure.symbol_id,
                name=figure.name,
                attributes={
                    "slug": figure.slug,
                    "english": figure.english,
                    "element": figure.ruling_element,
                    "rows": "".join(
                        (str(row)) for row in (figure.rows)
                    ),
                    "points": str(figure.points),
                },
            )
        )
        for figure in FIGURES
    ),
)

SHIELD module-attribute

SHIELD = Spread(
    id="geomancy.spread.shield.v1",
    name="Shield Chart",
    positions=(
        *(_quad("mother", "Mother")),
        *(_quad("daughter", "Daughter")),
        *(_quad("niece", "Niece")),
        Position(
            "right_witness",
            "Right Witness",
            "Witness formed from the first two Nieces.",
        ),
        Position(
            "left_witness",
            "Left Witness",
            "Witness formed from the last two Nieces.",
        ),
        Position(
            "judge",
            "Judge",
            "The verdict, formed from the two Witnesses.",
        ),
    ),
)

GeomancyEngine

Bases: AbstractEngine

Western geomancy engine.

Four Mother figures are generated from random points, then the Daughters, Nieces, two Witnesses, and the Judge are derived deterministically by geomantic addition. The fifteen figures fill the shield spread.

id class-attribute instance-attribute

id = 'geomancy.engine'

version class-attribute instance-attribute

version = '0.1.0'

deck

deck(request: ReadingRequest) -> Deck

Return the geomancy figure deck.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request whose deck_id must match GEOMANCY_DECK.id.

required

Returns:

Type Description
Deck

The bundled geomancy deck.

Raises:

Type Description
ValidationError

If the request names an unsupported deck.

Source code in src/fortune_telling_core/traditions/geomancy/engine.py
def deck(self, request: ReadingRequest) -> Deck:
    """Return the geomancy figure deck.

    Args:
        request: Reading request whose ``deck_id`` must match
            ``GEOMANCY_DECK.id``.

    Returns:
        The bundled geomancy deck.

    Raises:
        ValidationError: If the request names an unsupported deck.
    """

    if request.deck_id != GEOMANCY_DECK.id:
        raise ValidationError(f"unsupported geomancy deck: {request.deck_id}")
    return GEOMANCY_DECK

spread

spread(request: ReadingRequest) -> Spread

Return the shield spread.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request whose spread_id must match SHIELD.id.

required

Returns:

Type Description
Spread

The bundled shield spread.

Raises:

Type Description
ValidationError

If the spread is not supported.

Source code in src/fortune_telling_core/traditions/geomancy/engine.py
def spread(self, request: ReadingRequest) -> Spread:
    """Return the shield spread.

    Args:
        request: Reading request whose ``spread_id`` must match
            ``SHIELD.id``.

    Returns:
        The bundled shield spread.

    Raises:
        ValidationError: If the spread is not supported.
    """

    if request.spread_id != SHIELD.id:
        raise ValidationError(f"unsupported geomancy spread: {request.spread_id}")
    return SHIELD

draw

draw(request: ReadingRequest, rng: Rng) -> Draw

Cast a geomantic shield for the request.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request.

required
rng Rng

Random source; sixteen floats are consumed (one per Mother row).

required

Returns:

Type Description
Draw

A draw with the fifteen shield selections in reading order.

Raises:

Type Description
ValidationError

If the requested deck or spread is unsupported.

ExhaustedRngError

If the supplied RNG cannot provide enough values.

Source code in src/fortune_telling_core/traditions/geomancy/engine.py
def draw(self, request: ReadingRequest, rng: Rng) -> Draw:
    """Cast a geomantic shield for the request.

    Args:
        request: Reading request.
        rng: Random source; sixteen floats are consumed (one per Mother
            row).

    Returns:
        A draw with the fifteen shield selections in reading order.

    Raises:
        ValidationError: If the requested deck or spread is unsupported.
        ExhaustedRngError: If the supplied RNG cannot provide enough values.
    """

    self.deck(request)
    spread = self.spread(request)
    shield = cast_shield(rng)
    figures = (
        *shield.mothers,
        *shield.daughters,
        *shield.nieces,
        shield.right_witness,
        shield.left_witness,
        shield.judge,
    )
    common = _common_modifiers(shield)
    selections = tuple(
        Selection(
            position_id=position.id,
            symbol_id=figure.symbol_id,
            modifiers={
                **common,
                "role": position.id,
                "figure": figure.name,
                "element": figure.ruling_element,
            },
        )
        for position, figure in zip(spread.positions, figures, strict=True)
    )
    return Draw(deck_id=GEOMANCY_DECK.id, spread_id=SHIELD.id, selections=selections)

build_engine

build_engine() -> GeomancyEngine

Create a Western geomancy engine.

Returns:

Type Description
GeomancyEngine

A new GeomancyEngine instance.

Source code in src/fortune_telling_core/traditions/geomancy/engine.py
def build_engine() -> GeomancyEngine:
    """Create a Western geomancy engine.

    Returns:
        A new ``GeomancyEngine`` instance.
    """

    return GeomancyEngine()