using System; using System.Collections.Generic; using System.Linq; using STVrogue.GameLogic; namespace STVrogue.Utils { /// /// Contain the forall and exists quantifiers, and some additional predicate /// operators. /// Also contains a bunch of useful helper predicates. /// public class HelperPredicates { /// /// A forall-quantifier over a collection. /// Example: Forall(C, x => x>=0) checks whether all integers in the collection /// C are non-negative. /// public static bool Forall(ICollection C, Predicate p) { foreach (T x in C) // not using linq to make the code explicit for you { if (!p(x)) return false; } return true; } /// /// An exist-quantifier over a collection. /// Example: Exists(C, x => x>=0) checks whether there is a non-negative integer /// in the collection C. /// public static bool Exists(ICollection C, Predicate p) { return !Forall(C, x => !p(x)); } /// /// A forall-quantifier over an array. /// Example: Forall(a, i => a[i]==0) checks whether for all valid indices i /// of the array a, a[i]==0. /// public static bool Forall(T[] a, Predicate p) { for (int k = 0; k < a.Length; k++) { if (!p(k)) return false; } return true; } /// /// An exists-quatifier over an array. /// Example: Exists(a, i => a[i]==0) checks whether for all valid indices i /// of the array a, a[i]==0. /// public static bool Exists(T[] a, Predicate p) { return !Forall(a, k => !p(k)) ; } // just for demonstrating the syntax to you: private void test() { List C = new List(); Forall(C, x=>x >= 0); Forall(C, (int x)=>x >= 0) ; // if you need to explicitly specify the type of x Exists(C, x=>x < 0); Exists(C, (int x)=>x < 0) ; } /// /// Check if p implies q (which is equivalent to !p or q). /// public static bool Imp(bool p, bool q) { return !p || q; } /// /// Check if all rooms in a dungeon are reachable from the start-room. /// public static bool AllReachableFromStart(Dungeon dungeon) { return Forall(dungeon.Rooms, room => dungeon.StartRoom.CanReach(room)); } /// /// Check if the given dungeon has a unique start room and a unique exit room. /// public static bool HasUniqueStartAndExit(Dungeon dungeon) { return dungeon.StartRoom != dungeon.ExitRoom && dungeon.StartRoom.RoomType == RoomType.STARTroom && dungeon.ExitRoom.RoomType == RoomType.EXITroom // start and exit rooms should be in the dungeon: && Exists(dungeon.Rooms, room => room == dungeon.StartRoom) && Exists(dungeon.Rooms, room => room == dungeon.ExitRoom) // start is the only start-room, and exit is the only exit-room: && Forall(dungeon.Rooms, room => Imp(room.RoomType == RoomType.STARTroom, room == dungeon.StartRoom)) && Forall(dungeon.Rooms, room => Imp(room.RoomType == RoomType.EXITroom, room == dungeon.ExitRoom)) ; } /// /// Check if all ids in the given dungeon are indeed unique. /// public static bool AllIdsAreUnique(Dungeon dungeon) { // collect all the ids" var ids = new List(); foreach(Room room in dungeon.Rooms) { ids.Add(room.Id); ids.AddRange(from m in room.Creatures select m.Id); ids.AddRange(from i in room.Items select i.Id); } var ids_ = ids.ToArray(); // unique if forall i,k: ids[i]=ids[k] ==> i=k : return Forall(ids_, i => Forall(ids_, k => Imp(ids_[i] == ids_[k], i == k))); } /// /// Check if the given dungeon forms a list starting at the start-room. /// public static bool IsLinear(Dungeon dungeon) { if (!HasUniqueStartAndExit(dungeon)) return false; if (!AllReachableFromStart(dungeon)) return false; // all rooms are reachable from the start-room // It is sufficient to check that the start and exit room has exactly one neighbor, // and all the other rooms have exactly two. // If one has less neighbor, or link to itself, some room will be unreachable, which // contradict the previous assumotion. // More generally, if there is a cycle in the graph, since all rooms have at most two // neighbors, it will cause some room to be unreachable. return Forall(dungeon.Rooms, room => // start and exit rooms should have exactly one neighbor, which is not itself: Imp(room == dungeon.StartRoom || room == dungeon.ExitRoom, room.Neighbors.Count == 1 && room.Neighbors.First() != room ) // other rooms should have exactly two neighbors: && Imp(room != dungeon.StartRoom && room != dungeon.ExitRoom, room.Neighbors.Count == 2 ) ); } /// /// Check if the given dungeon forms a tree rooted at the start-room. /// public static bool IsTree(Dungeon dungeon) { throw new NotImplementedException(); } } }