QuestMap/QuestMap/Node.cs

168 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Lumina.Excel.GeneratedSheets;
namespace QuestMap {
internal class Node<T> {
internal uint Id { get; }
internal List<Node<T>> Parents { get; set; }
internal T Value { get; set; }
internal List<Node<T>> Children { get; } = new();
internal Node(List<Node<T>> parents, uint id, T value) {
this.Id = id;
this.Parents = parents;
this.Value = value;
}
private Node(uint id) {
this.Id = id;
this.Parents = new List<Node<T>>();
this.Value = default!;
}
internal Node<T>? Find(uint id) {
if (this.Id == id) {
return this;
}
foreach (var child in this.Children) {
var result = child.Find(id);
if (result != null) {
return result;
}
}
return null;
}
internal IEnumerable<Node<T>> Ancestors() {
var parents = new Stack<Node<T>>();
foreach (var parent in this.Parents) {
parents.Push(parent);
}
while (parents.Any()) {
var next = parents.Pop();
yield return next;
foreach (var parent in next.Parents) {
parents.Push(parent);
}
}
}
internal IEnumerable<Node<T>> Ancestors(Func<T, T?> consolidator) {
var parents = new Stack<Node<T>>();
foreach (var parent in this.Parents) {
var consolidated = consolidator(parent.Value);
parents.Push(consolidated == null
? parent
: new Node<T>(new List<Node<T>>(), parent.Id, consolidated) {
Children = { this },
});
}
while (parents.Any()) {
var next = parents.Pop();
yield return next;
foreach (var parent in next.Parents) {
var consolidated = consolidator(parent.Value);
parents.Push(consolidated == null
? parent
: new Node<T>(new List<Node<T>>(), parent.Id, consolidated) {
Children = { next },
});
}
}
}
internal IEnumerable<Node<T>> Traverse() {
var stack = new Stack<Node<T>>();
stack.Push(this);
while (stack.Any()) {
var next = stack.Pop();
yield return next;
foreach (var child in next.Children) {
stack.Push(child);
}
}
}
internal IEnumerable<Tuple<Node<T>, uint>> TraverseWithDepth() {
var stack = new Stack<Tuple<Node<T>, uint>>();
stack.Push(Tuple.Create(this, (uint) 0));
while (stack.Any()) {
var next = stack.Pop();
yield return next;
foreach (var child in next.Item1.Children) {
stack.Push(Tuple.Create(child, next.Item2 + 1));
}
}
}
internal static (List<Node<Quest>>, Dictionary<uint, Node<Quest>>) BuildTree(Dictionary<uint, Quest> layouts) {
var lookup = new Dictionary<uint, Node<Quest>>();
var rootNodes = new List<Node<Quest>>();
var allNodes = new Dictionary<uint, Node<Quest>>();
foreach (var item in layouts) {
if (lookup.TryGetValue(item.Key, out var ourNode)) {
ourNode.Value = item.Value;
} else {
ourNode = new Node<Quest>(new List<Node<Quest>>(), item.Key, item.Value);
lookup[item.Key] = ourNode;
allNodes[item.Key] = ourNode;
}
var previous = item.Value.PreviousQuests().ToList();
if (previous.Count == 0) {
rootNodes.Add(ourNode);
} else {
foreach (var prev in previous) {
if (!lookup.TryGetValue(prev.RowId, out var parentNode)) {
// create preliminary parent
parentNode = new Node<Quest>(prev.RowId);
lookup[prev.RowId] = parentNode;
allNodes[prev.RowId] = parentNode;
}
parentNode.Children.Add(ourNode);
ourNode.Parents.Add(parentNode);
}
}
}
return (rootNodes, allNodes);
}
}
internal static class NodeExt {
internal static Node<T>? Find<T>(this IEnumerable<Node<T>> nodes, uint id) {
foreach (var node in nodes) {
var found = node.Find(id);
if (found != null) {
return found;
}
}
return null;
}
internal static IEnumerable<Quest> PreviousQuests(this Quest quest) {
if (quest.PreviousQuest0.Row != 0) {
yield return quest.PreviousQuest0.Value!;
}
if (quest.PreviousQuest1.Row != 0) {
yield return quest.PreviousQuest1.Value!;
}
if (quest.PreviousQuest2.Row != 0) {
yield return quest.PreviousQuest2.Value!;
}
}
}
}