import { pairs } from '../util.js'; /* agent structure: * { * id: string * net: network * state: network_state * x, y: number * flags: object * } */ /* action structure: * { * name: string, * propose: (agent) => proposal[] * } */ /* proposal structure * all proposed world and agent changes must be included for the proposed action to be valid * similarly, if any world or agent change creates merge conflicts, the proposal cannot be merged * and must be removed * { * world_changes: proposal_world_change[]? * agent_changes: proposal_agent_change[]? * } */ /* proposal_world_change * { * x, y: number * from: string * to: string * } */ /* proposal_agent_change * { * agent_id: string * x, y: number? * flags: object? * } */ function world_change_conflict(a, b) { if (a.x != b.x) { return [false, false]; } if (a.y != b.y) { return [false, false]; } if (a.to != b.to) { return [true, false]; } // x, y, and to all match -- merge return [false, true]; } function proposal_conflict_merge(a, b) { const [world_conflict, world_merge] = pairs(a.world_changes, b.world_changes).reduce( (acc, [a, b]) => { const [conflict, merge] = world_change_conflict(a, b); return [acc[0] || conflict, acc[1] || merge]; }, [false, false] ) return [world_conflict, world_merge]; } // merge proposals // if two sub-updates conflict, they are both omitted from the final merged proposal export function proposal_merge(arr, proposal) { const conflict_merge = arr.map(x => proposal_conflict_merge(x, proposal)); // if any conflicts are detected then don't merge if (conflict_merge.reduce((acc, [c, m]) => acc || c, false)) { const conflict_free = arr.filter((x, i) => !conflict_merge[i][0]); return conflict_free; } else { // no conflicts, but need to merge identical actions const no_merge = arr.filter((x, i) => !conflict_merge[i][1]); return [...no_merge, proposal]; } }