/// <summary> /// Legt fest, dass eine Tür durchschritten wurde /// </summary> /// <param name="set">Das Lookup-Set</param> /// <param name="room">Der Raum</param> /// <param name="direction">Die Richtung</param> private static void SetBeenThere(HashSet<Tuple<IRoom4, Door4>> set, IRoom4 room, Door4 direction) { Contract.Requires(set != null); Contract.Requires(room != null); Contract.Requires(direction.ExactlyOneValueSet()); set.Add(new Tuple<IRoom4, Door4>(room, direction)); }
/// <summary> /// Setzt einen Startraum und ermittelt den Raum, der am weitesten entfernt ist /// </summary> /// <param name="startRoom">Der Startraum</param> /// <param name="distances">Die Karte der Entfernungen vom Startraum</param> /// <returns>Die Liste der Sackgassen mit ihren Entfernungen zum Startpunkt</returns> /// <exception cref="ArgumentException">Der angegebene Raum liegt nicht im Labyrinth</exception> public IList<Tuple<int, IRoom4>> SetStartingPoint(IRoom4 startRoom, out int[,] distances) { // TODO: Logik in den Generator übernehmen. Contract.Requires(startRoom != null); Contract.Ensures(Contract.Result<IList<Tuple<int, IRoom4>>>() != null); Contract.Ensures(Contract.ValueAtReturn(out distances) != null); var startPosition = GetPosition(startRoom); if (startPosition == null) throw new ArgumentException("Der gegebene Raum liegt nicht im Labyrinth", "startRoom"); // Werte intialisieren int width = Rooms.GetLength(0); int height = Rooms.GetLength(1); int roomCount = width * height; int roomsVisited = 1; // Hilfslisten vorbereiten HashSet<Tuple<IRoom4, Door4>> visited = new HashSet<Tuple<IRoom4, Door4>>(); distances = new int[width, height]; int lastDistance = 0; // Die Zielwerte List<Tuple<int, IRoom4>> deadEnds = new List<Tuple<int, IRoom4>>(); // Bewegungs-Hilfsklasse erzeugen Marcher4 marcher = new Marcher4(Door4.East, startPosition.Item1, startPosition.Item2); // Und los. while (roomsVisited < roomCount) { IRoom4 currentRoom = GetRoom(marcher.X, marcher.Y); Door4 currentDoors = currentRoom.Doors; // Signalify your life OnMarcherMoved(new DistanceTrackingEventArgs(marcher.X, marcher.Y, marcher.Direction)); // Distanz setzen. // - Wenn wir nicht der Startraum sind und noch keine Distanz haben, alte Distanz erhöhen und setzen // - In jedem anderen Fall: "alte Distanz" auf aktuelle Distanz setzen (backtracking!) int currentDistance; if (currentRoom != startRoom && distances[marcher.X, marcher.Y] == 0 ) { currentDistance = ++lastDistance; distances[marcher.X, marcher.Y] = currentDistance; ++roomsVisited; } else { lastDistance = currentDistance = distances[marcher.X, marcher.Y]; } // Wenn wir uns nach links bewegen können und dort noch nicht waren if (marcher.CanTurnLeft(currentDoors) && !BeenThere(visited, currentRoom, marcher.LeftDirection)) { SetBeenThere(visited, currentRoom, marcher.LeftDirection); marcher.TurnLeft().MoveForward(); continue; } // Wenn wir uns vorwärts bewegen können und dort noch nicht waren if (marcher.CanMoveForward(currentDoors) && !BeenThere(visited, currentRoom, marcher.Direction)) { SetBeenThere(visited, currentRoom, marcher.Direction); marcher.MoveForward(); continue; } // Wenn wir uns nach rechts bewegen können und dort noch nicht waren // (entspricht mehrfacher Linksdrehung) if (marcher.CanTurnRight(currentDoors) && !BeenThere(visited, currentRoom, marcher.RightDirection)) { SetBeenThere(visited, currentRoom, marcher.RightDirection); marcher.TurnRight().MoveForward(); continue; } // Hier angekommen, kann weder vorwärts, noch rückwärts gegangen werden // Raum als Sackgasse markieren if (currentRoom != startRoom) deadEnds.Add(new Tuple<int, IRoom4>(currentDistance, currentRoom)); marcher.TurnAround().MoveForward(); } // Entfernteste Punkte zurückgeben deadEnds.Sort((a, b) => -1 * a.Item1.CompareTo(b.Item1)); deadEnds.TrimExcess(); return deadEnds; }
/// <summary> /// Ermittelt, ob eine Tür bereits durchschritten wurde /// </summary> /// <param name="set">Das Lookup-Set</param> /// <param name="room">Der Raum</param> /// <param name="direction">Die Richtung</param> /// <returns><c>true</c>, wenn der angegebene Raum bereits durch diese Tür verlassen wurde, ansonsten <c>false</c></returns> private static bool BeenThere(HashSet<Tuple<IRoom4, Door4>> set, IRoom4 room, Door4 direction) { Contract.Requires(set != null); Contract.Requires(room != null); Contract.Requires(direction.ExactlyOneValueSet()); return set.Contains(new Tuple<IRoom4, Door4>(room, direction)); }
/// <summary> /// Ermittelt die Koordinaten eines Raumes /// </summary> /// <param name="room">Der zu findende Raum</param> /// <returns>Die Position des Raumes oder <c>null</c>, falls der Raum nicht gefunden wurde</returns> public Tuple<int, int> GetPosition(IRoom4 room) { Contract.Requires(room != null); Tuple<int, int> position; return _roomIndexLookup.TryGetValue(room, out position) ? position : null; }