Skip to content

Instantly share code, notes, and snippets.

@poemdexter
Last active May 12, 2016 13:43
Show Gist options
  • Save poemdexter/b74e655dc51e75431d89 to your computer and use it in GitHub Desktop.
Save poemdexter/b74e655dc51e75431d89 to your computer and use it in GitHub Desktop.
C# implementation of dungeon generation based off https://github.com/adonaac/blog/issues/1
using System;
using System.Collections.Generic;
// from https://github.com/adonaac/blog/issues/1
public class DungeonGenerator
{
/** GRID OF CELLS **/
private Cell[,] grid;
private int gridWidth, gridHeight; // of grid
private const int UNASSIGNED = 0, EASY = 1, MED = 2, HARD = 3; // difficulty
private int easyCells, mediumCells, hardCells; // sum of all rooms must equal gridwidth x gridheight
/** DUNGEON ROOMS **/
private List<Room> rooms;
private int defaultChance, wideChance, tallChance, squareChance; // sum of all room chances must be 100
private const int DEFAULT = 1; // room type 1 x 1
private const int WIDE = 2; // room type 2 x 1
private const int TALL = 3; // room type 1 x 2
private const int SQUARE = 4; // room type 2 x 2
private float connectedness;
private static Random random = new Random();
public DungeonGenerator(int gridWidth, int gridHeight, int easyCells, int mediumCells, int hardCells, int defaultChance, int wideChance, int tallChance, int squareChance, float connectedness)
{
this.gridWidth = gridWidth;
this.gridHeight = gridHeight;
this.easyCells = easyCells;
this.mediumCells = mediumCells;
this.hardCells = hardCells;
this.defaultChance = defaultChance;
this.wideChance = wideChance;
this.tallChance = tallChance;
this.squareChance = squareChance;
this.connectedness = connectedness;
}
public Cell[,] GetGrid()
{
return grid;
}
public List<Room> GetRooms()
{
return rooms;
}
// returns true if we generated a dungeon with all rooms connected
public void Generate()
{
// create new grid and populate with Cells
grid = new Cell[gridWidth, gridHeight];
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
grid[x,y] = new Cell(x,y);
}
}
// dungeon generation steps
AssignDifficulty();
CreatePath();
CreateRooms();
ConnectRooms();
}
private void ConnectRooms()
{
// -- connection positions based on room type --
// DEFAULT 0 WIDE 0 1 TALL 0 SQUARE 0 1
// 3 o 1 5 o x 2 5 x 1 7 x x 2
// 2 4 3 4 o 2 6 o x 3
// 3 5 4
// create all possible connections between rooms
foreach (Room room in rooms)
{
int x = room.X;
int y = room.Y;
switch (room.Type)
{
case DEFAULT:
if ( y + 1 < gridHeight && grid[x , y + 1].IsRoom()) room.Connections[0] = grid[x , y + 1]; // 0
if (x + 1 < gridWidth && grid[x + 1, y ].IsRoom()) room.Connections[1] = grid[x + 1, y ]; // 1
if ( y - 1 >= 0 && grid[x , y - 1].IsRoom()) room.Connections[2] = grid[x , y - 1]; // 2
if (x - 1 >= 0 && grid[x - 1, y ].IsRoom()) room.Connections[3] = grid[x - 1, y ]; // 3
break;
case WIDE:
if ( y + 1 < gridHeight && grid[x , y + 1].IsRoom()) room.Connections[0] = grid[x , y + 1]; // 0
if (x + 1 < gridWidth && y + 1 < gridHeight && grid[x + 1, y + 1].IsRoom()) room.Connections[1] = grid[x + 1, y + 1]; // 1
if (x + 2 < gridWidth && grid[x + 2, y ].IsRoom()) room.Connections[2] = grid[x + 2, y ]; // 2
if (x + 1 < gridWidth && y - 1 >= 0 && grid[x + 1, y - 1].IsRoom()) room.Connections[3] = grid[x + 1, y - 1]; // 3
if ( y - 1 >= 0 && grid[x , y - 1].IsRoom()) room.Connections[4] = grid[x , y - 1]; // 4
if (x - 1 >= 0 && grid[x - 1, y ].IsRoom()) room.Connections[5] = grid[x - 1, y ]; // 5
break;
case TALL:
if ( y + 2 < gridHeight && grid[x , y + 2].IsRoom()) room.Connections[0] = grid[x , y + 2]; // 0
if (x + 1 < gridWidth && y + 1 < gridHeight && grid[x + 1, y + 1].IsRoom()) room.Connections[1] = grid[x + 1, y + 1]; // 1
if (x + 1 < gridWidth && grid[x + 1, y ].IsRoom()) room.Connections[2] = grid[x + 1, y ]; // 2
if ( y - 1 < gridHeight && grid[x , y - 1].IsRoom()) room.Connections[3] = grid[x , y - 1]; // 3
if (x - 1 >= 0 && grid[x - 1, y ].IsRoom()) room.Connections[4] = grid[x - 1, y ]; // 4
if (x - 1 >= 0 && y + 1 < gridHeight && grid[x - 1, y + 1].IsRoom()) room.Connections[5] = grid[x - 1, y + 1]; // 5
break;
case SQUARE:
if ( y + 2 < gridHeight && grid[x , y + 2].IsRoom()) room.Connections[0] = grid[x , y + 2]; // 0
if (x + 1 < gridWidth && y + 2 < gridHeight && grid[x + 1, y + 2].IsRoom()) room.Connections[1] = grid[x + 1, y + 2]; // 1
if (x + 2 < gridWidth && y + 1 < gridHeight && grid[x + 2, y + 1].IsRoom()) room.Connections[2] = grid[x + 2, y + 1]; // 2
if (x + 2 < gridWidth && grid[x + 2, y ].IsRoom()) room.Connections[3] = grid[x + 2, y ]; // 3
if (x + 1 < gridWidth && y - 1 >= 0 && grid[x + 1, y - 1].IsRoom()) room.Connections[4] = grid[x + 1, y - 1]; // 4
if ( y - 1 >= 0 && grid[x , y - 1].IsRoom()) room.Connections[5] = grid[x , y - 1]; // 5
if (x - 1 >= 0 && grid[x - 1, y ].IsRoom()) room.Connections[6] = grid[x - 1, y ]; // 6
if (x - 1 >= 0 && y + 1 < gridHeight && grid[x - 1, y + 1].IsRoom()) room.Connections[7] = grid[x - 1, y + 1]; // 7
break;
}
}
// randomly remove connections until a certain number of connections per room is met
int connectionsCount = GetTotalRoomConnections();
int connectionsNeeded = (int)Math.Ceiling(connectionsCount * connectedness);
int connectionsToRemove = connectionsCount - connectionsNeeded;
for (int i = 1; i <= connectionsToRemove; i++)
{
int r;
do
{
r = random.Next(rooms.Count);
}
while (!rooms[r].CanRemoveConnection());
rooms[r].RemoveRandomConnection();
}
ValidateDungeonConnections();
}
private void ValidateDungeonConnections()
{
// flood fill to ensure we have a connection from start to finish
List<int> floodedRooms = DoFloodFillOfRooms();
// if flooded rooms count not equal total rooms count, start adding single connections and rechecking
while (floodedRooms.Count != rooms.Count)
{
// find earliest room not flooded
for (int i = 0; i < rooms.Count; i++)
{
if (!floodedRooms.Contains(i))
{
// check room's adjacent cells to see if we can be connected to a flooded room
// -- connection positions based on room type --
// DEFAULT 0 WIDE 0 1 TALL 0 SQUARE 0 1
// 3 o 1 5 o x 2 5 x 1 7 x x 2
// 2 4 3 4 o 2 6 o x 3
// 3 5 4
Room room = rooms[i];
int x = room.X;
int y = room.Y;
switch (room.Type)
{
case DEFAULT:
if ( y + 1 < gridHeight && floodedRooms.Contains(grid[x , y + 1].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 0, "N", grid[x , y + 1]); // 0
else if (x + 1 < gridWidth && floodedRooms.Contains(grid[x + 1, y ].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 1, "E", grid[x + 1, y ]); // 1
else if ( y - 1 >= 0 && floodedRooms.Contains(grid[x , y - 1].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 2, "S", grid[x , y - 1]); // 2
else if (x - 1 >= 0 && floodedRooms.Contains(grid[x - 1, y ].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 3, "W", grid[x - 1, y ]); // 3
break;
case WIDE:
if ( y + 1 < gridHeight && floodedRooms.Contains(grid[x , y + 1].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 0, "N", grid[x , y + 1]); // 0
else if (x + 1 < gridWidth && y + 1 < gridHeight && floodedRooms.Contains(grid[x + 1, y + 1].RoomId)) ConnectTwoRooms(room, grid[x + 1,y ], 1, "N", grid[x + 1, y + 1]); // 1
else if (x + 2 < gridWidth && floodedRooms.Contains(grid[x + 2, y ].RoomId)) ConnectTwoRooms(room, grid[x + 1,y ], 2, "E", grid[x + 2, y ]); // 2
else if (x + 1 < gridWidth && y - 1 >= 0 && floodedRooms.Contains(grid[x + 1, y - 1].RoomId)) ConnectTwoRooms(room, grid[x + 1,y ], 3, "S", grid[x + 1, y - 1]); // 3
else if ( y - 1 >= 0 && floodedRooms.Contains(grid[x , y - 1].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 4, "S", grid[x , y - 1]); // 4
else if (x - 1 >= 0 && floodedRooms.Contains(grid[x - 1, y ].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 5, "W", grid[x - 1, y ]); // 5
break;
case TALL:
if ( y + 2 < gridHeight && floodedRooms.Contains(grid[x , y + 2].RoomId)) ConnectTwoRooms(room, grid[x ,y + 1], 0, "N", grid[x , y + 2]); // 0
else if (x + 1 < gridWidth && y + 1 < gridHeight && floodedRooms.Contains(grid[x + 1, y + 1].RoomId)) ConnectTwoRooms(room, grid[x ,y + 1], 1, "E", grid[x + 1, y + 1]); // 1
else if (x + 1 < gridWidth && floodedRooms.Contains(grid[x + 1, y ].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 2, "E", grid[x + 1, y ]); // 2
else if ( y - 1 < gridHeight && floodedRooms.Contains(grid[x , y - 1].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 3, "S", grid[x , y - 1]); // 3
else if (x - 1 >= 0 && floodedRooms.Contains(grid[x - 1, y ].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 4, "W", grid[x - 1, y ]); // 4
else if (x - 1 >= 0 && y + 1 < gridHeight && floodedRooms.Contains(grid[x - 1, y + 1].RoomId)) ConnectTwoRooms(room, grid[x ,y + 1], 5, "W", grid[x - 1, y + 1]); // 5
break;
case SQUARE:
else if ( y + 2 < gridHeight && floodedRooms.Contains(grid[x , y + 2].RoomId)) ConnectTwoRooms(room, grid[x ,y + 1], 0, "N", grid[x , y + 2]); // 0
else if (x + 1 < gridWidth && y + 2 < gridHeight && floodedRooms.Contains(grid[x + 1, y + 2].RoomId)) ConnectTwoRooms(room, grid[x + 1,y + 1], 1, "N", grid[x + 1, y + 2]); // 1
else if (x + 2 < gridWidth && y + 1 < gridHeight && floodedRooms.Contains(grid[x + 2, y + 1].RoomId)) ConnectTwoRooms(room, grid[x + 1,y + 1], 2, "E", grid[x + 2, y + 1]); // 2
else if (x + 2 < gridWidth && floodedRooms.Contains(grid[x + 2, y ].RoomId)) ConnectTwoRooms(room, grid[x + 1,y ], 3, "E", grid[x + 2, y ]); // 3
else if (x + 1 < gridWidth && y - 1 >= 0 && floodedRooms.Contains(grid[x + 1, y - 1].RoomId)) ConnectTwoRooms(room, grid[x + 1,y ], 4, "S", grid[x + 1, y - 1]); // 4
if ( y - 1 >= 0 && floodedRooms.Contains(grid[x , y - 1].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 5, "S", grid[x , y - 1]); // 5
else if (x - 1 >= 0 && floodedRooms.Contains(grid[x - 1, y ].RoomId)) ConnectTwoRooms(room, grid[x ,y ], 6, "W", grid[x - 1, y ]); // 6
else if (x - 1 >= 0 && y + 1 < gridHeight && floodedRooms.Contains(grid[x - 1, y + 1].RoomId)) ConnectTwoRooms(room, grid[x ,y + 1], 7, "W", grid[x - 1, y + 1]); // 7
break;
}
}
}
// recheck flood fill
floodedRooms = DoFloodFillOfRooms();
}
}
private void ConnectTwoRooms(Room currentRoom, Cell currentRoomCell, int connectionPosition, String connectionDirection, Cell connectingFloodCell)
{
// set cell connection for current room
currentRoom.Connections[connectionPosition] = connectingFloodCell;
// get room for connectingFloodCell
Room room = rooms[connectingFloodCell.RoomId];
// get room's connection position (polar opposite of connectionDirection)
// -- connection positions based on room type --
// DEFAULT 0s WIDE 0s 1s TALL 0s SQUARE 0s 1s
// 3e oo 1w 5e oo xx 2w 5e xx 1w 7e xx xx 2w
// 2n 4n 3n 4e oo 2w 6e oo xx 3w
// 3n 5n 4n
switch(room.Type)
{
case DEFAULT:
if ("S".equals(connectionDirection)) room.Connections[0] = currentRoomCell;
if ("W".equals(connectionDirection)) room.Connections[1] = currentRoomCell;
if ("N".equals(connectionDirection)) room.Connections[2] = currentRoomCell;
if ("E".equals(connectionDirection)) room.Connections[3] = currentRoomCell;
break;
case WIDE:
if ("S".equals(connectionDirection))
{
if (room.X == connectingCell.X) room.Connections[0] = currentRoomCell; // aligns with origin (o)
else room.Connections[1] = currentRoomCell;
}
if ("W".equals(connectionDirection)) room.Connections[2] = currentRoomCell;
if ("N".equals(connectionDirection))
{
if (room.X == connectingCell.X) room.Connections[4] = currentRoomCell; // aligns with origin (o)
else room.Connections[3] = currentRoomCell;
}
if ("E".equals(connectionDirection)) room.Connections[5] = currentRoomCell;
break;
case TALL:
if ("S".equals(connectionDirection)) room.Connections[0] = currentRoomCell;
if ("W".equals(connectionDirection))
{
if (room.Y == connectingCell.Y) room.Connections[2] = currentRoomCell; // aligns with origin (o)
else room.Connections[1] = currentRoomCell;
}
if ("N".equals(connectionDirection)) room.Connections[3] = currentRoomCell;
if ("E".equals(connectionDirection))
{
if (room.Y == connectingCell.Y) room.Connections[4] = currentRoomCell; // aligns with origin (o)
else room.Connections[5] = currentRoomCell;
}
break;
case SQUARE:
if ("S".equals(connectionDirection))
{
if (room.X == connectingCell.X) room.Connections[0] = currentRoomCell; // aligns with origin (o)
else room.Connections[1] = currentRoomCell;
}
if ("W".equals(connectionDirection))
{
if (room.Y == connectingCell.Y) room.Connections[3] = currentRoomCell; // aligns with origin (o)
else room.Connections[2] = currentRoomCell;
}
if ("N".equals(connectionDirection))
{
if (room.X == connectingCell.X) room.Connections[5] = currentRoomCell; // aligns with origin (o)
else room.Connections[4] = currentRoomCell;
}
if ("E".equals(connectionDirection))
{
if (room.Y == connectingCell.Y) room.Connections[6] = currentRoomCell; // aligns with origin (o)
else room.Connections[7] = currentRoomCell;
}
break;
}
}
private void DoFloodFillOfRooms()
{
List<int> floodedRooms = new List<int>();
Queue<Room> roomQueue = new Queue<Room>();
Room startRoom = rooms[0];
roomQueue.Enqueue(startRoom);
while (roomQueue.Count > 0)
{
Room room = roomQueue.Dequeue();
floodedRooms.Add(room.Id);
// queue up each new room we're connected to
foreach(Cell cell in room.Connections)
{
if (cell != null && !floodedRooms.Contains(cell.RoomId) && !roomQueue.Contains(cell.RoomId))
{
roomQueue.Enqueue(rooms[cell.RoomId]);
}
}
}
return floodedRooms;
}
private int GetTotalRoomConnections()
{
int total = 0;
foreach (Room room in rooms)
{
foreach(Cell cell in room.Connections)
{
total += (cell != null) ? 1 : 0;
}
}
return total;
}
private void CreateRooms()
{
rooms = new List<Room>();
int roomId = 0;
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
Cell cell = grid[x,y];
if (cell.IsActive && !cell.IsRoom()) // part of path cells but isn't room yet
{
int type;
do
{
type = GetRandomRoomType();
}
while (!ValidateRoomType(cell, type));
CreateRoom(cell, type, roomId);
roomId++;
}
}
}
}
private void CreateRoom(Cell cell, int roomType, int roomId)
{
// mark cells used in room and add room to rooms list
// all cells used in room use origin cell's difficulty
Room room = new Room(roomId, cell.X, cell.Y, roomType);
switch (roomType)
{
case DEFAULT:
room.Connections = new Cell[4];
room.Width = 1;
room.Height = 1;
cell.RoomId = roomId;
break;
case WIDE:
room.Connections = new Cell[6];
room.Width = 2;
room.Height = 1;
cell.RoomId = roomId;
grid[cell.X + 1, cell.Y].RoomId = roomId;
grid[cell.X + 1, cell.Y].Difficulty = cell.Difficulty;
break;
case TALL:
room.Connections = new Cell[6];
room.Width = 1;
room.Height = 2;
cell.RoomId = roomId;
grid[cell.X, cell.Y + 1].RoomId = roomId;
grid[cell.X, cell.Y + 1].Difficulty = cell.Difficulty;
break;
case SQUARE:
room.Connections = new Cell[8];
room.Width = 2;
room.Height = 2;
cell.RoomId = roomId;
grid[cell.X + 1, cell.Y ].RoomId = roomId;
grid[cell.X, cell.Y + 1].RoomId = roomId;
grid[cell.X + 1, cell.Y + 1].RoomId = roomId;
grid[cell.X + 1, cell.Y ].Difficulty = cell.Difficulty;
grid[cell.X, cell.Y + 1].Difficulty = cell.Difficulty;
grid[cell.X + 1, cell.Y + 1].Difficulty = cell.Difficulty;
break;
}
rooms.Add(room);
}
private bool ValidateRoomType(Cell cell, int roomType)
{
// ensure we're not going off the grid when we use not default size rooms
switch (roomType)
{
case DEFAULT:
return true;
case WIDE:
return cell.X + 1 < gridWidth;
case TALL:
return cell.Y + 1 < gridHeight;
case SQUARE:
return cell.X + 1 < gridWidth && cell.Y + 1 < gridHeight;
default:
return false;
}
}
private int GetRandomRoomType()
{
int c = random.Next(100);
if (c < defaultChance) return DEFAULT;
if (c < defaultChance + wideChance) return WIDE;
if (c < defaultChance + wideChance + tallChance) return TALL;
if (c < defaultChance + wideChance + tallChance + squareChance) return SQUARE;
return -1;
}
private void CreatePath()
{
// choose two cells on opposite ends of grid for start and finish point
int y1 = random.Next(gridHeight);
int y2 = random.Next(gridHeight);
Cell startCell = grid[0 , y1];
Cell finishCell = grid[gridWidth - 1, y2];
startCell.IsPathed = true;
startCell.ParentX = startCell.X;
startCell.ParentY = startCell.Y;
// A* from one cell to the other
List<Cell> openList = new List<Cell>();
List<Cell> closedList = new List<Cell>();
openList.Add(startCell);
bool found = false;
while (openList.Count > 0 && !found)
{
Cell parentCell = openList[0];
openList.Remove(parentCell);
closedList.Add(parentCell);
if (parentCell.X == finishCell.X && parentCell.Y == finishCell.Y)
{
found = true;
break;
}
// path check order ignoring cells in closed list and avoiding HARD cells
// 1
// 4 x 2
// 3
int x = parentCell.X;
int y = parentCell.Y;
if ( y + 1 < gridHeight && grid[x , y + 1].Difficulty != HARD && !closedList.Contains(grid[x , y + 1])) MarkPath(grid[x , y + 1], parentCell, openList); // 1
if (x + 1 < gridWidth && grid[x + 1, y ].Difficulty != HARD && !closedList.Contains(grid[x + 1, y ])) MarkPath(grid[x + 1, y ], parentCell, openList); // 2
if ( y - 1 >= 0 && grid[x , y - 1].Difficulty != HARD && !closedList.Contains(grid[x , y - 1])) MarkPath(grid[x , y - 1], parentCell, openList); // 3
if (x - 1 >= 0 && grid[x - 1, y ].Difficulty != HARD && !closedList.Contains(grid[x - 1, y ])) MarkPath(grid[x - 1, y ], parentCell, openList); // 4
}
// backtrack through closed list parents to get path
List<Cell> path = new List<Cell>();
if (found)
{
Cell currentCell = finishCell;
bool pathing = true;
while (pathing)
{
if (currentCell.ParentX == startCell.X && currentCell.ParentY == startCell.Y)
{
pathing = false;
}
path.Add(currentCell);
currentCell = grid[currentCell.ParentX, currentCell.ParentY];
}
}
foreach(Cell c in path)
{
// mark cells on path active
c.IsActive = true;
int x = c.X;
int y = c.Y;
// mark neighbors of path cells active
// check order
// 1 2 3
// 8 x 4
// 7 6 5
if (x - 1 >= 0 && y + 1 < gridHeight) grid[x - 1, y + 1].IsActive = true; // 1
if ( y + 1 < gridHeight) grid[x , y + 1].IsActive = true; // 2
if (x + 1 < gridWidth && y + 1 < gridHeight) grid[x + 1, y + 1].IsActive = true; // 3
if (x + 1 < gridWidth ) grid[x + 1, y ].IsActive = true; // 4
if (x + 1 < gridWidth && y - 1 >= 0 ) grid[x + 1, y - 1].IsActive = true; // 5
if ( y - 1 >= 0 ) grid[x , y - 1].IsActive = true; // 6
if (x - 1 >= 0 && y - 1 >= 0 ) grid[x - 1, y - 1].IsActive = true; // 7
if (x - 1 >= 0 ) grid[x - 1, y ].IsActive = true; // 8
}
}
private void MarkPath(Cell cell, Cell parentCell, List<Cell> openList)
{
if (!openList.Contains(cell)) // new path cell so set parent and add to open list
{
cell.ParentX = parentCell.X;
cell.ParentY = parentCell.Y;
cell.G = parentCell.G + 10;
cell.H = Math.Abs(cell.X - parentCell.X) + Math.Abs(cell.Y - parentCell.Y); // manhattan distance
openList.Add(cell);
}
else
{
if (cell.G > parentCell.G + 10) // see if linking to new parent is faster path
{
cell.G = parentCell.G + 10;
cell.ParentX = parentCell.X;
cell.ParentY = parentCell.Y;
}
}
}
private void AssignDifficulty()
{
while (hardCells > 0)
{
Cell cell = AssignHardCell();
if (mediumCells > 0) AssignRandomNeighborDifficulty(cell, MED); // for each hard cell, assign one neighbor medium
if (easyCells > 0) AssignRandomNeighborDifficulty(cell, EASY); // for each hard cell, assign one neighbor easy
}
// assign rest of cells randomly with remaining numbers of rooms
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
if (grid[x,y].Difficulty == UNASSIGNED)
{
int d = random.Next(1,3); // gets EASY or MED
if (d == EASY && easyCells > 0)
{
grid[x,y].Difficulty = EASY;
easyCells--;
}
else
{
grid[x,y].Difficulty = MED;
mediumCells--;
}
}
}
}
}
private Cell AssignHardCell()
{
Cell cell;
do
{
cell = GetRandomEmptyCell();
}
while (IsAdjacentToRedCell(cell)); // no red cell can have a red neighbor
cell.Difficulty = HARD;
hardCells--; // decrement after assigning a cell
return cell;
}
private void AssignRandomNeighborDifficulty(Cell cell, int difficulty)
{
int x = cell.X;
int y = cell.Y;
bool[] neighbors = new bool[4]; // unassigned cell flags
// position
// 0
// 3 x 1
// 2
neighbors[0] = ( y + 1 < gridHeight);
neighbors[1] = (x + 1 < gridWidth );
neighbors[2] = ( y - 1 >= 0 );
neighbors[3] = (x - 1 >= 0 );
int i;
do
{
i = random.Next(4);
}
while (!neighbors[i]); // only want valid neighbor
switch(i)
{
case 0: // N
grid[x , y + 1].Difficulty = difficulty;
break;
case 1: // E
grid[x + 1, y ].Difficulty = difficulty;
break;
case 2: // S
grid[x , y - 1].Difficulty = difficulty;
break;
case 3: // W
grid[x - 1, y ].Difficulty = difficulty;
break;
}
if (difficulty == MED) mediumCells--; // decrement after assigning a cell
if (difficulty == EASY) easyCells--; // decrement after assigning a cell
}
private Cell GetRandomEmptyCell()
{
int x,y;
do
{
x = random.Next(gridWidth);
y = random.Next(gridHeight);
}
while (grid[x,y].Difficulty != UNASSIGNED);
return grid[x,y];
}
private bool IsAdjacentToRedCell(Cell cell)
{
int x = cell.X;
int y = cell.Y;
// check order
// 1
// 4 x 2
// 3
if ( y + 1 < gridHeight && grid[x , y + 1].Difficulty == HARD) return true; // 1
if (x + 1 < gridWidth && grid[x + 1, y ].Difficulty == HARD) return true; // 2
if ( y - 1 >= 0 && grid[x , y - 1].Difficulty == HARD) return true; // 3
if (x - 1 >= 0 && grid[x - 1, y ].Difficulty == HARD) return true; // 4
return false;
}
}
using System.Collections.Generic;
public class Cell
{
public bool IsActive { get; set; }
public int RoomId { get; set; }
// position
public int X { get; set; }
public int Y { get; set; }
// difficulty: 1 easy, 2 medium, 3 hard
public int Difficulty { get; set; }
// pathfinding
public bool IsPathed { get; set; }
public int ParentX { get; set; }
public int ParentY { get; set; }
public int G { get; set; }
public int H { get; set; }
public int F { get { return G + H; } }
public Cell(int x, int y)
{
X = x;
Y = y;
RoomId = -1; // set to invalid value
}
public bool IsRoom()
{
return RoomId != -1;
}
}
using System;
public class Room
{
// 0 indexed for easy retrieval from List
public int Id { get; set; }
// origin is bottom left corner cell
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int Type { get; set; } // DEFAULT, WIDE, TALL, SQUARE
public Cell[] Connections { get; set; }
public Room(int roomId, int x, int y, int type)
{
Id = roomId;
X = x;
Y = y;
Type = type;
}
// only remove connection if connection count >= 2
public bool CanRemoveConnection()
{
int connectionCount = 0;
for (int i = 0; i < Connections.Length; i++)
{
if (Connections[i] != null) connectionCount++;
}
return connectionCount >= 2;
}
public void RemoveRandomConnection()
{
int r;
Random random = new Random();
do
{
r = random.Next(Connections.Length);
}
while (Connections[r] == null);
Connections[r] = null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment