From 001db8409da64e09ec0d288b5b543a41ae2860f7 Mon Sep 17 00:00:00 2001 From: Sylar Date: Sun, 17 Dec 2023 19:43:57 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=AA=84=20flyinstar=20judgement=20feature?= =?UTF-8?q?=20#143?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 12 ++ package.json | 2 +- src/__tests__/astro/astro.test.ts | 48 +++---- src/__tests__/astro/palace.test.ts | 30 +++++ src/astro/FunctionalAstrolabe.ts | 6 +- src/astro/FunctionalHoroscope.ts | 19 +-- src/astro/FunctionalPalace.ts | 206 ++++++++++++++++++++++++++++- src/astro/analyzer.ts | 55 +++++--- 8 files changed, 324 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3a9adc1..7d62347b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ - 🛠️ 修复(fix) - 🧹 琐事(Chore) +## v2.1.0 + +- 🪄 功能(feature) + + 🇨🇳 + + - 宫位飞星判断 #143 + + 🇺🇸 + + - flyinstar judgement feature #143 + ## v2.0.8 - 🛠️ 修复(fix) diff --git a/package.json b/package.json index ad393c97..3f4216be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iztro", - "version": "2.0.8", + "version": "2.1.0", "description": "轻量级紫微斗数星盘生成库。可以通过出生年月日获取到紫微斗数星盘信息、生肖、星座等信息。A lightweight kit to astrolabe generator of The Purple Star Astrology (Zi Wei Dou Shu). The Purple Star Astrology(Zi Wei Dou Shu) is a Chinese ancient astrology. You're able to get your horoscope and personality from the astrolabe", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/__tests__/astro/astro.test.ts b/src/__tests__/astro/astro.test.ts index 9767c10d..8ba988a2 100644 --- a/src/__tests__/astro/astro.test.ts +++ b/src/__tests__/astro/astro.test.ts @@ -174,33 +174,33 @@ describe('Astrolabe', () => { const decadalSurpalaces = horoscope.surroundPalaces('命宫', 'decadal'); - expect(decadalSurpalaces.target).toHaveProperty('name', '夫妻'); - expect(decadalSurpalaces.target).toHaveProperty('heavenlyStem', '庚'); - expect(decadalSurpalaces.target).toHaveProperty('earthlyBranch', '辰'); - expect(decadalSurpalaces.opposite).toHaveProperty('name', '官禄'); - expect(decadalSurpalaces.opposite).toHaveProperty('heavenlyStem', '丙'); - expect(decadalSurpalaces.opposite).toHaveProperty('earthlyBranch', '戌'); - expect(decadalSurpalaces.career).toHaveProperty('name', '福德'); - expect(decadalSurpalaces.career).toHaveProperty('heavenlyStem', '甲'); - expect(decadalSurpalaces.career).toHaveProperty('earthlyBranch', '申'); - expect(decadalSurpalaces.wealth).toHaveProperty('name', '迁移'); - expect(decadalSurpalaces.wealth).toHaveProperty('heavenlyStem', '戊'); - expect(decadalSurpalaces.wealth).toHaveProperty('earthlyBranch', '子'); + expect(decadalSurpalaces?.target).toHaveProperty('name', '夫妻'); + expect(decadalSurpalaces?.target).toHaveProperty('heavenlyStem', '庚'); + expect(decadalSurpalaces?.target).toHaveProperty('earthlyBranch', '辰'); + expect(decadalSurpalaces?.opposite).toHaveProperty('name', '官禄'); + expect(decadalSurpalaces?.opposite).toHaveProperty('heavenlyStem', '丙'); + expect(decadalSurpalaces?.opposite).toHaveProperty('earthlyBranch', '戌'); + expect(decadalSurpalaces?.career).toHaveProperty('name', '福德'); + expect(decadalSurpalaces?.career).toHaveProperty('heavenlyStem', '甲'); + expect(decadalSurpalaces?.career).toHaveProperty('earthlyBranch', '申'); + expect(decadalSurpalaces?.wealth).toHaveProperty('name', '迁移'); + expect(decadalSurpalaces?.wealth).toHaveProperty('heavenlyStem', '戊'); + expect(decadalSurpalaces?.wealth).toHaveProperty('earthlyBranch', '子'); const originalSurpalaces = horoscope.surroundPalaces('夫妻', 'origin'); - expect(originalSurpalaces.target).toHaveProperty('name', '夫妻'); - expect(originalSurpalaces.target).toHaveProperty('heavenlyStem', '庚'); - expect(originalSurpalaces.target).toHaveProperty('earthlyBranch', '辰'); - expect(originalSurpalaces.opposite).toHaveProperty('name', '官禄'); - expect(originalSurpalaces.opposite).toHaveProperty('heavenlyStem', '丙'); - expect(originalSurpalaces.opposite).toHaveProperty('earthlyBranch', '戌'); - expect(originalSurpalaces.career).toHaveProperty('name', '福德'); - expect(originalSurpalaces.career).toHaveProperty('heavenlyStem', '甲'); - expect(originalSurpalaces.career).toHaveProperty('earthlyBranch', '申'); - expect(originalSurpalaces.wealth).toHaveProperty('name', '迁移'); - expect(originalSurpalaces.wealth).toHaveProperty('heavenlyStem', '戊'); - expect(originalSurpalaces.wealth).toHaveProperty('earthlyBranch', '子'); + expect(originalSurpalaces?.target).toHaveProperty('name', '夫妻'); + expect(originalSurpalaces?.target).toHaveProperty('heavenlyStem', '庚'); + expect(originalSurpalaces?.target).toHaveProperty('earthlyBranch', '辰'); + expect(originalSurpalaces?.opposite).toHaveProperty('name', '官禄'); + expect(originalSurpalaces?.opposite).toHaveProperty('heavenlyStem', '丙'); + expect(originalSurpalaces?.opposite).toHaveProperty('earthlyBranch', '戌'); + expect(originalSurpalaces?.career).toHaveProperty('name', '福德'); + expect(originalSurpalaces?.career).toHaveProperty('heavenlyStem', '甲'); + expect(originalSurpalaces?.career).toHaveProperty('earthlyBranch', '申'); + expect(originalSurpalaces?.wealth).toHaveProperty('name', '迁移'); + expect(originalSurpalaces?.wealth).toHaveProperty('heavenlyStem', '戊'); + expect(originalSurpalaces?.wealth).toHaveProperty('earthlyBranch', '子'); const yearlyPalace = horoscope.palace('命宫', 'yearly'); diff --git a/src/__tests__/astro/palace.test.ts b/src/__tests__/astro/palace.test.ts index 21a085ec..a1a562df 100644 --- a/src/__tests__/astro/palace.test.ts +++ b/src/__tests__/astro/palace.test.ts @@ -1,3 +1,4 @@ +import { astro } from '../..'; import { getFiveElementsClass, getSoulAndBody, getPalaceNames, getHoroscope } from '../../astro'; import { FiveElementsClass } from '../../data'; import { t } from '../../i18n'; @@ -101,4 +102,33 @@ describe('astro/palace', () => { [1, 13, 25, 37, 49, 61, 73], ]); }); + + test('fliesTo() & notFlyTo() & fliesOneOfTo()', () => { + const astrolabe = astro.bySolar('2017-12-4', 12, 'male'); + + expect(astrolabe.palace('命宫')?.fliesTo('兄弟', '忌')).toBeTruthy(); + expect(astrolabe.palace('命宫')?.notFlyTo('兄弟', '科')).toBeTruthy(); + expect(astrolabe.palace('田宅')?.fliesTo('福德', ['禄', '科'])).toBeTruthy(); + expect(astrolabe.palace('田宅')?.notFlyTo('福德', ['禄', '科'])).toBeFalsy(); + expect(astrolabe.palace('兄弟')?.fliesTo('夫妻', ['权', '科'])).toBeFalsy(); + expect(astrolabe.palace('兄弟')?.fliesOneOfTo('夫妻', ['权', '科'])).toBeTruthy(); + expect(astrolabe.palace('兄弟')?.fliesOneOfTo('夫妻', ['权', '禄'])).toBeFalsy(); + expect(astrolabe.palace('兄弟')?.notFlyTo('夫妻', ['权', '科'])).toBeFalsy(); + expect(astrolabe.palace('仆役')?.selfMutaged('科')).toBeTruthy(); + expect(astrolabe.palace('仆役')?.selfMutaged(['科', '权'])).toBeFalsy(); + expect(astrolabe.palace('仆役')?.selfMutagedOneOf(['科', '权'])).toBeTruthy(); + expect(astrolabe.palace('仆役')?.selfMutagedOneOf()).toBeTruthy(); + expect(astrolabe.palace('仆役')?.selfMutaged('权')).toBeFalsy(); + expect(astrolabe.palace('仆役')?.notSelfMutaged()).toBeFalsy(); + expect(astrolabe.palace('仆役')?.notSelfMutaged('权')).toBeTruthy(); + expect(astrolabe.palace('仆役')?.notSelfMutaged(['权', '科'])).toBeFalsy(); + + const palaces = astrolabe.palace('命宫')?.mutagedPlaces() ?? []; + + expect(palaces).toHaveLength(4); + expect(palaces[0]).toHaveProperty('name', '命宫'); + expect(palaces[1]).toHaveProperty('name', '迁移'); + expect(palaces[2]).toHaveProperty('name', '仆役'); + expect(palaces[3]).toHaveProperty('name', '兄弟'); + }); }); diff --git a/src/astro/FunctionalAstrolabe.ts b/src/astro/FunctionalAstrolabe.ts index 114050cf..32bda5d4 100644 --- a/src/astro/FunctionalAstrolabe.ts +++ b/src/astro/FunctionalAstrolabe.ts @@ -10,7 +10,7 @@ import { getPalace, getSurroundedPalaces } from './analyzer'; import { IFunctionalPalace } from './FunctionalPalace'; import { IFunctionalSurpalaces } from './FunctionalSurpalaces'; import { getPalaceNames } from './palace'; -import FunctionalHoroscope from './FunctionalHoroscope'; +import FunctionalHoroscope, { IFunctionalHoroscope } from './FunctionalHoroscope'; /** * 获取运限数据 @@ -28,7 +28,7 @@ const _getHoroscopeBySolarDate = ( $: FunctionalAstrolabe, targetDate: string | Date = new Date(), timeIndex?: number, -): FunctionalHoroscope => { +): IFunctionalHoroscope => { const _birthday = solar2lunar($.solarDate); const _date = solar2lunar(targetDate); const convertTimeIndex = timeToIndex(dayjs(targetDate).hour()); @@ -171,7 +171,7 @@ export interface IFunctionalAstrolabe extends Astrolabe { * @param timeIndex 时辰索引【可选】,默认会自动读取当前时间的时辰 * @returns 运限数据 */ - horoscope: (date?: string | Date, timeIndex?: number) => Horoscope; + horoscope: (date?: string | Date, timeIndex?: number) => IFunctionalHoroscope; /** * 通过星耀名称获取到当前星耀的对象实例 diff --git a/src/astro/FunctionalHoroscope.ts b/src/astro/FunctionalHoroscope.ts index 615efcce..6185331e 100644 --- a/src/astro/FunctionalHoroscope.ts +++ b/src/astro/FunctionalHoroscope.ts @@ -1,12 +1,12 @@ import { Horoscope, Scope } from '../data/types'; import { Mutagen, MutagenKey, PalaceName, StarKey, StarName, kot } from '../i18n'; import { IFunctionalAstrolabe } from './FunctionalAstrolabe'; -import { FunctionalSurpalaces } from './FunctionalSurpalaces'; -import FunctionalPalace from './FunctionalPalace'; +import { IFunctionalSurpalaces } from './FunctionalSurpalaces'; +import { IFunctionalPalace } from './FunctionalPalace'; import { mergeStars } from '../utils'; import { MUTAGEN } from '../data'; -const _getHoroscopePalaceIndex = ($: FunctionalHoroscope, scope: Scope, palaceName: PalaceName) => { +const _getHoroscopePalaceIndex = ($: IFunctionalHoroscope, scope: Scope, palaceName: PalaceName) => { let palaceIndex = -1; if (scope === 'origin') { @@ -27,14 +27,15 @@ const _getHoroscopePalaceIndex = ($: FunctionalHoroscope, scope: Scope, palaceNa }; export interface IFunctionalHoroscope extends Horoscope { + astrolabe: IFunctionalAstrolabe; /** * 获取小限宫位 * * @version v1.3.0 * - * @returns {FunctionalPalace | undefined} 小限宫位 + * @returns {IFunctionalPalace | undefined} 小限宫位 */ - agePalace: () => FunctionalPalace | undefined; + agePalace: () => IFunctionalPalace | undefined; /** * 获取运限宫位 @@ -43,9 +44,9 @@ export interface IFunctionalHoroscope extends Horoscope { * * @param palaceName 宫位名称 * @param scope 指定获取哪个运限的宫位 - * @returns {FunctionalPalace | undefined} 指定宫位 + * @returns {IFunctionalPalace | undefined} 指定宫位 */ - palace: (palaceName: PalaceName, scope: Scope) => FunctionalPalace | undefined; + palace: (palaceName: PalaceName, scope: Scope) => IFunctionalPalace | undefined; /** * 获取运限指定宫位的三方四正宫位 @@ -54,9 +55,9 @@ export interface IFunctionalHoroscope extends Horoscope { * * @param palaceName 宫位名称 * @param scope 指定获取哪个运限的宫位 - * @returns {FunctionalSurpalaces | undefined} 指定宫位的三方四正 + * @returns {IFunctionalSurpalaces | undefined} 指定宫位的三方四正 */ - surroundPalaces: (palaceName: PalaceName, scope: Scope) => FunctionalSurpalaces | undefined; + surroundPalaces: (palaceName: PalaceName, scope: Scope) => IFunctionalSurpalaces | undefined; /** * 判断在指定运限的宫位内是否包含流耀,需要全部包含才返回true diff --git a/src/astro/FunctionalPalace.ts b/src/astro/FunctionalPalace.ts index f0accacc..0f267fb7 100644 --- a/src/astro/FunctionalPalace.ts +++ b/src/astro/FunctionalPalace.ts @@ -1,6 +1,14 @@ import { Palace } from '../data/types'; -import { Mutagen, StarName } from '../i18n'; -import { hasMutagenInPlace, hasOneOfStars, hasStars, notHaveMutagenInPalce, notHaveStars } from './analyzer'; +import { Mutagen, PalaceName, StarName } from '../i18n'; +import { IFunctionalAstrolabe } from './FunctionalAstrolabe'; +import { + hasMutagenInPlace, + hasOneOfStars, + hasStars, + mutagensToStars, + notHaveMutagenInPalce, + notHaveStars, +} from './analyzer'; /** * 宫位类的接口定义。 @@ -71,6 +79,98 @@ export interface IFunctionalPalace extends Palace { * @returns {boolean} true | false */ isEmpty: (excludeStars?: StarName[]) => boolean; + + /** + * 给宫位设置星盘对象 + * + * @version v2.1.0 + * + * @param astro 星盘对象 + * @returns {void} + */ + setAstrolabe: (astro: IFunctionalAstrolabe) => void; + + /** + * 获取当前宫位所在的星盘对象 + * + * @version v2.1.0 + * + * @returns {IFunctionalAstrolabe} + */ + astrolabe: () => IFunctionalAstrolabe | undefined; + + /** + * 判断是否从源宫位飞化到目标宫位,四化可传入一个数组或者一个字符串,传入四化全部飞化到目标宫位即返回true + * + * @version v2.1.0 + * + * @param from 源宫位 + * @param to 目标宫位 + * @param withMutagens 四化(禄、权、科、忌) + * @returns {boolean} + */ + fliesTo: (to: number | PalaceName, withMutagens: Mutagen | Mutagen[]) => boolean; + + /** + * 判断是否从源宫位飞化其中一颗四化星到目标宫位,传入四化只要有一颗飞化到目标宫位即返回true + * + * @version v2.1.0 + * + * @param to 目标宫位 + * @param withMutagens 四化(禄、权、科、忌) + * @returns {boolean} + */ + fliesOneOfTo: (to: number | PalaceName, withMutagens: Mutagen[]) => boolean; + + /** + * 判断是否没有从源宫位飞化到目标宫位,四化可传入一个数组或者一个字符串,传入四化全部没有飞化到目标宫位才返回true + * + * @version v2.1.0 + * + * @param to 目标宫位 + * @param withMutagens 四化(禄、权、科、忌) + * @returns {boolean} + */ + notFlyTo: (to: number | PalaceName, withMutagens: Mutagen | Mutagen[]) => boolean; + + /** + * 判断宫位是否有自化,传入四化数组时需要全部满足才返回true + * + * @version v2.1.0 + * + * @param withMutagens 四化(禄、权、科、忌) + * @returns {boolean} + */ + selfMutaged: (withMutagens: Mutagen | Mutagen[]) => boolean; + + /** + * 判断宫位是否有自化,若不传入参数则会判断所有四化,满足一颗即返回true + * + * @version v2.1.0 + * + * @param withMutagens 四化(禄、权、科、忌) + * @returns {boolean} + */ + selfMutagedOneOf: (withMutagens?: Mutagen[]) => boolean; + + /** + * 判断宫位是否有自化,如果传入参数,则只判断传入的四化是否有自化,否则将会判断所有四化 + * + * @version v2.1.0 + * + * @param withMutagens 【可选】四化(禄、权、科、忌) + * @returns {boolean} + */ + notSelfMutaged: (withMutagens?: Mutagen | Mutagen[]) => boolean; + + /** + * 获取当前宫位产生四化的4个宫位数组,下标分别对【禄,权,科,忌】 + * + * @version v2.1.0 + * + * @returns {(IFunctionalPalace | undefined)[]} + */ + mutagedPlaces: () => (IFunctionalPalace | undefined)[]; } /** @@ -79,6 +179,7 @@ export interface IFunctionalPalace extends Palace { * 文档地址:https://docs.iztro.com/posts/palace.html#functionalastrolabe */ export default class FunctionalPalace implements IFunctionalPalace { + private _astrolabe?: IFunctionalAstrolabe; name; isBodyPalace; isOriginalPalace; @@ -129,4 +230,105 @@ export default class FunctionalPalace implements IFunctionalPalace { return true; }; + setAstrolabe = (astro: IFunctionalAstrolabe) => (this._astrolabe = astro); + astrolabe = () => this._astrolabe; + fliesTo = (to: number | PalaceName, withMutagens: Mutagen | Mutagen[]) => { + const toPalace = this.astrolabe()?.palace(to); + + if (!toPalace) { + return false; + } + + const { heavenlyStem } = this; + const stars = mutagensToStars(heavenlyStem, withMutagens); + + if (!stars || !stars.length) { + return false; + } + + return toPalace.has(stars); + }; + + fliesOneOfTo = (to: number | PalaceName, withMutagens: Mutagen[]) => { + const toPalace = this.astrolabe()?.palace(to); + + if (!toPalace) { + return false; + } + const { heavenlyStem } = this; + const stars = mutagensToStars(heavenlyStem, withMutagens); + + if (!stars || !stars.length) { + return true; + } + + return toPalace.hasOneOf(stars); + }; + + notFlyTo = (to: number | PalaceName, withMutagens: Mutagen | Mutagen[]) => { + const toPalace = this.astrolabe()?.palace(to); + + if (!toPalace) { + return false; + } + + const { heavenlyStem } = this; + const stars = mutagensToStars(heavenlyStem, withMutagens); + + if (!stars || !stars.length) { + return true; + } + + return toPalace.notHave(stars); + }; + + selfMutaged = (withMutagens: Mutagen | Mutagen[]) => { + const { heavenlyStem } = this; + const stars = mutagensToStars(heavenlyStem, withMutagens); + + return this.has(stars); + }; + + selfMutagedOneOf = (withMutagens?: Mutagen[]) => { + let muts: Mutagen | Mutagen[] = []; + + if (!withMutagens || !withMutagens.length) { + muts = ['禄', '权', '科', '忌']; + } else { + muts = withMutagens; + } + + const { heavenlyStem } = this; + const stars = mutagensToStars(heavenlyStem, muts); + + return this.hasOneOf(stars); + }; + + notSelfMutaged = (withMutagens?: Mutagen | Mutagen[]) => { + let muts: Mutagen | Mutagen[] = []; + + if (!withMutagens || !withMutagens.length) { + muts = ['禄', '权', '科', '忌']; + } else { + muts = withMutagens; + } + + const { heavenlyStem } = this; + const stars = mutagensToStars(heavenlyStem, muts); + + return this.notHave(stars); + }; + + mutagedPlaces = () => { + const { heavenlyStem } = this; + const astrolabe = this.astrolabe(); + + if (!astrolabe) { + return []; + } + + const stars = mutagensToStars(heavenlyStem, ['禄', '权', '科', '忌']); + + return stars.map((star) => astrolabe.star(star).palace()); + }; } diff --git a/src/astro/analyzer.ts b/src/astro/analyzer.ts index 15d403b0..3abe1df2 100644 --- a/src/astro/analyzer.ts +++ b/src/astro/analyzer.ts @@ -1,6 +1,7 @@ +import { MUTAGEN } from '../data'; import { Star, SurroundedPalaces } from '../data/types'; -import { kot, Mutagen, MutagenKey, PalaceKey, PalaceName, StarKey, StarName } from '../i18n'; -import { fixEarthlyBranchIndex, fixIndex } from '../utils'; +import { HeavenlyStemName, kot, Mutagen, MutagenKey, PalaceKey, PalaceName, StarKey, StarName } from '../i18n'; +import { fixEarthlyBranchIndex, fixIndex, getMutagensByHeavenlyStem } from '../utils'; import { IFunctionalAstrolabe } from './FunctionalAstrolabe'; import { IFunctionalPalace } from './FunctionalPalace'; import { FunctionalSurpalaces, IFunctionalSurpalaces } from './FunctionalSurpalaces'; @@ -102,27 +103,33 @@ export const getSurroundedPalaces = ( * @returns 宫位实例 */ export const getPalace = ($: IFunctionalAstrolabe, indexOrName: number | PalaceName): IFunctionalPalace | undefined => { + let palace: IFunctionalPalace | undefined; + if (typeof indexOrName === 'number') { if (indexOrName < 0 || indexOrName > 11) { throw new Error('invalid palace index.'); } - return $.palaces[indexOrName]; + palace = $.palaces[indexOrName]; + } else { + palace = $.palaces.find((item) => { + if (kot(indexOrName) === 'originalPalace' && item.isOriginalPalace) { + return item; + } + + if (kot(indexOrName) === 'bodyPalace' && item.isBodyPalace) { + return item; + } + + if (kot(item.name) === kot(indexOrName)) { + return item; + } + }); } - return $.palaces.find((item) => { - if (kot(indexOrName) === 'originalPalace' && item.isOriginalPalace) { - return item; - } - - if (kot(indexOrName) === 'bodyPalace' && item.isBodyPalace) { - return item; - } + palace?.setAstrolabe($); - if (kot(item.name) === kot(indexOrName)) { - return item; - } - }); + return palace; }; /** @@ -236,3 +243,21 @@ export const notSurroundedByStars = ($: IFunctionalSurpalaces, stars: StarName[] return _excludeAll(allStarsInPalace, stars); }; + +export const mutagensToStars = (heavenlyStem: HeavenlyStemName, mutagens: Mutagen | Mutagen[]) => { + const muts = Array.isArray(mutagens) ? mutagens : [mutagens]; + const stars: StarName[] = []; + const mutagenStars = getMutagensByHeavenlyStem(heavenlyStem); + + muts.forEach((withMutagen) => { + const mutagenIndex = MUTAGEN.indexOf(kot(withMutagen)); + + if (!mutagenStars[mutagenIndex]) { + return; + } + + stars.push(mutagenStars[mutagenIndex]); + }); + + return stars; +};