import { Creature } from './creature' import { Encounter, Action } from './combat' import { LogEntry } from './interface' import { PassAction } from './combat/actions' import { NoPassDecider, NoReleaseDecider, ChanceDecider, NoSurrenderDecider } from './ai/deciders' /** * A Decider determines how favorable an action is to perform. */ export interface Decider { decide: (encounter: Encounter, user: Creature, target: Creature, action: Action) => number; } export class AI { constructor (private deciders: Decider[]) { } decide (actor: Creature, encounter: Encounter): LogEntry { const options = encounter.combatants.flatMap(enemy => actor.validActions(enemy).map(action => ({ target: enemy, action: action, weight: 1 }))) this.deciders.forEach( decider => options.forEach( option => { option.weight *= decider.decide(encounter, actor, option.target, option.action) } ) ) let total = options.reduce( (sum: number, option) => sum + option.weight, 0 ) total *= Math.random() const chosen = options.find( option => { if (total < option.weight) { return true } else { total -= option.weight return false } } ) if (chosen !== undefined) { return chosen.action.try(actor, chosen.target) } // if we filtered out EVERY action, we should just give up and pass return new PassAction().try(actor, actor) } } /** * The VoreAI tries to eat opponents, but only if the odds are good enough */ export class VoreAI extends AI { constructor () { super( [ new NoReleaseDecider(), new NoSurrenderDecider(), new NoPassDecider(), new ChanceDecider() ] ) } }