# ランダム生成ダンジョン

class RoomInstance
	attr_reader :left, :top, :width, :height

	def initialize(left, top, width, height)
		@left, @top, @width, @height = left, top, width, height
	end
	
	def right
		left + width - 1
	end
	
	def bottom
		top + height - 1
	end
	

	def each_cell
		(left..right).each do |x|
			(top..bottom).each do |y|
				yield(x, y)
			end
		end
	end
	alias each each_cell
	
	def blit(floor)
	
		(left..right).each do |x|
			floor.set_wall(x, top, DIR_N)
			floor.set_wall(x, bottom, DIR_S)
			
		end
		
		(top..bottom).each do |y|
			floor.set_wall(left, y, DIR_W)
			floor.set_wall(right, y, DIR_E)
		end
		
		floor.set_room(left, top, width, height)
		

	end
	
	def set_doors(floor)
		door_candidates = {}
	
		(left..right).each do |x|
			if floor.flooring(x, top - 1) != Area::WALL then
				section_id = floor.cells[x][top - 1].section_id
				door_candidates[section_id] ||= []
				door_candidates[section_id] << [x, top, DIR_N]
			end
			
			if floor.flooring(x, bottom + 1) != Area::WALL then
				section_id = floor.cells[x][bottom + 1].section_id
				door_candidates[section_id] ||= []
				door_candidates[section_id] << [x, bottom, DIR_S]
			end
		end
		
		(top..bottom).each do |y|
			if floor.flooring(left - 1, y) != Area::WALL then
				section_id = floor.cells[left - 1][y].section_id
				door_candidates[section_id] ||= []
				door_candidates[section_id] << [left, y, DIR_W]
			end
			
			if floor.flooring(right + 1, y) != Area::WALL then
				section_id = floor.cells[right + 1][y].section_id
				door_candidates[section_id] ||= []
				door_candidates[section_id] << [right, y, DIR_E]
			end

		end
		
		
		# ドアセット
		door_candidates.each_pair do |section_id, arg_list|
			unless arg_list.empty? then
				args = arg_list[rand(arg_list.size)]
				args << Area::DOOR
				floor.set_wall(*args)
			end
		end
	end
	
	def overlay?(other)
		each_cell do |x, y|
			return true if other.include_cell?(x, y)
		end
		
		return false
	end
	
	def include_cell?(x, y)
		(left..right).include?(x) && (top..bottom).include?(y)
	end
end



@width = 60
@height = 60

SPARSE_BREAK_TRY = 2
ROOM_SET_TRY = 20

initialize_form


cx, cy = 10, 0

set_wall(DIR_N, cx, cy, Area::EXIT_DOOR)
set_route(cx, cy, 1, 1)

visited = []
@width.times{visited << []}
visited.each do |row|
	@height.times{row <<  false}
end
visited[cx][cy] = true


list = []
dir = DIR_S


10000.times do

	list.clear
	list << DIR_N << DIR_E << DIR_S << DIR_W
	
	# random turn
	dir = nil if rand(100) < 60
	
	until list.empty? do
	
		dir ||= Util.random_pick(list)

		case dir
		when DIR_N
			x = cx
			y = cy - 2
		when DIR_E
			x = cx + 2
			y = cy
		when DIR_S
			x = cx
			y = cy + 2
		when DIR_W
			x = cx - 2
			y = cy
		end
		
		# valid check
		if x >= 0 and y >= 0 and x < 30 and y < 30 and
		flooring(x, y) == Area::WALL and not visited[x][y] then
		
			# extend route
			case dir
			when DIR_N
				set_route(x, y, 1, 3)
				cy -= 2
			when DIR_E
				set_route(cx, cy, 3, 1)
				cx += 2
			when DIR_S
				set_route(cx, cy, 1, 3)
				cy += 2
			when DIR_W
				set_route(x, y, 3, 1)
				cx -= 2
			end
			
			
			
			visited[x][y] = true

				
			break
			
			
		else
			list.delete(dir)
			dir = nil

		end
	end
	
	# reposition
	if list.empty? then
		#puts "deadend.(#{cx}, #{cy})"
	
		visited_list = []
	
		(0..14).each do |x|
			(0..14).each do |y|
				visited_list << [x * 2, y * 2] if visited[x * 2][y * 2]
			end
		end
		
		
		if visited_list.size >= 15 * 15 then   # if all cell is visited 
			break 
		else
			cx, cy = Util.random_pick(visited_list)
		end
		#puts "-> repositioning.(#{cx}, #{cy})"
		
	end
end



# 行き詰まり削減
sparse_list = []
dirlist = [DIR_N, DIR_E, DIR_S, DIR_W]

SPARSE_BREAK_TRY.times do
	sparse_list.clear

	(0...30).each do |x|
		(0...30).each do |y|
			unless flooring(x, y) == Area::WALL then
				
				if open_dir_number(x, y) <= 1 then
					sparse_list << x << y
				end
				
			end
		end
	end
	
	until sparse_list.empty? do
		cx = sparse_list.shift; cy = sparse_list.shift
		
		8.times do
		
			back_dir = dirlist.find{|x| wall(cx, cy, x) != Area::WALL}
			set_wall_area(cx, cy, 1, 1)
			case back_dir
			when DIR_N; cy -= 1
			when DIR_E; cx += 1
			when DIR_S; cy += 1
			when DIR_W; cx -= 1
			end
			
			break if open_dir_number(cx, cy) >= 2
		end
	end
end


# 行き詰まり繋ぎ
(0...30).each do |x|
	(0...30).each do |y|
		unless flooring(x, y) == Area::WALL then
			
			if open_dir_number(x, y) <= 1 and rand(100) < 40 then
				
			end
			
		end
	end
end





rooms = []
# set rooms
ROOM_SET_TRY.times do |number|
	room = RoomInstance.new(rand(26), rand(26), rand(4) + 2, rand(4) + 2)
	
	if rooms.any?{|x| x.overlay?(room)} then
		redo
	end
	
	rooms << room
end


rooms.each{|x| x.blit(self)}
update_section_data
rooms.each{|x| x.set_doors(self)}
