using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using STVrogue.Utils;
using static STVrogue.Utils.HelperPredicates;
namespace STVrogue.GameLogic
{
///
/// Representing a dungeon. A dungeon consists of rooms, connected to from a graph.
/// It has one unique starting room and one unique exit room. All rooms should be
/// reachable from the starting room.
///
public class Dungeon
{
#region Fields and Properties
///
/// All rooms in the dungeon, including the start and exit rooms.
///
public List Rooms { get; } = new List();
public Room StartRoom { get; set; }
public Room ExitRoom { get; set; }
// Use this to force deterministic random numbers generation for testing purposes.
// IRandomGenerator randomGenerator = new STVControlledRandom();
IRandomGenerator randomGenerator = new RandomGenerator();
#endregion
protected Dungeon() { }
///
/// Create a dungeon with the indicated number of rooms and the indicated shape.
/// A dungeon shape can be "linear" (list-shaped), "tree", or "random".
///
/// -
/// A dungeon should have a unique start-room and a unique exit-room.
/// -
/// All rooms in the dungeon must be reachable from the start-room.
/// -
/// Each room is set to have a random capacity between 1 and the given maximum-capacity.
/// Start and exit-rooms should have capacity 0.
///
///
public Dungeon(DungeonShapeType shape, int numberOfRooms, int maximumRoomCapacity) : base()
{
switch (shape)
{
case DungeonShapeType.LINEARshape:
MkLinearDungeon(numberOfRooms, maximumRoomCapacity);
break;
case DungeonShapeType.TREEshape:
MkTreeDungeon(numberOfRooms, maximumRoomCapacity);
break;
case DungeonShapeType.RANDOMshape:
MkRandomDungeon(numberOfRooms, maximumRoomCapacity);
break;
}
}
private void MkLinearDungeon(int numberOfRooms, int maximumRoomCapacity)
{
Room prev = null;
Room r = null;
for (int k = 0 ; k 0)
{
int capacity = randomGenerator.NextInt(maximumRoomCapacity) + 1 ; // kutu note
Room r = new Room("R" + freshId, RoomType.ORDINARYroom, capacity);
parent.Connect(r);
int N = Math.Max(1, numberOfRooms / 3);
int lastUsedId = MkTreeDungeonWorker(r, N, maximumRoomCapacity);
freshId = lastUsedId + 1;
numberOfRooms -= N;
}
return freshId; // kutu minor note
}
void MkRandomDungeon(int numberOfRooms, int maximumRoomCapacity)
{
bool isTree = true;
bool[] visited = new bool[numberOfRooms];
for (int k = 0 ; k b))
{
int j=i;
while(j==i)
{
j = randomGenerator.NextInt(numberOfRooms - 1);
}
if(!Rooms[i].Neighbors.Contains(Rooms[j]))
if (visited[j])
//this means j has multiple parents
isTree = false;
Rooms[i].Connect(Rooms[j]);
visited[j] = true;
i = j;
}
if (isTree)
{
//Our graph is not allowed to be a tree. By adding
//a new edge that did not exist before we can accomplish this
int j;
do
{
i = randomGenerator.NextInt(numberOfRooms - 1);
j = randomGenerator.NextInt(numberOfRooms - 1);
} while (Rooms[i].Neighbors.Contains(Rooms[j]));
Rooms[i].Connect(Rooms[j]);
}
//Create dungeons
//i=0
//while(not all dungeons have been visited){
// do{
// j=random()
// }while(i==j)
// add j as neighbour of i
// visited[j] = true
//}
//throw new NotImplementedException();
}
#region additional getters
///
/// Return all creatures in the Dungeon. The player is excluded.
///
public List Creatures
{
get => throw new NotImplementedException();
}
///
/// Return all items in this Dungeon. The items in the player's bag
/// are excluded.
///
public List- Items
{
get => throw new NotImplementedException();
}
#endregion
///
/// Populate the dungeon with the specified number of monsters and items.
/// They are dropped in random locations. Keep in mind that the number of
/// monsters in a room should not exceed the room's capacity. There are also
/// other constraints; see the Project Document.
///
/// Note that it is not always possible to populate the dungeon according to
/// the specified parameters. E.g. in a dungeon with N rooms whose capacity
/// are between 0 and k, it is definitely not possible to populate it with
/// (N-2)*k monsters or more.
/// The method returns true if it manages to populate the dungeon as specified,
/// else it returns false.
///
/// If it fails to populate the dungeon, it returns false.
///
public bool SeedMonstersAndItems(int numberOfMonster, int numberOfHealingPotion, int numberOfRagePotion)
{
throw new NotImplementedException();
}
}
[Serializable()]
public enum DungeonShapeType
{
LINEARshape,
TREEshape,
RANDOMshape
}
///
/// Representing different types of rooms.
///
public enum RoomType
{
STARTroom, // the starting room of the player.
EXITroom, // representing the player's final destination.
ORDINARYroom // the type of the rest of the rooms.
}
///
/// Representing a room in a dungeon.
///
public class Room : GameEntity
{
#region fields and properties
///
/// The type of this node: either start-node, exit-node, or common-node.
///
public RoomType RoomType { get; }
///
/// The number of monsters in this room cannot exceed this capacity.
///
public int Capacity { get; }
///
/// Neighbors are nodes that are considered connected to this node.
/// The connection is bidirectional. If u is in this.neighbors of this room,
/// you have to make sure that this room is also in u.neighbors.
///
public List Neighbors { get; } = new List();
///
/// All creatures, excluding the players, which are currently in this room.
///
public List Creatures { get; } = new List();
///
/// All items, excluding those in the player's bag, which are currently in this room.
///
public List
- Items { get; } = new List
- ();
#endregion
public Room(string uniqueId, RoomType roomTy, int capacity) : base(uniqueId)
{
RoomType = roomTy;
Capacity = capacity;
}
#region additional getters
///
/// The number of monsters in this room. The player does not count as a monster.
///
public int NumberOfMonsters => Creatures.Count(c => c is Monster);
#endregion
///
/// To add the given room as a neighbor of this room.
///
public void Connect(Room r)
{
Neighbors.Add(r); r.Neighbors.Add(this);
}
///
/// To disconnect the given room. That is, the room r will no longer be a
/// neighbor of this room.
///
public void Disconnect(Room r)
{
Neighbors.Remove(r); r.Neighbors.Remove(this);
}
///
/// return the set of all rooms which are reachable from this room.
///
public List ReachableRooms()
{
Room x = this;
var seen = new List();
var todo = new List();
todo.Add(x);
while (todo.Count > 0)
{
x = todo[0] ; todo.RemoveAt(0) ;
seen.Add(x);
foreach (Room y in x.Neighbors)
{
if (seen.Contains(y)
|| todo.Contains(y))
continue;
todo.Add(y);
}
}
return seen;
}
///
/// Check if the given room is reachable from this room.
///
public bool CanReach(Room r)
{
return ReachableRooms().Contains(r); // not the most efficient way of checking it btw
}
}
}