dsh-metaphysics 0.14.3 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +213 -166
- package/README.md +229 -221
- package/client.js +1551 -111
- package/engine/bazi.js +505 -515
- package/engine/calendar.js +325 -256
- package/engine/crosscheck.js +133 -0
- package/engine/daliuren.js +339 -342
- package/engine/diff.js +197 -0
- package/engine/fail.js +13 -0
- package/engine/followup.js +173 -0
- package/engine/geo.js +1 -1
- package/engine/hepan.js +147 -144
- package/engine/index.js +566 -513
- package/engine/interpret-knowledge.js +277 -0
- package/engine/interpret-plain.js +197 -0
- package/engine/interpret.js +1356 -1176
- package/engine/liuyao.js +237 -238
- package/engine/lru.js +69 -0
- package/engine/meihua.js +137 -136
- package/engine/nayin.js +24 -0
- package/engine/qimen.js +442 -438
- package/engine/sanhe.js +26 -0
- package/engine/validate.js +56 -0
- package/engine/ziwei-hepan.js +123 -120
- package/engine/ziwei.js +821 -828
- package/index.js +229 -212
- package/package.json +1 -1
package/engine/ziwei.js
CHANGED
|
@@ -1,828 +1,821 @@
|
|
|
1
|
-
// 紫微斗数安星:命身宫 → 五行局 → 紫微/天府 → 十四主星 → 诸辅煞杂曜 → 十二宫 → 四化 → 大限小限流年 → 格局
|
|
2
|
-
import { STEMS, BRANCHES, ganzhiName, sexagenaryIndex, jdToParts, partsToJd, formatJd } from './calendar.js';
|
|
3
|
-
import { solarToLunar } from './lunar.js';
|
|
4
|
-
import { applyTrueSolar } from './bazi.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
export const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
export
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
export const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
export
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
put(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
put(
|
|
275
|
-
put(
|
|
276
|
-
put(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
put(
|
|
284
|
-
|
|
285
|
-
put(
|
|
286
|
-
put(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
put(
|
|
291
|
-
put(
|
|
292
|
-
|
|
293
|
-
put(
|
|
294
|
-
put(
|
|
295
|
-
put(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
put(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
put(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
put(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
put(
|
|
310
|
-
put(
|
|
311
|
-
put(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
put(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
:
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
},
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
return
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (
|
|
621
|
-
if (
|
|
622
|
-
if ((
|
|
623
|
-
|
|
624
|
-
add('
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
if ((
|
|
629
|
-
if ((
|
|
630
|
-
|
|
631
|
-
add('
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
if (jia('
|
|
636
|
-
if (
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
if (
|
|
643
|
-
|
|
644
|
-
if (
|
|
645
|
-
if (
|
|
646
|
-
|
|
647
|
-
//
|
|
648
|
-
|
|
649
|
-
if (
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
const
|
|
763
|
-
const
|
|
764
|
-
const
|
|
765
|
-
const
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
const
|
|
770
|
-
const
|
|
771
|
-
const
|
|
772
|
-
const
|
|
773
|
-
const
|
|
774
|
-
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
year: { ...point(yB), doujun: BRANCHES[doujun] },
|
|
823
|
-
month: { index: lr.month, ...point(mIdx) },
|
|
824
|
-
day: { index: lr.day, ...point(dIdx) },
|
|
825
|
-
hour: { hourIndex: hbNow, ...point(hIdx) },
|
|
826
|
-
rules: '流月命宫自流年斗君(该年支起正月逆数至生月,再起子时顺数至生时)起正月,顺行至该农历月;流日自流月命宫起初一,顺行至该农历日;流时自流日命宫起子时,顺行至该时辰;四化取落点宫的原局宫干',
|
|
827
|
-
};
|
|
828
|
-
}
|
|
1
|
+
// 紫微斗数安星:命身宫 → 五行局 → 紫微/天府 → 十四主星 → 诸辅煞杂曜 → 十二宫 → 四化 → 大限小限流年 → 格局
|
|
2
|
+
import { STEMS, BRANCHES, ganzhiName, sexagenaryIndex, jdToParts, partsToJd, formatJd } from './calendar.js';
|
|
3
|
+
import { solarToLunar } from './lunar.js';
|
|
4
|
+
import { applyTrueSolar } from './bazi.js';
|
|
5
|
+
import { assertCivil } from './validate.js';
|
|
6
|
+
import { nayinOf } from './nayin.js';
|
|
7
|
+
import { resolveAddress } from './geo.js';
|
|
8
|
+
|
|
9
|
+
const fix = (i) => ((i % 12) + 12) % 12;
|
|
10
|
+
|
|
11
|
+
// ── 基础表 ────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export const PALACE_NAMES = [
|
|
14
|
+
'命宫', '兄弟', '夫妻', '子女', '财帛', '疾厄',
|
|
15
|
+
'迁移', '交友', '官禄', '田宅', '福德', '父母',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const BUREAU = {
|
|
19
|
+
水: { name: '水二局', number: 2 },
|
|
20
|
+
木: { name: '木三局', number: 3 },
|
|
21
|
+
金: { name: '金四局', number: 4 },
|
|
22
|
+
土: { name: '土五局', number: 5 },
|
|
23
|
+
火: { name: '火六局', number: 6 },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function nayinElement(ganzhiIdx) {
|
|
27
|
+
return nayinOf(ganzhiIdx);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const ZIWEI_GROUP = [
|
|
31
|
+
{ offset: 0, name: '紫微' }, { offset: -1, name: '天机' }, { offset: -3, name: '太阳' },
|
|
32
|
+
{ offset: -4, name: '武曲' }, { offset: -5, name: '天同' }, { offset: -8, name: '廉贞' },
|
|
33
|
+
];
|
|
34
|
+
const TIANFU_GROUP = [
|
|
35
|
+
{ offset: 0, name: '天府' }, { offset: 1, name: '太阴' }, { offset: 2, name: '贪狼' },
|
|
36
|
+
{ offset: 3, name: '巨门' }, { offset: 4, name: '天相' }, { offset: 5, name: '天梁' },
|
|
37
|
+
{ offset: 6, name: '七杀' }, { offset: 10, name: '破军' },
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 十四主星庙旺得利平不陷(地支序:子丑寅卯辰巳午未申酉戌亥)
|
|
42
|
+
* 采用通行表(《紫微斗数全书》系)。各家流派在少数宫位有出入(尤其太阳、太阴的庙旺界限),
|
|
43
|
+
* 此处只标注十四主星——辅星亮度各派差异更大,不作硬标。
|
|
44
|
+
* 等级从吉到凶:庙 > 旺 > 得 > 利 > 平 > 不 > 陷
|
|
45
|
+
*/
|
|
46
|
+
export const BRIGHTNESS = {
|
|
47
|
+
紫微: ['平', '庙', '庙', '旺', '得', '旺', '庙', '庙', '旺', '平', '得', '旺'],
|
|
48
|
+
天机: ['庙', '陷', '得', '旺', '利', '平', '庙', '陷', '得', '旺', '利', '平'],
|
|
49
|
+
太阳: ['陷', '陷', '旺', '庙', '旺', '旺', '庙', '得', '得', '平', '陷', '陷'],
|
|
50
|
+
武曲: ['旺', '庙', '得', '利', '庙', '平', '旺', '庙', '得', '利', '庙', '平'],
|
|
51
|
+
天同: ['旺', '陷', '利', '庙', '平', '庙', '陷', '陷', '旺', '平', '平', '庙'],
|
|
52
|
+
廉贞: ['平', '利', '庙', '平', '利', '陷', '平', '利', '庙', '平', '利', '陷'],
|
|
53
|
+
天府: ['庙', '庙', '庙', '得', '庙', '得', '旺', '庙', '得', '旺', '庙', '得'],
|
|
54
|
+
太阴: ['庙', '庙', '陷', '陷', '陷', '陷', '陷', '陷', '利', '旺', '旺', '庙'],
|
|
55
|
+
贪狼: ['旺', '庙', '平', '利', '庙', '陷', '旺', '庙', '平', '利', '庙', '陷'],
|
|
56
|
+
巨门: ['旺', '旺', '庙', '庙', '陷', '旺', '旺', '旺', '庙', '庙', '陷', '旺'],
|
|
57
|
+
天相: ['庙', '庙', '庙', '陷', '得', '得', '庙', '庙', '庙', '陷', '得', '得'],
|
|
58
|
+
天梁: ['庙', '旺', '庙', '庙', '庙', '陷', '庙', '旺', '庙', '庙', '庙', '陷'],
|
|
59
|
+
七杀: ['旺', '庙', '庙', '陷', '庙', '平', '旺', '庙', '庙', '陷', '庙', '平'],
|
|
60
|
+
破军: ['庙', '旺', '得', '陷', '旺', '平', '庙', '旺', '得', '陷', '旺', '平'],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const MUTAGEN = {
|
|
64
|
+
甲: { 廉贞: '禄', 破军: '权', 武曲: '科', 太阳: '忌' },
|
|
65
|
+
乙: { 天机: '禄', 天梁: '权', 紫微: '科', 太阴: '忌' },
|
|
66
|
+
丙: { 天同: '禄', 天机: '权', 文昌: '科', 廉贞: '忌' },
|
|
67
|
+
丁: { 太阴: '禄', 天同: '权', 天机: '科', 巨门: '忌' },
|
|
68
|
+
戊: { 贪狼: '禄', 太阴: '权', 右弼: '科', 天机: '忌' },
|
|
69
|
+
己: { 武曲: '禄', 贪狼: '权', 天梁: '科', 文曲: '忌' },
|
|
70
|
+
庚: { 太阳: '禄', 武曲: '权', 太阴: '科', 天同: '忌' },
|
|
71
|
+
辛: { 巨门: '禄', 太阳: '权', 文曲: '科', 文昌: '忌' },
|
|
72
|
+
壬: { 天梁: '禄', 紫微: '权', 左辅: '科', 武曲: '忌' },
|
|
73
|
+
癸: { 破军: '禄', 巨门: '权', 太阴: '科', 贪狼: '忌' },
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// 年干系
|
|
77
|
+
export const LUCUN = { 甲: 2, 乙: 3, 丙: 5, 丁: 6, 戊: 5, 己: 6, 庚: 8, 辛: 9, 壬: 11, 癸: 0 };
|
|
78
|
+
export const KUI_YUE = {
|
|
79
|
+
甲: [1, 7], 戊: [1, 7], 庚: [1, 7], 乙: [0, 8], 己: [0, 8],
|
|
80
|
+
丙: [11, 9], 丁: [11, 9], 辛: [6, 2], 壬: [3, 5], 癸: [3, 5],
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** 命主:按命宫地支 */
|
|
84
|
+
export const MING_ZHU = {
|
|
85
|
+
子: '贪狼', 丑: '巨门', 寅: '禄存', 卯: '文曲', 辰: '廉贞', 巳: '武曲',
|
|
86
|
+
午: '破军', 未: '武曲', 申: '廉贞', 酉: '文曲', 戌: '禄存', 亥: '巨门',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** 身主:按生年地支 */
|
|
90
|
+
export const SHEN_ZHU = {
|
|
91
|
+
子: '铃星', 丑: '天相', 寅: '天梁', 卯: '天同', 辰: '文昌', 巳: '天机',
|
|
92
|
+
午: '火星', 未: '天相', 申: '天梁', 酉: '天同', 戌: '文昌', 亥: '天机',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// 三合局(寅午戌 / 申子辰 / 巳酉丑 / 亥卯未)
|
|
96
|
+
const TRIADS = {
|
|
97
|
+
寅午戌: [2, 6, 10],
|
|
98
|
+
申子辰: [8, 0, 4],
|
|
99
|
+
巳酉丑: [5, 9, 1],
|
|
100
|
+
亥卯未: [11, 3, 7],
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function triadOf(branchIndex) {
|
|
104
|
+
const b = fix(branchIndex);
|
|
105
|
+
for (const [name, arr] of Object.entries(TRIADS)) {
|
|
106
|
+
if (arr.includes(b)) return name;
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`三合局未匹配:${branchIndex}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** 四正三会(寅卯辰 / 巳午未 / 申酉戌 / 亥子丑),用于孤辰寡宿 */
|
|
112
|
+
const DIRECTIONS = { 寅卯辰: [2, 3, 4],
|
|
113
|
+
巳午未: [5, 6, 7],
|
|
114
|
+
申酉戌: [8, 9, 10],
|
|
115
|
+
亥子丑: [11, 0, 1],
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
function directionOf(branchIndex) {
|
|
119
|
+
const b = fix(branchIndex);
|
|
120
|
+
for (const [name, arr] of Object.entries(DIRECTIONS)) {
|
|
121
|
+
if (arr.includes(b)) return name;
|
|
122
|
+
}
|
|
123
|
+
throw new Error(`四正未匹配:${branchIndex}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const HUO_START = { 寅午戌: 1, 申子辰: 2, 巳酉丑: 3, 亥卯未: 9 };
|
|
127
|
+
export const LING_START = { 寅午戌: 3, 申子辰: 10, 巳酉丑: 10, 亥卯未: 10 };
|
|
128
|
+
export const TIANMA = { 寅午戌: 8, 申子辰: 2, 巳酉丑: 11, 亥卯未: 5 };
|
|
129
|
+
export const XIANCHI = { 寅午戌: 3, 申子辰: 9, 巳酉丑: 6, 亥卯未: 0 };
|
|
130
|
+
export const HUAGAI = { 寅午戌: 10, 申子辰: 4, 巳酉丑: 1, 亥卯未: 7 };
|
|
131
|
+
export const JIANGXING = { 寅午戌: 6, 申子辰: 0, 巳酉丑: 9, 亥卯未: 3 };
|
|
132
|
+
|
|
133
|
+
export const GUCHEN = { 寅卯辰: 5, 巳午未: 8, 申酉戌: 11, 亥子丑: 2 };
|
|
134
|
+
export const GUASU = { 寅卯辰: 1, 巳午未: 4, 申酉戌: 7, 亥子丑: 10 };
|
|
135
|
+
|
|
136
|
+
/** 破碎:子午卯酉在巳,寅申巳亥在酉,辰戌丑未在丑 */
|
|
137
|
+
export function posuIndex(yearBranch) {
|
|
138
|
+
const b = fix(yearBranch);
|
|
139
|
+
if ([0, 6, 3, 9].includes(b)) return 5; // 子午卯酉 -> 巳
|
|
140
|
+
if ([2, 8, 5, 11].includes(b)) return 9; // 寅申巳亥 -> 酉
|
|
141
|
+
return 1; // 辰戌丑未 -> 丑
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const XIAOXIAN_START = {
|
|
145
|
+
male: { 寅午戌: 4, 申子辰: 10, 巳酉丑: 7, 亥卯未: 1 },
|
|
146
|
+
female: { 寅午戌: 10, 申子辰: 4, 巳酉丑: 1, 亥卯未: 7 },
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// 十二神名序
|
|
150
|
+
export const CHANGSHENG_NAMES = ['长生', '沐浴', '冠带', '临官', '帝旺', '衰', '病', '死', '墓', '绝', '胎', '养'];
|
|
151
|
+
export const BOSHI_NAMES = ['博士', '力士', '青龙', '小耗', '将军', '奏书', '飞廉', '喜神', '病符', '大耗', '伏兵', '官府'];
|
|
152
|
+
export const JIANGQIAN_NAMES = ['将星', '攀鞍', '岁驿', '息神', '华盖', '劫煞', '灾煞', '天煞', '指背', '咸池', '月煞', '亡神'];
|
|
153
|
+
export const SUIQI_NAMES = ['岁建', '晦气', '丧门', '贯索', '官符', '小耗', '大耗', '龙德', '白虎', '天德', '吊客', '病符'];
|
|
154
|
+
|
|
155
|
+
/** 长生起点(五行局 → 地支):水二申、木三亥、金四巳、土五申、火六寅 */
|
|
156
|
+
export const CHANGSHENG_START = { 2: 8, 3: 11, 4: 5, 5: 8, 6: 2 };
|
|
157
|
+
|
|
158
|
+
// ── 基础算法 ──────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
/** 时辰序:0=子,23:00 归子时 */
|
|
161
|
+
export function hourBranchIndex(hour, minute = 0) {
|
|
162
|
+
const total = hour * 60 + minute;
|
|
163
|
+
if (total >= 23 * 60 || total < 60) return 0;
|
|
164
|
+
return Math.floor((total - 60) / 120) + 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** 紫微与天府所在宫(子 = 0)。借数法:局数除日数取商,商自寅起顺行,借数奇逆偶顺 */
|
|
168
|
+
export function ziweiTianfuIndex(lunarDay, bureauNumber) {
|
|
169
|
+
let offset = 0;
|
|
170
|
+
let quotient = 0;
|
|
171
|
+
for (;;) {
|
|
172
|
+
const divisor = lunarDay + offset;
|
|
173
|
+
quotient = Math.floor(divisor / bureauNumber);
|
|
174
|
+
if (divisor % bureauNumber === 0) break;
|
|
175
|
+
offset++;
|
|
176
|
+
if (offset > 60) throw new Error('紫微定位失败');
|
|
177
|
+
}
|
|
178
|
+
quotient %= 12;
|
|
179
|
+
let yinBased = quotient - 1;
|
|
180
|
+
yinBased += offset % 2 === 0 ? offset : -offset;
|
|
181
|
+
const ziwei = fix(yinBased + 2);
|
|
182
|
+
return { ziwei, tianfu: fix(4 - ziwei) };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** 某宫的小限虚岁(1..12) */
|
|
186
|
+
export function xiaoxianAge(branchIndex, triad, gender) {
|
|
187
|
+
const start = XIAOXIAN_START[gender === 'female' ? 'female' : 'male'][triad];
|
|
188
|
+
const b = fix(branchIndex);
|
|
189
|
+
return gender === 'female' ? fix(start - b) + 1 : fix(b - start) + 1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── 主流程 ────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* 排紫微斗数命盘
|
|
196
|
+
* @param {object} input { year,month,day,hour,minute, gender, leapAsNext? }
|
|
197
|
+
*/
|
|
198
|
+
export function buildZiwei(input) {
|
|
199
|
+
assertCivil(input);
|
|
200
|
+
const { year: cy, month: cm, day: cd, hour: ch = 0, minute: cmin = 0, gender = 'male' } = input;
|
|
201
|
+
const useTrueSolar = input.trueSolar !== false;
|
|
202
|
+
const geo = input.geo || (input.address ? resolveAddress(input.address) : null);
|
|
203
|
+
const longitude = typeof input.longitude === 'number'
|
|
204
|
+
? input.longitude
|
|
205
|
+
: (geo && typeof geo.lon === 'number' ? geo.lon : undefined);
|
|
206
|
+
const civilJd = partsToJd({ year: cy, month: cm, day: cd, hour: ch, minute: cmin });
|
|
207
|
+
const solar = useTrueSolar
|
|
208
|
+
? applyTrueSolar(civilJd, longitude)
|
|
209
|
+
: { offsetMinutes: 0, jd: civilJd, note: '未启用真太阳时' };
|
|
210
|
+
// 紫微按「时辰」起盘:真太阳时若跨时辰/跨日,命盘随之改变
|
|
211
|
+
const eff = jdToParts(solar.jd);
|
|
212
|
+
const year = eff.year;
|
|
213
|
+
const month = eff.month;
|
|
214
|
+
const day = eff.day;
|
|
215
|
+
const hour = eff.hour;
|
|
216
|
+
const minute = eff.minute;
|
|
217
|
+
|
|
218
|
+
const lunarRaw = solarToLunar(year, month, day);
|
|
219
|
+
const lunarMonth = lunarRaw.isLeap && input.leapAsNext
|
|
220
|
+
? (lunarRaw.month % 12) + 1
|
|
221
|
+
: lunarRaw.month;
|
|
222
|
+
const lunarDay = lunarRaw.day;
|
|
223
|
+
|
|
224
|
+
const hb = hourBranchIndex(hour, minute);
|
|
225
|
+
const hourBranch = BRANCHES[hb];
|
|
226
|
+
|
|
227
|
+
const yearGanzhi = ganzhiName((((lunarRaw.lunarYear - 4) % 60) + 60) % 60);
|
|
228
|
+
const yearStem = yearGanzhi.stem;
|
|
229
|
+
const yb = BRANCHES.indexOf(yearGanzhi.branch);
|
|
230
|
+
const triad = triadOf(yb);
|
|
231
|
+
|
|
232
|
+
// 命宫 / 身宫
|
|
233
|
+
const soulIndex = fix(2 + (lunarMonth - 1) - hb);
|
|
234
|
+
const bodyIndex = fix(2 + (lunarMonth - 1) + hb);
|
|
235
|
+
|
|
236
|
+
// 命宫天干(五虎遁)
|
|
237
|
+
const yinStem = ((STEMS.indexOf(yearStem) % 5) * 2 + 2) % 10;
|
|
238
|
+
const soulStem = (yinStem + fix(soulIndex - 2)) % 10;
|
|
239
|
+
const soulNayin = nayinElement(sexagenaryIndex(soulStem, soulIndex));
|
|
240
|
+
const bureau = BUREAU[soulNayin.element];
|
|
241
|
+
|
|
242
|
+
const { ziwei, tianfu } = ziweiTianfuIndex(lunarDay, bureau.number);
|
|
243
|
+
|
|
244
|
+
// 星曜落宫
|
|
245
|
+
const starsByBranch = Array.from({ length: 12 }, () => []);
|
|
246
|
+
// 安星:主星顺带记下所在宫的庙旺得利平不陷(BRIGHTNESS 以地支序索引,与 fix(branch) 一致)
|
|
247
|
+
const put = (branch, name, type) => {
|
|
248
|
+
const idx = fix(branch);
|
|
249
|
+
const star = { name, type };
|
|
250
|
+
if (type === '主星' && BRIGHTNESS[name]) {
|
|
251
|
+
const b = BRIGHTNESS[name][idx];
|
|
252
|
+
if (b) star.brightness = b;
|
|
253
|
+
}
|
|
254
|
+
starsByBranch[idx].push(star);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
for (const s of ZIWEI_GROUP) put(ziwei + s.offset, s.name, '主星');
|
|
258
|
+
for (const s of TIANFU_GROUP) put(tianfu + s.offset, s.name, '主星');
|
|
259
|
+
|
|
260
|
+
// 时系辅星
|
|
261
|
+
const wenchang = fix(10 - hb);
|
|
262
|
+
const wenqu = fix(4 + hb);
|
|
263
|
+
put(wenchang, '文昌', '辅星');
|
|
264
|
+
put(wenqu, '文曲', '辅星');
|
|
265
|
+
const zuofu = fix(4 + (lunarMonth - 1));
|
|
266
|
+
const youbi = fix(10 - (lunarMonth - 1));
|
|
267
|
+
put(zuofu, '左辅', '辅星');
|
|
268
|
+
put(youbi, '右弼', '辅星');
|
|
269
|
+
put(6 + hb, '台辅', '吉星'); // 午起子时顺行
|
|
270
|
+
put(2 + hb, '封诰', '吉星'); // 寅起子时顺行
|
|
271
|
+
|
|
272
|
+
// 年干系
|
|
273
|
+
const lucun = LUCUN[yearStem];
|
|
274
|
+
put(lucun, '禄存', '吉星');
|
|
275
|
+
put(lucun + 1, '擎羊', '煞星');
|
|
276
|
+
put(lucun - 1, '陀罗', '煞星');
|
|
277
|
+
const [kui, yue] = KUI_YUE[yearStem];
|
|
278
|
+
put(kui, '天魁', '吉星');
|
|
279
|
+
put(yue, '天钺', '吉星');
|
|
280
|
+
|
|
281
|
+
// 年支系
|
|
282
|
+
const hongluan = fix(3 - yb);
|
|
283
|
+
put(hongluan, '红鸾', '吉星');
|
|
284
|
+
put(hongluan + 6, '天喜', '吉星');
|
|
285
|
+
put(6 - yb, '天哭', '煞星');
|
|
286
|
+
put(6 + yb, '天虚', '煞星');
|
|
287
|
+
put(4 + yb, '龙池', '吉星');
|
|
288
|
+
put(10 - yb, '凤阁', '吉星');
|
|
289
|
+
put(TIANMA[triad], '天马', '吉星');
|
|
290
|
+
put(XIANCHI[triad], '咸池', '煞星');
|
|
291
|
+
put(HUAGAI[triad], '华盖', '杂曜');
|
|
292
|
+
const dirGroup = directionOf(yb);
|
|
293
|
+
put(GUCHEN[dirGroup], '孤辰', '煞星');
|
|
294
|
+
put(GUASU[dirGroup], '寡宿', '煞星');
|
|
295
|
+
put(posuIndex(yb), '破碎', '煞星');
|
|
296
|
+
|
|
297
|
+
// 月系
|
|
298
|
+
put(9 + (lunarMonth - 1), '天刑', '煞星'); // 酉起正月顺行
|
|
299
|
+
put(1 + (lunarMonth - 1), '天姚', '煞星'); // 丑起正月顺行
|
|
300
|
+
|
|
301
|
+
// 日系(以左辅右弼、文昌文曲为基)
|
|
302
|
+
put(zuofu + (lunarDay - 1), '三台', '吉星');
|
|
303
|
+
put(youbi - (lunarDay - 1), '八座', '吉星');
|
|
304
|
+
put(wenchang + (lunarDay - 2), '恩光', '吉星');
|
|
305
|
+
put(wenqu + (lunarDay - 2), '天贵', '吉星');
|
|
306
|
+
|
|
307
|
+
// 时系煞星
|
|
308
|
+
put(HUO_START[triad] + hb, '火星', '煞星');
|
|
309
|
+
put(LING_START[triad] + hb, '铃星', '煞星');
|
|
310
|
+
put(11 + hb, '地劫', '煞星'); // 亥宫起子时,顺行
|
|
311
|
+
put(11 - hb, '地空', '煞星'); // 亥宫起子时,逆行
|
|
312
|
+
|
|
313
|
+
// 固定宫位:天伤恒在交友,天使恒在疾厄
|
|
314
|
+
put(soulIndex - 7, '天伤', '杂曜');
|
|
315
|
+
put(soulIndex - 5, '天使', '杂曜');
|
|
316
|
+
|
|
317
|
+
// 生年四化
|
|
318
|
+
const mutagenMap = MUTAGEN[yearStem] ?? {};
|
|
319
|
+
for (const list of starsByBranch) {
|
|
320
|
+
for (const star of list) {
|
|
321
|
+
if (mutagenMap[star.name]) star.mutagen = mutagenMap[star.name];
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 十二宫:命宫起逆布
|
|
326
|
+
const palaceNames = [];
|
|
327
|
+
for (let i = 0; i < 12; i++) palaceNames[fix(soulIndex - i)] = PALACE_NAMES[i];
|
|
328
|
+
|
|
329
|
+
// 大限:阳男阴女顺行、阴男阳女逆行
|
|
330
|
+
const yearYang = STEMS.indexOf(yearStem) % 2 === 0;
|
|
331
|
+
const forward = (yearYang && gender === 'male') || (!yearYang && gender === 'female');
|
|
332
|
+
const startAge = bureau.number;
|
|
333
|
+
|
|
334
|
+
// 十二神(长生/博士 随大限顺逆,将前/岁前 恒顺行)
|
|
335
|
+
const csStart = CHANGSHENG_START[bureau.number];
|
|
336
|
+
const shenshaAt = (branch) => {
|
|
337
|
+
const d = forward ? 1 : -1;
|
|
338
|
+
return {
|
|
339
|
+
changsheng: CHANGSHENG_NAMES[fix(d * (branch - csStart))],
|
|
340
|
+
boshi: BOSHI_NAMES[fix(d * (branch - lucun))],
|
|
341
|
+
jiangqian: JIANGQIAN_NAMES[fix(branch - JIANGXING[triad])],
|
|
342
|
+
suiqi: SUIQI_NAMES[fix(branch - yb)],
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const palaces = [];
|
|
347
|
+
for (let i = 0; i < 12; i++) {
|
|
348
|
+
const branch = fix(soulIndex - i);
|
|
349
|
+
const stem = (yinStem + fix(branch - 2)) % 10;
|
|
350
|
+
const stepIndex = forward ? fix(branch - soulIndex) : fix(soulIndex - branch);
|
|
351
|
+
const dxStart = startAge + stepIndex * 10;
|
|
352
|
+
const gz = STEMS[stem] + BRANCHES[branch];
|
|
353
|
+
const pal = MUTAGEN[STEMS[stem]] ?? {};
|
|
354
|
+
const selfHua = starsByBranch[branch]
|
|
355
|
+
.filter((s) => pal[s.name])
|
|
356
|
+
.map((s) => ({ star: s.name, type: pal[s.name] }));
|
|
357
|
+
palaces.push({
|
|
358
|
+
palace: palaceNames[branch],
|
|
359
|
+
branch: BRANCHES[branch],
|
|
360
|
+
stem: STEMS[stem],
|
|
361
|
+
ganzhi: gz,
|
|
362
|
+
isSoul: branch === soulIndex,
|
|
363
|
+
isBody: branch === bodyIndex,
|
|
364
|
+
stars: starsByBranch[branch].map((s) => ({ ...s })),
|
|
365
|
+
shensha: shenshaAt(branch),
|
|
366
|
+
selfHua,
|
|
367
|
+
daxian: {
|
|
368
|
+
start: dxStart,
|
|
369
|
+
end: dxStart + 9,
|
|
370
|
+
stem: STEMS[stem],
|
|
371
|
+
mutagen: Object.entries(MUTAGEN[STEMS[stem]] ?? {}).map(([s, k]) => `${s}化${k}`),
|
|
372
|
+
},
|
|
373
|
+
xiaoxian: { age: xiaoxianAge(branch, triad, gender) },
|
|
374
|
+
sanfang: [branch, branch + 4, branch + 8, branch + 6]
|
|
375
|
+
.map((x) => palaceNames[fix(x)]),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 昌曲专论:与丑未的关系、是否入命三方、吸收面的清杂
|
|
380
|
+
const changqu = (() => {
|
|
381
|
+
const set = new Set([wenchang, wenqu]);
|
|
382
|
+
let relation;
|
|
383
|
+
if (wenchang === wenqu) relation = `同宫于${BRANCHES[wenchang]}`;
|
|
384
|
+
else if (set.has(0) && set.has(2)) relation = '夹丑(子·寅)';
|
|
385
|
+
else if (set.has(6) && set.has(8)) relation = '夹未(午·申)';
|
|
386
|
+
else if (set.has(5) && set.has(9)) relation = '丑三合(巳·酉)';
|
|
387
|
+
else if (set.has(11) && set.has(3)) relation = '未三合(亥·卯)';
|
|
388
|
+
else relation = '与丑未无关';
|
|
389
|
+
|
|
390
|
+
const sanfang = [soulIndex, fix(soulIndex + 4), fix(soulIndex + 8), fix(soulIndex + 6)];
|
|
391
|
+
const pick = (branch, name) => {
|
|
392
|
+
const stars = starsByBranch[branch];
|
|
393
|
+
const self = stars.find((s) => s.name === name) || {};
|
|
394
|
+
return {
|
|
395
|
+
branch: BRANCHES[branch],
|
|
396
|
+
palace: palaceNames[branch],
|
|
397
|
+
mutagen: self.mutagen ?? null,
|
|
398
|
+
sha: stars.filter((s) => s.type === '煞星').map((s) => s.name),
|
|
399
|
+
};
|
|
400
|
+
};
|
|
401
|
+
const wc = pick(wenchang, '文昌');
|
|
402
|
+
const wq = pick(wenqu, '文曲');
|
|
403
|
+
const jiBranches = [wc, wq].filter((x) => x.mutagen === '忌').map((x) => x.branch);
|
|
404
|
+
const shaAll = [...new Set([...wc.sha, ...wq.sha])];
|
|
405
|
+
const tone = jiBranches.length
|
|
406
|
+
? { label: '杂', reason: `昌曲化忌于${jiBranches.join('、')}` }
|
|
407
|
+
: shaAll.length >= 2
|
|
408
|
+
? { label: '杂', reason: `昌曲两宫会煞 ${shaAll.join('、')}` }
|
|
409
|
+
: { label: '清', reason: '昌曲无化忌、不明显会煞' };
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
relation,
|
|
413
|
+
inSoulSanfang: sanfang.includes(wenchang) || sanfang.includes(wenqu),
|
|
414
|
+
wenchang: wc,
|
|
415
|
+
wenqu: wq,
|
|
416
|
+
absorb: tone,
|
|
417
|
+
};
|
|
418
|
+
})();
|
|
419
|
+
|
|
420
|
+
// 流年(当年起 12 年)
|
|
421
|
+
const thisYear = new Date().getFullYear();
|
|
422
|
+
const liunian = [];
|
|
423
|
+
for (let y = thisYear; y < thisYear + 12; y++) {
|
|
424
|
+
const gz = ganzhiName((((y - 4) % 60) + 60) % 60);
|
|
425
|
+
const b = BRANCHES.indexOf(gz.branch);
|
|
426
|
+
const lu = LUCUN[gz.stem];
|
|
427
|
+
const age = y - lunarRaw.lunarYear + 1;
|
|
428
|
+
const xxAge = ((age - 1) % 12) + 1;
|
|
429
|
+
const xxPalace = palaces.find((p) => p.xiaoxian.age === xxAge);
|
|
430
|
+
const dxPalace = palaces.find((p) => age >= p.daxian.start && age <= p.daxian.end);
|
|
431
|
+
liunian.push({
|
|
432
|
+
year: y,
|
|
433
|
+
ganzhi: gz.name,
|
|
434
|
+
age,
|
|
435
|
+
lifePalace: { branch: gz.branch, palace: palaceNames[b] },
|
|
436
|
+
doujun: BRANCHES[fix(b - (lunarMonth - 1) + hb)],
|
|
437
|
+
sanfang: [fix(b), fix(b + 4), fix(b + 8), fix(b + 6)].map((x) => palaceNames[x]),
|
|
438
|
+
liulu: BRANCHES[lu],
|
|
439
|
+
liuyang: BRANCHES[fix(lu + 1)],
|
|
440
|
+
liutuo: BRANCHES[fix(lu - 1)],
|
|
441
|
+
liukui: BRANCHES[KUI_YUE[gz.stem][0]],
|
|
442
|
+
liuyue: BRANCHES[KUI_YUE[gz.stem][1]],
|
|
443
|
+
mutagen: Object.entries(MUTAGEN[gz.stem] ?? {}).map(([s, k]) => `${s}化${k}`),
|
|
444
|
+
daxian: dxPalace
|
|
445
|
+
? { palace: dxPalace.palace, start: dxPalace.daxian.start, end: dxPalace.daxian.end }
|
|
446
|
+
: null,
|
|
447
|
+
xiaoxian: xxPalace ? { age: xxAge, palace: xxPalace.palace } : null,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const chart = {
|
|
452
|
+
system: 'ziwei',
|
|
453
|
+
input: {
|
|
454
|
+
year: cy, month: cm, day: cd, hour: ch, minute: cmin,
|
|
455
|
+
gender,
|
|
456
|
+
leapAsNext: Boolean(input.leapAsNext),
|
|
457
|
+
longitude: longitude ?? null,
|
|
458
|
+
address: input.address || null,
|
|
459
|
+
geo,
|
|
460
|
+
trueSolar: useTrueSolar,
|
|
461
|
+
},
|
|
462
|
+
solarTime: {
|
|
463
|
+
civilText: `${cy}-${String(cm).padStart(2, '0')}-${String(cd).padStart(2, '0')} ${String(ch).padStart(2, '0')}:${String(cmin).padStart(2, '0')}`,
|
|
464
|
+
trueSolarText: formatJd(solar.jd),
|
|
465
|
+
offsetMinutes: solar.offsetMinutes,
|
|
466
|
+
note: solar.note,
|
|
467
|
+
},
|
|
468
|
+
lunar: {
|
|
469
|
+
year: lunarRaw.lunarYear,
|
|
470
|
+
month: lunarMonth,
|
|
471
|
+
day: lunarDay,
|
|
472
|
+
isLeap: lunarRaw.isLeap,
|
|
473
|
+
monthStart: lunarRaw.monthStart,
|
|
474
|
+
...(lunarRaw.isLeap ? { note: '闰月按本月起盘(可用 leapAsNext 改为按次月)' } : {}),
|
|
475
|
+
},
|
|
476
|
+
hourBranch,
|
|
477
|
+
yearGanzhi: yearGanzhi.name,
|
|
478
|
+
soul: {
|
|
479
|
+
palace: '命宫',
|
|
480
|
+
branch: BRANCHES[soulIndex],
|
|
481
|
+
stem: STEMS[soulStem],
|
|
482
|
+
ganzhi: STEMS[soulStem] + BRANCHES[soulIndex],
|
|
483
|
+
nayin: soulNayin.name,
|
|
484
|
+
},
|
|
485
|
+
body: { palace: '身宫', branch: BRANCHES[bodyIndex], lands: palaceNames[bodyIndex] },
|
|
486
|
+
lifeMaster: MING_ZHU[BRANCHES[soulIndex]],
|
|
487
|
+
bodyMaster: SHEN_ZHU[BRANCHES[yb]],
|
|
488
|
+
doujun: {
|
|
489
|
+
zinian: BRANCHES[fix(hb - (lunarMonth - 1))],
|
|
490
|
+
rule: '流年支起正月,逆数至生月,再起子时顺数至生时',
|
|
491
|
+
},
|
|
492
|
+
bureau: { name: bureau.name, number: bureau.number, element: soulNayin.element },
|
|
493
|
+
ziwei: BRANCHES[ziwei],
|
|
494
|
+
tianfu: BRANCHES[tianfu],
|
|
495
|
+
changqu,
|
|
496
|
+
palaces,
|
|
497
|
+
liunian,
|
|
498
|
+
daxian: {
|
|
499
|
+
direction: forward ? '顺行' : '逆行',
|
|
500
|
+
rule: `${yearYang ? '阳' : '阴'}年${gender === 'male' ? '男' : '女'}${forward ? '顺行' : '逆行'}`,
|
|
501
|
+
startAge,
|
|
502
|
+
},
|
|
503
|
+
xiaoxianRule: {
|
|
504
|
+
start: XIAOXIAN_START[gender === 'female' ? 'female' : 'male'][triad],
|
|
505
|
+
direction: gender === 'male' ? '顺行' : '逆行',
|
|
506
|
+
note: '小限一岁起宫按年支三合,男顺女逆,每宫一岁,十二年一周',
|
|
507
|
+
},
|
|
508
|
+
rules: {
|
|
509
|
+
ziweiPlacement: '借数法:局数除日数取商,商数自寅宫顺行,借数奇逆偶顺',
|
|
510
|
+
mutagen: '生年四化用中州派常用表;庚干各派有异(有以天同化科、太阴化忌者)',
|
|
511
|
+
leapMonth: '闰月默认按本月,可切换为按次月',
|
|
512
|
+
auxStars: '火铃按年支三合起点顺数至生时;羊陀夹禄存;魁钺按年干;地空亥起子时逆行、地劫顺行;三台八座随辅弼;恩光天贵随昌曲退一位;台辅封诰按时支',
|
|
513
|
+
shensha: '长生、博士随大限顺逆;将前、岁前恒顺行;天伤恒在交友、天使恒在疾厄',
|
|
514
|
+
masters: '命主按命宫地支,身主按生年地支',
|
|
515
|
+
liunian: '流年命宫取该年地支宫;三方四正为本宫与 +4/+8/+6;并给该年所属大限与小限',
|
|
516
|
+
changqu: '昌曲落宫只由时支决定,故与丑未的关系(夹/三合/同宫/无关)是结构性结论;absorb 的「清/杂」是启发式标记,依据昌曲化忌与会煞,不是断语',
|
|
517
|
+
},
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
chart.patterns = detectPatterns(chart);
|
|
521
|
+
chart.daxianList = daxianList(chart);
|
|
522
|
+
enrichLiunian(chart);
|
|
523
|
+
// 流月/流日/流时:默认按本机当前时刻,可用 asOf 指定(如 "2026-10-22T16:00")
|
|
524
|
+
chart.flow = flowOf(chart, input.asOf);
|
|
525
|
+
chart.patternSummary = {
|
|
526
|
+
count: chart.patterns.length,
|
|
527
|
+
good: chart.patterns.filter((p) => p.tone === 'good').map((p) => p.name),
|
|
528
|
+
bad: chart.patterns.filter((p) => p.tone === 'bad').map((p) => p.name),
|
|
529
|
+
note: '格局只描述结构,不作吉凶断语;同一命盘可并存吉格与忌格',
|
|
530
|
+
};
|
|
531
|
+
return chart;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ── 格局识别(规则写在 evidence 里,便于复核)────────────────────
|
|
535
|
+
|
|
536
|
+
function starBranchOf(chart, name) {
|
|
537
|
+
const p = chart.palaces.find((x) => x.stars.some((s) => s.name === name));
|
|
538
|
+
return p ? BRANCHES.indexOf(p.branch) : -1;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function palaceAt(chart, branchIndex) {
|
|
542
|
+
const b = fix(branchIndex);
|
|
543
|
+
return chart.palaces.find((p) => BRANCHES.indexOf(p.branch) === b);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export function detectPatterns(chart) {
|
|
547
|
+
const soul = chart.palaces.find((p) => p.isSoul);
|
|
548
|
+
const soulIdx = BRANCHES.indexOf(soul.branch);
|
|
549
|
+
const sanfang = [soulIdx, fix(soulIdx + 4), fix(soulIdx + 8), fix(soulIdx + 6)];
|
|
550
|
+
const starsIn = (indices) => new Set(indices.flatMap((b) => palaceAt(chart, b)?.stars.map((s) => s.name) ?? []));
|
|
551
|
+
const near = starsIn(sanfang);
|
|
552
|
+
const soulStars = soul.stars.filter((s) => s.type === '主星').map((s) => s.name);
|
|
553
|
+
const out = [];
|
|
554
|
+
|
|
555
|
+
const add = (name, evidence) => out.push({ name, evidence });
|
|
556
|
+
|
|
557
|
+
if (soulStars.length === 0) add('命无正曜', '命宫无十四主星,需借对宫安星');
|
|
558
|
+
if (soulStars.some((s) => ['七杀', '破军', '贪狼'].includes(s))) {
|
|
559
|
+
add('杀破狼', `命宫坐 ${soulStars.filter((s) => ['七杀', '破军', '贪狼'].includes(s)).join('、')}`);
|
|
560
|
+
}
|
|
561
|
+
const jyTL = ['天机', '太阴', '天同', '天梁'];
|
|
562
|
+
if (jyTL.every((s) => near.has(s)) && soulStars.some((s) => jyTL.includes(s))) {
|
|
563
|
+
add('机月同梁', '命宫三方四正会齐天机、太阴、天同、天梁');
|
|
564
|
+
}
|
|
565
|
+
if (soulIdx === 11 && starBranchOf(chart, '太阴') === 11) add('月朗天门', '太阴在亥守命');
|
|
566
|
+
if (soulIdx === 3 && starBranchOf(chart, '太阳') === 3) add('日出扶桑', '太阳在卯守命');
|
|
567
|
+
if (near.has('天府') && near.has('天相')) add('府相朝垣', '命宫三方四正见天府、天相');
|
|
568
|
+
|
|
569
|
+
const lu = starBranchOf(chart, '禄存');
|
|
570
|
+
const ma = starBranchOf(chart, '天马');
|
|
571
|
+
if (lu >= 0 && ma >= 0 && (lu === ma || fix(lu - ma) === 6)) add('禄马交驰', `禄存在${chart.ziwei && BRANCHES[lu]},天马在${BRANCHES[ma]}`);
|
|
572
|
+
|
|
573
|
+
const huo = starBranchOf(chart, '火星');
|
|
574
|
+
const ling = starBranchOf(chart, '铃星');
|
|
575
|
+
if (fix(soulIdx - 1) === huo && fix(soulIdx + 1) === ling) add('火铃夹命', '火星在命宫前一宫、铃星在后一宫夹命');
|
|
576
|
+
if (fix(soulIdx - 1) === ling && fix(soulIdx + 1) === huo) add('火铃夹命', '铃星与火星夹命');
|
|
577
|
+
|
|
578
|
+
const ji = chart.palaces.find((p) => p.stars.some((s) => s.mutagen === '忌'));
|
|
579
|
+
if (ji) {
|
|
580
|
+
const jb = BRANCHES.indexOf(ji.branch);
|
|
581
|
+
if (fix(jb - 1) === starBranchOf(chart, '擎羊') && fix(jb + 1) === starBranchOf(chart, '陀罗')) {
|
|
582
|
+
add('羊陀夹忌', `化忌在${ji.branch},擎羊陀罗夹之`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// 铃昌陀武:四星会于命宫三方四正
|
|
587
|
+
const lctw = ['铃星', '文昌', '陀罗', '武曲'].map((n) => starBranchOf(chart, n));
|
|
588
|
+
if (lctw.every((b) => b >= 0) && lctw.every((b) => sanfang.includes(fix(b)))) {
|
|
589
|
+
add('铃昌陀武', `铃${BRANCHES[lctw[0]]}·昌${BRANCHES[lctw[1]]}·陀${BRANCHES[lctw[2]]}·武${BRANCHES[lctw[3]]} 会于命宫(${BRANCHES[soulIdx]})三方四正`);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const hualu = chart.palaces.find((p) => p.stars.some((s) => s.mutagen === '禄'));
|
|
593
|
+
if (hualu && lu >= 0) {
|
|
594
|
+
const hb2 = BRANCHES.indexOf(hualu.branch);
|
|
595
|
+
if (hb2 === lu || fix(hb2 - lu) === 6) add('双禄交流', `禄存在${BRANCHES[lu]},化禄在${hualu.branch}`);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// ── 以下为扩展格局(条件都是星位判断,evidence 写明落在哪宫)──
|
|
599
|
+
const inSoul = (name) => soul.stars.some((s) => s.name === name);
|
|
600
|
+
const nearStar = (name) => near.has(name);
|
|
601
|
+
const jia = (a, b) => {
|
|
602
|
+
const ba = starBranchOf(chart, a);
|
|
603
|
+
const bb = starBranchOf(chart, b);
|
|
604
|
+
return ba >= 0 && bb >= 0 && fix(ba + 1) === soulIdx && fix(bb - 1) === soulIdx;
|
|
605
|
+
};
|
|
606
|
+
const mutagensIn = (indices) => {
|
|
607
|
+
const set = new Set();
|
|
608
|
+
for (const b of indices) for (const s of (palaceAt(chart, b)?.stars ?? [])) if (s.mutagen) set.add(s.mutagen);
|
|
609
|
+
return set;
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
// 紫微系
|
|
613
|
+
if (inSoul('紫微') && inSoul('天府')) add('紫府同宫', '紫微、天府同守命宫');
|
|
614
|
+
if (nearStar('紫微') && nearStar('天府')) add('紫府朝垣', '命宫三方四正会紫微、天府');
|
|
615
|
+
if ((inSoul('紫微') || inSoul('天府')) && ['左辅', '右弼', '天魁', '天钺'].filter(nearStar).length >= 2) {
|
|
616
|
+
const l = ['左辅', '右弼', '天魁', '天钺'].filter(nearStar);
|
|
617
|
+
add('君臣庆会', `紫微/天府坐命,三方会${l.join('、')}`);
|
|
618
|
+
}
|
|
619
|
+
if (soulIdx === 6 && starBranchOf(chart, '紫微') === 6) add('极向离明', '紫微在午守命');
|
|
620
|
+
if ((soulIdx === 0 || soulIdx === 6) && inSoul('七杀')) add('七杀朝斗', `七杀在${BRANCHES[soulIdx]}守命`);
|
|
621
|
+
if ((soulIdx === 0 || soulIdx === 6) && inSoul('破军')) add('英星入庙', `破军在${BRANCHES[soulIdx]}守命`);
|
|
622
|
+
if ((soulIdx === 0 || soulIdx === 6) && inSoul('巨门')) add('石中隐玉', `巨门在${BRANCHES[soulIdx]}守命`);
|
|
623
|
+
if (starBranchOf(chart, '巨门') >= 0 && starBranchOf(chart, '巨门') === starBranchOf(chart, '太阳')) {
|
|
624
|
+
add('巨日同宫', `巨门太阳同宫于${BRANCHES[starBranchOf(chart, '巨门')]}`);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
// 日月
|
|
628
|
+
if (jia('太阳', '太阴')) add('日月夹命', '太阳、太阴夹命宫');
|
|
629
|
+
if (starBranchOf(chart, '太阳') >= 0 && starBranchOf(chart, '太阴') >= 0
|
|
630
|
+
&& fix(starBranchOf(chart, '太阳') - starBranchOf(chart, '太阴')) === 6) {
|
|
631
|
+
add('日月对照', `太阳在${BRANCHES[starBranchOf(chart, '太阳')]},太阴在${BRANCHES[starBranchOf(chart, '太阴')]},隔宫对照`);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// 夹局
|
|
635
|
+
if (jia('左辅', '右弼')) add('左右夹命', '左辅、右弼夹命宫');
|
|
636
|
+
if (jia('文昌', '文曲')) add('昌曲夹命', '文昌、文曲夹命宫');
|
|
637
|
+
if (jia('天魁', '天钺')) add('魁钺夹命', '天魁、天钺夹命宫');
|
|
638
|
+
if (jia('擎羊', '陀罗')) add('羊陀夹命', '擎羊、陀罗夹命宫');
|
|
639
|
+
|
|
640
|
+
// 四化
|
|
641
|
+
const muts = mutagensIn(sanfang);
|
|
642
|
+
if (muts.has('禄') && muts.has('权') && muts.has('科')) add('三奇嘉会', '化禄、化权、化科会于命宫三方四正');
|
|
643
|
+
const luStars = ['禄存', '化禄'].filter((n) => n === '禄存' ? nearStar(n) : muts.has('禄'));
|
|
644
|
+
if (luStars.length === 2) add('双禄朝垣', '禄存与化禄同会命宫三方四正');
|
|
645
|
+
if (near.has('地空') && near.has('地劫') && (inSoul('地空') || inSoul('地劫'))) add('命里逢空', '地空、地劫会命');
|
|
646
|
+
|
|
647
|
+
// 煞忌结构
|
|
648
|
+
if (soulIdx === 6 && starBranchOf(chart, '擎羊') === 6) add('马头带箭', '擎羊在午守命');
|
|
649
|
+
if (soulIdx === 4 || soulIdx === 10) add('天罗地网', `命宫在${BRANCHES[soulIdx]},辰戌为天罗地网`);
|
|
650
|
+
const lianzhen = starBranchOf(chart, '廉贞');
|
|
651
|
+
const tianxiang = starBranchOf(chart, '天相');
|
|
652
|
+
const qingyang = starBranchOf(chart, '擎羊');
|
|
653
|
+
if (lianzhen >= 0 && qingyang >= 0 && tianxiang >= 0
|
|
654
|
+
&& fix(lianzhen + 1) === tianxiang && fix(qingyang - 1) === tianxiang) {
|
|
655
|
+
add('刑囚夹印', '廉贞与擎羊夹天相(刑囚夹印)');
|
|
656
|
+
}
|
|
657
|
+
if (near.has('贪狼') && (near.has('火星') || near.has('铃星'))) {
|
|
658
|
+
add('火贪铃贪', `贪狼与${near.has('火星') ? '火星' : '铃星'}会于命宫三方四正`);
|
|
659
|
+
}
|
|
660
|
+
if (starBranchOf(chart, '武曲') >= 0 && starBranchOf(chart, '武曲') === starBranchOf(chart, '贪狼')) {
|
|
661
|
+
add('武贪同行', `武曲贪狼同宫于${BRANCHES[starBranchOf(chart, '武曲')]}`);
|
|
662
|
+
}
|
|
663
|
+
if (soulIdx === 0 && inSoul('贪狼')) add('泛水桃花', '贪狼在子守命');
|
|
664
|
+
|
|
665
|
+
// 同名的结构只保留第一条(evidence 已足够说明),并标注倾向
|
|
666
|
+
const seen = new Set();
|
|
667
|
+
return out
|
|
668
|
+
.filter((p) => (seen.has(p.name) ? false : (seen.add(p.name), true)))
|
|
669
|
+
.map((p) => ({ ...p, tone: PATTERN_TONE[p.name] || 'neutral' }));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const PATTERN_TONE = {
|
|
673
|
+
紫府同宫: 'good', 紫府朝垣: 'good', 君臣庆会: 'good', 极向离明: 'good',
|
|
674
|
+
七杀朝斗: 'good', 英星入庙: 'good', 石中隐玉: 'good', 巨日同宫: 'good',
|
|
675
|
+
日月夹命: 'good', 日月对照: 'good', 左右夹命: 'good', 昌曲夹命: 'good',
|
|
676
|
+
魁钺夹命: 'good', 三奇嘉会: 'good', 双禄朝垣: 'good', 府相朝垣: 'good',
|
|
677
|
+
禄马交驰: 'good', 月朗天门: 'good', 日出扶桑: 'good', 机月同梁: 'good',
|
|
678
|
+
武贪同行: 'neutral', 命无正曜: 'neutral', 杀破狼: 'neutral', 双禄交流: 'neutral',
|
|
679
|
+
火贪铃贪: 'neutral', 火铃夹命: 'bad', 羊陀夹命: 'bad', 羊陀夹忌: 'bad',
|
|
680
|
+
铃昌陀武: 'bad', 命里逢空: 'bad', 马头带箭: 'bad', 天罗地网: 'bad',
|
|
681
|
+
刑囚夹印: 'bad', 泛水桃花: 'bad',
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// ── 大限一览:宫干四化落宫、三方主星 ──────────────────────────
|
|
685
|
+
|
|
686
|
+
export function daxianList(chart) {
|
|
687
|
+
const mainOf = (palaceNames) => {
|
|
688
|
+
const set = new Set();
|
|
689
|
+
for (const name of palaceNames) {
|
|
690
|
+
const p = chart.palaces.find((x) => x.palace === name);
|
|
691
|
+
for (const s of (p?.stars ?? [])) if (s.type === '主星') set.add(s.name);
|
|
692
|
+
}
|
|
693
|
+
return [...set];
|
|
694
|
+
};
|
|
695
|
+
return chart.palaces
|
|
696
|
+
.map((p) => ({
|
|
697
|
+
palace: p.palace,
|
|
698
|
+
branch: p.branch,
|
|
699
|
+
stem: p.stem,
|
|
700
|
+
ganzhi: p.ganzhi,
|
|
701
|
+
ages: [p.daxian.start, p.daxian.end],
|
|
702
|
+
mutagen: p.daxian.mutagen,
|
|
703
|
+
selfHua: p.selfHua,
|
|
704
|
+
sanfang: p.sanfang,
|
|
705
|
+
mainStars: mainOf(p.sanfang),
|
|
706
|
+
mutagenHits: Object.entries(MUTAGEN[p.stem] ?? {}).map(([star, type]) => {
|
|
707
|
+
const b = starBranchOf(chart, star);
|
|
708
|
+
return { star, type, branch: b >= 0 ? BRANCHES[b] : null, palace: b >= 0 ? chart.palaces.find((x) => x.branch === BRANCHES[b])?.palace ?? null : null };
|
|
709
|
+
}),
|
|
710
|
+
}))
|
|
711
|
+
.sort((a, b) => a.ages[0] - b.ages[0]);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// ── 流年四化落宫 ────────────────────────────────────────────
|
|
715
|
+
|
|
716
|
+
export function enrichLiunian(chart) {
|
|
717
|
+
for (const ln of chart.liunian) {
|
|
718
|
+
const stem = ln.ganzhi[0];
|
|
719
|
+
ln.mutagenHits = Object.entries(MUTAGEN[stem] ?? {}).map(([star, type]) => {
|
|
720
|
+
const b = starBranchOf(chart, star);
|
|
721
|
+
return {
|
|
722
|
+
star,
|
|
723
|
+
type,
|
|
724
|
+
branch: b >= 0 ? BRANCHES[b] : null,
|
|
725
|
+
palace: b >= 0 ? chart.palaces.find((x) => x.branch === BRANCHES[b])?.palace ?? null : null,
|
|
726
|
+
};
|
|
727
|
+
});
|
|
728
|
+
ln.sanfangMainStars = [...new Set(ln.sanfang.flatMap((name) => {
|
|
729
|
+
const p = chart.palaces.find((x) => x.palace === name);
|
|
730
|
+
return (p?.stars ?? []).filter((s) => s.type === '主星').map((s) => s.name);
|
|
731
|
+
}))];
|
|
732
|
+
}
|
|
733
|
+
return chart.liunian;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// ── 流月 / 流日 / 流时 ──────────────────────────────────────
|
|
737
|
+
// 一级一级往下推:流年斗君起正月 → 顺数至该农历月;流月命宫起初一 → 顺数至该农历日;
|
|
738
|
+
// 流日命宫起子时 → 顺数至该时辰。四化一律取落点宫的原局宫干。
|
|
739
|
+
// 这条链是紫微看「这个月/这一天/这个时辰」的标准入口,缺了它就只能看到流年。
|
|
740
|
+
|
|
741
|
+
export function asOfParts(v) {
|
|
742
|
+
if (!v) return null;
|
|
743
|
+
if (typeof v === 'object') return v;
|
|
744
|
+
const m = String(v).match(/^(\d{4})-(\d{1,2})-(\d{1,2})(?:[T ](\d{1,2}):(\d{1,2}))?$/);
|
|
745
|
+
if (!m) return null;
|
|
746
|
+
return {
|
|
747
|
+
year: Number(m[1]), month: Number(m[2]), day: Number(m[3]),
|
|
748
|
+
hour: m[4] === undefined ? 12 : Number(m[4]),
|
|
749
|
+
minute: m[5] === undefined ? 0 : Number(m[5]),
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export function flowOf(chart, asOf) {
|
|
754
|
+
const now = new Date();
|
|
755
|
+
const a = asOfParts(asOf) || {};
|
|
756
|
+
const Y = a.year || now.getFullYear();
|
|
757
|
+
const M = a.month || now.getMonth() + 1;
|
|
758
|
+
const D = a.day || now.getDate();
|
|
759
|
+
const H = a.hour === undefined ? now.getHours() : a.hour;
|
|
760
|
+
const MI = a.minute === undefined ? now.getMinutes() : a.minute;
|
|
761
|
+
|
|
762
|
+
const lr = solarToLunar(Y, M, D);
|
|
763
|
+
const leapAsNext = Boolean(chart.input && chart.input.leapAsNext);
|
|
764
|
+
const natalMonth = chart.lunar.isLeap && leapAsNext ? (chart.lunar.month % 12) + 1 : chart.lunar.month;
|
|
765
|
+
const natalHB = BRANCHES.indexOf(chart.hourBranch);
|
|
766
|
+
const hbNow = hourBranchIndex(H, MI);
|
|
767
|
+
|
|
768
|
+
const gz = ganzhiName((((lr.lunarYear - 4) % 60) + 60) % 60);
|
|
769
|
+
const yB = BRANCHES.indexOf(gz.branch);
|
|
770
|
+
const doujun = fix(yB - (natalMonth - 1) + natalHB);
|
|
771
|
+
const mIdx = fix(doujun + (lr.month - 1));
|
|
772
|
+
const dIdx = fix(mIdx + (lr.day - 1));
|
|
773
|
+
const hIdx = fix(dIdx + hbNow);
|
|
774
|
+
|
|
775
|
+
const point = (idx) => {
|
|
776
|
+
const br = BRANCHES[idx];
|
|
777
|
+
const p = chart.palaces.find((x) => x.branch === br);
|
|
778
|
+
const stem = p ? p.stem : null;
|
|
779
|
+
const mutagenHits = stem
|
|
780
|
+
? Object.entries(MUTAGEN[stem] ?? {}).map(([star, type]) => {
|
|
781
|
+
const b = starBranchOf(chart, star);
|
|
782
|
+
return {
|
|
783
|
+
star,
|
|
784
|
+
type,
|
|
785
|
+
branch: b >= 0 ? BRANCHES[b] : null,
|
|
786
|
+
palace: b >= 0 ? chart.palaces.find((x) => x.branch === BRANCHES[b])?.palace ?? null : null,
|
|
787
|
+
};
|
|
788
|
+
})
|
|
789
|
+
: [];
|
|
790
|
+
const tri = [idx, fix(idx + 4), fix(idx + 8), fix(idx + 6)];
|
|
791
|
+
const sanfang = tri.map((x) => chart.palaces.find((q) => q.branch === BRANCHES[x])?.palace ?? null);
|
|
792
|
+
const sanfangMainStars = [...new Set(tri.flatMap((x) => {
|
|
793
|
+
const q = chart.palaces.find((z) => z.branch === BRANCHES[x]);
|
|
794
|
+
return (q?.stars ?? []).filter((s) => s.type === '主星').map((s) => s.name);
|
|
795
|
+
}))];
|
|
796
|
+
return {
|
|
797
|
+
branch: br,
|
|
798
|
+
palace: p ? p.palace : null,
|
|
799
|
+
stem,
|
|
800
|
+
ganzhi: p ? p.ganzhi : null,
|
|
801
|
+
mutagenHits,
|
|
802
|
+
sanfang,
|
|
803
|
+
sanfangMainStars,
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const two = (n) => String(n).padStart(2, '0');
|
|
808
|
+
return {
|
|
809
|
+
asOf: {
|
|
810
|
+
solar: `${Y}-${two(M)}-${two(D)} ${two(H)}:${two(MI)}`,
|
|
811
|
+
lunar: `${lr.lunarYear}年${lr.isLeap ? '闰' : ''}${lr.month}月${lr.day}日${BRANCHES[hbNow]}时`,
|
|
812
|
+
yearGanzhi: gz.name,
|
|
813
|
+
hourBranch: BRANCHES[hbNow],
|
|
814
|
+
},
|
|
815
|
+
year: { ...point(yB), doujun: BRANCHES[doujun] },
|
|
816
|
+
month: { index: lr.month, ...point(mIdx) },
|
|
817
|
+
day: { index: lr.day, ...point(dIdx) },
|
|
818
|
+
hour: { hourIndex: hbNow, ...point(hIdx) },
|
|
819
|
+
rules: '流月命宫自流年斗君(该年支起正月逆数至生月,再起子时顺数至生时)起正月,顺行至该农历月;流日自流月命宫起初一,顺行至该农历日;流时自流日命宫起子时,顺行至该时辰;四化取落点宫的原局宫干',
|
|
820
|
+
};
|
|
821
|
+
}
|