From 35b6644dd082ae46e43ca51d19a6c5aff9500496 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sun, 13 Apr 2025 00:03:20 +0700 Subject: [PATCH] feat: implemented Graph --- .../ru/dragonestia/msb3/api/debug/Graph.java | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java diff --git a/api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java b/api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java new file mode 100644 index 0000000..531cc6b --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java @@ -0,0 +1,129 @@ +package ru.dragonestia.msb3.api.debug; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.minestom.server.coordinate.Vec; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +public class Graph { + + private final static AtomicInteger freeId = new AtomicInteger(1); + + private final Map nodes = new HashMap<>(); + + public Node addNode(Vec pos) { + var node = new Node(pos); + nodes.put(node.getId(), node); + return node; + } + + public boolean addNodeLink(Node from, Node to) { + return from.addLink(to) && to.addLink(from); + } + + public void removeLink(Node from, Node to) { + from.removeLink(to); + to.removeLink(from); + } + + public void removeNode(Node node) { + nodes.remove(node.getId()); + node.getLinks().forEach(other -> removeLink(other, node)); + } + + public Collection getNodes() { + return nodes.values(); + } + + public Optional findNode(int id) { + return Optional.ofNullable(nodes.get(id)); + } + + public SerializedGraph serialize() { + var nodes = new ArrayList(); + var links = new HashSet(); + + for (var node: getNodes()) { + nodes.add(new SerializedNode(node.id, node.position.x(), node.position.y(), node.position.z())); + + for (var otherNode: node.getLinks()) { + if (links.contains(new IntPair(otherNode.getId(), node.getId()))) continue; + links.add(new IntPair(node.getId(), otherNode.getId())); + } + } + return new SerializedGraph(nodes, links); + } + + public static Graph deserialize(SerializedGraph serializedGraph) { + var graph = new Graph(); + for (var node: serializedGraph.nodes) { + graph.addNode(new Vec(node.x(), node.y(), node.z())); + } + for (var link: serializedGraph.links) { + graph.addNodeLink(graph.findNode(link.a).orElseThrow(), graph.findNode(link.b).orElseThrow()); + } + return graph; + } + + @RequiredArgsConstructor + public static class Node { + + @Getter private final int id = freeId.getAndIncrement(); + @Getter private final Vec position; + private final Map links = new HashMap<>(); + + private boolean addLink(Node node) { + if (id == node.getId()) return false; + return links.put(node, position.distance(node.position)) == null; + } + + private void removeLink(Node node) { + links.remove(node); + node.links.remove(this); + } + + public boolean hasLink(Node node) { + if (id == node.getId()) return true; + return links.containsKey(node); + } + + public Set getLinks() { + return links.keySet(); + } + + public Collection getLinksWithDistance() { + return links.entrySet().stream() + .map(entry -> new DistancePair(entry.getKey(), entry.getValue())) + .toList(); + } + + public double getDistanceTo(Node node) { + return links.getOrDefault(node, Double.NaN); + } + + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null) return false; + if (obj instanceof Node other) { + return id == other.id; + } + return false; + } + } + + public record SerializedGraph(List nodes, Set links) {} + + public record SerializedNode(int id, double x, double y, double z) {} + + public record IntPair(int a, int b) {} + + public record DistancePair(Node node, double distance) {} +}