using System.Diagnostics;
using System.Collections.Generic;
public const int SEED = 7777;
public const int MAP_WIDTH = 40;
public const int MAP_HEIGHT =15;
public const int MAX_LEAVES_COUNT = 100;
public const int MIN_SIZE = 4;
public const int MAX_SIZE = 20;
public const float QUIT_RATE = 0.4f;
public const int SINGLE_DOOR_PROB = 2;
public const int DOUBLE_DOOR_PROB = 1;
public const int HALLWAY_DOOR_PROB = 2;
public static void Main()
Console.WriteLine("Room Generator v0.12 By Fangwei Lee\n");
R.r = new Random(Config.SEED);
Tree tree = new Tree(1,1,Config.MAP_WIDTH-1,Config.MAP_HEIGHT-1);
public List<Leaf> tree = new List<Leaf>();
public List<Room> rooms = new List<Room>();
public Tree(int x, int y, int width, int height)
root = new Leaf(x, y, width, height);
while (splitIdx < tree.Count && tree.Count < Config.MAX_LEAVES_COUNT)
Leaf toSplit = tree[splitIdx];
tree.Add( toSplit.leftChild );
tree.Add( toSplit.rightChild );
foreach (Leaf leaf in tree)
Console.WriteLine("Room List:");
foreach (Room r in rooms)
Console.WriteLine(r.asString());
char[][] lines = new char[Config.MAP_WIDTH][];
for( int i = 0; i < Config.MAP_WIDTH; i++ )
lines[i] = new char[Config.MAP_HEIGHT];
for( int j = 0; j < Config.MAP_HEIGHT; j++ )
foreach( Leaf r in tree )
foreach (Room h in r.doors)
int area = h.rect.Width*h.rect.Height;
lines[h.rect.X][h.rect.Y] = '0';
for (int i = 0; i<h.rect.Width; i++)
for (int j = 0; j<h.rect.Height; j++)
lines[h.rect.X+i][h.rect.Y+j] = '0';
for (int i = 0; i<h.rect.Width; i++)
for (int j = 0; j<h.rect.Height; j++)
lines[h.rect.X+i][h.rect.Y+j] = '.';
for( int i = 0; i < d.rect.Width; i++ )
for( int j = 0; j < d.rect.Height; j++ )
lines[ d.rect.X+i ][ d.rect.Y+j ] = '.';
for( int i = 0; i < Config.MAP_WIDTH; i++ )
for( int j = 0; j < Config.MAP_HEIGHT; j++ )
for( int i = 0; i < Config.MAP_WIDTH; i++ )
Console.Write(lines[i][j]);
Console.WriteLine("\nLedgend:\n");
Console.WriteLine("#: Wall");
Console.WriteLine(".: Floor");
Console.WriteLine("0: Single Door");
Console.WriteLine("00: Double Door\n");
public int x, y, width, height, seed;
public Leaf parent, leftChild, rightChild;
public List<Room> doors = new List<Room>();
public enum Direction:byte { Left=0, Right=1, Top=2, Bottom=3 };
public Leaf(int inX, int inY, int inWidth, int inHeight, Leaf inParent=null, byte inConnectDir=0)
connectDir = inConnectDir;
if (connectDir == (byte)Direction.Top || connectDir == (byte)Direction.Left)
Debug.WriteLine("id="+id);
if (leftChild != null || rightChild != null)
if (R.r.NextDouble() < Config.QUIT_RATE && Math.Max(width, height) < Config.MAX_SIZE)
bool isSplitTopBottom = height>width;
Debug.WriteLine(height+","+width+","+isSplitTopBottom);
int max = (isSplitTopBottom?height:width) - Config.MIN_SIZE;
if (max <= Config.MIN_SIZE)
int splitPos = R.r.Next(Config.MIN_SIZE, max);
leftChild = new Leaf(x, y, width, splitPos, this, (byte)Direction.Bottom);
rightChild = new Leaf(x, y + splitPos, width, height - splitPos, this, (byte)Direction.Top);
leftChild = new Leaf(x, y, splitPos, height, this, (byte)Direction.Right);
rightChild = new Leaf(x + splitPos, y, width - splitPos, height, this, (byte)Direction.Left);
public void createRooms()
if (leftChild != null || rightChild != null)
rightChild.createRooms();
if (leftChild != null && rightChild != null)
Room leftRoom = leftChild.getRoomConnectToward(leftChild.connectDir);
createDoor(leftRoom, rightChild.getRoomNextTo(leftRoom));
int roomHeight = Math.Max(height, Config.MIN_SIZE )-1;
int roomWidth = Math.Max(width, Config.MIN_SIZE )-1;
room = new Room(x, y, roomWidth, roomHeight);
Debug.WriteLine("new room:"+x+","+y+","+roomWidth+","+roomHeight);
public Room getRoomConnectToward(byte connectDir)
Debug.WriteLine(connectDir);
lRoom = leftChild.getRoomConnectToward(connectDir);
rRoom = rightChild.getRoomConnectToward(connectDir);
if (lRoom==null && rRoom==null)
case (byte)Direction.Left:
if (rRoom.rect.Left==lRoom.rect.Left)
return R.r.Next(2)==1?rRoom:lRoom;
return rRoom.rect.Left<lRoom.rect.Left?rRoom:lRoom;
case (byte)Direction.Right:
if (rRoom.rect.Right==lRoom.rect.Right)
return R.r.Next(2)==1?rRoom:lRoom;
return (rRoom.rect.Right>lRoom.rect.Right?rRoom:lRoom);
case (byte)Direction.Top:
if (rRoom.rect.Top==lRoom.rect.Top)
return R.r.Next(2)==1?rRoom:lRoom;
return (rRoom.rect.Top<lRoom.rect.Top?rRoom:lRoom);
case (byte)Direction.Bottom:
if (rRoom.rect.Bottom==lRoom.rect.Bottom)
return R.r.Next(2)==1?rRoom:lRoom;
return (rRoom.rect.Bottom>lRoom.rect.Bottom?rRoom:lRoom);
return R.r.Next(2)==1?rRoom:lRoom;
public Room getRoomNextTo(Room target)
lRoom = leftChild.getRoomNextTo(target);
rRoom = rightChild.getRoomNextTo(target);
if (getSharedWall(rRoom, target)!=null)
else if (getSharedWall(lRoom, target)!=null)
public void createDoor(Room l, Room r)
Room sharedWall = getSharedWall(r, l);
bool isVertical = sharedWall.rect.Width < sharedWall.rect.Height;
int wallWidth = isVertical?sharedWall.rect.Height:sharedWall.rect.Width;
int doorWidth = getDoorWidth(wallWidth);
int margin = wallWidth - doorWidth;
Debug.WriteLine(wallWidth+", "+doorWidth+", "+margin);
doors.Add(new Room(sharedWall.rect.X, sharedWall.rect.Y + R.r.Next(margin), 1, doorWidth));
doors.Add(new Room(sharedWall.rect.X + R.r.Next(margin), sharedWall.rect.Y, doorWidth, 1));
private Room getSharedWall(Room r, Room l)
Room output = new Room(Rectangle.Intersect(Rectangle.Inflate(r.rect, 1, 1),Rectangle.Inflate(l.rect, 1, 1)));
if (output.rect.Width*output.rect.Height < 3)
output.rect.Inflate(-1, 0);
if (output.rect.Height>1)
output.rect.Inflate(0, -1);
if (output.rect.Width != 1 && output.rect.Height != 1)
private int getDoorWidth(int max)
x = R.r.Next(Config.SINGLE_DOOR_PROB + Config.DOUBLE_DOOR_PROB + Config.HALLWAY_DOOR_PROB);
if (x < Config.SINGLE_DOOR_PROB)
else if (x >= Config.SINGLE_DOOR_PROB && x < Config.SINGLE_DOOR_PROB + Config.DOUBLE_DOOR_PROB)
return max>=3?R.r.Next(3, max+1):max;
public Color accentColor;
enum RoomType:byte { livingRoom, bedRoom, kitchen, storage, foyer, corridor, bathRoom }
public Room(int x, int y, int width, int height)
return "id="+id+" "+rect;
enum DoorType:byte { singleDoor, doubleDoor, hallWay }