Skip to content

Hexagram

Bases: BaseModel

A Hexagram (64卦之一) consists of an inner Trigram (内卦) and an outer Trigram (外卦).

Source code in src/ichingpy/model/hexagram.py
 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
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
class Hexagram(BaseModel):
    """A Hexagram (64卦之一) consists of an inner Trigram (内卦) and an outer Trigram (外卦)."""

    inner: Trigram
    outer: Trigram

    interpretation: (
        HexagramInterpretationBase[TrigramInterpretationBase[LineInterpretationBase], LineInterpretationBase] | None
    ) = None

    @property
    def lines(self) -> list[Line]:
        """Get the lines of the Hexagram.
        返回卦之六爻。
        """
        return self.inner.lines + self.outer.lines

    @property
    def values(self) -> list[int]:
        """Get the values of the Hexagram.
        返回卦之六爻之数。
        """
        return [line.value for line in self.lines]

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

    def __str__(self):
        return repr(self)

    @cached_property
    def transformed(self) -> "Hexagram":
        """Get the transformed Hexagram (变卦)."""
        return Hexagram(inner=self.inner.transformed, outer=self.outer.transformed)

    @classmethod
    def from_lines(cls, lines: list[Line]) -> Self:
        """Create a new instance of the Hexagram class from a list of Lines."""
        hexagram = cls(inner=Trigram(lines=lines[:3]), outer=Trigram(lines=lines[3:]))
        from ichingpy.divination.iching import IChingDivinationEngine

        engine = IChingDivinationEngine()
        engine.execute(hexagram)
        return hexagram

    @classmethod
    def from_binary(cls, lines: list[int]) -> Self:
        """Create a new instance of the Hexagram class from a list of binary integers."""
        if len(lines) != 6:
            raise ValueError("Hexagram should have exactly 6 lines")
        return cls.from_lines(lines=[Line(status=LineStatus(i)) for i in lines])

    @classmethod
    def from_three_coins(cls) -> Self:
        """Create a new instance of the Hexagram class from tossing three coins six times (增删卜易).
        two heads:   lesser  yang  少阳
        one head:    lesser  yin   少阴
        zero head:   greater yang  太阳 (变爻)
        three heads: greater yin   太阴 (变爻)
        """
        # 0: tail, 1: head
        flip_results = [sum([1 - random.getrandbits(1) for _ in range(3)]) for _ in range(6)]
        lines = [Line(status=LineStatus(res)) for res in flip_results]
        return cls.from_lines(lines=lines)

    @classmethod
    def random(cls) -> Self:
        """Create a random  Hexagram instance. This will"""
        return cls.from_lines(lines=[Line.random() for _ in range(6)])

    @classmethod
    def from_datetime(cls, dt: datetime) -> Self:
        """Create a new instance of the Hexagram class from a datetime object.
        八字起卦:
        1. 年月日三支之和除以8取余为外卦之数,余数0作8
        2. 年月日时四支之和除以8取余为内卦之数,余数0作8
        3. 年月日时四支之和除以6取余为变爻之数,余数0作6
        """
        four_pillars = FourPillars.from_datetime(dt)
        year = four_pillars.year.branch.value
        month = four_pillars.month.branch.value
        day = four_pillars.day.branch.value
        hour = four_pillars.hour.branch.value

        remainder_ymd = (year + month + day) % 8
        remainder_ymd = 8 if remainder_ymd == 0 else remainder_ymd

        remainder_ymdh = (year + month + day + hour) % 8
        remainder_ymdh = 8 if remainder_ymdh == 0 else remainder_ymdh

        outer_trigram_lines = Trigram.from_pre_trigram_number(remainder_ymd).lines
        inner_trigram_lines = Trigram.from_pre_trigram_number(remainder_ymdh).lines
        lines = inner_trigram_lines + outer_trigram_lines

        line_to_transform_int = (year + month + day + hour) % 6
        if line_to_transform_int == 0:
            line_to_transform_int = 6
        line_to_transform_int -= 1
        lines[line_to_transform_int] = lines[line_to_transform_int].transform()
        return cls.from_lines(lines=lines)

    @classmethod
    def from_yarrow_stalks(cls) -> Self:
        """Create a new instance of the Hexagram class from ... (蓍草起卦)."""
        # get_lines 6: old yin, 7: young yang, 8: young yin, 9: old yang
        # status    0: old yin, 1: young yang, 2: young yin, 3: old yang
        lines = [Line(status=LineStatus(cls.get_line() - 6)) for _ in range(6)]
        return cls.from_lines(lines=lines)

    @staticmethod
    def get_line() -> int:
        total = 50 - 1  # 大衍之数五十,其用四十有九
        remaining_stalks_1 = Hexagram.bian(total)
        assert remaining_stalks_1 in [40, 44]
        remaining_stalks_2 = Hexagram.bian(remaining_stalks_1)
        assert remaining_stalks_2 in [32, 36, 40]
        remaining_stalks_3 = Hexagram.bian(remaining_stalks_2)
        return remaining_stalks_3 // 4

    @staticmethod
    def bian(num: int) -> int:
        # Divide all stalks into 2 piles
        # 分而二以象两
        left = random.randint(1, num - 1)
        right = num - left

        # Subtract a single stalk from left hand and put between little finger and ring finger
        # 挂一以象三
        x = 1
        left -= 1

        # Get the remainder of the number of stalks in both piles divided by 4
        # 揲之以四以象四时
        y = min(left, 4) if left < 4 else (4 if left % 4 == 0 else left % 4)
        z = min(right, 4) if right < 4 else (4 if right % 4 == 0 else right % 4)
        return num - x - y - z

lines property

Get the lines of the Hexagram. 返回卦之六爻。

transformed cached property

Get the transformed Hexagram (变卦).

values property

Get the values of the Hexagram. 返回卦之六爻之数。

from_binary(lines) classmethod

Create a new instance of the Hexagram class from a list of binary integers.

Source code in src/ichingpy/model/hexagram.py
64
65
66
67
68
69
@classmethod
def from_binary(cls, lines: list[int]) -> Self:
    """Create a new instance of the Hexagram class from a list of binary integers."""
    if len(lines) != 6:
        raise ValueError("Hexagram should have exactly 6 lines")
    return cls.from_lines(lines=[Line(status=LineStatus(i)) for i in lines])

from_datetime(dt) classmethod

Create a new instance of the Hexagram class from a datetime object. 八字起卦: 1. 年月日三支之和除以8取余为外卦之数,余数0作8 2. 年月日时四支之和除以8取余为内卦之数,余数0作8 3. 年月日时四支之和除以6取余为变爻之数,余数0作6

Source code in src/ichingpy/model/hexagram.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@classmethod
def from_datetime(cls, dt: datetime) -> Self:
    """Create a new instance of the Hexagram class from a datetime object.
    八字起卦:
    1. 年月日三支之和除以8取余为外卦之数,余数0作8
    2. 年月日时四支之和除以8取余为内卦之数,余数0作8
    3. 年月日时四支之和除以6取余为变爻之数,余数0作6
    """
    four_pillars = FourPillars.from_datetime(dt)
    year = four_pillars.year.branch.value
    month = four_pillars.month.branch.value
    day = four_pillars.day.branch.value
    hour = four_pillars.hour.branch.value

    remainder_ymd = (year + month + day) % 8
    remainder_ymd = 8 if remainder_ymd == 0 else remainder_ymd

    remainder_ymdh = (year + month + day + hour) % 8
    remainder_ymdh = 8 if remainder_ymdh == 0 else remainder_ymdh

    outer_trigram_lines = Trigram.from_pre_trigram_number(remainder_ymd).lines
    inner_trigram_lines = Trigram.from_pre_trigram_number(remainder_ymdh).lines
    lines = inner_trigram_lines + outer_trigram_lines

    line_to_transform_int = (year + month + day + hour) % 6
    if line_to_transform_int == 0:
        line_to_transform_int = 6
    line_to_transform_int -= 1
    lines[line_to_transform_int] = lines[line_to_transform_int].transform()
    return cls.from_lines(lines=lines)

from_lines(lines) classmethod

Create a new instance of the Hexagram class from a list of Lines.

Source code in src/ichingpy/model/hexagram.py
54
55
56
57
58
59
60
61
62
@classmethod
def from_lines(cls, lines: list[Line]) -> Self:
    """Create a new instance of the Hexagram class from a list of Lines."""
    hexagram = cls(inner=Trigram(lines=lines[:3]), outer=Trigram(lines=lines[3:]))
    from ichingpy.divination.iching import IChingDivinationEngine

    engine = IChingDivinationEngine()
    engine.execute(hexagram)
    return hexagram

from_three_coins() classmethod

Create a new instance of the Hexagram class from tossing three coins six times (增删卜易). two heads: lesser yang 少阳 one head: lesser yin 少阴 zero head: greater yang 太阳 (变爻) three heads: greater yin 太阴 (变爻)

Source code in src/ichingpy/model/hexagram.py
71
72
73
74
75
76
77
78
79
80
81
82
@classmethod
def from_three_coins(cls) -> Self:
    """Create a new instance of the Hexagram class from tossing three coins six times (增删卜易).
    two heads:   lesser  yang  少阳
    one head:    lesser  yin   少阴
    zero head:   greater yang  太阳 (变爻)
    three heads: greater yin   太阴 (变爻)
    """
    # 0: tail, 1: head
    flip_results = [sum([1 - random.getrandbits(1) for _ in range(3)]) for _ in range(6)]
    lines = [Line(status=LineStatus(res)) for res in flip_results]
    return cls.from_lines(lines=lines)

from_yarrow_stalks() classmethod

Create a new instance of the Hexagram class from ... (蓍草起卦).

Source code in src/ichingpy/model/hexagram.py
120
121
122
123
124
125
126
@classmethod
def from_yarrow_stalks(cls) -> Self:
    """Create a new instance of the Hexagram class from ... (蓍草起卦)."""
    # get_lines 6: old yin, 7: young yang, 8: young yin, 9: old yang
    # status    0: old yin, 1: young yang, 2: young yin, 3: old yang
    lines = [Line(status=LineStatus(cls.get_line() - 6)) for _ in range(6)]
    return cls.from_lines(lines=lines)

random() classmethod

Create a random Hexagram instance. This will

Source code in src/ichingpy/model/hexagram.py
84
85
86
87
@classmethod
def random(cls) -> Self:
    """Create a random  Hexagram instance. This will"""
    return cls.from_lines(lines=[Line.random() for _ in range(6)])