所以我一直在努力研究如何处理我最初的一个简单问题 - 结果我是个白痴,不知道我在做什么。
首先,我的数据结构如下:
public class Points{
public List<Points> connectsTo = new List<Points>();
public Vector3 position;
}
// main script
List<Points> allWorldPoints = new List<Points>();
这个想法是点是连接器,创建墙壁,我需要找到房间。这是我想要实现的目标的图像:
房间不一定是方形/矩形,它们可以是L形或T形等。
问题是我不知道如何处理这个问题的逻辑,所以我正在寻找建议,因为逻辑真的让我感到困惑。
答案 0 :(得分:3)
顺便说一句,您的班级应该被命名为Point
而不是Points
。
更新:我添加了一个有效的例子。
好的,首先让我们获得必要的基础设施。我将实现几个类:Point
,Room
和ImmutableStack<T>
(后者用于简化遍历路径):
public class ImmutableStack<T> : IEnumerable<T>
{
private readonly T head;
private readonly ImmutableStack<T> tail;
public int Count { get; }
public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
private ImmutableStack()
{
head = default(T);
tail = null;
Count = 0;
}
private ImmutableStack(T head, ImmutableStack<T> tail)
{
Debug.Assert(tail != null);
this.head = head;
this.tail = tail;
Count = tail.Count + 1;
}
public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);
public T Peek()
{
if (this == Empty)
throw new InvalidOperationException("Can not peek an empty stack.");
return head;
}
public ImmutableStack<T> Pop()
{
if (this == Empty)
throw new InvalidOperationException("Can not pop an empty stack.");
return tail;
}
public IEnumerator<T> GetEnumerator()
{
var current = this;
while (current != Empty)
{
yield return current.Peek();
current = current.tail;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public override string ToString() => string.Join(" -> ", this);
}
public class Point: IEquatable<Point>
{
private readonly List<Point> connectedPoints;
public int X { get; }
public int Y { get; }
public IEnumerable<Point> ConnectedPoints => connectedPoints.Select(p => p);
public Point(int x, int y)
{
X = x;
Y = y;
connectedPoints = new List<Point>();
}
public void ConnectWith(Point p)
{
Debug.Assert(p != null);
Debug.Assert(!Equals(p));
if (!connectedPoints.Contains(p))
{
connectedPoints.Add(p);
p.connectedPoints.Add(this);
}
}
public bool Equals(Point p)
{
if (ReferenceEquals(p, null))
return false;
return X == p.X && Y == p.Y;
}
public override bool Equals(object obj) => this.Equals(obj as Point);
public override int GetHashCode() => X ^ Y;
public override string ToString() => $"[{X}, {Y}]";
}
public class Room
{
public IEnumerable<Point> Points { get; }
public Room(IEnumerable<Point> points)
{
Points = points;
}
}
好的,现在我们只执行上面列举的步骤:
public static IEnumerable<Room> GetRooms(this IEnumerable<Point> points)
{
if (points.Count() < 3) //need at least 3 points to build a room
yield break;
var startCandidates = points;
while (startCandidates.Any())
{
var start = startCandidates.First();
var potentialRooms = GetPaths(start, start, ImmutableStack<Point>.Empty).OrderBy(p => p.Count);
if (potentialRooms.Any())
{
var roomPath = potentialRooms.First();
yield return new Room(roomPath);
startCandidates = startCandidates.Except(roomPath);
}
else
{
startCandidates = startCandidates.Except(new[] { start });
}
}
}
private static IEnumerable<ImmutableStack<Point>> GetPaths(Point start, Point current, ImmutableStack<Point> path)
{
if (current == start &&
path.Count > 2) //discard backtracking
{
yield return path;
}
else if (path.Contains(current))
{
yield break;
}
else
{
var newPath = path.Push(current);
foreach (var point in current.ConnectedPoints)
{
foreach (var p in GetPaths(start, point, newPath))
{
yield return p;
}
}
}
}
当然,如果我们测试几何体:
public static void Main(string[] args)
{
var p1 = new Point(0, 0);
var p2 = new Point(0, 1);
var p3 = new Point(0, 2);
var p4 = new Point(1, 2);
var p5 = new Point(1, 1);
var p6 = new Point(1, 0);
var p7 = new Point(2, 0);
var p8 = new Point(2, 1);
p1.ConnectWith(p2);
p2.ConnectWith(p3);
p3.ConnectWith(p4);
p4.ConnectWith(p5);
p5.ConnectWith(p6);
p6.ConnectWith(p1);
p6.ConnectWith(p7);
p7.ConnectWith(p8);
p8.ConnectWith(p5);
var rooms = new[] { p1, p2, p3, p4, p5, p6, p7, p8 }.GetRooms();
}
我们得到了预期的两个房间。
请注意,例如,可以将算法更高效地将ImmtuableStack
更改为ImmutableHashSet
。