6rz6
Add Medieval Village AI Emulator
a32dc8b
/**
* Behavior Tree Implementation for Villager AI
*
* This implementation uses a behavior tree approach to manage complex villager behaviors
* including decision-making for daily routines, work, rest, and social interactions.
*/
// Base Node class for behavior tree
class Node {
constructor() {
this.status = 'INVALID'; // INVALID, SUCCESS, FAILURE, RUNNING
}
execute(agent, deltaTime) {
throw new Error('Execute method must be implemented');
}
}
// Composite node that can have children
class CompositeNode extends Node {
constructor() {
super();
this.children = [];
}
addChild(child) {
this.children.push(child);
}
}
// Selector node - tries children in order until one succeeds
class SelectorNode extends CompositeNode {
constructor() {
super();
this.currentChildIndex = 0;
}
execute(agent, deltaTime) {
// If we were running a child previously, continue with it
if (this.status === 'RUNNING' && this.currentChildIndex < this.children.length) {
const result = this.children[this.currentChildIndex].execute(agent, deltaTime);
if (result === 'RUNNING') {
return 'RUNNING';
} else if (result === 'SUCCESS') {
this.status = 'SUCCESS';
this.currentChildIndex = 0;
return 'SUCCESS';
}
// If child failed, try next child
}
// Try children in order
for (let i = 0; i < this.children.length; i++) {
const result = this.children[i].execute(agent, deltaTime);
if (result === 'RUNNING') {
this.status = 'RUNNING';
this.currentChildIndex = i;
return 'RUNNING';
} else if (result === 'SUCCESS') {
this.status = 'SUCCESS';
this.currentChildIndex = 0;
return 'SUCCESS';
}
// If child failed, try next child
}
// All children failed
this.status = 'FAILURE';
this.currentChildIndex = 0;
return 'FAILURE';
}
}
// Sequence node - executes children in order until one fails
class SequenceNode extends CompositeNode {
constructor() {
super();
this.currentChildIndex = 0;
}
execute(agent, deltaTime) {
// If we were running a child previously, continue with it
if (this.status === 'RUNNING' && this.currentChildIndex < this.children.length) {
const result = this.children[this.currentChildIndex].execute(agent, deltaTime);
if (result === 'RUNNING') {
return 'RUNNING';
} else if (result === 'FAILURE') {
this.status = 'FAILURE';
this.currentChildIndex = 0;
return 'FAILURE';
}
// If child succeeded, try next child
}
// Try children in order
for (let i = this.currentChildIndex; i < this.children.length; i++) {
const result = this.children[i].execute(agent, deltaTime);
if (result === 'RUNNING') {
this.status = 'RUNNING';
this.currentChildIndex = i;
return 'RUNNING';
} else if (result === 'FAILURE') {
this.status = 'FAILURE';
this.currentChildIndex = 0;
return 'FAILURE';
}
// If child succeeded, continue to next child
}
// All children succeeded
this.status = 'SUCCESS';
this.currentChildIndex = 0;
return 'SUCCESS';
}
}
// Leaf node for specific actions
class ActionNode extends Node {
constructor(action) {
super();
this.action = action;
}
execute(agent, deltaTime) {
return this.action(agent, deltaTime);
}
}
// Condition node to check specific conditions
class ConditionNode extends Node {
constructor(condition) {
super();
this.condition = condition;
}
execute(agent, deltaTime) {
return this.condition(agent, deltaTime) ? 'SUCCESS' : 'FAILURE';
}
}
// Villager behavior tree implementation
class VillagerBehaviorTree {
constructor() {
this.root = this.createBehaviorTree();
}
createBehaviorTree() {
// Root selector - decides what the villager should do
const root = new SelectorNode();
// Check if it's time to sleep
const sleepSequence = new SequenceNode();
sleepSequence.addChild(new ConditionNode((agent) => agent.isTired()));
sleepSequence.addChild(new ActionNode((agent) => agent.sleep()));
// Check if it's time to work
const workSequence = new SequenceNode();
workSequence.addChild(new ConditionNode((agent) => agent.shouldWork()));
workSequence.addChild(new ActionNode((agent) => agent.work()));
// Check if it's time to socialize
const socializeSequence = new SequenceNode();
socializeSequence.addChild(new ConditionNode((agent) => agent.shouldSocialize()));
socializeSequence.addChild(new ActionNode((agent) => agent.socialize()));
// Default action - idle
const idleAction = new ActionNode((agent) => agent.idle());
// Add all sequences to root
root.addChild(sleepSequence);
root.addChild(workSequence);
root.addChild(socializeSequence);
root.addChild(idleAction);
return root;
}
update(agent, deltaTime) {
this.root.execute(agent, deltaTime);
}
}
export { VillagerBehaviorTree, Node, CompositeNode, SelectorNode, SequenceNode, ActionNode, ConditionNode };