|  | @@ -1,7 +1,8 @@ | 
														
													
														
															
																|  |  | import { Creature } from './creature' |  |  | import { Creature } from './creature' | 
														
													
														
															
																|  |  | import { Encounter } from './combat' |  |  |  | 
														
													
														
															
																|  |  | import { LogEntry } from './interface' |  |  |  | 
														
													
														
															
																|  |  |  |  |  | import { Encounter, Action } from './combat' | 
														
													
														
															
																|  |  |  |  |  | import { LogEntry, nilLog } from './interface' | 
														
													
														
															
																|  |  | import { ReleaseAction, TransferAction, PassAction } from './combat/actions' |  |  | import { ReleaseAction, TransferAction, PassAction } from './combat/actions' | 
														
													
														
															
																|  |  |  |  |  | import { NoPassDecider, NoReleaseDecider, ChanceDecider } from './ai/deciders' | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  | export interface AI { |  |  | export interface AI { | 
														
													
														
															
																|  |  | name: string; |  |  | name: string; | 
														
													
												
													
														
															
																|  | @@ -15,6 +16,62 @@ export class NoAI implements AI { | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | 
 |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | /** | 
														
													
														
															
																|  |  |  |  |  | * 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 DeciderAI implements AI { | 
														
													
														
															
																|  |  |  |  |  | constructor (public name: string, 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 | 
														
													
														
															
																|  |  |  |  |  | }))) | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | console.log(options) | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | 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() | 
														
													
														
															
																|  |  |  |  |  | console.log(total) | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | 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) | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | // should never reach this point! | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  |  |  |  | throw new Error("Couldn't pick an action") | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | } | 
														
													
														
															
																|  |  |  |  |  | 
 | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * The RandomAI is **COMPLETELY** random. Good luck. |  |  | * The RandomAI is **COMPLETELY** random. Good luck. | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
												
													
														
															
																|  | @@ -33,21 +90,15 @@ export class RandomAI implements AI { | 
														
													
														
															
																|  |  | /** |  |  | /** | 
														
													
														
															
																|  |  | * The VoreAI tries to eat opponents, but only if the odds are good enough |  |  | * The VoreAI tries to eat opponents, but only if the odds are good enough | 
														
													
														
															
																|  |  | */ |  |  | */ | 
														
													
														
															
																|  |  | export class VoreAI extends RandomAI { |  |  |  | 
														
													
														
															
																|  |  | name = "Vore AI" |  |  |  | 
														
													
														
															
																|  |  | decide (actor: Creature, encounter: Encounter): LogEntry { |  |  |  | 
														
													
														
															
																|  |  | const actions = encounter.combatants.flatMap(enemy => actor.validActions(enemy).map(action => ({ |  |  |  | 
														
													
														
															
																|  |  | target: enemy, |  |  |  | 
														
													
														
															
																|  |  | action: action |  |  |  | 
														
													
														
															
																|  |  | }))) |  |  |  | 
														
													
														
															
																|  |  | const voreActions = actions.filter(action => actor.otherContainers.concat(actor.containers).some(container => container.actions.includes(action.action) || action instanceof TransferAction)) |  |  |  | 
														
													
														
															
																|  |  | const aggressiveActions = voreActions.filter(action => !(action.action instanceof ReleaseAction) && !(action.action instanceof PassAction)) |  |  |  | 
														
													
														
															
																|  |  | const likelyActions = aggressiveActions.filter(action => action.action.odds(actor, action.target) > 0.4) |  |  |  | 
														
													
														
															
																|  |  | likelyActions.forEach(action => console.log(action.action.odds(actor, action.target))) |  |  |  | 
														
													
														
															
																|  |  | const chosen = likelyActions[Math.floor(Math.random() * likelyActions.length)] |  |  |  | 
														
													
														
															
																|  |  | if (chosen === undefined) { |  |  |  | 
														
													
														
															
																|  |  | return super.decide(actor, encounter) |  |  |  | 
														
													
														
															
																|  |  | } |  |  |  | 
														
													
														
															
																|  |  | return chosen.action.try(actor, chosen.target) |  |  |  | 
														
													
														
															
																|  |  |  |  |  | export class VoreAI extends DeciderAI { | 
														
													
														
															
																|  |  |  |  |  | constructor () { | 
														
													
														
															
																|  |  |  |  |  | super ( | 
														
													
														
															
																|  |  |  |  |  | "Vore AI", | 
														
													
														
															
																|  |  |  |  |  | [ | 
														
													
														
															
																|  |  |  |  |  | new NoPassDecider(), | 
														
													
														
															
																|  |  |  |  |  | new NoReleaseDecider(), | 
														
													
														
															
																|  |  |  |  |  | new ChanceDecider() | 
														
													
														
															
																|  |  |  |  |  | ] | 
														
													
														
															
																|  |  |  |  |  | ) | 
														
													
														
															
																|  |  | } |  |  | } | 
														
													
														
															
																|  |  | } |  |  | } |