public static void Link(Room a, Room b, Link link)
 {
     Debug.Assert(a != b);
     var edge = new Edge
     {
         RoomA = a,
         RoomB = b,
         Linkage = link
     };
     a.Edges.Add(edge);
     b.Edges.Add(edge);
 }
        private Link? PlaceRoomFixed(FixedRoom src, FixedRoom target, int connPt, int sep)
        {
            var conn = src.ConnectionPoints[connPt];

            var targetDirection = conn.Item1.Reverse();
            var targetConns = (Tuple<Direction, int>[])target.ConnectionPoints.Clone();
            rand.Shuffle(targetConns);
            Tuple<Direction, int> targetConnPt = null;
            foreach (var targetConn in targetConns)
                if (targetConn.Item1 == targetDirection)
                {
                    targetConnPt = targetConn;
                    break;
                }

            if (targetConnPt == null)
                return null;

            int x, y;
            Link? link = null;
            switch (conn.Item1)
            {
                case Direction.North:
                case Direction.South:
                    // North & South
                    x = src.Pos.X + conn.Item2 - targetConnPt.Item2;

                    if (conn.Item1 == Direction.South)
                        y = src.Pos.Y + src.Height + sep;
                    else
                        y = src.Pos.Y - sep - target.Height;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    link = new Link(conn.Item1, src.Pos.X + conn.Item2);
                    break;

                case Direction.East:
                case Direction.West:
                    // East & West
                    y = src.Pos.Y + conn.Item2 - targetConnPt.Item2;

                    if (conn.Item1 == Direction.East)
                        x = src.Pos.X + src.Width + sep;
                    else
                        x = src.Pos.X - sep - target.Width;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    link = new Link(conn.Item1, src.Pos.Y + conn.Item2);
                    break;
            }

            collision.Add(target);
            return link;
        }
        private Link? PlaceRoomSourceFixed(FixedRoom src, Room target, int connPt, int sep)
        {
            var conn = src.ConnectionPoints[connPt];
            int x, y;
            Link? link = null;

            switch (conn.Item1)
            {
                case Direction.North:
                case Direction.South:
                    // North & South
                    int minX = src.Pos.X + conn.Item2 + template.CorridorWidth - target.Width;
                    int maxX = src.Pos.X + conn.Item2;
                    x = rand.Next(minX, maxX + 1);

                    if (conn.Item1 == Direction.South)
                        y = src.Pos.Y + src.Height + sep;
                    else
                        y = src.Pos.Y - sep - target.Height;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    link = new Link(conn.Item1, src.Pos.X + conn.Item2);
                    break;

                case Direction.East:
                case Direction.West:
                    // East & West
                    int minY = src.Pos.Y + conn.Item2 + template.CorridorWidth - target.Height;
                    int maxY = src.Pos.Y + conn.Item2;
                    y = rand.Next(minY, maxY + 1);

                    if (conn.Item1 == Direction.East)
                        x = src.Pos.X + src.Width + sep;
                    else
                        x = src.Pos.X - sep - target.Width;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    var linkY = new Range(src.Pos.Y, src.Pos.Y + src.Height).Intersection(
                        new Range(target.Pos.Y, target.Pos.Y + target.Height));
                    link = new Link(conn.Item1, src.Pos.Y + conn.Item2);
                    break;
            }

            collision.Add(target);
            return link;
        }
        private Link? PlaceRoomTargetFixed(Room src, FixedRoom target, int connPt, int sep)
        {
            var targetDir = ((Direction)connPt).Reverse();

            var connPts = (Tuple<Direction, int>[])target.ConnectionPoints.Clone();
            rand.Shuffle(connPts);
            Tuple<Direction, int> conn = null;
            foreach (var pt in connPts)
            {
                if (pt.Item1 == targetDir)
                {
                    conn = pt;
                    break;
                }
            }

            if (conn == null)
                return null;

            int x, y;
            Link? link = null;
            switch (conn.Item1)
            {
                case Direction.North:
                case Direction.South:
                    // North & South
                    int minX = src.Pos.X - conn.Item2;
                    int maxX = src.Pos.X + src.Width - template.CorridorWidth - conn.Item2;
                    x = rand.Next(minX, maxX + 1);

                    if (conn.Item1 == Direction.North)
                        y = src.Pos.Y + src.Height + sep;
                    else
                        y = src.Pos.Y - sep - target.Height;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    link = new Link((Direction)connPt, target.Pos.X + conn.Item2);
                    break;

                case Direction.East:
                case Direction.West:
                    // East & West
                    int minY = src.Pos.Y - conn.Item2;
                    int maxY = src.Pos.Y + src.Height - template.CorridorWidth - conn.Item2;
                    y = rand.Next(minY, maxY + 1);

                    if (conn.Item1 == Direction.West)
                        x = src.Pos.X + src.Width + sep;
                    else
                        x = src.Pos.X - sep - target.Width;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    link = new Link((Direction)connPt, target.Pos.Y + conn.Item2);
                    break;
            }

            collision.Add(target);
            return link;
        }
        private Link? PlaceRoomFree(Room src, Room target, Direction connPt, int sep)
        {
            int x, y;
            Link? link = null;

            switch (connPt)
            {
                case Direction.North:
                case Direction.South:
                    // North & South
                    int minX = src.Pos.X + template.CorridorWidth - target.Width;
                    int maxX = src.Pos.X + src.Width - template.CorridorWidth;
                    x = rand.Next(minX, maxX + 1);

                    if (connPt == Direction.South)
                        y = src.Pos.Y + src.Height + sep;
                    else
                        y = src.Pos.Y - sep - target.Height;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    var linkX = new Range(src.Pos.X, src.Pos.X + src.Width).Intersection(
                        new Range(target.Pos.X, target.Pos.X + target.Width));
                    link = new Link(connPt, new Range(linkX.Begin, linkX.End - template.CorridorWidth).Random(rand));
                    break;

                case Direction.East:
                case Direction.West:
                    // East & West
                    int minY = src.Pos.Y + template.CorridorWidth - target.Height;
                    int maxY = src.Pos.Y + src.Height - template.CorridorWidth;
                    y = rand.Next(minY, maxY + 1);

                    if (connPt == Direction.East)
                        x = src.Pos.X + src.Width + sep;
                    else
                        x = src.Pos.X - sep - target.Width;

                    target.Pos = new Point(x, y);
                    if (collision.HitTest(target))
                        return null;

                    var linkY = new Range(src.Pos.Y, src.Pos.Y + src.Height).Intersection(
                        new Range(target.Pos.Y, target.Pos.Y + target.Height));
                    link = new Link(connPt, new Range(linkY.Begin, linkY.End - template.CorridorWidth).Random(rand));
                    break;
            }

            collision.Add(target);
            return link;
        }