summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/world/agent.js91
-rw-r--r--src/world/agent.test.js68
2 files changed, 139 insertions, 20 deletions
diff --git a/src/world/agent.js b/src/world/agent.js
index f7e5a63..95d869c 100644
--- a/src/world/agent.js
+++ b/src/world/agent.js
@@ -46,39 +46,90 @@ import { pairs, deepEqual } from '../util.js';
*/
+function flags_compatible(a, b) {
+ const keys = [...new Set(Object.keys(a).concat(Object.keys(b)))];
+ return keys.reduce(
+ (acc, key) => {
+ const eq = (a[key] === undefined || b[key] === undefined) ? true : deepEqual(a[key], b[key]);
+ return acc && eq;
+ },
+ true
+ );
+}
+
+
// return a tuple [conflict, merge]
// conflict is true if the two world_changes are incompatible
// merge is true if the two world changes are identical
// otherwise they are false
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];
+ if (deepEqual(a, b)) {
+ // merge
+ return [false, true];
+ }
+ if (
+ a.x === b.x &&
+ a.y === b.y &&
+ (a.to != b.to || !flags_compatible(a.flags || {}, b.flags || {}))
+ ) {
+ // conflict!
+ return [true, false];
+ } else {
+ // no conflict c:
+ return [false, false];
+ }
}
+// returns true as long as x & y are both defined
+function pos_exists(a) {
+ if (a.x !== undefined && a.y !== undefined) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+function pos_equal(a, b) {
+ if (a.x !== b.x) { return false; }
+ if (a.y !== b.y) { return false; }
+ return true;
+}
+
+
+// agent changes merge if they are identical
+// they conflict if two agents are trying to move to the same tile, or
+// if the same agent is trying to move to two different tiles, or if an agent
+// is being updated to incompatible flags
+//
+//
function agent_change_conflict(a, b) {
if (deepEqual(a, b)) {
// identical: merge
return [false, true];
- } else if (
- (a.agent_id === b.agent_id) &&
- ((a.x != b.x) || (a.y != b.y))
- ) {
- // same agent, different tiles: conflict
- return [true, false];
- } else if (
- (a.agent_id != b.agent_id) &&
- (a.x === b.x) &&
- (a.y === b.y)
- ) {
- // different agents, same tile: conflict
- return [true, false];
+ } else if (a.agent_id === b.agent_id) {
+ if (
+ pos_exists(a) && pos_exists(b) && !pos_equal(a, b)
+ ) {
+ // same agent, different positions: conflict
+ return [true, false];
+ } else if (!flags_compatible(a.flags, b.flags)) {
+ // same agent, incompatible flags: conflict
+ return [true, false];
+ } else {
+ // no conflict c:
+ return [false, false];
+ }
} else {
- // no conflict c:
- return [false, false];
+ // different agents
+ if (pos_exists(a) && pos_exists(b) && pos_equal(a, b)) {
+ // different agents, same position: conflict
+ return [true, false];
+ } else {
+ // no conflict c:
+ return [false, false];
+ }
}
}
diff --git a/src/world/agent.test.js b/src/world/agent.test.js
index 2a86908..fe73b25 100644
--- a/src/world/agent.test.js
+++ b/src/world/agent.test.js
@@ -5,6 +5,8 @@ import {
// --===== proposal conflicts =====--
+// tile updates
+
test("proposals changing different tiles don't conflict", () => {
const a = {
world_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable' }],
@@ -41,6 +43,32 @@ test("proposals changing the same tile to the same state merge to a single propo
});
+test("proposals with identical tile updates but incompatible tile flags conflict", () => {
+ const a = {
+ world_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'a' } }],
+ };
+ const b = {
+ world_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'b' } }],
+ };
+
+ expect(proposal_merge([a], b)).toEqual([]);
+});
+
+
+test("proposals with identical tile updates but compatible tile flags do not conflict", () => {
+ const a = {
+ world_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'a', r: 'd' } }],
+ };
+ const b = {
+ world_changes: [{ x: 4, y: 3, from: 'empty', to: 'mutable', flags: { v: 'a', u: 'b' } }],
+ };
+
+ expect(proposal_merge([a], b)).toEqual([a, b]);
+});
+
+
+
+
test("proposals moving two agents to the same tile conflict", () => {
const a = {
agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
@@ -53,6 +81,7 @@ test("proposals moving two agents to the same tile conflict", () => {
});
+// agent updates
test("proposals moving two agents to different tiles do not conflict", () => {
const a = {
agent_changes: [{ agent_id: 'aaa', x: 4, y: 3 }],
@@ -87,3 +116,42 @@ test("proposals moving the same agent to the same tile merge", () => {
expect(proposal_merge([a], b)).toEqual([a]);
});
+
+
+test("proposals setting flags on different agents do not conflict", () => {
+ const a = {
+ agent_changes: [{ agent_id: 'aaa', flags: { frozen: false } }],
+ };
+
+ const b = {
+ agent_changes: [{ agent_id: 'bbb', flags: { frozen: false } }],
+ };
+
+ expect(proposal_merge([a], b)).toEqual([a, b]);
+});
+
+
+test("setting the same agent to compatible flags does not conflict", () => {
+ const a = {
+ agent_changes: [{ agent_id: 'aaa', flags: { frozen: false } }],
+ };
+
+ const b = {
+ agent_changes: [{ agent_id: 'aaa', flags: { crumpet: 'hi' } }],
+ };
+
+ expect(proposal_merge([a], b)).toEqual([a, b]);
+});
+
+
+test("setting the same agent to incompatible flags does conflict", () => {
+ const a = {
+ agent_changes: [{ agent_id: 'aaa', flags: { frozen: false } }],
+ };
+
+ const b = {
+ agent_changes: [{ agent_id: 'aaa', flags: { frozen: true, crumpet: 'hi' } }],
+ };
+
+ expect(proposal_merge([a], b)).toEqual([]);
+});