From 3faab5e2068df2c6905232983030f624ba86717f Mon Sep 17 00:00:00 2001 From: Dustlint Date: Mon, 30 Mar 2026 16:31:19 +0200 Subject: [PATCH] basic brain --- src/app/logic/chibi-behaviour/basic.brain.ts | 91 +++++++++++++++++ src/app/logic/chibi-behaviour/brain-map.ts | 7 +- src/app/logic/chibi-behaviour/brain.ts | 2 +- .../chibi-behaviour/brains/aperio.brain.ts | 97 ++----------------- .../chibi-behaviour/brains/caethya.brain.ts | 36 +++++++ .../chibi-behaviour/brains/haetor.brain.ts | 36 +++++++ .../interactions/interactions.component.ts | 4 +- src/app/types/chibi/chibi-name.ts | 1 + 8 files changed, 179 insertions(+), 95 deletions(-) create mode 100644 src/app/logic/chibi-behaviour/basic.brain.ts create mode 100644 src/app/logic/chibi-behaviour/brains/caethya.brain.ts create mode 100644 src/app/logic/chibi-behaviour/brains/haetor.brain.ts diff --git a/src/app/logic/chibi-behaviour/basic.brain.ts b/src/app/logic/chibi-behaviour/basic.brain.ts new file mode 100644 index 0000000..8b6e76b --- /dev/null +++ b/src/app/logic/chibi-behaviour/basic.brain.ts @@ -0,0 +1,91 @@ +import { Chibi } from "../../types/chibi/chibi"; +import { EChibiInteraction } from "../../types/chibi/chibi-interaction"; +import { ChibiState, STATES } from "../../types/chibi/chibi-state"; +import { EChibiStateName } from "../../types/chibi/chibi-state-name"; +import { RandomService } from "../random.service"; +import { IBrain } from "./brain"; + +export type InteractionMap = { [key in EChibiStateName]: InteractionRecord | undefined } + +export type ChibiInteraction = { + name: EChibiInteraction; + payload: T; +} + +export type InteractionResolutionFunction = (interaction: ChibiInteraction) => ChibiState; +export type InteractionRecord = Partial>; + +export type ResolutionFunction = () => ChibiState | undefined; +export type ResolutionMap = { [key in EChibiStateName]: ResolutionFunction | undefined } + +export abstract class BasicBrain implements IBrain { + + protected randomService!: RandomService; + protected chibi!: Chibi; + + protected readonly defaultState: EChibiStateName = EChibiStateName.Sleeping; + + protected hasBeenAwakeFor: number = 0; + protected timeSinceLastInteraction: number = 0; + protected timeInCurrentState: number = 0; + + protected abstract interactionMap: InteractionMap; + protected abstract resolutionMap: ResolutionMap; + + get state(): ChibiState { + return STATES[this.chibi.currentStateName]; + } + + init(chibi: Chibi, randomService: RandomService): void { + this.randomService = randomService; + this.chibi = chibi; + } + + exist(): void { + this.timeInCurrentState++; + const resolution = this.matchResolution(this.resolutionMap, STATES[this.chibi.currentStateName]); + if (resolution) { + const newState = resolution(); + if (newState && newState != STATES[this.chibi.currentStateName]) { + this.changeState(newState); + } + } + } + + resolveInteraction(interaction: ChibiInteraction): void { + this.timeSinceLastInteraction = 0; + const resolution = this.matchInteraction(this.interactionMap, interaction.name, STATES[this.chibi.currentStateName]); + console.log('resolution', resolution); + console.log(`${this.chibi.name} should ${interaction.name} with payload: ${interaction.payload}`); + if (resolution) { + this.changeState(resolution(interaction)); + } + } + + matchInteraction(map: InteractionMap, interaction: EChibiInteraction, state: ChibiState): InteractionResolutionFunction | undefined { + const directMatch = map?.[state.name]?.[interaction]; + if (directMatch) { + return directMatch; + } + if (state.parent) { + return this.matchInteraction(map, interaction, state.parent); + } + return undefined; + } + + matchResolution(map: ResolutionMap, state: ChibiState): ResolutionFunction | undefined { + const directMatch = map?.[state.name]; + if (directMatch) { + return directMatch; + } + if (state.parent) { + return this.matchResolution(map, state.parent); + } + return undefined; + } + + protected changeState(state: ChibiState): void { + this.chibi.currentStateName = state.name; + this.timeInCurrentState = 0; + } +}; \ No newline at end of file diff --git a/src/app/logic/chibi-behaviour/brain-map.ts b/src/app/logic/chibi-behaviour/brain-map.ts index 3519f1a..ebaeac2 100644 --- a/src/app/logic/chibi-behaviour/brain-map.ts +++ b/src/app/logic/chibi-behaviour/brain-map.ts @@ -1,15 +1,20 @@ import { EChibiName } from "../../types/chibi/chibi-name"; import { IBrain } from "./brain"; import { Aperio } from "./brains/aperio.brain"; +import { Caethya } from "./brains/caethya.brain"; +import { Haetor } from "./brains/haetor.brain"; const APERIO = new Aperio(); +const CAETHYA = new Caethya(); +const HAETOR = new Haetor(); export const BRAIN_MAP: Record = { [EChibiName.Aperio]: APERIO, [EChibiName.Basra]: APERIO, - [EChibiName.Caethya]: APERIO, + [EChibiName.Caethya]: CAETHYA, [EChibiName.Eos]: APERIO, [EChibiName.Evina]: APERIO, + [EChibiName.Haetor]: HAETOR, [EChibiName.Sol]: APERIO, [EChibiName.Sybil]: APERIO, [EChibiName.Vinera]: APERIO diff --git a/src/app/logic/chibi-behaviour/brain.ts b/src/app/logic/chibi-behaviour/brain.ts index 69dd775..c94cf44 100644 --- a/src/app/logic/chibi-behaviour/brain.ts +++ b/src/app/logic/chibi-behaviour/brain.ts @@ -1,6 +1,6 @@ import { Chibi } from "../../types/chibi/chibi"; import { RandomService } from "../random.service"; -import { ChibiInteraction } from "./brains/aperio.brain"; +import { ChibiInteraction } from "./basic.brain"; export interface IBrain { diff --git a/src/app/logic/chibi-behaviour/brains/aperio.brain.ts b/src/app/logic/chibi-behaviour/brains/aperio.brain.ts index 575a78a..4897fc3 100644 --- a/src/app/logic/chibi-behaviour/brains/aperio.brain.ts +++ b/src/app/logic/chibi-behaviour/brains/aperio.brain.ts @@ -1,23 +1,9 @@ -import { Chibi } from "../../../types/chibi/chibi"; import { EChibiInteraction } from "../../../types/chibi/chibi-interaction"; -import { ChibiState, isState, STATE_AWAKE, STATE_EATING, STATE_GRUMBLING, STATE_IDLE, STATE_SLEEPING, STATES } from "../../../types/chibi/chibi-state"; +import { ChibiState, STATE_AWAKE, STATE_EATING, STATE_GRUMBLING, STATE_IDLE, STATE_SLEEPING } from "../../../types/chibi/chibi-state"; import { EChibiStateName } from "../../../types/chibi/chibi-state-name"; import { Food } from "../../../types/food"; -import { RandomOutcome, RandomService } from "../../random.service"; -import { IBrain } from "../brain"; - -export type InteractionMap = { [key in EChibiStateName]: InteractionRecord | undefined } - -export type ChibiInteraction = { - name: EChibiInteraction; - payload: T; -} - -type InteractionResolutionFunction = (interaction: ChibiInteraction) => ChibiState; -type InteractionRecord = Partial>; - -type ResolutionFunction = () => ChibiState | undefined; -type ResolutionMap = { [key in EChibiStateName]: ResolutionFunction | undefined } +import { RandomOutcome, } from "../../random.service"; +import { BasicBrain, ChibiInteraction, InteractionMap, ResolutionMap } from "../basic.brain"; const MAX_TIME_AWAKE: number = 10; @@ -27,21 +13,12 @@ const SADNESS_FROM_NEGLECT: number = 50; const MAX_DURATION_GRUMBLE: number = 1; const MAX_DURATION_EATING: number = 1; -export class Aperio implements IBrain { +export class Aperio extends BasicBrain { - private randomService!: RandomService; - private chibi!: Chibi; - - private readonly defaultState: EChibiStateName = EChibiStateName.Sleeping; - private previousState: EChibiStateName = this.defaultState; - - private hasBeenAwakeFor: number = 0; - private timeSinceLastInteraction: number = 0; - private timeInCurrentState: number = 0; private lastConsumedFoods: string[] = []; private preferredFoods: string[] = []; - private interactionMap: InteractionMap = { + protected override interactionMap: InteractionMap = { [EChibiStateName.Sleeping]: { [EChibiInteraction.WakeUp]: (interaction: ChibiInteraction): ChibiState => this.wakeUp(interaction), }, @@ -60,7 +37,7 @@ export class Aperio implements IBrain { [EChibiStateName.Depressed]: undefined, [EChibiStateName.Tired]: undefined } - private resolutionMap: ResolutionMap = { + protected override resolutionMap: ResolutionMap = { [EChibiStateName.Sleeping]: (): ChibiState | undefined => this.resolveSleeping(), [EChibiStateName.Awake]: (): ChibiState | undefined => this.resolveAwake(), [EChibiStateName.Snoring]: undefined, @@ -76,62 +53,6 @@ export class Aperio implements IBrain { [EChibiStateName.Tired]: undefined } - get state(): ChibiState { - return STATES[this.chibi.currentStateName]; - } - - init(chibi: Chibi, randomService: RandomService): void { - this.randomService = randomService; - this.chibi = chibi; - } - - exist(): void { - this.timeInCurrentState++; - const resolution = this.matchResolution(this.resolutionMap, STATES[this.chibi.currentStateName]); - if (resolution) { - const newState = resolution(); - if (newState && newState != STATES[this.chibi.currentStateName]) { - this.changeState(newState); - } - } - } - - resolveInteraction(interaction: ChibiInteraction): void { - this.timeSinceLastInteraction = 0; - const resolution = this.matchInteraction(this.interactionMap, interaction.name, STATES[this.chibi.currentStateName]); - console.log('resolution', resolution); - console.log(`${this.chibi.name} should ${interaction.name} with payload: ${interaction.payload}`); - if (resolution) { - this.changeState(resolution(interaction)); - } - } - - isAwake(): boolean { - return isState(this.state, EChibiStateName.Awake); - } - - matchInteraction(map: InteractionMap, interaction: EChibiInteraction, state: ChibiState): InteractionResolutionFunction | undefined { - const directMatch = map?.[state.name]?.[interaction]; - if (directMatch) { - return directMatch; - } - if (state.parent) { - return this.matchInteraction(map, interaction, state.parent); - } - return undefined; - } - - matchResolution(map: ResolutionMap, state: ChibiState): ResolutionFunction | undefined { - const directMatch = map?.[state.name]; - if (directMatch) { - return directMatch; - } - if (state.parent) { - return this.matchResolution(map, state.parent); - } - return undefined; - } - // resolvers // functions which automatically resolve based on the existence cycle @@ -189,12 +110,6 @@ export class Aperio implements IBrain { this.chibi.happyness.current = this.chibi.happyness.current - (neglectTime * sadness); } - private changeState(state: ChibiState): void { - this.previousState = this.state.name; - this.chibi.currentStateName = state.name; - this.timeInCurrentState = 0; - } - /* visiting caethya will wake her up and go into lovey-dovey mode diff --git a/src/app/logic/chibi-behaviour/brains/caethya.brain.ts b/src/app/logic/chibi-behaviour/brains/caethya.brain.ts new file mode 100644 index 0000000..acad40d --- /dev/null +++ b/src/app/logic/chibi-behaviour/brains/caethya.brain.ts @@ -0,0 +1,36 @@ +import { EChibiStateName } from "../../../types/chibi/chibi-state-name"; +import { BasicBrain, InteractionMap, ResolutionMap } from "../basic.brain"; + +export class Caethya extends BasicBrain { + protected override interactionMap: InteractionMap = { + [EChibiStateName.Sleeping]: undefined, + [EChibiStateName.Awake]: undefined, + [EChibiStateName.Snoring]: undefined, + [EChibiStateName.Nightmare]: undefined, + [EChibiStateName.Grumbling]: undefined, + [EChibiStateName.Bored]: undefined, + [EChibiStateName.Idle]: undefined, + [EChibiStateName.Exited]: undefined, + [EChibiStateName.Fighting]: undefined, + [EChibiStateName.Eating]: undefined, + [EChibiStateName.Hungry]: undefined, + [EChibiStateName.Depressed]: undefined, + [EChibiStateName.Tired]: undefined + }; + protected override resolutionMap: ResolutionMap = { + [EChibiStateName.Sleeping]: undefined, + [EChibiStateName.Snoring]: undefined, + [EChibiStateName.Nightmare]: undefined, + [EChibiStateName.Grumbling]: undefined, + [EChibiStateName.Awake]: undefined, + [EChibiStateName.Bored]: undefined, + [EChibiStateName.Idle]: undefined, + [EChibiStateName.Exited]: undefined, + [EChibiStateName.Fighting]: undefined, + [EChibiStateName.Eating]: undefined, + [EChibiStateName.Hungry]: undefined, + [EChibiStateName.Depressed]: undefined, + [EChibiStateName.Tired]: undefined + }; + +} \ No newline at end of file diff --git a/src/app/logic/chibi-behaviour/brains/haetor.brain.ts b/src/app/logic/chibi-behaviour/brains/haetor.brain.ts new file mode 100644 index 0000000..27255cc --- /dev/null +++ b/src/app/logic/chibi-behaviour/brains/haetor.brain.ts @@ -0,0 +1,36 @@ +import { EChibiStateName } from "../../../types/chibi/chibi-state-name"; +import { BasicBrain, InteractionMap, ResolutionMap } from "../basic.brain"; + +export class Haetor extends BasicBrain { + protected override interactionMap: InteractionMap = { + [EChibiStateName.Sleeping]: undefined, + [EChibiStateName.Awake]: undefined, + [EChibiStateName.Snoring]: undefined, + [EChibiStateName.Nightmare]: undefined, + [EChibiStateName.Grumbling]: undefined, + [EChibiStateName.Bored]: undefined, + [EChibiStateName.Idle]: undefined, + [EChibiStateName.Exited]: undefined, + [EChibiStateName.Fighting]: undefined, + [EChibiStateName.Eating]: undefined, + [EChibiStateName.Hungry]: undefined, + [EChibiStateName.Depressed]: undefined, + [EChibiStateName.Tired]: undefined + }; + protected override resolutionMap: ResolutionMap = { + [EChibiStateName.Sleeping]: undefined, + [EChibiStateName.Snoring]: undefined, + [EChibiStateName.Nightmare]: undefined, + [EChibiStateName.Grumbling]: undefined, + [EChibiStateName.Awake]: undefined, + [EChibiStateName.Bored]: undefined, + [EChibiStateName.Idle]: undefined, + [EChibiStateName.Exited]: undefined, + [EChibiStateName.Fighting]: undefined, + [EChibiStateName.Eating]: undefined, + [EChibiStateName.Hungry]: undefined, + [EChibiStateName.Depressed]: undefined, + [EChibiStateName.Tired]: undefined + }; + +} \ No newline at end of file diff --git a/src/app/pages/interactions/interactions.component.ts b/src/app/pages/interactions/interactions.component.ts index 938fbdd..8e04a31 100644 --- a/src/app/pages/interactions/interactions.component.ts +++ b/src/app/pages/interactions/interactions.component.ts @@ -12,12 +12,12 @@ import { IBrain } from '../../logic/chibi-behaviour/brain'; import { BRAIN_MAP } from '../../logic/chibi-behaviour/brain-map'; import { TranslateableComponent } from '../../components/translateable.component'; import { Food } from '../../types/food'; -import { interval, Subscribable, Subscription } from 'rxjs'; +import { interval, Subscription } from 'rxjs'; import { RandomService } from '../../logic/random.service'; -import { ChibiInteraction } from '../../logic/chibi-behaviour/brains/aperio.brain'; import { isState, STATES } from '../../types/chibi/chibi-state'; import { EChibiStateName } from '../../types/chibi/chibi-state-name'; import { VisitorListComponent } from "../../components/interaction-menus/visitor-list/visitor-list.component"; +import { ChibiInteraction } from '../../logic/chibi-behaviour/basic.brain'; enum EInteractionMenuState { Neutral, diff --git a/src/app/types/chibi/chibi-name.ts b/src/app/types/chibi/chibi-name.ts index b949c30..30a61eb 100644 --- a/src/app/types/chibi/chibi-name.ts +++ b/src/app/types/chibi/chibi-name.ts @@ -4,6 +4,7 @@ export enum EChibiName { Caethya = 'Caethya', Eos = 'Eos', Evina = 'Evina', + Haetor = 'Haetor', Sol = 'Sol', Sybil = 'Sybil', Vinera = 'Vinera'