Pārlūkot izejas kodu

Start working on a new AI system

The AI uses a collection of Decider objects to assign weights to
each possible action.
master
Fen Dweller pirms 5 gadiem
vecāks
revīzija
9b92d97717
2 mainītis faili ar 108 papildinājumiem un 18 dzēšanām
  1. +69
    -18
      src/game/ai.ts
  2. +39
    -0
      src/game/ai/deciders.ts

+ 69
- 18
src/game/ai.ts Parādīt failu

@@ -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()
]
)
} }
} }

+ 39
- 0
src/game/ai/deciders.ts Parādīt failu

@@ -0,0 +1,39 @@
import { Decider } from '../ai'
import { Encounter, Action } from '../combat'
import { Creature } from '../creature'
import { PassAction, ReleaseAction } from '../combat/actions'

/**
* Specifically avoids using a [[PassAction]]
*/
export class NoPassDecider implements Decider {
decide (encounter: Encounter, user: Creature, target: Creature, action: Action): number {
if (action instanceof PassAction) {
return 0
} else {
return 1
}
}
}

/**
* Specifically avoids using a [[ReleaseAction]]
*/
export class NoReleaseDecider implements Decider {
decide (encounter: Encounter, user: Creature, target: Creature, action: Action): number {
if (action instanceof ReleaseAction) {
return 0
} else {
return 1
}
}
}

/**
* Weights actions based on how likely they are to succeed
*/
export class ChanceDecider implements Decider {
decide (encounter: Encounter, user: Creature, target: Creature, action: Action): number {
return action.odds(user, target)
}
}

Notiek ielāde…
Atcelt
Saglabāt