Files
tarot/src/utils/misc.py
nose 79d4f1a09e k
2025-11-25 22:19:36 -08:00

195 lines
7.5 KiB
Python

"""
Miscellaneous utilities for Tarot, including personality typing and other tools.
This module contains specialized utilities that don't fit into other categories.
"""
from dataclasses import dataclass
from typing import Optional, TYPE_CHECKING
from enum import Enum
if TYPE_CHECKING:
from tarot.deck.deck import CourtCard
class MBTIType(Enum):
"""16 MBTI personality types."""
ISTJ = "ISTJ"
ISFJ = "ISFJ"
INFJ = "INFJ"
INTJ = "INTJ"
ISTP = "ISTP"
ISFP = "ISFP"
INFP = "INFP"
INTP = "INTP"
ESTP = "ESTP"
ESFP = "ESFP"
ENFP = "ENFP"
ENTP = "ENTP"
ESTJ = "ESTJ"
ESFJ = "ESFJ"
ENFJ = "ENFJ"
ENTJ = "ENTJ"
@dataclass
class Personality:
"""
MBTI Personality Type mapped to a specific Tarot Court Card.
This class creates a direct 1-to-1 relationship between each of the 16 MBTI
personality types and their corresponding Tarot court cards. Based on the
comprehensive system developed by Dante DiMatteo at 78 Revelations Per Minute:
https://78revelationsaminute.wordpress.com/2015/07/08/personality-types-the-tarot-court-cards-and-the-myers-briggs-type-indicator/
The mapping is based on:
- SUITS correspond to Jung's 4 cognitive functions:
* Wands: Intuition (N)
* Cups: Feeling (F)
* Swords: Thinking (T)
* Pentacles: Sensation (S)
- RANKS correspond to MBTI traits:
* Kings (E + J): Extraverted Judgers
* Queens (I + J): Introverted Judgers
* Princes (E + P): Extraverted Perceivers
* Princesses (I + P): Introverted Perceivers
Attributes:
mbti_type: The MBTI personality type (e.g., ENFP)
court_card: The single CourtCard object representing this personality
description: Brief description of the personality archetype
"""
mbti_type: MBTIType
court_card: Optional['CourtCard'] = None
description: str = ""
# Direct MBTI-to-CourtCard mapping (1-to-1 relationship)
# Format: MBTI_TYPE -> (Rank, Suit)
_MBTI_TO_CARD_MAPPING = {
# KINGS (E + J) - Extraverted Judgers
"ENTJ": ("Knight", "Wands"), # Fiery, forceful leadership
"ENFJ": ("Knight", "Cups"), # Sensitive, mission-driven
"ESTJ": ("Knight", "Swords"), # Practical, pragmatic
"ESFJ": ("Knight", "Pentacles"), # Sociable, consensus-seeking
# QUEENS (I + J) - Introverted Judgers
"INTJ": ("Queen", "Wands"), # Analytical, self-motivated
"INFJ": ("Queen", "Cups"), # Sensitive, interconnected
"ISTJ": ("Queen", "Swords"), # Pragmatic, duty-fulfiller
"ISFJ": ("Queen", "Pentacles"), # Caring, earth-mother type
# PRINCES (E + P) - Extraverted Perceivers
"ENTP": ("Prince", "Wands"), # Visionary, quick-study
"ENFP": ("Prince", "Cups"), # Inspiring, intuitive
"ESTP": ("Prince", "Swords"), # Action-oriented, risk-taker
"ESFP": ("Prince", "Pentacles"), # Aesthete, sensualist
# PRINCESSES (I + P) - Introverted Perceivers
"INTP": ("Princess", "Wands"), # Thinker par excellence
"INFP": ("Princess", "Cups"), # Idealistic, devoted
"ISTP": ("Princess", "Swords"), # Observer, mechanic
"ISFP": ("Princess", "Pentacles"), # Aesthete, free spirit
}
@classmethod
def from_mbti(cls, mbti_type: str, deck: Optional[object] = None) -> 'Personality':
"""
Create a Personality from an MBTI type string.
Args:
mbti_type: MBTI type as string (e.g., "ENFP", "ISTJ")
deck: Optional Tarot Deck to fetch the court card from. If not provided,
court card will be fetched dynamically when accessed.
Returns:
Personality object with associated court card
Raises:
ValueError: If mbti_type is not a valid MBTI type
Example:
>>> from tarot import Tarot
>>> personality = Personality.from_mbti("ENFP", Tarot.deck)
>>> print(personality.mbti_type.value)
ENFP
>>> print(f"{personality.court_card.court_rank} of {personality.court_card.suit.name}")
Prince of Cups
"""
mbti_type = mbti_type.upper()
# Validate MBTI type
try:
mbti_enum = MBTIType[mbti_type]
except KeyError:
raise ValueError(
f"Invalid MBTI type: {mbti_type}. Must be one of: "
f"{', '.join([t.value for t in MBTIType])}"
)
# Get the rank and suit for this MBTI type
rank, suit = cls._MBTI_TO_CARD_MAPPING.get(mbti_type, (None, None))
if not rank or not suit:
raise ValueError(f"No court card mapping found for MBTI type {mbti_type}")
# Get court card from deck if provided
court_card = None
if deck is not None:
# Import here to avoid circular imports
from tarot import Tarot
# Use provided deck or default to Tarot
d = deck if hasattr(deck, 'card') else Tarot.deck
cards = d.card.filter(type="Court", court_rank=rank, suit=suit)
if cards:
court_card = cards[0]
# Get description
descriptions = {
"ENTJ": "The Commander - Strategic, ambitious, leader of Wands",
"ENFJ": "The Protagonist - Inspiring, empathetic, leader of Cups",
"ESTJ": "The Supervisor - Practical, decisive, leader of Swords",
"ESFJ": "The Consul - Sociable, cooperative, leader of Pentacles",
"INTJ": "The Architect - Strategic, logical, sage of Wands",
"INFJ": "The Advocate - Insightful, idealistic, sage of Cups",
"ISTJ": "The Logistician - Practical, reliable, sage of Swords",
"ISFJ": "The Defender - Caring, conscientious, sage of Pentacles",
"ENTP": "The Debater - Innovative, quick-witted, explorer of Wands",
"ENFP": "The Campaigner - Enthusiastic, social, explorer of Cups",
"ESTP": "The Entrepreneur - Energetic, bold, explorer of Swords",
"ESFP": "The Entertainer - Spontaneous, outgoing, explorer of Pentacles",
"INTP": "The Logician - Analytical, curious, seeker of Wands",
"INFP": "The Mediator - Idealistic, authentic, seeker of Cups",
"ISTP": "The Virtuoso - Practical, observant, seeker of Swords",
"ISFP": "The Adventurer - Sensitive, spontaneous, seeker of Pentacles",
}
return cls(
mbti_type=mbti_enum,
court_card=court_card,
description=descriptions.get(mbti_type, "")
)
def __str__(self) -> str:
"""Return string representation of personality with court card."""
if self.court_card:
card_str = f"{self.court_card.court_rank} of {self.court_card.suit.name}"
else:
card_str = "No court card loaded"
return f"{self.mbti_type.value} - {self.description}\n Court Card: {card_str}"
def __repr__(self) -> str:
"""Return detailed representation."""
card_name = (
f"{self.court_card.court_rank} of {self.court_card.suit.name}"
if self.court_card
else "None"
)
return f"Personality(mbti_type={self.mbti_type.value}, court_card={card_name})"