From bb653db5b1448e2d6f05f6162a2800f1ba4a79a3 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Wed, 20 Sep 2023 19:53:45 +0200 Subject: [PATCH 01/24] add passing tests for linklock waiting --- src/graph.test.ts | 2 ++ src/graph.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/graph.test.ts b/src/graph.test.ts index 32ce61a..2fa35ac 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1202,9 +1202,11 @@ describe('Components', () => { expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") expect(linkLock.requestLock('agent1', 'down')).toEqual("FREE") expect(linkLock.requestLock('agent2', 'up')).toEqual("CON") + expect(linkLock.isWaiting('agent2')).toBeTruthy() linkLock.unlock('agent1', 'up') expect(linkLock.requestLock('agent2', 'up')).toEqual("CON") + expect(linkLock.isWaiting('agent2')).toBeTruthy() expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") linkLock.unlock('agent1', 'down') diff --git a/src/graph.ts b/src/graph.ts index 7ccba8d..1d89953 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -104,6 +104,10 @@ class LinkLock { } } + isWaiting (who: string) { + return this._lock.waiting.has(who) + } + requestLock (byWhom: string, direction: string): LinkLockType { this.check(direction) // already locked by me From 05f37008d6cae6a2ecfb945bf4a9e8998fecb87d Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Wed, 20 Sep 2023 19:54:23 +0200 Subject: [PATCH 02/24] clear waiting when obtained --- src/graph.test.ts | 1 + src/graph.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/graph.test.ts b/src/graph.test.ts index 2fa35ac..1fb3dde 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1211,6 +1211,7 @@ describe('Components', () => { expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") linkLock.unlock('agent1', 'down') expect(linkLock.requestLock('agent2', 'up')).toEqual("PRO") + expect(linkLock.isWaiting('agent2')).toBeFalsy() }) }) }) diff --git a/src/graph.ts b/src/graph.ts index 1d89953..1096eec 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -123,6 +123,7 @@ class LinkLock { // if its locked by anyone else, in the direction we are going if (this._lock.isLocked() && this._directions.size === 1 && this._directions.has(direction)) { this._lock.forceLock(byWhom) // add ourselves to the list + this._lock.waiting.delete(byWhom) return "PRO" } From 79626aa2091cc6c378f52e1a557fb1beecbf77e5 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Wed, 20 Sep 2023 18:46:30 +0200 Subject: [PATCH 03/24] ensure we wait for the other direction --- src/graph.test.ts | 1 + src/graph.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index 1fb3dde..bf1c9bd 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1195,6 +1195,7 @@ describe('Components', () => { expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") expect(linkLock.requestLock('agent2', 'up')).toEqual("PRO") expect(linkLock.requestLock('agent1', 'down')).toEqual("CON") + expect(linkLock.isWaiting('agent1')).toBeTruthy() }) test('agent cannot lock if both directions already locked', () => { const creator = new Graferse(node => node.id) diff --git a/src/graph.ts b/src/graph.ts index 1096eec..221f554 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -113,8 +113,11 @@ class LinkLock { // already locked by me if (this._lock.isLocked(byWhom)) { if (this._directions.size === 1 && !this._directions.has(direction)) { - if (this._lock.isLockedByOtherThan(byWhom)) + if (this._lock.isLockedByOtherThan(byWhom)) { + debug(`Resource 'link from ${direction}' is locked, ${byWhom} will wait`) + this._lock.waiting.add(byWhom) return "CON" + } this._directions.add(direction) } return "FREE" From 4ffa850ac0ebd5b8ab2f349ae01ac7198e1ebe5e Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Wed, 20 Sep 2023 23:00:58 +0200 Subject: [PATCH 04/24] linklock can do its own special locking --- src/graph.ts | 117 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index 221f554..4af3954 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -84,81 +84,104 @@ class Lock { type LinkLockType = "FREE" | "PRO" | "CON" class LinkLock { - private _lock: Lock = new Lock("linklock") - private _directions: Set = new Set() - private _allowed_directions: Set + private _lockers = new Map>() + private _waiters = new Map>() + private _otherdir = new Map() check (direction: string) { - if (!this._allowed_directions.has(direction)) - throw new Error(`no such direction ${direction}`) + if (!this._waiters.get(direction)) + throw new Error(`no such wait direction ${direction}`) + if (!this._lockers.get(direction)) + throw new Error(`no such lock direction ${direction}`) + if (!this._otherdir.get(direction)) + throw new Error(`no such other direction ${direction}`) } - constructor (from: string, to: string) { - this._allowed_directions = new Set([from, to]) + //isWaiting (who: string, direction: string) { + // check(direction) + // return this._waiters.get(direction).has(who) + //} + + isWaiting (who: string) { + return Array.from(this._otherdir.keys()).some(dir => { + const waiters = this._waiters.get(dir) + return waiters?.has(who) + }) } getDetails() { return { - directions: this._directions, - who: this._lock.lockedBy, + lockers: this._lockers, + waiters: this._waiters, } } - isWaiting (who: string) { - return this._lock.waiting.has(who) + constructor (to: string, from: string) { + this._lockers.set(to, new Set()) + this._lockers.set(from, new Set()) + + this._waiters.set(to, new Set()) + this._waiters.set(from, new Set()) + + this._otherdir.set(to, from) + this._otherdir.set(from, to) } requestLock (byWhom: string, direction: string): LinkLockType { this.check(direction) - // already locked by me - if (this._lock.isLocked(byWhom)) { - if (this._directions.size === 1 && !this._directions.has(direction)) { - if (this._lock.isLockedByOtherThan(byWhom)) { - debug(`Resource 'link from ${direction}' is locked, ${byWhom} will wait`) - this._lock.waiting.add(byWhom) - return "CON" - } - this._directions.add(direction) - } + + // I already have it locked in this direction + const lockers = this._lockers.get(direction) + if (!lockers) throw new Error("no lockers!") + if (lockers.has(byWhom)) return "FREE" - } - // if its locked by anyone else, in the direction we are going - if (this._lock.isLocked() && this._directions.size === 1 && this._directions.has(direction)) { - this._lock.forceLock(byWhom) // add ourselves to the list - this._lock.waiting.delete(byWhom) - return "PRO" + // No one except me has it locked in the other direction + const against = this._lockers.get(this._otherdir.get(direction) as string) || new Set() + if (against.size === 0 || (against.size === 1 && against.has(byWhom))) { + lockers.add(byWhom) + return lockers.size > 1 + ? "PRO" + : "FREE" } - // its not locked by anyone - if (this._lock.requestLock(byWhom, "link from " + direction)) { - this._directions.add(direction) - return "FREE" - } + debug(`Resource 'link from ${direction}' is locked, ${byWhom} should wait`) + this._waiters.get(direction)?.add(byWhom) return "CON" } unlock (byWhom: string, direction?: string) { - if (direction) this.check(direction) - // if its locked only by a single robot - if (this._lock.isLocked(byWhom) && !this._lock.isLockedByOtherThan(byWhom)) { - if (direction) { - this._directions.delete(direction) - - // if we still are holding one direction, dont release lock - if (this._directions.size > 0) - return - } else { - this._directions.clear() - } - } + const dirsToUnlock = Array + .from(this._otherdir.keys()) + .filter(dir => !direction || dir === direction) + + dirsToUnlock.forEach(dir => this._lockers.get(dir)?.delete(byWhom)) + + const waiters = new Set() + + dirsToUnlock.forEach(dir => { + const otherdirwaiters = this._waiters.get(this._otherdir.get(dir) as string) + otherdirwaiters?.forEach(waiter => { + const tmp = new Set(this._lockers.get(dir)) + tmp.delete(waiter) + if (tmp.size === 0) { + waiters.add(waiter) + otherdirwaiters.delete(waiter) + } + }) + }) - return this._lock.unlock(byWhom) + return waiters } isLocked(byWhom?: string) { - return this._lock.isLocked(byWhom) + return Array.from(this._otherdir.keys()).some(dir => { + const lockers = this._lockers.get(dir) as Set + return byWhom + ? lockers.has(byWhom) + : lockers.size > 0 + }) } } From 186eebd955c7413ca7ce4be3c56c1a95fab9427e Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Wed, 20 Sep 2023 23:01:19 +0200 Subject: [PATCH 05/24] just the passing tests --- src/graph.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index bf1c9bd..b0f0f8d 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1182,7 +1182,7 @@ describe('Components', () => { logSpyError.mockReset() }) - describe('Locking in both directions', () => { + describe.only('Locking in both directions', () => { test('single owner can lock both directions', () => { const creator = new Graferse(node => node.id) const linkLock = creator.makeLinkLock('up', 'down', true) // is bidirectional @@ -1205,12 +1205,12 @@ describe('Components', () => { expect(linkLock.requestLock('agent2', 'up')).toEqual("CON") expect(linkLock.isWaiting('agent2')).toBeTruthy() - linkLock.unlock('agent1', 'up') + expect(linkLock.unlock('agent1', 'up')).toEqual(new Set()) expect(linkLock.requestLock('agent2', 'up')).toEqual("CON") expect(linkLock.isWaiting('agent2')).toBeTruthy() expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") - linkLock.unlock('agent1', 'down') + expect(linkLock.unlock('agent1', 'down')).toEqual(new Set(["agent2"])) expect(linkLock.requestLock('agent2', 'up')).toEqual("PRO") expect(linkLock.isWaiting('agent2')).toBeFalsy() }) From e179cce2451683adc3b4c7057f7c6726564ab3c5 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Wed, 20 Sep 2023 23:03:41 +0200 Subject: [PATCH 06/24] up one level --- src/graph.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index b0f0f8d..325dcf0 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1165,7 +1165,7 @@ describe('Components', () => { }) }) - describe('LinkLock', () => { + describe.only('LinkLock', () => { test('locking when directed edge', () => { const logSpyWarn = jest.spyOn(console, 'warn').mockImplementation() const logSpyError = jest.spyOn(console, 'error').mockImplementation() @@ -1182,7 +1182,7 @@ describe('Components', () => { logSpyError.mockReset() }) - describe.only('Locking in both directions', () => { + describe('Locking in both directions', () => { test('single owner can lock both directions', () => { const creator = new Graferse(node => node.id) const linkLock = creator.makeLinkLock('up', 'down', true) // is bidirectional From 5bc2d14925a24664de995c968e2a19fc3ec37ce6 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 09:51:38 +0200 Subject: [PATCH 07/24] unleash --- src/graph.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index 325dcf0..b950a55 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1165,7 +1165,7 @@ describe('Components', () => { }) }) - describe.only('LinkLock', () => { + describe('LinkLock', () => { test('locking when directed edge', () => { const logSpyWarn = jest.spyOn(console, 'warn').mockImplementation() const logSpyError = jest.spyOn(console, 'error').mockImplementation() From 0cc35bb543c7e50ccebe2e31c98a2f502d57131c Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 10:53:33 +0200 Subject: [PATCH 08/24] return only boolean --- src/graph.test.ts | 24 ++++++++++++------------ src/graph.ts | 18 +++++++----------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index b950a55..d606d35 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1173,7 +1173,7 @@ describe('Components', () => { const linkLock = creator.makeLinkLock('up', 'down') // by default is directed edge expect(logSpyWarn).not.toHaveBeenCalled() expect(logSpyError).not.toHaveBeenCalled() - expect(linkLock.requestLock('test', 'up')).toEqual("FREE") + expect(linkLock.requestLock('test', 'up')).toBeTruthy() expect(logSpyWarn).toHaveBeenCalled() expect(logSpyError).toHaveBeenCalled() @@ -1186,32 +1186,32 @@ describe('Components', () => { test('single owner can lock both directions', () => { const creator = new Graferse(node => node.id) const linkLock = creator.makeLinkLock('up', 'down', true) // is bidirectional - expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") - expect(linkLock.requestLock('agent1', 'down')).toEqual("FREE") + expect(linkLock.requestLock('agent1', 'up')).toBeTruthy() + expect(linkLock.requestLock('agent1', 'down')).toBeTruthy() }) test('owner cannot lock both directions if multiple owners', () => { const creator = new Graferse(node => node.id) const linkLock = creator.makeLinkLock('up', 'down', true) // is bidirectional - expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") - expect(linkLock.requestLock('agent2', 'up')).toEqual("PRO") - expect(linkLock.requestLock('agent1', 'down')).toEqual("CON") + expect(linkLock.requestLock('agent1', 'up')).toBeTruthy() + expect(linkLock.requestLock('agent2', 'up')).toBeTruthy() + expect(linkLock.requestLock('agent1', 'down')).toBeFalsy() expect(linkLock.isWaiting('agent1')).toBeTruthy() }) test('agent cannot lock if both directions already locked', () => { const creator = new Graferse(node => node.id) const linkLock = creator.makeLinkLock('up', 'down', true) // is bidirectional - expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") - expect(linkLock.requestLock('agent1', 'down')).toEqual("FREE") - expect(linkLock.requestLock('agent2', 'up')).toEqual("CON") + expect(linkLock.requestLock('agent1', 'up')).toBeTruthy() + expect(linkLock.requestLock('agent1', 'down')).toBeTruthy() + expect(linkLock.requestLock('agent2', 'up')).toBeFalsy() expect(linkLock.isWaiting('agent2')).toBeTruthy() expect(linkLock.unlock('agent1', 'up')).toEqual(new Set()) - expect(linkLock.requestLock('agent2', 'up')).toEqual("CON") + expect(linkLock.requestLock('agent2', 'up')).toBeFalsy() expect(linkLock.isWaiting('agent2')).toBeTruthy() - expect(linkLock.requestLock('agent1', 'up')).toEqual("FREE") + expect(linkLock.requestLock('agent1', 'up')).toBeTruthy() expect(linkLock.unlock('agent1', 'down')).toEqual(new Set(["agent2"])) - expect(linkLock.requestLock('agent2', 'up')).toEqual("PRO") + expect(linkLock.requestLock('agent2', 'up')).toBeTruthy() expect(linkLock.isWaiting('agent2')).toBeFalsy() }) }) diff --git a/src/graph.ts b/src/graph.ts index 4af3954..128ce8d 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -81,8 +81,6 @@ class Lock { } } -type LinkLockType = "FREE" | "PRO" | "CON" - class LinkLock { private _lockers = new Map>() private _waiters = new Map>() @@ -127,28 +125,26 @@ class LinkLock { this._otherdir.set(from, to) } - requestLock (byWhom: string, direction: string): LinkLockType { + requestLock (byWhom: string, direction: string): boolean { this.check(direction) // I already have it locked in this direction const lockers = this._lockers.get(direction) if (!lockers) throw new Error("no lockers!") if (lockers.has(byWhom)) - return "FREE" + return true // No one except me has it locked in the other direction const against = this._lockers.get(this._otherdir.get(direction) as string) || new Set() if (against.size === 0 || (against.size === 1 && against.has(byWhom))) { lockers.add(byWhom) - return lockers.size > 1 - ? "PRO" - : "FREE" + return true } debug(`Resource 'link from ${direction}' is locked, ${byWhom} should wait`) this._waiters.get(direction)?.add(byWhom) - return "CON" + return false } unlock (byWhom: string, direction?: string) { @@ -186,10 +182,10 @@ class LinkLock { } class OnewayLinkLock extends LinkLock { - requestLock (byWhom: string, direction: string): LinkLockType { + requestLock (byWhom: string, direction: string): boolean { console.error("Who is trying to lock a non-bidir link?", {byWhom, direction}) console.warn("This will cause problems because it should stop locking here") - return "FREE" + return true } } @@ -309,7 +305,7 @@ class Graferse const linkLockResult = linkLock.requestLock(byWhom, fromNodeId) // if it failed to lock because of opposing direction - if (linkLockResult === "CON") { + if (!linkLockResult) { console.warn(` fail - ${desc} locked against us`) console.warn(linkLock.getDetails()) return false From 72f8f9c79d7ac878adb35eb33197d283d815a120 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 11:07:36 +0200 Subject: [PATCH 09/24] simple locked node counter --- src/graph.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/graph.ts b/src/graph.ts index 128ce8d..f3197ee 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -288,7 +288,14 @@ class Graferse // till the last node in the path // returns false if first edge fails, otherwise returns true // as we can proceed some of the way in the same direction + + let lockedNodesEncountered = 0 const tryLockAllBidirectionalEdges = (subpath: T[]) => { + if (subpath.length > 0) { + if (getLock(subpath[0]).isLockedByOtherThan(byWhom)) { + lockedNodesEncountered++ + } + } if (subpath.length < 2) { return true } @@ -383,11 +390,13 @@ class Graferse // TODO consider returning the length of obtained edge locks // if its > 0, even though further failed, allow the againt to retain the node lock // so we can enter corridors as far as we can and wait there + lockedNodesEncountered = 0 if (!tryLockAllBidirectionalEdges(path.slice(i))) { // unlock previously obtained node lock whoCanMoveNow.addAll(lock.unlock(byWhom)) break } + console.log(`Encountered ${lockedNodesEncountered} locks along the way`) nextNodes.push({node: this.identity(path[i]), index: i}) } From 8a6e896b95ee3cfa41edf94c87499bef5c03f97d Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 11:39:49 +0200 Subject: [PATCH 10/24] wip idea --- src/graph.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/graph.ts b/src/graph.ts index f3197ee..c2c57f2 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -290,13 +290,19 @@ class Graferse // as we can proceed some of the way in the same direction let lockedNodesEncountered = 0 + let lastWasBidrectional = false + let lastEncouteredLock = undefined const tryLockAllBidirectionalEdges = (subpath: T[]) => { if (subpath.length > 0) { - if (getLock(subpath[0]).isLockedByOtherThan(byWhom)) { + const lock = getLock(subpath[0]) + if (lock.isLockedByOtherThan(byWhom)) { lockedNodesEncountered++ + lastEncouteredLock = lock } } + //TODO lockedNodesEncountered needs to be checked against if (subpath.length < 2) { + lastWasBidrectional = true return true } // TODO will these locks and unlocks trigger waiters? @@ -391,6 +397,8 @@ class Graferse // if its > 0, even though further failed, allow the againt to retain the node lock // so we can enter corridors as far as we can and wait there lockedNodesEncountered = 0 + lastWasBidrectional = false + lastEncouteredLock = undefined if (!tryLockAllBidirectionalEdges(path.slice(i))) { // unlock previously obtained node lock whoCanMoveNow.addAll(lock.unlock(byWhom)) From d4b2a31fe82cd154a9e4ee342db9a63de39d7332 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 11:57:17 +0200 Subject: [PATCH 11/24] experimental --- src/graph.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index c2c57f2..265ff42 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -290,8 +290,7 @@ class Graferse // as we can proceed some of the way in the same direction let lockedNodesEncountered = 0 - let lastWasBidrectional = false - let lastEncouteredLock = undefined + let lastEncouteredLock: Lock const tryLockAllBidirectionalEdges = (subpath: T[]) => { if (subpath.length > 0) { const lock = getLock(subpath[0]) @@ -302,7 +301,14 @@ class Graferse } //TODO lockedNodesEncountered needs to be checked against if (subpath.length < 2) { - lastWasBidrectional = true + if (lockedNodesEncountered > 0) { + // we ended our path on a bidir edge (likely a trolly location) + // fail, and wait on the last lock we encountered + if (lastEncouteredLock.requestLock(byWhom, "capacity")) { + throw new Error("This lock should not succeed") + } + return false + } return true } // TODO will these locks and unlocks trigger waiters? @@ -397,8 +403,7 @@ class Graferse // if its > 0, even though further failed, allow the againt to retain the node lock // so we can enter corridors as far as we can and wait there lockedNodesEncountered = 0 - lastWasBidrectional = false - lastEncouteredLock = undefined + lastEncouteredLock if (!tryLockAllBidirectionalEdges(path.slice(i))) { // unlock previously obtained node lock whoCanMoveNow.addAll(lock.unlock(byWhom)) From 5b0568d1fab13252f7ef62e018bb7c126ec9ca95 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 19:17:34 +0200 Subject: [PATCH 12/24] new linklocker is more cautious --- src/graph.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index d606d35..e70b4e2 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1054,15 +1054,18 @@ describe('ngraph', () => { agent1at('e') expect(nextNodes1).toEqual([{index: 4, node: 'e'}, {index: 5, node: 'z'}]) - expect(nextNodes2).toEqual([{index: 0, node: 'g'}, {index: 1, node: 'f'}]) // seems a little early to obtain nodeF + expect(nextNodes2).toEqual([{index: 0, node: 'g'}]) + agent1at('z') + expect(nextNodes1).toEqual([{index: 5, node: 'z'}]) + expect(nextNodes2).toEqual([{index: 0, node: 'g'}, {index: 1, node: 'f'}]) // now we can move to F agent2at('f') - expect(nextNodes1).toEqual([{index: 4, node: 'e'}, {index: 5, node: 'z'}]) - expect(nextNodes2).toEqual([{index: 1, node: 'f'}]) // cant get E because agent1 is tehre + expect(nextNodes1).toEqual([{index: 5, node: 'z'}]) + expect(nextNodes2).toEqual([{index: 1, node: 'f'}, {index: 2, node: 'e'}]) - agent1at('z') + agent2at('e') expect(nextNodes1).toEqual([{index: 5, node: 'z'}]) - expect(nextNodes2).toEqual([{index: 1, node: 'f'}, {index: 2, node: 'e'}]) // now we can move to E + expect(nextNodes2).toEqual([{index: 2, node: 'e'}, {index: 3, node: 'd'}]) }) // TODO add test that shows we are waiting on distant edge //test('two robots opposing directions in a narrow corridor', () => { From df3c7c4fa0115247c50a077918392aad0bf8f186 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 20:28:29 +0200 Subject: [PATCH 13/24] use linklocks on links --- src/graph.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index e70b4e2..75e416d 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -386,9 +386,9 @@ describe('ngraph', () => { // ^ // / // B - graph.addLink('a', 'c', creator.makeLock('ac')) - graph.addLink('b', 'c', creator.makeLock('bc')) - graph.addLink('c', 'd', creator.makeLock('cd')) + graph.addLink('a', 'c', creator.makeLinkLock('a', 'c')) + graph.addLink('b', 'c', creator.makeLinkLock('b', 'c')) + graph.addLink('c', 'd', creator.makeLinkLock('c', 'd')) const pathFinder = ngraphPath.aStar(graph, { oriented: true }) const s1Path = pathFinder.find('a', 'd').reverse() From d8754e462bd1464af98e62347efb82e3cbe754ef Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 20:28:45 +0200 Subject: [PATCH 14/24] new linklocker is more cautious --- src/graph.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index 75e416d..3d4a910 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -759,11 +759,11 @@ describe('ngraph', () => { s1LockNext.arrivedAt(s1Path.indexOf(nodeC)) expect(s1ForwardPath).toEqual([{index: 2, node: 'c'}, {index: 3, node: 'e'}]) - // agent2 obtains nodeD just before stepping off bidir lane - expect(s2ForwardPath).toEqual([{index: 0, node: 'd'}]) + expect(s2ForwardPath).toEqual([]) s1LockNext.arrivedAt(s1Path.indexOf(nodeE)) expect(s1ForwardPath).toEqual([{index: 3, node: 'e'}]) + // agent2 obtains nodeD and C after stepping off bidir lane expect(s2ForwardPath).toEqual([{index: 0, node: 'd'}, {index: 1, node: 'c'}]) }) From c183551f6e0ffe758f1db02767f4b0328e8eecca Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Thu, 21 Sep 2023 21:11:52 +0200 Subject: [PATCH 15/24] handle reversing paths with encountered agents --- src/graph.test.ts | 67 +++++++++++++++++++++++++++++++++++++++++++++++ src/graph.ts | 15 ++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/graph.test.ts b/src/graph.test.ts index 3d4a910..31d745d 100644 --- a/src/graph.test.ts +++ b/src/graph.test.ts @@ -1156,6 +1156,73 @@ describe('ngraph', () => { lockNext.arrivedAt(i) } }) + test('agent encountered on bidir path with reversal', () => { + const graph = ngraphCreateGraph() + const creator = new Graferse>(node => node.id) + + const makeNode = (id: string) => graph.addNode(id, creator.makeLock(id)) + const nodeA = makeNode('a') + const nodeB = makeNode('b') + const nodeC = makeNode('c') + const nodeD = makeNode('d') + + // A ----> B <----> C + // | + // v + // D + + const lockAB = creator.makeLinkLock('a', 'b', false) + const lockBC = creator.makeLinkLock('b', 'c', true) + const lockCD = creator.makeLinkLock('b', 'd', false) + + const linkAB = graph.addLink('a', 'b', lockAB) + const linkBC = graph.addLink('b', 'c', lockBC) + const linkBD = graph.addLink('b', 'd', lockCD) + + const linkDB = graph.addLink('d', 'b', lockCD) + const linkCB = graph.addLink('c', 'b', lockBC) + const linkBA = graph.addLink('b', 'a', lockAB) + + const s1Path = [nodeA, nodeB, nodeC, nodeB, nodeD] + + const makeLocker = creator.makeMakeLocker(node => node.data, getLockForLink) + var s1ForwardPath: Array = [] + const s1LockNext = makeLocker("agent1").makePathLocker(s1Path)((nextNodes) => { s1ForwardPath = nextNodes }) + + //console.dir({nodeC}, {depth: null}) + expect(nodeC.data.requestLock('agent2', 'static')).toBeTruthy() + // all nodes are unlocked + expect(nodeA.data.isLocked()).toBeFalsy() + expect(nodeB.data.isLocked()).toBeFalsy() + expect(nodeC.data.isLocked()).toBeTruthy() // locked by static agent2 + expect(nodeD.data.isLocked()).toBeFalsy() + // all links are unlocked + expect(linkAB.data.isLocked()).toBeFalsy() + expect(linkBC.data.isLocked()).toBeFalsy() + expect(linkBD.data.isLocked()).toBeFalsy() + expect(linkDB.data.isLocked()).toBeFalsy() + expect(linkCB.data.isLocked()).toBeFalsy() + expect(linkBA.data.isLocked()).toBeFalsy() + + expect(s1ForwardPath).toEqual([]) + + s1LockNext.arrivedAt(s1Path.indexOf(nodeA)) + // its current and next nodes are locked + // nodeB omitted becuase agent encountered on bidir path + expect(s1ForwardPath).toEqual([{index: 0, node: 'a'}/*, {index: 1, node: 'b'}*/]) + expect(nodeA.data.isLocked()).toBeTruthy() + expect(nodeB.data.isLocked()).toBeFalsy() + expect(nodeC.data.isLocked()).toBeTruthy() // still locked by agent2 + expect(nodeD.data.isLocked()).toBeFalsy() + + // all links are locked until path ends + expect(linkAB.data.isLocked()).toBeFalsy() // its oneway, never locked + expect(linkBC.data.isLocked()).toBeFalsy() + expect(linkBD.data.isLocked()).toBeFalsy() + expect(linkDB.data.isLocked()).toBeFalsy() + expect(linkCB.data.isLocked()).toBeFalsy() + expect(linkBA.data.isLocked()).toBeFalsy() // its oneway, never locked + }) }) describe('Components', () => { diff --git a/src/graph.ts b/src/graph.ts index 265ff42..dd6a6b1 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -291,7 +291,12 @@ class Graferse let lockedNodesEncountered = 0 let lastEncouteredLock: Lock + let willReverse = false const tryLockAllBidirectionalEdges = (subpath: T[]) => { + if (subpath.length > 2) { + if (this.identity(subpath[0]) === this.identity(subpath[2])) + willReverse = true + } if (subpath.length > 0) { const lock = getLock(subpath[0]) if (lock.isLockedByOtherThan(byWhom)) { @@ -318,6 +323,14 @@ class Graferse const fromNodeId = stringify(this.identity(subpath[0])) if (linkLock instanceof OnewayLinkLock) { console.debug(` ok - ${desc} not bidirectional`) + if (willReverse && lockedNodesEncountered > 0) { + // we ended our path on a bidir edge (likely a trolly location) + // fail, and wait on the last lock we encountered + if (lastEncouteredLock.requestLock(byWhom, "capacity")) { + throw new Error("This lock should not succeed") + } + return false + } return true } @@ -403,7 +416,7 @@ class Graferse // if its > 0, even though further failed, allow the againt to retain the node lock // so we can enter corridors as far as we can and wait there lockedNodesEncountered = 0 - lastEncouteredLock + willReverse = false if (!tryLockAllBidirectionalEdges(path.slice(i))) { // unlock previously obtained node lock whoCanMoveNow.addAll(lock.unlock(byWhom)) From 055e4ebf2eece1abbedbdb2d282706f95074b95f Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 06:06:33 +0200 Subject: [PATCH 16/24] extract getLockedGroupLock --- src/graph.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index dd6a6b1..adb3c8e 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -260,20 +260,27 @@ class Graferse this.lockGroups.push(lockGroup) } - isLockGroupAvailable(lock: Lock, byWhom: string) { + getLockedGroupLock(lock: Lock, byWhom: string) { for(const lockGroup of this.lockGroups) { if (lockGroup.includes(lock)) { const lockedNode = lockGroup.filter(l => l !== lock) .find(l => l.isLockedByOtherThan(byWhom)) if (lockedNode) { - // wait on this locked node - if (lockedNode.requestLock(byWhom, "lockGroup")) { - throw new Error("lock was locked, but then not?") - } - return false + return lockedNode } } } + } + + isLockGroupAvailable(lock: Lock, byWhom: string) { + const lockedNode = this.getLockedGroupLock(lock, byWhom) + if (lockedNode) { + // wait on this locked node + if (lockedNode.requestLock(byWhom, "lockGroup")) { + throw new Error("lock was locked, but then not?") + } + return false + } return true } From d9dc4b649519f4ef2c03803df830e21125d5a78c Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 06:06:59 +0200 Subject: [PATCH 17/24] also consider lock groups --- src/graph.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/graph.ts b/src/graph.ts index adb3c8e..99ae36f 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -309,6 +309,12 @@ class Graferse if (lock.isLockedByOtherThan(byWhom)) { lockedNodesEncountered++ lastEncouteredLock = lock + } else { + const grouplock = this.getLockedGroupLock(lock, byWhom) + if (grouplock) { + lockedNodesEncountered++ + lastEncouteredLock = grouplock + } } } //TODO lockedNodesEncountered needs to be checked against From 14a43577376d0d827a01ccbe12c973244471d8c9 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 06:44:02 +0200 Subject: [PATCH 18/24] this was supposed to be simpler --- src/graph.ts | 52 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index 99ae36f..685e6c8 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -296,30 +296,29 @@ class Graferse // returns false if first edge fails, otherwise returns true // as we can proceed some of the way in the same direction - let lockedNodesEncountered = 0 - let lastEncouteredLock: Lock - let willReverse = false + let destinationNode: T|undefined + const encounteredLocks = new Set() const tryLockAllBidirectionalEdges = (subpath: T[]) => { if (subpath.length > 2) { if (this.identity(subpath[0]) === this.identity(subpath[2])) - willReverse = true + destinationNode = subpath[1] } if (subpath.length > 0) { const lock = getLock(subpath[0]) if (lock.isLockedByOtherThan(byWhom)) { - lockedNodesEncountered++ - lastEncouteredLock = lock - } else { - const grouplock = this.getLockedGroupLock(lock, byWhom) - if (grouplock) { - lockedNodesEncountered++ - lastEncouteredLock = grouplock - } + encounteredLocks.add(lock) } } //TODO lockedNodesEncountered needs to be checked against if (subpath.length < 2) { - if (lockedNodesEncountered > 0) { + const lock = getLock(destinationNode || subpath[0]) + const destIsLocked = lock.isLockedByOtherThan(byWhom) + const groupLock = this.getLockedGroupLock(lock, byWhom) + const lastEncouteredLock = encounteredLocks.size > 0 + ? Array.from(encounteredLocks).at(-1) + : destIsLocked ? lock + : groupLock + if (lastEncouteredLock) { // we ended our path on a bidir edge (likely a trolly location) // fail, and wait on the last lock we encountered if (lastEncouteredLock.requestLock(byWhom, "capacity")) { @@ -336,13 +335,22 @@ class Graferse const fromNodeId = stringify(this.identity(subpath[0])) if (linkLock instanceof OnewayLinkLock) { console.debug(` ok - ${desc} not bidirectional`) - if (willReverse && lockedNodesEncountered > 0) { - // we ended our path on a bidir edge (likely a trolly location) - // fail, and wait on the last lock we encountered - if (lastEncouteredLock.requestLock(byWhom, "capacity")) { - throw new Error("This lock should not succeed") + if (destinationNode) { + const lock = getLock(destinationNode) + const destIsLocked = lock.isLockedByOtherThan(byWhom) + const groupLock = this.getLockedGroupLock(lock, byWhom) + const lastEncouteredLock = encounteredLocks.size > 0 + ? Array.from(encounteredLocks).at(-1) + : destIsLocked ? lock + : groupLock + if (lastEncouteredLock) { + // we ended our path on a bidir edge (likely a trolly location) + // fail, and wait on the last lock we encountered + if (lastEncouteredLock.requestLock(byWhom, "capacity")) { + throw new Error("This lock should not succeed") + } + return false } - return false } return true } @@ -428,14 +436,14 @@ class Graferse // TODO consider returning the length of obtained edge locks // if its > 0, even though further failed, allow the againt to retain the node lock // so we can enter corridors as far as we can and wait there - lockedNodesEncountered = 0 - willReverse = false + encounteredLocks.clear() + destinationNode = undefined if (!tryLockAllBidirectionalEdges(path.slice(i))) { // unlock previously obtained node lock whoCanMoveNow.addAll(lock.unlock(byWhom)) break } - console.log(`Encountered ${lockedNodesEncountered} locks along the way`) + console.log(`Encountered ${encounteredLocks.size} locks along the way`) nextNodes.push({node: this.identity(path[i]), index: i}) } From 7b24badd08f571f8ccde8b82160d3d767823fb34 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 07:30:15 +0200 Subject: [PATCH 19/24] extract isPathObstructed --- src/graph.ts | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index 685e6c8..14a3526 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -290,6 +290,24 @@ class Graferse ) { type NextNodes = (nextNodes: NextNode[], remaining: number) => void return (byWhom: string) => { + const isPathObstructed = (destinationNode: T, encounteredLocks: Set) => { + const lock = getLock(destinationNode) + const destIsLocked = lock.isLockedByOtherThan(byWhom) + const groupLock = this.getLockedGroupLock(lock, byWhom) + const lastEncouteredLock = encounteredLocks.size > 0 + ? Array.from(encounteredLocks).at(-1) + : destIsLocked ? lock + : groupLock + if (lastEncouteredLock) { + // we ended our path on a bidir edge (likely a trolly location) + // fail, and wait on the last lock we encountered + if (lastEncouteredLock.requestLock(byWhom, "capacity")) { + throw new Error("This lock should not succeed") + } + return true + } + } + const makePathLocker = (path: T[]) => (callback: NextNodes) => { // given an index in the path, tries to lock all bidirectional edges // till the last node in the path @@ -311,19 +329,7 @@ class Graferse } //TODO lockedNodesEncountered needs to be checked against if (subpath.length < 2) { - const lock = getLock(destinationNode || subpath[0]) - const destIsLocked = lock.isLockedByOtherThan(byWhom) - const groupLock = this.getLockedGroupLock(lock, byWhom) - const lastEncouteredLock = encounteredLocks.size > 0 - ? Array.from(encounteredLocks).at(-1) - : destIsLocked ? lock - : groupLock - if (lastEncouteredLock) { - // we ended our path on a bidir edge (likely a trolly location) - // fail, and wait on the last lock we encountered - if (lastEncouteredLock.requestLock(byWhom, "capacity")) { - throw new Error("This lock should not succeed") - } + if (isPathObstructed(destinationNode || subpath[0], encounteredLocks)) { return false } return true @@ -336,19 +342,7 @@ class Graferse if (linkLock instanceof OnewayLinkLock) { console.debug(` ok - ${desc} not bidirectional`) if (destinationNode) { - const lock = getLock(destinationNode) - const destIsLocked = lock.isLockedByOtherThan(byWhom) - const groupLock = this.getLockedGroupLock(lock, byWhom) - const lastEncouteredLock = encounteredLocks.size > 0 - ? Array.from(encounteredLocks).at(-1) - : destIsLocked ? lock - : groupLock - if (lastEncouteredLock) { - // we ended our path on a bidir edge (likely a trolly location) - // fail, and wait on the last lock we encountered - if (lastEncouteredLock.requestLock(byWhom, "capacity")) { - throw new Error("This lock should not succeed") - } + if (isPathObstructed(destinationNode, encounteredLocks)) { return false } } From 138a6561d9adb1d460a21ccfc24da5692e4fc53f Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 07:34:51 +0200 Subject: [PATCH 20/24] destinationNode -> pivotNode --- src/graph.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index 14a3526..1607303 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -314,12 +314,12 @@ class Graferse // returns false if first edge fails, otherwise returns true // as we can proceed some of the way in the same direction - let destinationNode: T|undefined + let pivotNode: T|undefined const encounteredLocks = new Set() const tryLockAllBidirectionalEdges = (subpath: T[]) => { if (subpath.length > 2) { if (this.identity(subpath[0]) === this.identity(subpath[2])) - destinationNode = subpath[1] + pivotNode = subpath[1] } if (subpath.length > 0) { const lock = getLock(subpath[0]) @@ -329,7 +329,7 @@ class Graferse } //TODO lockedNodesEncountered needs to be checked against if (subpath.length < 2) { - if (isPathObstructed(destinationNode || subpath[0], encounteredLocks)) { + if (isPathObstructed(pivotNode || subpath[0], encounteredLocks)) { return false } return true @@ -341,8 +341,8 @@ class Graferse const fromNodeId = stringify(this.identity(subpath[0])) if (linkLock instanceof OnewayLinkLock) { console.debug(` ok - ${desc} not bidirectional`) - if (destinationNode) { - if (isPathObstructed(destinationNode, encounteredLocks)) { + if (pivotNode) { + if (isPathObstructed(pivotNode, encounteredLocks)) { return false } } @@ -431,7 +431,7 @@ class Graferse // if its > 0, even though further failed, allow the againt to retain the node lock // so we can enter corridors as far as we can and wait there encounteredLocks.clear() - destinationNode = undefined + pivotNode = undefined if (!tryLockAllBidirectionalEdges(path.slice(i))) { // unlock previously obtained node lock whoCanMoveNow.addAll(lock.unlock(byWhom)) From 09a7167ceb0686546431ebd859bce9f35a581320 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 07:35:09 +0200 Subject: [PATCH 21/24] remove temp vars --- src/graph.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index 1607303..fe00987 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -292,12 +292,10 @@ class Graferse return (byWhom: string) => { const isPathObstructed = (destinationNode: T, encounteredLocks: Set) => { const lock = getLock(destinationNode) - const destIsLocked = lock.isLockedByOtherThan(byWhom) - const groupLock = this.getLockedGroupLock(lock, byWhom) const lastEncouteredLock = encounteredLocks.size > 0 ? Array.from(encounteredLocks).at(-1) - : destIsLocked ? lock - : groupLock + : lock.isLockedByOtherThan(byWhom) ? lock + : this.getLockedGroupLock(lock, byWhom) if (lastEncouteredLock) { // we ended our path on a bidir edge (likely a trolly location) // fail, and wait on the last lock we encountered From f948c25f94f22bb0d3e8de55ea63bd9f24a70e38 Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 07:46:00 +0200 Subject: [PATCH 22/24] comment obstructed path --- src/graph.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index fe00987..2b7b251 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -297,8 +297,6 @@ class Graferse : lock.isLockedByOtherThan(byWhom) ? lock : this.getLockedGroupLock(lock, byWhom) if (lastEncouteredLock) { - // we ended our path on a bidir edge (likely a trolly location) - // fail, and wait on the last lock we encountered if (lastEncouteredLock.requestLock(byWhom, "capacity")) { throw new Error("This lock should not succeed") } @@ -315,6 +313,7 @@ class Graferse let pivotNode: T|undefined const encounteredLocks = new Set() const tryLockAllBidirectionalEdges = (subpath: T[]) => { + // check if the path turns back on itself if (subpath.length > 2) { if (this.identity(subpath[0]) === this.identity(subpath[2])) pivotNode = subpath[1] @@ -325,8 +324,9 @@ class Graferse encounteredLocks.add(lock) } } - //TODO lockedNodesEncountered needs to be checked against if (subpath.length < 2) { + // we ended our path on a bidir edge (likely a trolly location) + // fail, and wait on the last lock we encountered if (isPathObstructed(pivotNode || subpath[0], encounteredLocks)) { return false } From a0e8ca8eaf8f28a5c38f60cbea3c09a5c842176c Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 08:03:19 +0200 Subject: [PATCH 23/24] reorder waiters --- src/graph.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index 2b7b251..fecaa78 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -292,10 +292,8 @@ class Graferse return (byWhom: string) => { const isPathObstructed = (destinationNode: T, encounteredLocks: Set) => { const lock = getLock(destinationNode) - const lastEncouteredLock = encounteredLocks.size > 0 - ? Array.from(encounteredLocks).at(-1) - : lock.isLockedByOtherThan(byWhom) ? lock - : this.getLockedGroupLock(lock, byWhom) + const lastEncouteredLock = lock.isLockedByOtherThan(byWhom) ? lock + : Array.from(encounteredLocks).at(-1) || this.getLockedGroupLock(lock, byWhom) if (lastEncouteredLock) { if (lastEncouteredLock.requestLock(byWhom, "capacity")) { throw new Error("This lock should not succeed") From b0e7364499900b707b25905102492f1d7f90bf6f Mon Sep 17 00:00:00 2001 From: Aaron Lipinski Date: Sat, 23 Sep 2023 08:32:38 +0200 Subject: [PATCH 24/24] isPathObstructed -> waitOnObstructor --- src/graph.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graph.ts b/src/graph.ts index fecaa78..e075f29 100644 --- a/src/graph.ts +++ b/src/graph.ts @@ -290,7 +290,7 @@ class Graferse ) { type NextNodes = (nextNodes: NextNode[], remaining: number) => void return (byWhom: string) => { - const isPathObstructed = (destinationNode: T, encounteredLocks: Set) => { + const waitOnObstructor = (destinationNode: T, encounteredLocks: Set) => { const lock = getLock(destinationNode) const lastEncouteredLock = lock.isLockedByOtherThan(byWhom) ? lock : Array.from(encounteredLocks).at(-1) || this.getLockedGroupLock(lock, byWhom) @@ -325,7 +325,7 @@ class Graferse if (subpath.length < 2) { // we ended our path on a bidir edge (likely a trolly location) // fail, and wait on the last lock we encountered - if (isPathObstructed(pivotNode || subpath[0], encounteredLocks)) { + if (waitOnObstructor(pivotNode || subpath[0], encounteredLocks)) { return false } return true @@ -338,7 +338,7 @@ class Graferse if (linkLock instanceof OnewayLinkLock) { console.debug(` ok - ${desc} not bidirectional`) if (pivotNode) { - if (isPathObstructed(pivotNode, encounteredLocks)) { + if (waitOnObstructor(pivotNode, encounteredLocks)) { return false } }