fortel-ziweidoushu
Version:
紫微斗數排盤 Zi Wei Dou Shu
270 lines (227 loc) • 9.51 kB
text/typescript
import { Cell } from '../model/cell'
import { DestinyBoard } from '../model/destinyBoard'
import { Ground } from '../model/ground'
import { MajorStar } from '../model/majorStar'
import { MiniStar } from '../model/miniStar'
import { MinorStar } from '../model/minorStar'
import { Star } from '../model/star'
import { StarDerivative } from '../model/starDerivative'
import { Temple } from '../model/temple'
import { str2Uni } from '../utils'
enum CellsScope {
SELF = 'SELF',
OPPOSITE = 'OPPOSITE',
SELF_OPPOSITE = 'OPPOSITE',
SUPPORT = 'SUPPORT',
TRIANGLE = 'TRIANGLE',
FOUR = 'FOUR',
}
class BoardCriteria {
#destinyBoard: DestinyBoard
#baseCellGround: Ground
#cellsScope: CellsScope
constructor(destinyBoard: DestinyBoard, baseCellGround?: Ground, cellsScope: CellsScope = CellsScope.SELF) {
this.#destinyBoard = destinyBoard
this.#baseCellGround = baseCellGround ?? destinyBoard.destinyTempleCellGround
this.#cellsScope = cellsScope
}
ofDestinyTemple() {
return new BoardCriteria(this.#destinyBoard, this.#destinyBoard.destinyTempleCellGround, this.#cellsScope)
}
ofTemple(temple: Temple) {
return new BoardCriteria(this.#destinyBoard, this.#destinyBoard.getCellByTemple(temple).ground, this.#cellsScope)
}
ofGroundCell(ground: Ground) {
return new BoardCriteria(this.#destinyBoard, ground, this.#cellsScope)
}
ofCell(cell: Cell) {
return new BoardCriteria(this.#destinyBoard, cell.ground, this.#cellsScope)
}
ofStar(star: Star) {
const targetCell = this.#destinyBoard.cells.find((cell) => {
return cell.hasStarInstance(star)
})
if (!targetCell) {
throw new Error('Star not found')
}
return new BoardCriteria(this.#destinyBoard, targetCell.ground, this.#cellsScope)
}
withCellsType(cellsScope: CellsScope) {
return new BoardCriteria(this.#destinyBoard, this.#baseCellGround, cellsScope)
}
get #__internal_baseCell(): Cell {
return this.#destinyBoard.getCellByGround(this.#baseCellGround)
}
static allStarsNameToKeyMap = ([...MajorStar.stars, ...MinorStar.stars, ...MiniStar.allStars] as Star[]).reduce<Map<string, string>>(
(prev: Map<string, string>, curr: Star) => {
if (!prev[curr.getDisplayName()]) {
prev[curr.getDisplayName()] = curr.getKey()
}
return prev
},
{} as Map<string, string>,
)
hasStar(star: Star) {
return BoardCriteria.#hasStar(this.#scopedCells, star)
}
hasAllStars(stars: Star[]) {
return BoardCriteria.#hasAllStars(this.#scopedCells, stars)
}
hasAnyStars(stars: Star[]) {
return BoardCriteria.#hasAnyStars(this.#scopedCells, stars)
}
notFoundStar(star: Star) {
return BoardCriteria.#notFoundStar(this.#scopedCells, star)
}
notFoundAllStars(stars: Star[]) {
return BoardCriteria.#notFoundAllStars(this.#scopedCells, stars)
}
notFoundAnyStars(stars: Star[]) {
return BoardCriteria.#notFoundAnyStars(this.#scopedCells, stars)
}
get #scopedCells(): Cell[] {
switch (this.#cellsScope) {
case CellsScope.SELF:
return this.#__internal_baseCell.selfWithBorrow
case CellsScope.OPPOSITE:
return this.#__internal_baseCell.oppositeCellWithBorrow
case CellsScope.SELF_OPPOSITE:
return this.#__internal_baseCell.selfOppositeCellsWithBorrow
case CellsScope.SUPPORT:
return this.#__internal_baseCell.supportCellsWithBorrow
case CellsScope.TRIANGLE:
return this.#__internal_baseCell.triangleCellsWithBorrow
case CellsScope.FOUR:
return this.#__internal_baseCell.fourCellsWithBorrow
default:
throw new Error('unknown cells type')
}
}
static #hasStar(cells: Cell[], star: Star) {
return !!cells.find((cell) => cell.hasStar(star))
}
static #hasAllStars(cells: Cell[], stars: Star[]) {
for (const star of stars) {
if (!cells.find((cell) => cell.hasStar(star))) {
return false
}
}
return true
}
static #hasAnyStars(cells: Cell[], stars: Star[]) {
for (const star of stars) {
if (cells.find((cell) => cell.hasStar(star))) {
return true
}
}
return false
}
static #notFoundStar(cells: Cell[], star: Star) {
return !BoardCriteria.#hasStar(cells, star)
}
static #notFoundAllStars(cells: Cell[], stars: Star[]) {
return !BoardCriteria.#hasAnyStars(cells, stars)
}
static #notFoundAnyStars(cells: Cell[], stars: Star[]) {
return !BoardCriteria.#hasAllStars(cells, stars)
}
static fromDescription(destinyBoard: DestinyBoard, description: string) {
let cell: Cell | null = null
let cellsScope: CellsScope | null = null
let stars: Star[] = []
const starDerivatives: StarDerivative[] = []
let isBothEarn = false
if ((!cellsScope && description.includes('入命')) || description.includes('在命')) {
cell = destinyBoard.getCellByTemple(Temple.TEMPLE_DESTINY)
cellsScope = CellsScope.SELF
}
if (!cellsScope && description.includes('三方四正')) {
cellsScope = CellsScope.FOUR
}
const templeChar = str2Uni('宮')
{
const matches = description.match(new RegExp(`.*?(.{1,2}${templeChar})`, 'u'))
if (matches && matches[1]) {
const targetTemple = Temple.TEMPLES.find((temple) => matches[1].includes(temple.getDisplayName()))
if (targetTemple) {
cell = destinyBoard.getCellByTemple(targetTemple)
// 入XX宮
if (description.match(new RegExp(`${str2Uni('入')}(.{1,2})${templeChar}`, 'u'))) {
cellsScope = CellsScope.SELF
}
// 在XX宮
if (description.match(new RegExp(`${str2Uni('在')}(.{1,2})${templeChar}`, 'u'))) {
cellsScope = CellsScope.SELF
}
}
}
}
if (!cellsScope && description.includes('在')) {
cellsScope = CellsScope.SELF
}
if (!cellsScope && description.includes('有')) {
cellsScope = CellsScope.SELF
}
if (!cellsScope && description.includes('見')) {
cellsScope = CellsScope.FOUR
}
if (!cellsScope && description.includes('會')) {
cellsScope = CellsScope.FOUR
}
stars = Object.keys(BoardCriteria.allStarsNameToKeyMap)
.filter((starName) => description.includes(starName))
.map((starName) => {
const key = BoardCriteria.allStarsNameToKeyMap[starName]
return MajorStar.getByKey(key) ?? MinorStar.getByKey(key) ?? MiniStar.getByKey(key)
})
.filter((star) => star) as Star[]
if (description.includes('火')) {
stars.push(MinorStar.MINOR_STAR_BURNING)
}
if (description.includes('鈴')) {
stars.push(MinorStar.MINOR_STAR_HIDDEN_FIRE)
}
if (description.includes('羊')) {
stars.push(MinorStar.MINOR_STAR_COMPETITION)
}
if (description.includes('陀')) {
stars.push(MinorStar.MINOR_STAR_HINDRANCE)
}
{
const matches = description.match(new RegExp(`${str2Uni('見')}${str2Uni('祿')}(${str2Uni('存')})?`, 'u'))
if (matches) {
cellsScope = CellsScope.FOUR
if (!matches[1]) {
isBothEarn = true
}
}
}
if (description.includes('化祿')) {
starDerivatives.push(StarDerivative.WEALTHINESS)
}
if (description.includes('科')) {
starDerivatives.push(StarDerivative.FAME)
}
if (description.includes('忌')) {
starDerivatives.push(StarDerivative.PROBLEM)
}
if (!cell) {
cell = destinyBoard.getCellByTemple(Temple.TEMPLE_DESTINY)
}
if (!cellsScope) {
cellsScope = CellsScope.SELF
}
const starDerivativeStars = starDerivatives.map((starDerivative) => destinyBoard.bornStarDerivativeMap.get(starDerivative)!)
const criteria = new BoardCriteria(destinyBoard).ofCell(cell).withCellsType(cellsScope)
if (isBothEarn) {
stars = stars.filter((star) => !star.equals(MinorStar.MINOR_STAR_EARN))
return (
criteria.hasAllStars(stars) &&
criteria.hasAnyStars([MinorStar.MINOR_STAR_EARN, destinyBoard.bornStarDerivativeMap.get(StarDerivative.WEALTHINESS)!])
)
} else {
return criteria.hasAllStars([...stars, ...starDerivativeStars])
}
}
}
export { BoardCriteria, CellsScope }