summaryrefslogtreecommitdiff
path: root/src/world/agent.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/world/agent.js')
-rw-r--r--src/world/agent.js83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/world/agent.js b/src/world/agent.js
new file mode 100644
index 0000000..7ab501c
--- /dev/null
+++ b/src/world/agent.js
@@ -0,0 +1,83 @@
+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];
+ }
+}