From fb00c358236c43a4dd1092d273f66a0827036fc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dandelion=20Man=C3=A9?=
Date: Mon, 5 Mar 2018 20:58:47 -0800
Subject: [PATCH] Factor eventide graph demo data to a new module (#71)
* Factor evertide graph demo data to a new module
It would be helpful to make our standard tiny graph available to other
test and demo instances, outside of just graph.test.js. This way we can
use it as a test case for the Graph Explorer.
---
src/backend/graph.test.js | 399 +++++++++++++++--------------------
src/backend/graphDemoData.js | 106 ++++++++++
2 files changed, 275 insertions(+), 230 deletions(-)
create mode 100644 src/backend/graphDemoData.js
diff --git a/src/backend/graph.test.js b/src/backend/graph.test.js
index 042cc95..0af5ea0 100644
--- a/src/backend/graph.test.js
+++ b/src/backend/graph.test.js
@@ -3,6 +3,7 @@
import type {Address, Addressable} from "./address";
import {sortedByAddress} from "./address";
import {Graph} from "./graph";
+import * as demoData from "./graphDemoData";
describe("graph", () => {
describe("#Graph", () => {
@@ -13,127 +14,23 @@ describe("graph", () => {
expect(sortedByAddress(xs)).toEqual(sortedByAddress(ys));
}
- // A Seafood Fruit Mix is made by cooking Mighty Bananas (picked
- // from a tree) and a Razorclaw Crab (grabbed from the beach). In
- // this graph, an edge from `u` to `v` means that `u` thanks `v` for
- // a particular contribution. For example, the meal thanks the hero
- // for cooking it, as well as thanking the bananas and the crab for
- // composing it.
- function makeAddress(id: string): Address {
- return {
- repositoryName: "sourcecred/eventide",
- pluginName: "hill_cooking_pot",
- id,
- };
- }
- const heroNode = () => ({
- address: makeAddress("hero_of_time#0"),
- payload: {},
- });
- const bananasNode = () => ({
- address: makeAddress("mighty_bananas#1"),
- payload: {},
- });
- const crabNode = () => ({
- address: makeAddress("razorclaw_crab#2"),
- payload: {},
- });
- const mealNode = () => ({
- address: makeAddress("seafood_fruit_mix#3"),
- payload: {
- effect: ["attack_power", 1],
- },
- });
- const pickEdge = () => ({
- address: makeAddress("hero_of_time#0@picks@mighty_bananas#1"),
- src: bananasNode().address,
- dst: heroNode().address,
- payload: {},
- });
- const grabEdge = () => ({
- address: makeAddress("hero_of_time#0@grabs@razorclaw_crab#2"),
- src: crabNode().address,
- dst: heroNode().address,
- payload: {},
- });
- const cookEdge = () => ({
- address: makeAddress("hero_of_time#0@cooks@seafood_fruit_mix#3"),
- src: mealNode().address,
- dst: heroNode().address,
- payload: {
- crit: false,
- },
- });
- const bananasIngredientEdge = () => ({
- address: makeAddress("mighty_bananas#1@included_in@seafood_fruit_mix#3"),
- src: mealNode().address,
- dst: bananasNode().address,
- payload: {},
- });
- const crabIngredientEdge = () => ({
- address: makeAddress("razorclaw_crab#2@included_in@seafood_fruit_mix#3"),
- src: mealNode().address,
- dst: crabNode().address,
- payload: {},
- });
- const eatEdge = () => ({
- address: makeAddress("hero_of_time#0@eats@seafood_fruit_mix#3"),
- src: heroNode().address,
- dst: mealNode().address,
- payload: {},
- });
- const simpleMealGraph = () =>
- new Graph()
- .addNode(heroNode())
- .addNode(bananasNode())
- .addNode(crabNode())
- .addNode(mealNode())
- .addEdge(pickEdge())
- .addEdge(grabEdge())
- .addEdge(cookEdge())
- .addEdge(bananasIngredientEdge())
- .addEdge(crabIngredientEdge())
- .addEdge(eatEdge());
-
- const crabLoopEdge = () => ({
- address: makeAddress("crab-self-assessment"),
- src: crabNode().address,
- dst: crabNode().address,
- payload: {evaluation: "not effective at avoiding hero"},
- });
-
- const duplicateCookEdge = () => ({
- address: makeAddress("hero_of_time#0@again_cooks@seafood_fruit_mix#3"),
- src: mealNode().address,
- dst: heroNode().address,
- payload: {
- crit: true,
- saveScummed: true,
- },
- });
-
- const advancedMealGraph = () =>
- simpleMealGraph()
- .addEdge(crabLoopEdge())
- .addEdge(duplicateCookEdge());
-
describe("construction", () => {
it("works for a simple graph", () => {
- simpleMealGraph();
+ demoData.simpleMealGraph();
});
it("works for an advanced graph", () => {
- advancedMealGraph();
+ demoData.advancedMealGraph();
});
it("forbids adding an edge with dangling `dst`", () => {
expect(() => {
- simpleMealGraph().addEdge({
- address: makeAddress(
+ demoData.simpleMealGraph().addEdge({
+ address: demoData.makeAddress(
"treasure_octorok#5@helps_cook@seafood_fruit_mix#3"
),
- src: mealNode().address,
- dst: makeAddress("treasure_octorok#5"),
+ src: demoData.mealNode().address,
+ dst: demoData.makeAddress("treasure_octorok#5"),
payload: {},
});
}).toThrow(/does not exist/);
@@ -141,10 +38,12 @@ describe("graph", () => {
it("forbids adding an edge with dangling `src`", () => {
expect(() => {
- simpleMealGraph().addEdge({
- address: makeAddress("health_bar#6@healed_by@seafood_fruit_mix#3"),
- src: makeAddress("health_bar#6"),
- dst: mealNode().address,
+ demoData.simpleMealGraph().addEdge({
+ address: demoData.makeAddress(
+ "health_bar#6@healed_by@seafood_fruit_mix#3"
+ ),
+ src: demoData.makeAddress("health_bar#6"),
+ dst: demoData.mealNode().address,
payload: {},
});
}).toThrow(/does not exist/);
@@ -192,44 +91,54 @@ describe("graph", () => {
describe("getting nodes and edges", () => {
it("correctly gets nodes in the simple graph", () => {
- const g = simpleMealGraph();
- [heroNode(), bananasNode(), crabNode(), mealNode()].forEach((x) => {
+ const g = demoData.simpleMealGraph();
+ [
+ demoData.heroNode(),
+ demoData.bananasNode(),
+ demoData.crabNode(),
+ demoData.mealNode(),
+ ].forEach((x) => {
expect(g.getNode(x.address)).toEqual(x);
});
});
it("correctly gets nodes in the advanced graph", () => {
- const g = advancedMealGraph();
- [heroNode(), bananasNode(), crabNode(), mealNode()].forEach((x) => {
+ const g = demoData.advancedMealGraph();
+ [
+ demoData.heroNode(),
+ demoData.bananasNode(),
+ demoData.crabNode(),
+ demoData.mealNode(),
+ ].forEach((x) => {
expect(g.getNode(x.address)).toEqual(x);
});
});
it("correctly gets edges in the simple graph", () => {
- const g = simpleMealGraph();
+ const g = demoData.simpleMealGraph();
[
- pickEdge(),
- grabEdge(),
- cookEdge(),
- bananasIngredientEdge(),
- crabIngredientEdge(),
- eatEdge(),
+ demoData.pickEdge(),
+ demoData.grabEdge(),
+ demoData.cookEdge(),
+ demoData.bananasIngredientEdge(),
+ demoData.crabIngredientEdge(),
+ demoData.eatEdge(),
].forEach((x) => {
expect(g.getEdge(x.address)).toEqual(x);
});
});
it("correctly gets edges in the advanced graph", () => {
- const g = advancedMealGraph();
+ const g = demoData.advancedMealGraph();
[
- pickEdge(),
- grabEdge(),
- cookEdge(),
- bananasIngredientEdge(),
- crabIngredientEdge(),
- eatEdge(),
- crabLoopEdge(),
- duplicateCookEdge(),
+ demoData.pickEdge(),
+ demoData.grabEdge(),
+ demoData.cookEdge(),
+ demoData.bananasIngredientEdge(),
+ demoData.crabIngredientEdge(),
+ demoData.eatEdge(),
+ demoData.crabLoopEdge(),
+ demoData.duplicateCookEdge(),
].forEach((x) => {
expect(g.getEdge(x.address)).toEqual(x);
});
@@ -237,36 +146,47 @@ describe("graph", () => {
it("returns `undefined` for nodes that do not exist", () => {
expect(
- simpleMealGraph().getNode(makeAddress("treasure_octorok#5"))
+ demoData
+ .simpleMealGraph()
+ .getNode(demoData.makeAddress("treasure_octorok#5"))
).toBeUndefined();
});
it("returns `undefined` for edges that do not exist", () => {
expect(
- simpleMealGraph().getNode(
- makeAddress("treasure_octorok#5@helps_cook@seafood_fruit_mix#3")
- )
+ demoData
+ .simpleMealGraph()
+ .getNode(
+ demoData.makeAddress(
+ "treasure_octorok#5@helps_cook@seafood_fruit_mix#3"
+ )
+ )
).toBeUndefined();
});
it("gets all nodes", () => {
- const expected = [heroNode(), bananasNode(), crabNode(), mealNode()];
- const actual = advancedMealGraph().getAllNodes();
+ const expected = [
+ demoData.heroNode(),
+ demoData.bananasNode(),
+ demoData.crabNode(),
+ demoData.mealNode(),
+ ];
+ const actual = demoData.advancedMealGraph().getAllNodes();
expectSameSorted(expected, actual);
});
it("gets all edges", () => {
const expected = [
- pickEdge(),
- grabEdge(),
- cookEdge(),
- bananasIngredientEdge(),
- crabIngredientEdge(),
- eatEdge(),
- crabLoopEdge(),
- duplicateCookEdge(),
+ demoData.pickEdge(),
+ demoData.grabEdge(),
+ demoData.cookEdge(),
+ demoData.bananasIngredientEdge(),
+ demoData.crabIngredientEdge(),
+ demoData.eatEdge(),
+ demoData.crabLoopEdge(),
+ demoData.duplicateCookEdge(),
];
- const actual = advancedMealGraph().getAllEdges();
+ const actual = demoData.advancedMealGraph().getAllEdges();
expectSameSorted(expected, actual);
});
});
@@ -274,8 +194,8 @@ describe("graph", () => {
describe("creating nodes and edges", () => {
it("forbids adding a node with existing address", () => {
expect(() =>
- simpleMealGraph().addNode({
- address: crabNode().address,
+ demoData.simpleMealGraph().addNode({
+ address: demoData.crabNode().address,
payload: {anotherCrab: true},
})
).toThrow(/already exists/);
@@ -283,29 +203,31 @@ describe("graph", () => {
it("forbids adding an edge with existing address", () => {
expect(() =>
- simpleMealGraph().addEdge({
- address: cookEdge().address,
- src: crabNode().address,
- dst: crabNode().address,
+ demoData.simpleMealGraph().addEdge({
+ address: demoData.cookEdge().address,
+ src: demoData.crabNode().address,
+ dst: demoData.crabNode().address,
payload: {},
})
).toThrow(/already exists/);
});
it("allows creating self-loops", () => {
- const g = simpleMealGraph();
- g.addEdge(crabLoopEdge());
- expect(g.getOutEdges(crabNode().address)).toContainEqual(
- crabLoopEdge()
+ const g = demoData.simpleMealGraph();
+ g.addEdge(demoData.crabLoopEdge());
+ expect(g.getOutEdges(demoData.crabNode().address)).toContainEqual(
+ demoData.crabLoopEdge()
+ );
+ expect(g.getInEdges(demoData.crabNode().address)).toContainEqual(
+ demoData.crabLoopEdge()
);
- expect(g.getInEdges(crabNode().address)).toContainEqual(crabLoopEdge());
});
it("allows creating multiple edges between the same nodes", () => {
- const g = simpleMealGraph();
- g.addEdge(duplicateCookEdge());
- [cookEdge(), duplicateCookEdge()].forEach((e) => {
- expect(g.getOutEdges(mealNode().address)).toContainEqual(e);
+ const g = demoData.simpleMealGraph();
+ g.addEdge(demoData.duplicateCookEdge());
+ [demoData.cookEdge(), demoData.duplicateCookEdge()].forEach((e) => {
+ expect(g.getOutEdges(demoData.mealNode().address)).toContainEqual(e);
expect(g.getEdge(e.address)).toEqual(e);
});
});
@@ -315,16 +237,16 @@ describe("graph", () => {
// the namespaces to be forced to be disjoint. In that case, we can
// certainly change these tests.
it("allows adding an edge with an existing node's address", () => {
- simpleMealGraph().addEdge({
- address: crabNode().address,
- src: crabNode().address,
- dst: crabNode().address,
+ demoData.simpleMealGraph().addEdge({
+ address: demoData.crabNode().address,
+ src: demoData.crabNode().address,
+ dst: demoData.crabNode().address,
payload: {message: "thanks for being you"},
});
});
it("allows adding a node with an existing edge's address", () => {
- simpleMealGraph().addNode({
- address: cookEdge().address,
+ demoData.simpleMealGraph().addNode({
+ address: demoData.cookEdge().address,
payload: {},
});
});
@@ -333,21 +255,21 @@ describe("graph", () => {
describe("in- and out-edges", () => {
it("gets out-edges", () => {
const nodeAndExpectedEdgePairs = [
- [heroNode(), [eatEdge()]],
- [bananasNode(), [pickEdge()]],
- [crabNode(), [grabEdge(), crabLoopEdge()]],
+ [demoData.heroNode(), [demoData.eatEdge()]],
+ [demoData.bananasNode(), [demoData.pickEdge()]],
+ [demoData.crabNode(), [demoData.grabEdge(), demoData.crabLoopEdge()]],
[
- mealNode(),
+ demoData.mealNode(),
[
- bananasIngredientEdge(),
- crabIngredientEdge(),
- cookEdge(),
- duplicateCookEdge(),
+ demoData.bananasIngredientEdge(),
+ demoData.crabIngredientEdge(),
+ demoData.cookEdge(),
+ demoData.duplicateCookEdge(),
],
],
];
nodeAndExpectedEdgePairs.forEach(([node, expectedEdges]) => {
- const actual = advancedMealGraph().getOutEdges(node.address);
+ const actual = demoData.advancedMealGraph().getOutEdges(node.address);
expectSameSorted(actual, expectedEdges);
});
});
@@ -355,62 +277,76 @@ describe("graph", () => {
it("gets in-edges", () => {
const nodeAndExpectedEdgePairs = [
[
- heroNode(),
- [pickEdge(), grabEdge(), cookEdge(), duplicateCookEdge()],
+ demoData.heroNode(),
+ [
+ demoData.pickEdge(),
+ demoData.grabEdge(),
+ demoData.cookEdge(),
+ demoData.duplicateCookEdge(),
+ ],
],
- [bananasNode(), [bananasIngredientEdge()]],
- [crabNode(), [crabIngredientEdge(), crabLoopEdge()]],
- [mealNode(), [eatEdge()]],
+ [demoData.bananasNode(), [demoData.bananasIngredientEdge()]],
+ [
+ demoData.crabNode(),
+ [demoData.crabIngredientEdge(), demoData.crabLoopEdge()],
+ ],
+ [demoData.mealNode(), [demoData.eatEdge()]],
];
nodeAndExpectedEdgePairs.forEach(([node, expectedEdges]) => {
- const actual = advancedMealGraph().getInEdges(node.address);
+ const actual = demoData.advancedMealGraph().getInEdges(node.address);
expectSameSorted(actual, expectedEdges);
});
});
it("fails to get out-edges for a nonexistent node", () => {
expect(() => {
- simpleMealGraph().getOutEdges(makeAddress("hinox"));
+ demoData.simpleMealGraph().getOutEdges(demoData.makeAddress("hinox"));
}).toThrow(/no node for address/);
});
it("fails to get in-edges for a nonexistent node", () => {
expect(() => {
- simpleMealGraph().getInEdges(makeAddress("hinox"));
+ demoData.simpleMealGraph().getInEdges(demoData.makeAddress("hinox"));
}).toThrow(/no node for address/);
});
});
describe("#equals", () => {
it("returns true for identity-equal graphs", () => {
- const g = advancedMealGraph();
+ const g = demoData.advancedMealGraph();
expect(g.equals(g)).toBe(true);
});
it("returns true for deep-equal graphs", () => {
- expect(advancedMealGraph().equals(advancedMealGraph())).toBe(true);
+ expect(
+ demoData.advancedMealGraph().equals(demoData.advancedMealGraph())
+ ).toBe(true);
});
it("returns false when the LHS has nodes missing in the RHS", () => {
- expect(advancedMealGraph().equals(simpleMealGraph())).toBe(false);
+ expect(
+ demoData.advancedMealGraph().equals(demoData.simpleMealGraph())
+ ).toBe(false);
});
it("returns false when the RHS has nodes missing in the LHS", () => {
- expect(simpleMealGraph().equals(advancedMealGraph())).toBe(false);
+ expect(
+ demoData.simpleMealGraph().equals(demoData.advancedMealGraph())
+ ).toBe(false);
});
const extraNode1 = () => ({
- address: makeAddress("octorok"),
+ address: demoData.makeAddress("octorok"),
payload: {},
});
const extraNode2 = () => ({
- address: makeAddress("hinox"),
+ address: demoData.makeAddress("hinox"),
payload: {status: "sleeping"},
});
it("returns false when the LHS has edges missing in the RHS", () => {
- const g1 = advancedMealGraph();
- const g2 = advancedMealGraph().addNode(extraNode1());
+ const g1 = demoData.advancedMealGraph();
+ const g2 = demoData.advancedMealGraph().addNode(extraNode1());
expect(g1.equals(g2)).toBe(false);
});
it("returns false when the LHS has edges missing in the RHS", () => {
- const g1 = advancedMealGraph().addNode(extraNode1());
- const g2 = advancedMealGraph();
+ const g1 = demoData.advancedMealGraph().addNode(extraNode1());
+ const g2 = demoData.advancedMealGraph();
expect(g1.equals(g2)).toBe(false);
});
it("returns true when nodes are added in different orders", () => {
@@ -468,33 +404,32 @@ describe("graph", () => {
}
it("conservatively recomposes a neighborhood decomposition", () => {
- const result = neighborhoodDecomposition(advancedMealGraph()).reduce(
- (g1, g2) => Graph.mergeConservative(g1, g2),
- new Graph()
- );
- expect(result.equals(advancedMealGraph())).toBe(true);
+ const result = neighborhoodDecomposition(
+ demoData.advancedMealGraph()
+ ).reduce((g1, g2) => Graph.mergeConservative(g1, g2), new Graph());
+ expect(result.equals(demoData.advancedMealGraph())).toBe(true);
});
it("conservatively recomposes an edge decomposition", () => {
- const result = edgeDecomposition(advancedMealGraph()).reduce(
+ const result = edgeDecomposition(demoData.advancedMealGraph()).reduce(
(g1, g2) => Graph.mergeConservative(g1, g2),
new Graph()
);
- expect(result.equals(advancedMealGraph())).toBe(true);
+ expect(result.equals(demoData.advancedMealGraph())).toBe(true);
});
it("conservatively merges a graph with itself", () => {
const result = Graph.mergeConservative(
- advancedMealGraph(),
- advancedMealGraph()
+ demoData.advancedMealGraph(),
+ demoData.advancedMealGraph()
);
- expect(result.equals(advancedMealGraph())).toBe(true);
+ expect(result.equals(demoData.advancedMealGraph())).toBe(true);
});
it("conservatively rejects a graph with conflicting nodes", () => {
const makeGraph: (nodePayload: string) => Graph = (nodePayload) =>
new Graph().addNode({
- address: makeAddress("conflicting-node"),
+ address: demoData.makeAddress("conflicting-node"),
payload: nodePayload,
});
const g1 = makeGraph("one");
@@ -505,14 +440,14 @@ describe("graph", () => {
});
it("conservatively rejects a graph with conflicting edges", () => {
- const srcAddress = makeAddress("src");
- const dstAddress = makeAddress("dst");
+ const srcAddress = demoData.makeAddress("src");
+ const dstAddress = demoData.makeAddress("dst");
const makeGraph: (edgePayload: string) => Graph = (edgePayload) =>
new Graph()
.addNode({address: srcAddress, payload: {}})
.addNode({address: dstAddress, payload: {}})
.addEdge({
- address: makeAddress("conflicting-edge"),
+ address: demoData.makeAddress("conflicting-edge"),
src: srcAddress,
dst: dstAddress,
payload: edgePayload,
@@ -530,20 +465,20 @@ describe("graph", () => {
it("has the empty graph as a left identity", () => {
const merged = Graph.merge(
new Graph(),
- advancedMealGraph(),
+ demoData.advancedMealGraph(),
assertNotCalled,
assertNotCalled
);
- expect(merged.equals(advancedMealGraph())).toBe(true);
+ expect(merged.equals(demoData.advancedMealGraph())).toBe(true);
});
it("has the empty graph as a right identity", () => {
const merged = Graph.merge(
- advancedMealGraph(),
+ demoData.advancedMealGraph(),
new Graph(),
assertNotCalled,
assertNotCalled
);
- expect(merged.equals(advancedMealGraph())).toBe(true);
+ expect(merged.equals(demoData.advancedMealGraph())).toBe(true);
});
it("trivially merges the empty graph with itself", () => {
const merged = Graph.merge(
@@ -558,39 +493,43 @@ describe("graph", () => {
describe("JSON functions", () => {
it("should serialize a simple graph", () => {
- expect(advancedMealGraph().toJSON()).toMatchSnapshot();
+ expect(demoData.advancedMealGraph().toJSON()).toMatchSnapshot();
});
it("should work transparently with JSON.stringify", () => {
// (This is guaranteed by the `JSON.stringify` API, and is more
// as documentation than actual test.)
- expect(JSON.stringify(advancedMealGraph())).toEqual(
- JSON.stringify(advancedMealGraph().toJSON())
+ expect(JSON.stringify(demoData.advancedMealGraph())).toEqual(
+ JSON.stringify(demoData.advancedMealGraph().toJSON())
);
});
it("should canonicalize away node insertion order", () => {
- const g1 = new Graph().addNode(heroNode()).addNode(mealNode());
- const g2 = new Graph().addNode(mealNode()).addNode(heroNode());
+ const g1 = new Graph()
+ .addNode(demoData.heroNode())
+ .addNode(demoData.mealNode());
+ const g2 = new Graph()
+ .addNode(demoData.mealNode())
+ .addNode(demoData.heroNode());
expect(g1.toJSON()).toEqual(g2.toJSON());
});
it("should canonicalize away edge insertion order", () => {
const g1 = new Graph()
- .addNode(heroNode())
- .addNode(mealNode())
- .addEdge(cookEdge())
- .addEdge(duplicateCookEdge());
+ .addNode(demoData.heroNode())
+ .addNode(demoData.mealNode())
+ .addEdge(demoData.cookEdge())
+ .addEdge(demoData.duplicateCookEdge());
const g2 = new Graph()
- .addNode(heroNode())
- .addNode(mealNode())
- .addEdge(duplicateCookEdge())
- .addEdge(cookEdge());
+ .addNode(demoData.heroNode())
+ .addNode(demoData.mealNode())
+ .addEdge(demoData.duplicateCookEdge())
+ .addEdge(demoData.cookEdge());
expect(g1.toJSON()).toEqual(g2.toJSON());
});
it("should no-op on a serialization--deserialization roundtrip", () => {
- const g = () => advancedMealGraph();
+ const g = () => demoData.advancedMealGraph();
expect(Graph.fromJSON(g().toJSON()).equals(g())).toBe(true);
});
it("should no-op on a deserialization--serialization roundtrip", () => {
- const json = () => advancedMealGraph().toJSON();
+ const json = () => demoData.advancedMealGraph().toJSON();
expect(Graph.fromJSON(json()).toJSON()).toEqual(json());
});
});
diff --git a/src/backend/graphDemoData.js b/src/backend/graphDemoData.js
new file mode 100644
index 0000000..f0aecaa
--- /dev/null
+++ b/src/backend/graphDemoData.js
@@ -0,0 +1,106 @@
+// @flow
+// This module provides some small demo graphs, which report
+// on a hero's adventures in cooking a seafood fruit mix.
+// It is factored as its own module so that it may be depended on by
+// multiple test and demo consumers.
+
+import type {Address} from "./address";
+import {Graph} from "./graph";
+
+export function makeAddress(id: string): Address {
+ return {
+ repositoryName: "sourcecred/eventide",
+ pluginName: "hill_cooking_pot",
+ id,
+ };
+}
+export const heroNode = () => ({
+ address: makeAddress("hero_of_time#0"),
+ payload: {},
+});
+export const bananasNode = () => ({
+ address: makeAddress("mighty_bananas#1"),
+ payload: {},
+});
+export const crabNode = () => ({
+ address: makeAddress("razorclaw_crab#2"),
+ payload: {},
+});
+export const mealNode = () => ({
+ address: makeAddress("seafood_fruit_mix#3"),
+ payload: {
+ effect: ["attack_power", 1],
+ },
+});
+export const pickEdge = () => ({
+ address: makeAddress("hero_of_time#0@picks@mighty_bananas#1"),
+ src: bananasNode().address,
+ dst: heroNode().address,
+ payload: {},
+});
+export const grabEdge = () => ({
+ address: makeAddress("hero_of_time#0@grabs@razorclaw_crab#2"),
+ src: crabNode().address,
+ dst: heroNode().address,
+ payload: {},
+});
+export const cookEdge = () => ({
+ address: makeAddress("hero_of_time#0@cooks@seafood_fruit_mix#3"),
+ src: mealNode().address,
+ dst: heroNode().address,
+ payload: {
+ crit: false,
+ },
+});
+export const bananasIngredientEdge = () => ({
+ address: makeAddress("mighty_bananas#1@included_in@seafood_fruit_mix#3"),
+ src: mealNode().address,
+ dst: bananasNode().address,
+ payload: {},
+});
+export const crabIngredientEdge = () => ({
+ address: makeAddress("razorclaw_crab#2@included_in@seafood_fruit_mix#3"),
+ src: mealNode().address,
+ dst: crabNode().address,
+ payload: {},
+});
+export const eatEdge = () => ({
+ address: makeAddress("hero_of_time#0@eats@seafood_fruit_mix#3"),
+ src: heroNode().address,
+ dst: mealNode().address,
+ payload: {},
+});
+export const simpleMealGraph = () =>
+ new Graph()
+ .addNode(heroNode())
+ .addNode(bananasNode())
+ .addNode(crabNode())
+ .addNode(mealNode())
+ .addEdge(pickEdge())
+ .addEdge(grabEdge())
+ .addEdge(cookEdge())
+ .addEdge(bananasIngredientEdge())
+ .addEdge(crabIngredientEdge())
+ .addEdge(eatEdge());
+
+export const crabLoopEdge = () => ({
+ address: makeAddress("crab-self-assessment"),
+ src: crabNode().address,
+ dst: crabNode().address,
+ payload: {evaluation: "not effective at avoiding hero"},
+});
+
+export const duplicateCookEdge = () => ({
+ address: makeAddress("hero_of_time#0@again_cooks@seafood_fruit_mix#3"),
+ src: mealNode().address,
+ dst: heroNode().address,
+ payload: {
+ crit: true,
+ saveScummed: true,
+ },
+});
+
+export const advancedMealGraph = () =>
+ simpleMealGraph()
+ .addEdge(crabLoopEdge())
+ .addEdge(duplicateCookEdge());