utilitai
Version:
Utility AI library built with TypeScript
89 lines (68 loc) • 3.01 kB
text/typescript
import { describe, it, expect } from "vitest";
import { Reasoner, IAppraisal, IConsideration, IContext } from "./main";
// Mock context
interface ExampleContext extends IContext {
hunger: number;
energy: number;
distanceToFood?: number;
distanceToBed?: number;
}
// Mock considerations
const hungerConsideration: IConsideration<ExampleContext> = {
evaluate: (context) => context.hunger / 100, // Normalize hunger (0 to 1)
curveFunction: (score) => score * score, // Quadratic curve
};
const energyConsideration: IConsideration<ExampleContext> = {
evaluate: (context) => (100 - context.energy) / 100, // Normalize energy (0 to 1)
curveFunction: (score) => score, // Linear curve
};
// Mock appraisals
const hungerAppraisal: IAppraisal<ExampleContext> = {
id: "hunger",
action: { type: "Eat" },
considerations: [
{ consideration: hungerConsideration },
],
weight: 1,
scoringFunction: (scores) => scores.reduce((a, b) => a + b, 0) / scores.length, // Average scoring
};
const energyAppraisal: IAppraisal<ExampleContext> = {
id: "energy",
action: { type: "Rest" },
considerations: [
{ consideration: energyConsideration },
],
weight: 1,
scoringFunction: (scores) => scores.reduce((a, b) => a + b, 0) / scores.length, // Average scoring
};
describe("Reasoner", () => {
it("should return the best action based on appraisals", () => {
const context: ExampleContext = { hunger: 80, energy: 50 };
const reasoner = new Reasoner<ExampleContext>();
reasoner.addAppraisal(hungerAppraisal);
reasoner.addAppraisal(energyAppraisal);
let bestAction = reasoner.getBestAction(context);
// "hunger" is closer to 100 than energy is to 0, so the action should be "Eat"
expect(bestAction).toEqual({ type: "Eat" });
context.hunger -= 50;
bestAction = reasoner.getBestAction(context);
// "energy" is closer to 0 than hunger is to 100, so the action should be "Rest"
expect(bestAction).toEqual({ type: "Rest" })
});
it("should handle multiple considerations for a single appraisal", () => {
const context: ExampleContext = { hunger: 50, energy: 50, distanceToBed: 10, distanceToFood: 20 };
const reasoner = new Reasoner<ExampleContext>();
const distanceToFoodConsideration: IConsideration<ExampleContext> = {
evaluate: context => Math.max(0, 1 - context.distanceToFood! / 100)
}
const distanceToBedConsideration: IConsideration<ExampleContext> = {
evaluate: context => Math.max(0, 1 - context.distanceToBed! / 100)
}
hungerAppraisal.considerations.push({ consideration: distanceToFoodConsideration });
energyAppraisal.considerations.push({ consideration: distanceToBedConsideration });
reasoner.addAppraisal(hungerAppraisal);
reasoner.addAppraisal(energyAppraisal);
const bestAction = reasoner.getBestAction(context);
expect(bestAction).toEqual({ type: "Rest" });
});
});