Skip to content

Commit 73b9127

Browse files
committed
Use a separate function instead of a method for building validity
1 parent f3d12e0 commit 73b9127

File tree

4 files changed

+177
-140
lines changed

4 files changed

+177
-140
lines changed

src/Building.test.ts

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
Turn,
1010
reverseConnection,
1111
onFloor,
12+
isValidBuilding,
13+
assertValidBuilding,
1214
} from ".";
1315

1416
const { RIGHT, LEFT, BACK, FRONT } = Direction;
@@ -300,61 +302,72 @@ describe("Building.validity", () => {
300302
it("marks valid buildings correctly", () => {
301303
const valid = expect.objectContaining({ valid: true });
302304

303-
expect(
304-
new Building([new Hallway([new Room("a"), new Room("b")])]).validity
305-
).toEqual(valid);
305+
const b = new Building([new Hallway([new Room("a"), new Room("b")])]);
306+
expect(isValidBuilding(b)).toEqual(valid);
307+
assertValidBuilding(b);
306308

307309
expect(
308-
new Building([
309-
new Hallway([
310-
new Room("a"),
311-
new Room("b"),
312-
new Turn(RIGHT),
313-
new Fork(LEFT, "a", ""),
314-
]),
315-
]).validity
310+
isValidBuilding(
311+
new Building([
312+
new Hallway([
313+
new Room("a"),
314+
new Room("b"),
315+
new Turn(RIGHT),
316+
new Fork(LEFT, "a", ""),
317+
]),
318+
])
319+
)
316320
).toEqual(valid);
317321

318322
expect(
319-
new Building([
320-
new Hallway([new Room("a"), new Room("b"), new Fork(LEFT, "a", "")]),
321-
new Hallway([
322-
new Room("z"),
323-
new Fork(RIGHT, reverseConnection("a"), ""),
324-
]),
325-
]).validity
323+
isValidBuilding(
324+
new Building([
325+
new Hallway([new Room("a"), new Room("b"), new Fork(LEFT, "a", "")]),
326+
new Hallway([
327+
new Room("z"),
328+
new Fork(RIGHT, reverseConnection("a"), ""),
329+
]),
330+
])
331+
)
326332
).toEqual(valid);
327333

328334
expect(
329-
new Building([
330-
new Hallway([
331-
new Room("a"),
332-
new Room("b"),
333-
new Stairs(LEFT, onFloor("a", 2)),
334-
]),
335-
new Hallway([new Room("z"), new Stairs(RIGHT, onFloor("a", 1))]),
336-
]).validity
335+
isValidBuilding(
336+
new Building([
337+
new Hallway([
338+
new Room("a"),
339+
new Room("b"),
340+
new Stairs(LEFT, onFloor("a", 2)),
341+
]),
342+
new Hallway([new Room("z"), new Stairs(RIGHT, onFloor("a", 1))]),
343+
])
344+
)
337345
).toEqual(valid);
338346
});
339347

340348
it("marks buildings with duplicated names as invalid", () => {
341-
expect(
342-
new Building([new Hallway([new Room("a"), new Room("a")])]).validity
343-
).toEqual({
349+
const b = new Building([new Hallway([new Room("a"), new Room("a")])]);
350+
351+
expect(isValidBuilding(b)).toEqual({
344352
valid: false,
345353
reason: "There's more than one room with the name 'a'",
346354
connectedSections: [],
347355
});
356+
expect(() => {
357+
assertValidBuilding(b);
358+
}).toThrow("There's more than one room with the name 'a'");
348359

349360
expect(
350-
new Building([
351-
new Hallway([
352-
new Room("a"),
353-
new Room("b"),
354-
new Stairs(LEFT, onFloor("c", 4)),
355-
]),
356-
new Hallway([new Room("a"), new Stairs(RIGHT, onFloor("b", 1))]),
357-
]).validity
361+
isValidBuilding(
362+
new Building([
363+
new Hallway([
364+
new Room("a"),
365+
new Room("b"),
366+
new Stairs(LEFT, onFloor("c", 4)),
367+
]),
368+
new Hallway([new Room("a"), new Stairs(RIGHT, onFloor("b", 1))]),
369+
])
370+
)
358371
).toEqual(
359372
expect.objectContaining({
360373
valid: false,
@@ -365,14 +378,16 @@ describe("Building.validity", () => {
365378

366379
it("marks buildings with negative weights as invalid", () => {
367380
expect(
368-
new Building([
369-
new Hallway([new Room("a"), new Room("b"), new Fork(LEFT, "a", "")]),
370-
new Hallway([
371-
new Fork(RIGHT, "f", ""),
372-
new Room("z"),
373-
new Fork(RIGHT, reverseConnection("a"), "", -2),
374-
]),
375-
]).validity
381+
isValidBuilding(
382+
new Building([
383+
new Hallway([new Room("a"), new Room("b"), new Fork(LEFT, "a", "")]),
384+
new Hallway([
385+
new Fork(RIGHT, "f", ""),
386+
new Room("z"),
387+
new Fork(RIGHT, reverseConnection("a"), "", -2),
388+
]),
389+
])
390+
)
376391
).toEqual(
377392
expect.objectContaining({
378393
valid: false,
@@ -383,15 +398,17 @@ describe("Building.validity", () => {
383398

384399
it("marks buildings with no nodes in a Hallway as invalid", () => {
385400
expect(
386-
new Building([
387-
new Hallway([
388-
new Room("a"),
389-
new Room("b"),
390-
new Fork(LEFT, reverseConnection("b"), ""),
391-
]),
392-
new Hallway([new Room("z"), new Fork(RIGHT, "b", "")]),
393-
new Hallway([new Room("c"), new Room("d")]),
394-
]).validity
401+
isValidBuilding(
402+
new Building([
403+
new Hallway([
404+
new Room("a"),
405+
new Room("b"),
406+
new Fork(LEFT, reverseConnection("b"), ""),
407+
]),
408+
new Hallway([new Room("z"), new Fork(RIGHT, "b", "")]),
409+
new Hallway([new Room("c"), new Room("d")]),
410+
])
411+
)
395412
).toEqual(
396413
expect.objectContaining({
397414
valid: false,
@@ -402,19 +419,21 @@ describe("Building.validity", () => {
402419

403420
it("marks buildings with disconnected graphs as invalid", () => {
404421
expect(
405-
new Building([
406-
new Hallway([new Room("a"), new Room("b"), new Fork(LEFT, "a", "")]),
407-
new Hallway([
408-
new Room("z"),
409-
new Fork(RIGHT, reverseConnection("b"), ""),
410-
]),
411-
new Hallway([new Room("c"), new Fork(RIGHT, "8", "")]),
412-
]).validity
422+
isValidBuilding(
423+
new Building([
424+
new Hallway([new Room("a"), new Room("b"), new Fork(LEFT, "a", "")]),
425+
new Hallway([
426+
new Room("z"),
427+
new Fork(RIGHT, reverseConnection("b"), ""),
428+
]),
429+
new Hallway([new Room("c"), new Fork(RIGHT, "8", "")]),
430+
])
431+
)
413432
).toEqual(
414433
expect.objectContaining({
415434
valid: false,
416435
reason:
417-
"Not all nodes are connected; see building.validity.connectedSections to find which node groups are separated",
436+
"Not all nodes are connected; see isValidBuilding(building).connectedSections to find which node groups are separated.",
418437
})
419438
);
420439
});

src/Building.ts

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -265,81 +265,4 @@ export class Building<
265265
typeof name === "string" && this.getHallwayIndexAndIndex(name) != null
266266
);
267267
}
268-
269-
/**
270-
* `building.validity.valid` is true if the building passes a few validity
271-
* tests. This is useful for testing.
272-
*
273-
* There are several reasons that it could be false:
274-
* 1. There's more than one room with the same name.
275-
* 2. There's at least one hallway that doesn't have any nodes (Forks or
276-
* Stairs) to connect it to the rest of the building.
277-
* 3. The graph isn't connected (`connectedSections > 1`). That means there's
278-
* a group of at least one node that isn't connected to the rest of the graph.
279-
* 4. There are negative edge weights in the graph.
280-
*
281-
* If `building.validity.valid` is false, `building.validity.reason` gives
282-
* the reason why it's invalid.
283-
*
284-
* `connectedSections` is a string[][], where each string[] is a list of nodes
285-
* that are all connected. (Each string[] forms a connected graph.) This is
286-
* useful for debugging to figure out which nodes aren't connected to the rest
287-
* of the graph.
288-
*/
289-
get validity():
290-
| { valid: true; connectedSections: string[][] }
291-
| { valid: false; reason: string; connectedSections: string[][] } {
292-
const connectedSections = isConnectedGraph(this.graph).connectedSections;
293-
294-
// More than one room can't have the same name
295-
let ret = null;
296-
this.roomsList.forEach((name, index) => {
297-
if (this.roomsList.indexOf(name) !== index) {
298-
ret = {
299-
valid: false,
300-
reason: `There's more than one room with the name '${name}'`,
301-
connectedSections,
302-
};
303-
}
304-
});
305-
if (ret != null) return ret;
306-
307-
// Edges can't have negative weights
308-
for (const [id1, obj] of Object.entries(this.graph)) {
309-
for (const [id2, edgeLen] of Object.entries(obj)) {
310-
if (edgeLen < 0) {
311-
return {
312-
valid: false,
313-
reason: `The edge from node '${id1}' to node '${id2}' has a negative weight`,
314-
connectedSections,
315-
};
316-
}
317-
}
318-
}
319-
320-
// If there's more than 1 hallway, each hallway should have a node to
321-
// connect it to the rest of the hallways
322-
const indexOfHallwayWithNoNodes = this.hallways.findIndex(
323-
h => h.nodes.length === 0
324-
);
325-
if (this.hallways.length > 1 && indexOfHallwayWithNoNodes !== -1) {
326-
return {
327-
valid: false,
328-
reason: `The hallway at index ${indexOfHallwayWithNoNodes} has no nodes (Forks or Stairs)`,
329-
connectedSections,
330-
};
331-
}
332-
333-
// Graph should be connected
334-
if (!isConnectedGraph(this.graph).connected) {
335-
return {
336-
valid: false,
337-
reason:
338-
"Not all nodes are connected; see building.validity.connectedSections to find which node groups are separated",
339-
connectedSections: isConnectedGraph(this.graph).connectedSections,
340-
};
341-
}
342-
343-
return { valid: true, connectedSections };
344-
}
345268
}

src/buildingValidity.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { Building } from "./Building";
2+
import { isConnectedGraph } from "./graph";
3+
4+
/**
5+
* `building.validity.valid` is true if the building passes a few validity
6+
* tests. This is useful for testing.
7+
*
8+
* There are several reasons that it could be false:
9+
* 1. There's more than one room with the same name.
10+
* 2. There's at least one hallway that doesn't have any nodes (Forks or
11+
* Stairs) to connect it to the rest of the building.
12+
* 3. The graph isn't connected (`connectedSections > 1`). That means there's
13+
* a group of at least one node that isn't connected to the rest of the graph.
14+
* 4. There are negative edge weights in the graph.
15+
*
16+
* If `building.validity.valid` is false, `building.validity.reason` gives
17+
* the reason why it's invalid.
18+
*
19+
* `connectedSections` is a string[][], where each string[] is a list of nodes
20+
* that are all connected. (Each string[] forms a connected graph.) This is
21+
* useful for debugging to figure out which nodes aren't connected to the rest
22+
* of the graph.
23+
*/
24+
export function isValidBuilding<
25+
ForkName extends string,
26+
StairName extends string
27+
>(
28+
b: Building<ForkName, StairName>
29+
):
30+
| { valid: true; connectedSections: string[][] }
31+
| { valid: false; reason: string; connectedSections: string[][] } {
32+
const connectedSections = isConnectedGraph(b.graph).connectedSections;
33+
34+
// More than one room can't have the same name
35+
let ret = null;
36+
b.roomsList.forEach((name, index) => {
37+
if (b.roomsList.indexOf(name) !== index) {
38+
ret = {
39+
valid: false,
40+
reason: `There's more than one room with the name '${name}'`,
41+
connectedSections,
42+
};
43+
}
44+
});
45+
if (ret != null) return ret;
46+
47+
// Edges can't have negative weights
48+
for (const [id1, obj] of Object.entries(b.graph)) {
49+
for (const [id2, edgeLen] of Object.entries(obj)) {
50+
if (edgeLen < 0) {
51+
return {
52+
valid: false,
53+
reason: `The edge from node '${id1}' to node '${id2}' has a negative weight`,
54+
connectedSections,
55+
};
56+
}
57+
}
58+
}
59+
60+
// If there's more than 1 hallway, each hallway should have a node to
61+
// connect it to the rest of the hallways
62+
const indexOfHallwayWithNoNodes = b.hallways.findIndex(
63+
h => h.nodes.length === 0
64+
);
65+
if (b.hallways.length > 1 && indexOfHallwayWithNoNodes !== -1) {
66+
return {
67+
valid: false,
68+
reason: `The hallway at index ${indexOfHallwayWithNoNodes} has no nodes (Forks or Stairs)`,
69+
connectedSections,
70+
};
71+
}
72+
73+
// Graph should be connected
74+
if (!isConnectedGraph(b.graph).connected) {
75+
return {
76+
valid: false,
77+
reason:
78+
"Not all nodes are connected; see isValidBuilding(building).connectedSections to find which node groups are separated.",
79+
connectedSections: isConnectedGraph(b.graph).connectedSections,
80+
};
81+
}
82+
83+
return { valid: true, connectedSections };
84+
}
85+
86+
export function assertValidBuilding<
87+
ForkName extends string,
88+
StairName extends string
89+
>(b: Building<ForkName, StairName>): void {
90+
const validity = isValidBuilding(b);
91+
if (!validity.valid) {
92+
throw `asserValidBuilding: This building is invalid. ${validity.reason}`;
93+
}
94+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export { Stairs } from "./Stairs";
1414
export { Turn } from "./Turn";
1515
export { reverseConnection } from "./ForkNode";
1616
export { onFloor } from "./StairNode";
17+
export { assertValidBuilding, isValidBuilding } from "./buildingValidity";
1718

1819
// We can't directly re-export types because Babel's
1920
// TypeScript uses --isolatedModules.

0 commit comments

Comments
 (0)