Skip to content

Trigram

Bases: BaseModel

A Trigram (八卦) in the I Ching

Source code in src/ichingpy/model/trigram.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class Trigram(BaseModel):
    """A Trigram (八卦) in the I Ching"""

    # 0: changing yin, 1: static yang, 2: static yin, 3: changing yang
    NAME_MAP: ClassVar[dict[tuple[int, int, int], str]] = {
        (1, 1, 1): "乾",
        (1, 1, 0): "兑",
        (1, 0, 1): "离",
        (1, 0, 0): "震",
        (0, 1, 1): "巽",
        (0, 1, 0): "坎",
        (0, 0, 1): "艮",
        (0, 0, 0): "坤",
    }

    lines: list[Line]

    interpretation: TrigramInterpretationBase[LineInterpretationBase] | None = None

    @field_validator("lines", mode="before")
    @classmethod
    def validate_line_length(cls, lines: list[Line]) -> list[Line]:
        if len(lines) != 3:
            raise ValueError("Trigram should have exactly 3 lines")
        return lines

    @property
    def value(self) -> list[int]:
        return [line.value for line in self.lines]

    @property
    def name(self) -> str:
        # 0: changing yin, 1: static yang, 2: static yin, 3: changing yang
        return self.NAME_MAP[(self.value[0] % 2, self.value[1] % 2, self.value[2] % 2)]

    @cached_property
    def transformed(self) -> "Trigram":
        transformed_lines = [line.get_transformed() if line.is_transform else line for line in self.lines]
        return Trigram(lines=transformed_lines)

    @classmethod
    def from_binary(cls, lines: list[int]) -> Self:
        assert len(lines) == 3
        return cls(lines=[Line(status=LineStatus(i)) for i in lines])

    @classmethod
    def random(cls) -> Self:
        return cls(lines=[Line.random() for _ in range(3)])

    def __repr__(self):
        return "\n".join(repr(line) for line in self.lines[::-1])

    @property
    def pre_trigram_number(self) -> int:
        # 返回先天卦数
        value = (self.value[0] % 2, self.value[1] % 2, self.value[2] % 2)
        return list(self.NAME_MAP.keys()).index(value) + 1

    @classmethod
    def from_pre_trigram_number(cls, trigram_number: int) -> Self:
        # 给定先天卦数,返回对应的八卦
        assert 1 <= trigram_number <= 8
        name_map = {v: k for k, v in cls.NAME_MAP.items()}
        name_list = list(name_map.keys())
        trigram_name = name_list[trigram_number - 1]
        lines_number = list(map(lambda x: 2 if x == 0 else x, name_map[trigram_name]))
        return cls(lines=[Line(status=LineStatus(i)) for i in lines_number])