開發歷程:達摩一掌經——刻意複製一套已驗證的命理架構

這是 LocalPapa Notes 開發紀錄系列的第十四篇。

上一篇紫微斗數花了很多篇幅講「如何建立一個命理服務的架構」。這篇要說的是:當架構被驗證過了,下一個服務就不應該重新發明輪子。

達摩一掌經是什麼?

達摩一掌經(又稱掌中金、一掌經)是流傳於民間的簡易命理術——輸入農曆生日(年月日時),每個單位對應一顆「掌中星」(共十二星,固定對應十二地支)。四柱各有一顆掌中星,從年柱看根性、月柱看個性與中年、日柱看成年後、時柱看晚年。

和紫微斗數相比,它的計算簡單得多:沒有天干推算、沒有飛星四化,只有地支→掌中星的對應,加上 mod12 的排法。但它的「好玩」程度不輸紫微——十二顆星各有生動的名字(天貴、天孤、天刃、天厄……),年柱的「前世根性」更是讓很多人玩了一遍又一遍。

第一個決定:完整複製,不要重新設計

開發前花了幾分鐘做了一個決定:刻意完整複製紫微斗數的三層架構,不重新設計。

`` build_yizhang_okf.py → 靜態知識層(OKF Markdown) → yizhang-interpret.js(瀏覽器解讀資料) yizhang-engine.js → 動態計算層(排盤運算) yizhang.html → 呈現層(渲染與 i18n) ``

這個決定有幾個理由:

  1. 已驗證的架構不需要再驗證。 紫微那套「產生器持有知識、雙輸出(OKF + interpret.js)、引擎純計算、頁面只查表組字」在前一個服務已經跑通、debug 過了。
  2. 一致性讓維護更容易。 兩個命理服務長得一樣,未來維護者(包括 AI)看第二個時已經知道架構。
  3. 最小新增、最大重用。 不複製架構就得重寫,而重寫的唯一理由是「這個問題不一樣」——但它一樣。

DRY 最值得說的一個決定:不帶農曆表

yizhang-engine.js 需要把西元生日換成農曆,才能知道年干支、月支、日支。農曆轉換需要一張 1900–2100 的 lunarInfo 壓縮表——紫微引擎裡已經有了。

選擇:不在 yizhang-engine.js 裡複製這張表,而是在頁面先載入 ziwei-engine.js,再用 window.ZiweiEngine.solarToLunar

這個選擇只省了幾 KB,但背後的原則值得記:重複的資料就是兩個出錯的地方。 農曆表若有一個邊界日期算錯,要修兩處而不是一處;更危險的是兩處算法版本漂移,兩個服務對同一個生日給出不同的農曆日——使用者會以為是 bug,但實際是「兩個一樣的表在不同時間被修了一次」。DRY 在這裡不只是風格選擇,而是防止靜默錯誤的安全閥。

排盤算法:四柱 mod12

一掌經的排法本身相當優雅,核心就是 mod12:

``` 十二掌中星固定對應地支: 子=天貴 丑=天厄 寅=天權 卯=天破 辰=天奸 巳=天文 午=天福 未=天驛 申=天孤 酉=天刃 戌=天藝 亥=天壽

四柱排法: 年宮 = 生年地支(直接對應) 月宮 = (年地支序 + 生月 - 1) mod 12 日宮 = (月地支序 + 生日 - 1) mod 12 時宮 = (日地支序 + 生時地支序) mod 12 ```

四個 mod12,四顆星。相比紫微的飛星四化,這部分幾乎是小學數學。

驗證也簡單:找幾組已知排盤的案例(家人、歷史名人),把引擎結果和手算核對。這個服務的計算邏輯幾乎沒有「哪派說不同」的問題——十二星對十二支是固定的,不像紫微的四化表有庚干爭議。

解讀層:年柱是根基,六道是前世

排盤只是擺好四顆星。解讀層才是讓使用者覺得「有意思」的地方。

一掌經的重點在年柱——它代表「前世根性」,是你天生帶來的底色。其他三柱分別代表青中晚年的性格與際遇。

進階一點,有個「六道」的詮釋:把十二掌中星依性質分成六道(天道/人道/修羅/畜生/餓鬼/地獄),年柱的星落在哪一道,傳統說法是代表前世的路徑。這個設計很聰明——十二顆星、六個群組,讓解讀有了層次感,而不只是「你的年柱是天孤,代表獨立性強」這樣的一句話。

但這裡有個誠實責任:六道是「一說」,各派略有差異。 頁面上標了「此六道歸類依傳統一說,各派略有差異」,OKF 的星曜頁也記了這個說明。不要讓使用者以為這是「唯一正確答案」。

四柱五行生剋:可驗證所以先做

解讀層的第二個維度是四柱五行生剋——哪根柱子五行偏旺、哪個缺,相鄰柱間是相生還是相剋。

這個功能是在六道之後做的,理由也寫在開發紀錄裡:選可驗證的先做。地支→五行的對應(寅卯木、巳午火、申酉金、亥子水、辰戌丑未土)是無爭議的傳統定義;五行生剋循環(木生火、火生土……)也是固定的。

判定用模運算:五行序列為 [木火土金水],取兩柱五行的序號差 d = (idx_b - idx_a + 5) % 5

  • 0 → 比和
  • 1 → 前生後
  • 2 → 前剋後
  • 3 → 後剋前
  • 4 → 後生前

純算術、可交叉驗證、不依賴任何流派設定——所以先做這個,而不是先做大限流年(大限流年各派分歧大、容易臆造)。

流派設定:把免責升級成選項

一掌經裡有兩個真正會改變排盤結果的流派分歧:

  1. 夜子歸日:晚上 11 點到午夜的「子時」,有些流派算當天、有些算隔天。
  2. 閏月歸月:農曆閏月是算前月還是後月?

與其在說明文字放一行「各派略有差異,僅供參考」,不如讓使用者自己選。選了就明確知道你的排盤依據哪個流派——比含糊的免責更誠實、更實用。

流派選項的文字放在 build_yizhang_okf.py 輸出的 yizhang-interpret.js 裡,頁面以 initSchools()YizhangInterpret.schools 動態建立下拉選單,語言切換時保留選取並重建文字。沒有任何選項文字硬編在 HTML 裡——跟紫微的「星曜名固定中文、其他標籤隨語言」是同一個原則。

流年:選可驗證的時間法

命理服務的時間維度是高需求功能,也是最多派別分歧的地方。一掌經的大限流年各家說法不同,隨便選一個很容易變成「我自己創了一套算法」。

解法:選可驗證的方法,然後明標依據。 選的是「流年地支掌中星法」——每一年的地支固定對應一顆掌中星(已有的星↔地支表直接套用),再以流年地支與本命年柱地支的關係(沖/合/三合/比和)論順逆。純運算、有傳統出處、可以用已知年份交叉驗證。

頁面上標了這個方法的依據,OKF 的 annual.md 也記了計算說明。使用者知道他看的是哪一套,不是一個黑箱給出的「2026 年很好」。

這個服務教的核心事

紫微斗數教的是「如何建立一個命理服務」。達摩一掌經教的是:一個已驗證的架構,下一次要完整複製,而不是有感情地重新設計。

「設計感」在這裡是負資產。寫第二個命理服務時如果想「這次要不一樣一點」,就會產生兩個略有差異的三層架構——之後維護兩套的成本,遠大於第一天多花五分鐘抵抗想要創新的衝動。

學到的事

  • 架構被驗證後,複製就是正確答案。 抵制「每次都要設計新的」的工程師衝動。
  • DRY 不只是風格,是防止靜默錯誤的機制。 農曆表一份維護,兩個服務出問題。
  • 選可驗證的功能先做。 五行生剋先於流年,是因為前者可驗證、後者各派分歧。
  • 把免責聲明升級成選項,是對使用者更誠實的做法。 「子時夜子各派不同」→給選單,而不是一句備注。
  • 時間維度要明標依據。 「流年地支掌中星法」搜得到、可核對,比「本工具依傳統排法」更負責。

一以貫之的問題

這個系列的每篇問的問題略有不同。這篇的問題是:你確定要重新設計嗎?還是你只是不想讓第二個服務看起來跟第一個一樣?

如果理由是後者,複製才是正確答案。架構一致、行為可預期、維護成本低——這比「有創意的多樣性」更有價值。

下一篇,命理服務系列繼續——塔羅牌佔卜如何用「22 張大牌逐張、56 張小牌組合」的設計,以同樣的三層架構涵蓋 78 張牌,而不是硬刻 78 × 2(正/逆位)= 156 筆。想追蹤系列?把 LocalPapa Notes 加入書籤吧。

Dev Log: Da Mo Yi Zhang Jing — Deliberately Copying a Proven Architecture

This is the fourteenth post in the LocalPapa Notes dev-log series.

The previous post on Zi Wei Dou Shu spent a lot of space explaining how to build a divination service architecture from the ground up. This one is about the opposite: when an architecture has been validated, the right move for the next service is to copy it wholesale.

What is Da Mo Yi Zhang Jing?

Da Mo Yi Zhang Jing (also known as the Palm Classic) is a folk divination system — you input your birth year, month, day and hour, and each unit maps to one of twelve "Palm Stars" (fixed to the twelve Earth Branches). The four pillars each carry a Palm Star: the year pillar shows root nature, the month pillar shows personality and middle years, the day pillar covers adulthood, and the hour pillar covers later life.

Compared to Zi Wei Dou Shu, the calculation is much simpler: no stem derivation, no flying star four-transformations, just Earth Branch → Palm Star mapping plus a mod12 placement rule. But its "fun factor" is just as high — the twelve stars have vivid names (Heavenly Noble, Heavenly Loner, Heavenly Blade, Heavenly Calamity…), and the year pillar's "past-life root nature" is something many users go back to read multiple times.

The first decision: copy, don't redesign

Before writing a line of code, the decision was made: deliberately copy Zi Wei Dou Shu's three-layer architecture wholesale, without redesigning it.

`` build_yizhang_okf.py → static knowledge layer (OKF Markdown) → yizhang-interpret.js (browser interpretation data) yizhang-engine.js → dynamic computation layer (chart calculation) yizhang.html → presentation layer (rendering and i18n) ``

A few reasons for this:

  1. A validated architecture doesn't need revalidating. Zi Wei's "generator holds knowledge, dual-output (OKF + interpret.js), engine is pure computation, page only looks up and assembles text" had already been built, debugged, and proven.
  2. Consistency makes maintenance easier. When two divination services look the same, a future maintainer (including an AI) who sees the second one already knows how it works.
  3. Maximum reuse, minimum new code. Not copying the architecture means rewriting it — and the only good reason to rewrite is "this problem is different." It wasn't.

The single most important DRY decision: no second lunar table

yizhang-engine.js needs to convert a Gregorian birth date to the lunar calendar to know the year, month and day Earth Branches. That requires a 1900–2100 lunarInfo compressed table — which already exists in the Zi Wei engine.

Decision: don't copy that table into yizhang-engine.js. Instead, load ziwei-engine.js first on the page, then call window.ZiweiEngine.solarToLunar.

This saves only a few kilobytes, but the principle behind it matters: duplicate data is two places to go wrong. If there's a boundary-date bug in the lunar table, you'd have to fix it in two places. Worse, the two copies can silently drift apart — you fix one and forget the other, and suddenly the same birthday gives different lunar dates in two different services. Users think it's a bug; it's actually version skew between two identical tables maintained separately. DRY here isn't a style preference; it's a guard against silent errors.

The chart algorithm: four pillars, mod12

The Yi Zhang Jing placement rule is elegant precisely because it's simple — core is just mod12:

``` Twelve Palm Stars fixed to Earth Branches: 子=Heavenly Noble 丑=Heavenly Calamity 寅=Heavenly Authority 卯=Heavenly Breaker 辰=Heavenly Cunning 巳=Heavenly Arts 午=Heavenly Blessing 未=Heavenly Wanderer 申=Heavenly Loner 酉=Heavenly Blade 戌=Heavenly Craft 亥=Heavenly Longevity

Four-pillar placement: Year palace = birth year Earth Branch (direct) Month palace = (year EB index + birth month - 1) mod 12 Day palace = (month EB index + birth day - 1) mod 12 Hour palace = (day EB index + birth hour EB index) mod 12 ```

Four mod12 operations, four stars. Compared to Zi Wei's flying-star transformations, this is almost arithmetic.

Verification is equally simple: find a few reference charts (family members, public figures) and compare the engine output to hand calculation. Unlike Zi Wei, there are virtually no "different schools disagree on this part" issues in the core computation — the twelve stars map to twelve branches, and that mapping is fixed.

The interpretation layer: year pillar as root, Six Paths as past lives

Laying out the chart is just placing the four stars. The interpretation layer is what makes users actually engage.

The central emphasis in Yi Zhang Jing is the year pillar — it represents your "past-life root nature," the baseline you bring into this life. The other three pillars cover personality and experiences in youth/middle age/adulthood/later years.

A richer layer is the "Six Paths" (六道) classification: the twelve Palm Stars are grouped into six paths (Heavenly Path, Human Path, Asura, Animal, Hungry Ghost, Hell Realm), and your year-pillar star's group is said to indicate your previous life's trajectory. This layering makes the interpretation richer — twelve stars, six groups, the year pillar now carries both a star-level meaning and a group-level meaning.

But honest labelling is required here: Six Paths groupings are "one school's view," and different traditions vary. The page carries a note: "this Six Paths classification follows one traditional reading; schools differ." The OKF star pages note this too. We're not presenting it as the one authoritative answer.

Five-element interaction: do the verifiable things first

The second dimension of the interpretation layer is Five-Element analysis across the four pillars — which pillar's element is dominant, which is absent, whether adjacent pillars are in a generative or controlling relationship.

This was built after the Six Paths layer, for a reason worth stating: do the verifiable things first. Earth Branch → Five Element mapping (寅卯木, 巳午火, 申酉金, 亥子水, 辰戌丑未土) is fixed, undisputed. The generative/controlling cycle (Wood→Fire→Earth→Metal→Water→Wood) is equally fixed.

The calculation uses modular arithmetic: Five-Element sequence [Wood Fire Earth Metal Water], and for adjacent pillars a and b, d = (idx_b - idx_a + 5) % 5 gives: 0 = same, 1 = a generates b, 2 = a controls b, 3 = b controls a, 4 = b generates a. Pure arithmetic, cross-verifiable, no school settings needed — which is why this was built before annual fortune (annual fortune has too many competing school variants to be verifiable).

School settings: turn disclaimers into options

There are two variant points in Yi Zhang Jing that actually change the chart result:

  1. Night-Zi hour rollover: The hour from 11pm to midnight is "Zi hour" — some schools attribute it to the current calendar day, others to the next.
  2. Leap-month assignment: An intercalary lunar month — does it count as the month before or the month after?

Rather than a footnote saying "different schools vary slightly, for reference only," we turned both into user-selectable options. Pick your school, and you know exactly which set of rules produced your chart — that's more honest and more useful than a vague caveat.

The school option labels live in build_yizhang_okf.py's output (yizhang-interpret.js), and the page builds the <select> dynamically from YizhangInterpret.schools via initSchools(). Language switching preserves the selection and rebuilds the option text. No option text is hardcoded in the HTML — the same principle as Zi Wei's "star names fixed in Chinese, other labels follow the language."

Annual fortune: label the method, then build it

Annual fortune is a high-demand feature and also the most school-dependent one. Yi Zhang Jing's various traditions disagree on how annual luck should be calculated, and picking any one version without documenting it risks looking like "we invented our own algorithm."

Solution: pick a verifiable method, then document it explicitly. We chose "annual Earth Branch Palm Star method" — each year's Earth Branch directly maps to a Palm Star (the star↔branch table already exists), and the year's relationship to your birth-year pillar branch (conflicting / combining / triple-combining / same) determines good/neutral/challenging. Pure computation, traceable tradition, cross-verifiable with known years.

The page labels which method is being used; the OKF annual.md documents the calculation. Users know exactly what rules produced their reading — not a black box that outputs "2026 will be good for you."

What this service really teaches

Zi Wei Dou Shu taught how to build a divination service architecture. Yi Zhang Jing taught something different: a validated architecture should be copied, not affectionately redesigned.

"Design flair" is a liability here. Building the second service with a slightly different three-layer architecture — because the first one felt too plain, or because it's tempting to do something new — creates two slightly different three-layer architectures to maintain in parallel. The cost of that drift compounds indefinitely. It outweighs the momentary satisfaction of "not copying."

What we learned

  • Copy validated architecture. Resist the engineer's impulse to redesign each time. Consistency compounds in your favour.
  • DRY prevents silent errors, not just redundancy. Two copies of the lunar table means two divergence points — one day they drift and nobody notices until users get conflicting results.
  • Do the verifiable things first. Five-element mapping before annual fortune because the former is verifiable and the latter has school variance.
  • Upgrade disclaimers to options. "Schools differ on night-Zi hour" → give a selector, not a footnote. More honest and more useful.
  • Label the method before you build it. "Annual Earth Branch Palm Star method" is traceable; "traditional calculation" isn't.

The same question, applied to architecture

This series usually asks "can this be done in the browser." This post asks a different question: are you redesigning because the problem is different, or because you're tired of looking at the same thing?

If it's the second reason, copy the architecture. Consistent structure, predictable behaviour, low maintenance cost — that combination is more valuable than creative variety in your service implementations.

Next in the series, the divination arc continues — how tarot uses "22 major arcana individually, 56 minor arcana as combinations" to cover all 78 cards with the same three-layer architecture, instead of hardcoding 78 × 2 (upright/reversed) = 156 entries. Want to follow the series? Bookmark LocalPapa Notes.

探索 39+ 個隱私優先的瀏覽器工具
全程本地運算、檔案不上傳。
前往 LocalPapa →
Explore 39+ privacy-first browser tools
Everything runs locally — your files never leave your device.
Visit LocalPapa →

想看英文版?點右上角 EN 切換語言。

Prefer Chinese? Tap at the top-right to switch.