How do I split a rectangle into random smaller rectangles?

It’s on a grid, these smaller rectangles should have a minimum size and they should have random size.
I will probably need to cap the amount of rooms too.
I’m having a problem on the “repeat until it’s too small to split” part mainly.
I’ve got it to divide it one time, but I can’t get my head around this loop.

I’m using Javascript(Unityscript).

This is what I would like to make. A house similar to this.

alt text

The grey tiles are empty, white tiles are the floor that I’m trying to build and rooms should have 7 tiles in size to be divided, for rooms to have a minimum of 3x3 in size and maximum of 6x6.

I’m using an int[,] array as grid and setting values to instantiate objects later on another function.

After building the floor (white) I’ll build the walls around, and the doors, which I have no idea how to put them where they’ll work.

I’ll attach the code in case someone would like to take a look.

#pragma strict

var size:int;
var minroomsize:int;
var splitpointx:int;
var splitpointy:int;
var grid:int[,];
var wall:GameObject;
var floor:GameObject;
var binary:int;
var newgrid;
var walls:GameObject;
var floors:GameObject;
var grid1:int[,];
var grid2:int[,];

var g1lengthx:int;
var g2lengthx:int;
var g1lengthy:int;
var g2lengthy:int;

var group:GameObject;
var group1:GameObject;
var group2:GameObject;

function Start(){
size=20;
minroomsize=4;
splitpointx=0;
splitpointy=0;
grid= new int[size,size];
Run(size);

}

function Run(size:int){
	House(size);
	binary=Random.Range(0.0,2.0);
	if(binary==1){
		SplitV(grid);
	}else{
		SplitH(grid);
	}
	Debug.Log("g1lengthx = "+g1lengthx+" g1lengthy = "+g1lengthy+" g2lengthx = "+g2lengthx+" g2lengthy = "+g2lengthy);
	Loop(g1lengthx,g1lengthy,grid1);
	Loop(g2lengthx,g2lengthy,grid2);
	Test(size,grid1,grid2);
}

function Update(){
	if(Input.GetButtonDown("F5")){
		Reset();
		Run(size);

	}
}

function House(size:int){

	for(var x:int;x<size;x++){
		for(var y:int;y<size;y++){
			if(x>0&&x<size-1&&y>0&&y<size-1){
				grid[x,y]=2;		
			}			
		}
	}
}
function Loop(x:int,y:int,grid:int[,]){
	binary=Random.Range(0.0,2.0);
	var number:int;
	number=0;
	if(binary==1){
		while(x>minroomsize){
			SplitV(grid);
			if(x<=minroomsize){
				break;
			}
			number++;
			if(number>20){
				break;
			}
			if(number==20){
				Debug.Log("wrong");
			}
		}
	}else{
		while(y>minroomsize){
			SplitH(grid);
			number++;
			if(y<=minroomsize){
				break;
			}
			if(number>20){
				break;
			}
			if(number==20){
				Debug.Log("wrong");
			}
		}
	}
}

function SplitV(grid:int[,]){
	
	splitpointx=Random.Range(minroomsize,size-1-minroomsize);

	//Debug.Log("vertical split");
	grid1=new int[size,size];
	grid2=new int[size,size];
	
	for(var m:int=1;m<grid.GetLength(0);m++){
		for(var n:int=1;n<grid.GetLength(1);n++){
			if(m<splitpointx){
				//grid2[m,n]=0;
				grid1[m,n]=grid[m,n];
				if(grid1[1,n]==2){
					g1lengthy++;
				}
				if(grid1[m,1]==2){
					g1lengthx++;
				}
			}else if(m>splitpointx){
				//grid1[m,n]=0;
				grid2[m,n]=grid[m,n];
				if(grid2[grid.GetLength(0)-1,n]==2){
					g2lengthy++;
				}
				if(grid2[m,grid.GetLength(1)-1]==2){
					g2lengthx++;
				}
			}
		}
	}
}

function SplitH(grid:int[,]){
	splitpointy=Random.Range(minroomsize,size-1-minroomsize);
//Debug.Log("horizontal split");
	grid1=new int[size,size];
	grid2=new int[size,size];
	
	for(var m:int=1;m<grid.GetLength(0);m++){
		for(var n:int=1;n<grid.GetLength(1);n++){
			if(n<splitpointy){			
				grid1[m,n]=grid[m,n];
				if(grid1[1,n]==2){
					g1lengthy++;
				}
				if(grid1[m,1]==2){
					g1lengthx++;
				}
			}else if(n>splitpointy){
				grid2[m,n]=grid[m,n];
				if(grid2[grid.GetLength(0)-1,n]==2){
					g2lengthy++;
				}
				if(grid2[m,grid.GetLength(1)-1]==2){
					g2lengthx++;
				}
			}
		}
	}
}


function Build(size:int){
	for(var x:int;x<size;x++){
		for(var y:int;y<size;y++){
			if(grid[x,y]==1){
				var walls=Instantiate(wall,Vector3(x,wall.transform.localScale.y/2,y),Quaternion.identity);
				walls.transform.parent=this.transform;
			}
			if(grid[x,y]==2){
				var floors=Instantiate(floor,Vector3(x,0,y),Quaternion.Euler(90,0,0));
				floors.transform.parent=this.transform;
			}
		}
	}
}

function Test(size:int,grid1:int[,],grid2:int[,]){
	//Debug.Log("Test");
	
	for(var x:int=1;x<size;x++){
		for(var y:int=1;y<size;y++){
			
			if(grid1[x,y]==1){
				//Debug.Log("grid1 wall");
				var walls=Instantiate(wall,Vector3(x,wall.transform.localScale.y/2,y),Quaternion.identity);
				walls.transform.parent=group1.transform;
			}
			if(grid1[x,y]==2){
				//Debug.Log("grid1 floor");
				var floors=Instantiate(floor,Vector3(x,0,y),Quaternion.Euler(90,0,0));
				floors.transform.parent=group1.transform;
			}
			if(grid2[x,y]==1){
				//Debug.Log("grid2 wall");
				walls=Instantiate(wall,Vector3(x,wall.transform.localScale.y/2,y),Quaternion.identity);
				walls.transform.parent=group2.transform;
			}
			if(grid2[x,y]==2){
				//Debug.Log("grid2 floor");
				floors=Instantiate(floor,Vector3(x,0,y),Quaternion.Euler(90,0,0));
				floors.transform.parent=group2.transform;
			}
		}
	}
}

function Reset(){
	if(this.transform.childCount!=0){
		var child:int;
		child=this.transform.childCount;
		for(var x:int;x<child;x++){
			Destroy(this.transform.GetChild(x).gameObject);
		}
	}
}

I really appreciate if someone helped me out on this. =)

I’ve just written two quick estension methods for Rect:

Just place this code in a C# file inside the “plugins” folder in your assets folder. You can use it from C# and UnityScript that way.

// C#
public static class RectSplitter
{
	public static List<Rect> SplitOnce(this Rect aSource, Vector2 aMinSize)
	{
		float aspect = aMinSize.x/aMinSize.y;
		if (aSource.width > aSource.height*aspect)
		{
			if (aSource.width > aMinSize.x*2)
			{
				float range = (aSource.width-aMinSize.x*2);
				float split = Random.Range(0,range) + aMinSize.x;
				Rect R1 = aSource;
				Rect R2 = aSource;
				R1.xMax -= split;
				R2.xMin = R1.xMax;
				var result = new List<Rect>();
				result.Add(R1);
				result.Add(R2);
				return result;
			}
			else
				return null;
		}
		else
		{
			if (aSource.height > aMinSize.y*2)
			{
				float range = (aSource.height-aMinSize.y*2);
				float split = Random.Range(0,range) + aMinSize.y;
				Rect R1 = aSource;
				Rect R2 = aSource;
				R1.yMax -= split;
				R2.yMin = R1.yMax;
				var result = new List<Rect>();
				result.Add(R1);
				result.Add(R2);
				return result;
			}
			else
				return null;
		}
	}
	
	public static List<Rect> Split(this Rect aSource, Vector2 aMinSize)
	{
		var result = new List<Rect>();
		var tmp = SplitOnce(aSource,aMinSize);
		if (tmp != null)
		{
			foreach(Rect R in tmp)
			{
				result.AddRange(Split(R,aMinSize));
			}
		}
		else 
			result.Add(aSource);
		return result;
	}
}

This method splits a given Rect into smaller rects based on the given min-size. It goes recursively over the rects until they reached a size that can’t be splitted anymore.

Note: The method works on rects and float values, so if you need them applied to a grid, you have to round the min and max values.

var rects = baseRect.Split(new Vector2(5,4));
for (var i = 0; i < rects.Count; i++)
{
    rects_.xMin = Mathf.Round(rects*.xMin);*_

rects_.yMin = Mathf.Round(rects*.yMin);
rects.xMax = Mathf.Round(rects.xMax);
rects.yMax = Mathf.Round(rects.yMax);
}*

Also keep in mind that since you want a 1 grid wall, all rects should be 1 grid bigger. I used a min value of 5 and 4 to get a min room of 4x3
You just have to add the walls on two sides of each room (bottom and right for example). The baserect should be one grid smaller than the actual space since you need the border wall top and left.
Your whold room creation procedure should be based more on objects than on seperate tiles on a grid. Regarding your “door problem”: Each final room-rect has 4 possible doors (on each wall), however you first have to figure out which rooms are actually “touching” and how much they overlap. It’s up to you to define limits (if your door is 1, 2, … grids wide). Once you know the overlapping “zone” you can just place a door in the middle. If you store the information which room has a connection to what other room you can do some “path” checks by vitually walking through the house by starting in a room and go from room to room without using the same door twice. If you walked the whole “tree” (so you used every door in each room) you should have visited each room. If not you’re missing doors :wink: Maybe your min size is too small in that case._