mirror of
https://github.com/morten-olsen/bob-the-algorithm.git
synced 2026-02-08 00:46:25 +01:00
update
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Context, GraphNode } from "#/types/graph";
|
||||
import { UserLocation } from "#/types/location";
|
||||
import { Task } from "#/types/task";
|
||||
import { getNext } from "./get-next";
|
||||
import { getImpossible, getNext } from "./get-next";
|
||||
|
||||
enum Strategies {
|
||||
all = 'all',
|
||||
@@ -13,6 +13,7 @@ type RunningStatus = {
|
||||
current: 'running';
|
||||
nodes: number;
|
||||
start: Date;
|
||||
strategy: Strategies,
|
||||
cancel: () => void;
|
||||
}
|
||||
|
||||
@@ -21,6 +22,7 @@ type CompletedStatus = {
|
||||
start: Date;
|
||||
end: Date;
|
||||
nodes: number;
|
||||
strategy: Strategies,
|
||||
}
|
||||
|
||||
type Status = RunningStatus | CompletedStatus;
|
||||
@@ -65,38 +67,62 @@ const buildGraph = async ({
|
||||
sleepTime = 10,
|
||||
}: BuildGraphOptions) => {
|
||||
const start = new Date();
|
||||
let leafs: GraphNode[] = [{
|
||||
let nodeCount = 0;
|
||||
let running = true;
|
||||
const { remaining, impossible } = getImpossible(tasks, time);
|
||||
let leafList: GraphNode[] = [{
|
||||
location,
|
||||
time: {
|
||||
end: time,
|
||||
start: time,
|
||||
},
|
||||
score: 0,
|
||||
remainingTasks: tasks,
|
||||
impossibeTasks: [],
|
||||
remainingTasks: remaining,
|
||||
impossibeTasks: impossible,
|
||||
status: {
|
||||
dead: false,
|
||||
completed: false,
|
||||
},
|
||||
}];
|
||||
let nodes = 0;
|
||||
let running = true;
|
||||
const final: GraphNode[] = [];
|
||||
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) {
|
||||
nodes++;
|
||||
nodeCount++;
|
||||
if (!running) {
|
||||
return [];
|
||||
}
|
||||
const node = leafs.pop();
|
||||
if (
|
||||
leafList.length === 0
|
||||
&& completedList.length === 0
|
||||
&& strategy !== Strategies.all
|
||||
) {
|
||||
strategy = Strategies.all;
|
||||
leafList.push(...deadList);
|
||||
}
|
||||
const node = leafList.pop();
|
||||
if (!node) {
|
||||
break;
|
||||
}
|
||||
if (nodes % batchSize === 1) {
|
||||
if (nodeCount % batchSize === 0) {
|
||||
if (callback) {
|
||||
callback({
|
||||
current: 'running',
|
||||
nodes,
|
||||
nodes: nodeCount,
|
||||
strategy,
|
||||
start,
|
||||
cancel: () => {
|
||||
running = false;
|
||||
@@ -106,38 +132,28 @@ const buildGraph = async ({
|
||||
await sleep(sleepTime);
|
||||
}
|
||||
const next = await getNext(node, context);
|
||||
const [alive, completed] = fil([
|
||||
n => !n.status.dead && !n.status.completed,
|
||||
n => !!n.status.completed && !n.status.dead
|
||||
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);
|
||||
leafs.push(...alive);
|
||||
leafList.push(...alive);
|
||||
if (strategy === Strategies.firstValid && completed.length > 0) {
|
||||
if (callback) {
|
||||
callback({ current: 'completed', nodes, start, end: new Date() })
|
||||
}
|
||||
return completed;
|
||||
return complete(completed);
|
||||
}
|
||||
if (completed.length > 0) {
|
||||
final.push(...completed)
|
||||
completedList.push(...completed)
|
||||
}
|
||||
if (strategy === Strategies.firstComplet) {
|
||||
const fullComplete = completed.find(c => c.impossibeTasks.length === 0);
|
||||
if (fullComplete) {
|
||||
if (callback) {
|
||||
callback({ current: 'completed', nodes, start, end: new Date() })
|
||||
}
|
||||
return [fullComplete];
|
||||
return complete([fullComplete]);
|
||||
}
|
||||
}
|
||||
deadList.push(...dead);
|
||||
}
|
||||
|
||||
console.log('nodes', nodes);
|
||||
if (callback) {
|
||||
callback({ current: 'completed', nodes, start, end: new Date() })
|
||||
}
|
||||
return final
|
||||
.filter(n => n.status.completed)
|
||||
.sort((a, b) => b.score - a.score);
|
||||
return complete(completedList);
|
||||
}
|
||||
|
||||
export type { Status, BuildGraphOptions };
|
||||
|
||||
@@ -13,7 +13,7 @@ type GetImpossibleResult = {
|
||||
impossible: Task[];
|
||||
}
|
||||
|
||||
const getImpossible = (
|
||||
export const getImpossible = (
|
||||
tasks: Task[],
|
||||
time: Date,
|
||||
) => {
|
||||
@@ -50,15 +50,15 @@ const calculateScore = ({
|
||||
score += task.priority * 10;
|
||||
impossible.forEach((task) => {
|
||||
if (task.required) {
|
||||
score -= 1000;
|
||||
score -= 10000 + (1 * task.priority);
|
||||
} else {
|
||||
score -= task.priority;
|
||||
score -= 100 + (1 * task.priority);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (transition) {
|
||||
const minutes = transition.time / 1000 / 60
|
||||
score -= minutes;
|
||||
score -= 10 + (1 * minutes);
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
15
src/features/planner/context.ts
Normal file
15
src/features/planner/context.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { createContext } from 'react';
|
||||
import { Strategies } from "./algorithm/build-graph";
|
||||
|
||||
type PlannerOptions = {
|
||||
strategy: Strategies;
|
||||
}
|
||||
type PlannerContextValue = {
|
||||
options: PlannerOptions;
|
||||
setOptions: (options: Partial<PlannerOptions>) => void;
|
||||
}
|
||||
|
||||
const PlannerContext = createContext<PlannerContextValue>(undefined as any);
|
||||
|
||||
export type { PlannerContextValue, PlannerOptions };
|
||||
export { PlannerContext };
|
||||
@@ -5,9 +5,10 @@ import { useAsyncCallback } from "#/hooks/async";
|
||||
import { UserLocation } from "#/types/location";
|
||||
import { useDate } from "../calendar";
|
||||
import { useTasksWithContext } from "../agenda-context";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useContext, useMemo, useState } from "react";
|
||||
import { PlanItem } from "#/types/plans";
|
||||
import { Task } from "#/types/task";
|
||||
import { PlannerContext } from "./context";
|
||||
|
||||
export type UsePlanOptions = {
|
||||
location: UserLocation;
|
||||
@@ -23,10 +24,21 @@ export type UsePlan = [
|
||||
}
|
||||
]
|
||||
|
||||
export const usePlanOptions = () => {
|
||||
const { options } = useContext(PlannerContext);
|
||||
return options;
|
||||
}
|
||||
|
||||
export const useSetPlanOptions = () => {
|
||||
const { setOptions } = useContext(PlannerContext);
|
||||
return setOptions;
|
||||
}
|
||||
|
||||
export const usePlan = ({
|
||||
location,
|
||||
}: UsePlanOptions): UsePlan => {
|
||||
const today = useDate();
|
||||
const planOptions = usePlanOptions();
|
||||
const [status, setStatus] = useState<Status>();
|
||||
const all = useTasksWithContext();
|
||||
const enabled = useMemo(() => all.filter(f => f.enabled), [all])
|
||||
@@ -37,7 +49,7 @@ export const usePlan = ({
|
||||
location,
|
||||
time: start || today,
|
||||
tasks: enabled,
|
||||
strategy: Strategies.firstComplet,
|
||||
strategy: planOptions.strategy,
|
||||
context: {
|
||||
getTransition,
|
||||
},
|
||||
@@ -50,7 +62,7 @@ export const usePlan = ({
|
||||
agenda: day,
|
||||
};
|
||||
},
|
||||
[today, location, all, setStatus],
|
||||
[today, location, all, setStatus, planOptions],
|
||||
);
|
||||
|
||||
return [
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
export { PlannerProvider } from './provider';
|
||||
export type { PlannerOptions } from './context';
|
||||
export { Strategies } from './algorithm/build-graph';
|
||||
export * from './hooks';
|
||||
|
||||
32
src/features/planner/provider.tsx
Normal file
32
src/features/planner/provider.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { ReactNode, useCallback, useState } from 'react';
|
||||
import { Strategies } from './algorithm/build-graph';
|
||||
import { PlannerContext, PlannerOptions } from './context';
|
||||
|
||||
type PlannerProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const PlannerProvider: React.FC<PlannerProviderProps> = ({ children }) => {
|
||||
const [options, setOwnOptions] = useState<PlannerOptions>({
|
||||
strategy: Strategies.firstComplet,
|
||||
})
|
||||
|
||||
const setOptions = useCallback(
|
||||
(next: Partial<PlannerOptions>) => {
|
||||
setOwnOptions(current => ({
|
||||
...current,
|
||||
...next,
|
||||
}))
|
||||
},
|
||||
[setOwnOptions],
|
||||
);
|
||||
|
||||
return (
|
||||
<PlannerContext.Provider value={{ options, setOptions }}>
|
||||
{children}
|
||||
</PlannerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export type { PlannerProviderProps };
|
||||
export { PlannerProvider };
|
||||
Reference in New Issue
Block a user