aboutsummaryrefslogtreecommitdiff
path: root/server/tools/src/graph_summary.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-09-19 22:27:46 +0200
committermetamuffin <metamuffin@disroot.org>2025-09-19 22:40:39 +0200
commit402067b8317195fd2bc4ab4d92b5ace94fadb7c0 (patch)
tree2437c52ae71a11c4d17a6fa4597f8152dae96ddc /server/tools/src/graph_summary.rs
parent2f311fec691cd7a62fa4f95ee0419089913b5dd8 (diff)
downloadhurrycurry-402067b8317195fd2bc4ab4d92b5ace94fadb7c0.tar
hurrycurry-402067b8317195fd2bc4ab4d92b5ace94fadb7c0.tar.bz2
hurrycurry-402067b8317195fd2bc4ab4d92b5ace94fadb7c0.tar.zst
Refactor book part 1
Diffstat (limited to 'server/tools/src/graph_summary.rs')
-rw-r--r--server/tools/src/graph_summary.rs149
1 files changed, 149 insertions, 0 deletions
diff --git a/server/tools/src/graph_summary.rs b/server/tools/src/graph_summary.rs
new file mode 100644
index 00000000..be53e768
--- /dev/null
+++ b/server/tools/src/graph_summary.rs
@@ -0,0 +1,149 @@
+/*
+ Hurry Curry! - a game about cooking
+ Copyright (C) 2025 Hurry Curry! Contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, version 3 of the License only.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+use anyhow::Result;
+use hurrycurry_protocol::{ItemIndex, Recipe, TileIndex};
+use hurrycurry_server::data::DataIndex;
+use std::collections::HashSet;
+
+pub(crate) fn graph_summary() -> Result<()> {
+ let mut index = DataIndex::default();
+ index.reload()?;
+
+ println!("digraph {{");
+
+ let (data, sdata, _) = index.generate("5star")?;
+
+ struct Node {
+ inputs: Vec<ItemIndex>,
+ outputs: Vec<ItemIndex>,
+ tool_item: Option<ItemIndex>,
+ tool_tile: Option<TileIndex>,
+ instant: bool,
+ demand: bool,
+ }
+
+ let map_items = sdata
+ .initial_map
+ .iter()
+ .flat_map(|(_, (_, i))| *i)
+ .collect::<HashSet<_>>();
+
+ let mut nodes = Vec::new();
+ for r in &data.recipes {
+ nodes.push(Node {
+ inputs: r.inputs(),
+ outputs: r.outputs(),
+ tool_item: None,
+ tool_tile: r.tile(),
+ instant: matches!(r, Recipe::Instant { .. }),
+ demand: false,
+ })
+ }
+ for d in &data.demands {
+ nodes.push(Node {
+ demand: true,
+ instant: false,
+ inputs: vec![d.input],
+ outputs: d.output.into_iter().collect(),
+ tool_item: None,
+ tool_tile: None,
+ })
+ }
+
+ loop {
+ let node_count_before = nodes.len();
+
+ let mut has_fdeps = vec![false; data.item_names.len()];
+ for n in &nodes {
+ for ItemIndex(i) in &n.inputs {
+ has_fdeps[*i] = true;
+ }
+ }
+ // Remove demand outputs
+ for n in &mut nodes {
+ n.outputs.retain(|_item| !n.demand);
+ }
+ // Remove outputs that are not depended on
+ for n in &mut nodes {
+ n.outputs.retain(|item| n.demand || has_fdeps[item.0])
+ }
+ // Remove outputs that exist on the map, like pots and plates
+ for n in &mut nodes {
+ n.outputs.retain(|item| !map_items.contains(item))
+ }
+ // Convert map item inputs to tools
+ for n in &mut nodes {
+ n.inputs.retain(|i| {
+ if map_items.contains(i) {
+ n.tool_item = Some(*i);
+ false
+ } else {
+ true
+ }
+ })
+ }
+ // Remove outputless recipes
+ nodes.retain(|n| n.demand || !n.outputs.is_empty());
+
+ if nodes.len() == node_count_before {
+ break;
+ }
+ }
+
+ let mut items = HashSet::<ItemIndex>::new();
+ for n in &nodes {
+ items.extend(&n.inputs);
+ items.extend(&n.outputs);
+ }
+
+ for ItemIndex(i) in items {
+ println!("i{i} [label=\"{}\"]", data.item_name(ItemIndex(i)))
+ }
+ for (ni, node) in nodes.iter().enumerate() {
+ let color = if node.demand {
+ "#c4422b"
+ } else if node.instant {
+ "#5452d8"
+ } else {
+ "#47c42b"
+ };
+ println!(
+ "r{ni} [label=\"{}\" shape=box color={color:?} fillcolor={color:?} style=filled]",
+ if let Some(tool) = node.tool_tile {
+ data.tile_name(tool)
+ } else if let Some(tool) = node.tool_item {
+ data.item_name(tool)
+ } else if node.instant {
+ "Combine"
+ } else if node.demand {
+ "Demand"
+ } else {
+ "Passive"
+ }
+ );
+ for ItemIndex(input) in &node.inputs {
+ println!("i{input} -> r{ni}")
+ }
+ for ItemIndex(output) in &node.outputs {
+ println!("r{ni} -> i{output}")
+ }
+ }
+
+ println!("}}");
+ Ok(())
+}