36 lines
1.9 KiB
JavaScript
36 lines
1.9 KiB
JavaScript
const heuristic = (state, goal) => Object.keys(goal).reduce((h, key) => h + (state[key] === goal[key] ? 0 : Math.abs((state[key] || 0) - (goal[key] || 0))), 0), preconditionsMet = (state, preconditions = {}) => Object.entries(preconditions).every(([key, value]) => (typeof value === 'number' ? (state[key] || 0) >= value : state[key] === value));
|
|
const findPlan = (initialState, goalState, actions = []) => {
|
|
const openSet = [{ state: initialState, plan: [], g: 0, h: heuristic(initialState, goalState) }];
|
|
const visited = new Map([[JSON.stringify(initialState), 0]]);
|
|
while (openSet.length) {
|
|
openSet.sort((a, b) => (a.g + a.h) - (b.g + b.h));
|
|
const { state, plan, g } = openSet.shift();
|
|
if (heuristic(state, goalState) === 0) return plan;
|
|
actions.forEach((action) => {
|
|
if (!preconditionsMet(state, action.preconditions)) return;
|
|
const nextState = { ...state, ...(action.effects || {}) };
|
|
const key = JSON.stringify(nextState);
|
|
const nextG = g + 1;
|
|
if (!visited.has(key) || nextG < visited.get(key)) {
|
|
visited.set(key, nextG);
|
|
openSet.push({ state: nextState, plan: [...plan, action.name], g: nextG, h: heuristic(nextState, goalState) });
|
|
}
|
|
});
|
|
}
|
|
return [];
|
|
};
|
|
|
|
self.onmessage = function(e) {
|
|
const { type, data } = e.data;
|
|
if (type === 'REASON') {
|
|
const factMap = new Map(data.facts || []);
|
|
const results = (data.rules || []).filter((rule) => (rule.triggerFacts || []).every((fact) => factMap.get(fact))).map((rule) => ({ rule: rule.description, outcome: rule.workerOutcome || 'OFF-THREAD MATCH', triggerFacts: rule.triggerFacts || [], confidence: rule.confidence ?? 0.5 }));
|
|
self.postMessage({ type: 'REASON_RESULT', results });
|
|
return;
|
|
}
|
|
if (type === 'PLAN') {
|
|
const plan = findPlan(data.initialState || {}, data.goalState || {}, data.actions || []);
|
|
self.postMessage({ type: 'PLAN_RESULT', plan });
|
|
}
|
|
};
|