mirror of
https://github.com/morten-olsen/bob-the-algorithm.git
synced 2026-02-08 00:46:25 +01:00
161 lines
3.6 KiB
TypeScript
161 lines
3.6 KiB
TypeScript
import { Context, GraphNode } from "#/types/graph";
|
|
import { UserLocation } from "#/types/location";
|
|
import { Task } from "#/types/task";
|
|
import { getImpossible, getNext } from "./get-next";
|
|
|
|
enum Strategies {
|
|
all = 'all',
|
|
allValid = 'all-valid',
|
|
firstValid = 'first-valid',
|
|
firstComplet = 'first-complete',
|
|
}
|
|
type RunningStatus = {
|
|
current: 'running';
|
|
nodes: number;
|
|
start: Date;
|
|
strategy: Strategies,
|
|
cancel: () => void;
|
|
}
|
|
|
|
type CompletedStatus = {
|
|
current: 'completed';
|
|
start: Date;
|
|
end: Date;
|
|
nodes: number;
|
|
strategy: Strategies,
|
|
}
|
|
|
|
type Status = RunningStatus | CompletedStatus;
|
|
|
|
type BuildGraphOptions = {
|
|
location: UserLocation;
|
|
time: Date;
|
|
tasks: Task[];
|
|
context: Context;
|
|
strategy?: Strategies;
|
|
batchSize?: number;
|
|
sleepTime?: number;
|
|
callback?: (status: Status) => void;
|
|
};
|
|
|
|
const sleep = (time: number) => new Promise(resolve => setTimeout(resolve, time));
|
|
|
|
const fil = <T>(
|
|
fn: ((item: T) => boolean)[],
|
|
input: T[],
|
|
): T[][] => {
|
|
const output: T[][] = new Array(fn.length).fill(undefined).map(() => []);
|
|
for (let i = 0; i < input.length; i++) {
|
|
for (let b = 0; b < fn.length; b++) {
|
|
if (fn[b](input[i])) {
|
|
output[b].push(input[i]);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
};
|
|
|
|
const buildGraph = async ({
|
|
location,
|
|
time,
|
|
tasks,
|
|
context,
|
|
strategy = Strategies.allValid,
|
|
callback,
|
|
batchSize = 1000,
|
|
sleepTime = 10,
|
|
}: BuildGraphOptions) => {
|
|
const start = new Date();
|
|
let nodeCount = 0;
|
|
let running = true;
|
|
const { remaining, impossible } = getImpossible(tasks, time);
|
|
let leafList: GraphNode[] = [{
|
|
location,
|
|
time: {
|
|
end: time,
|
|
start: time,
|
|
},
|
|
score: 0,
|
|
remainingTasks: remaining,
|
|
impossibeTasks: impossible,
|
|
status: {
|
|
dead: false,
|
|
completed: false,
|
|
},
|
|
}];
|
|
const completedList: GraphNode[] = [];
|
|
const deadList: GraphNode[] = [];
|
|
|
|
const complete = (nodes: GraphNode[]) => {
|
|
if (callback) {
|
|
callback({
|
|
current: 'completed',
|
|
nodes: nodeCount,
|
|
start,
|
|
end: new Date(),
|
|
strategy,
|
|
});
|
|
}
|
|
return nodes.sort((a, b) => b.score - a.score);
|
|
}
|
|
|
|
while (true) {
|
|
nodeCount++;
|
|
if (!running) {
|
|
return [];
|
|
}
|
|
if (
|
|
leafList.length === 0
|
|
&& completedList.length === 0
|
|
&& strategy !== Strategies.all
|
|
) {
|
|
strategy = Strategies.all;
|
|
leafList.push(...deadList);
|
|
}
|
|
const node = leafList.pop();
|
|
if (!node) {
|
|
break;
|
|
}
|
|
if (nodeCount % batchSize === 0) {
|
|
if (callback) {
|
|
callback({
|
|
current: 'running',
|
|
nodes: nodeCount,
|
|
strategy,
|
|
start,
|
|
cancel: () => {
|
|
running = false;
|
|
}
|
|
})
|
|
}
|
|
await sleep(sleepTime);
|
|
}
|
|
const next = await getNext(node, context);
|
|
const [alive, completed, dead] = fil([
|
|
n => (strategy === Strategies.all || !n.status.dead) && !n.status.completed,
|
|
n => !!n.status.completed && (strategy === Strategies.all || !n.status.dead),
|
|
n => n.status.dead,
|
|
], next);
|
|
leafList.push(...alive);
|
|
if (strategy === Strategies.firstValid && completed.length > 0) {
|
|
return complete(completed);
|
|
}
|
|
if (completed.length > 0) {
|
|
completedList.push(...completed)
|
|
}
|
|
if (strategy === Strategies.firstComplet) {
|
|
const fullComplete = completed.find(c => c.impossibeTasks.length === 0);
|
|
if (fullComplete) {
|
|
return complete([fullComplete]);
|
|
}
|
|
}
|
|
deadList.push(...dead);
|
|
}
|
|
|
|
return complete(completedList);
|
|
}
|
|
|
|
export type { Status, BuildGraphOptions };
|
|
export { buildGraph, Strategies };
|