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();
}
}
}