diff --git a/game/engine/generator/map/Roomer.lua b/game/engine/generator/map/Roomer.lua
new file mode 100644
index 0000000000000000000000000000000000000000..9530aee1f654ead61d8986969d0afb49060c0bdd
--- /dev/null
+++ b/game/engine/generator/map/Roomer.lua
@@ -0,0 +1,195 @@
+require "engine.class"
+local Map = require "engine.Map"
+require "engine.Generator"
+module(..., package.seeall, class.inherit(engine.Generator))
+
+function _M:init(map, grid_list, data)
+	engine.Generator.init(self, map)
+	self.data = data
+	self.data.tunnel_change = self.data.tunnel_change or 30
+	self.data.tunnel_random = self.data.tunnel_random or 10
+	self.data.tunnel_premature_end = self.data.tunnel_premature_end or 10
+	self.grid_list = grid_list
+
+	self.rooms = {}
+	for i, file in ipairs(data.rooms) do
+		table.insert(self.rooms, self:loadRoom(file))
+	end
+
+	self.room_map = {}
+	for i = 0, self.map.w - 1 do
+		self.room_map[i] = {}
+		for j = 0, self.map.h - 1 do
+			self.room_map[i][j] = {}
+		end
+	end
+end
+
+function _M:loadRoom(file)
+	local f = loadfile("/data/rooms/"..file..".lua")
+	local d = {}
+	setfenv(f, d)
+	local ret, err = f()
+	if not ret and err then error(err) end
+
+	-- Init the room with name and size
+	local t = { name=file, w=ret[1]:len(), h=#ret }
+
+	-- Read the room map
+	for j, line in ipairs(ret) do
+		local i = 1
+		for c in line:gmatch(".") do
+			t[i] = t[i] or {}
+			t[i][j] = c
+			i = i + 1
+		end
+	end
+	print("loaded room",file,t.w,t.h)
+
+	return t
+end
+
+function _M:resolve(c)
+	local res = self.data[c]
+	if type(res) == "function" then
+		return res()
+	elseif type(res) == "table" then
+		return res[rng.range(1, #res)]
+	else
+		return res
+	end
+end
+
+function _M:roomAlloc(room, id)
+	print("alloc", room.name)
+
+	local tries = 100
+	while tries > 0 do
+		local ok = true
+		local x, y = rng.range(1, self.map.w - 2 - room.w), rng.range(1, self.map.h - 2 - room.h)
+
+		-- Do we stomp ?
+		for i = 1, room.w do
+			for j = 1, room.h do
+				if self.room_map[i-1+x][j-1+y].room then ok = false break end
+			end
+			if not ok then break end
+		end
+
+		if ok then
+			-- ok alloc it
+			for i = 1, room.w do
+				for j = 1, room.h do
+					self.room_map[i-1+x][j-1+y].room = id
+					local c = room[i][j]
+					if c == '!' then
+						self.room_map[i-1+x][j-1+y].can_open = true
+						self.map(i-1+x, j-1+y, Map.TERRAIN, self.grid_list[self:resolve('#')])
+					else
+						self.map(i-1+x, j-1+y, Map.TERRAIN, self.grid_list[self:resolve(c)])
+					end
+				end
+			end
+			print("room allocated at", x, y,"with center",math.floor(x+(room.w-1)/2), math.floor(y+(room.h-1)/2))
+			return { id=id, x=x, y=y, cx=math.floor(x+(room.w-1)/2), cy=math.floor(y+(room.h-1)/2), room=room }
+		end
+		tries = tries - 1
+	end
+	return false
+end
+
+function _M:randDir()
+	local dirs = {4,6,8,2}
+	local d = dir_to_coord[dirs[rng.range(1, #dirs)]]
+	return d[1], d[2]
+end
+
+function _M:tunnelDir(x1, y1, x2, y2)
+	local xdir = (x1 == x2) and 0 or ((x1 < x2) and 1 or -1)
+	local ydir = (y1 == y2) and 0 or ((y1 < y2) and 1 or -1)
+	if xdir ~= 0 and ydir ~= 0 then
+		if rng.percent(50) then xdir = 0
+		else ydir = 0
+		end
+	end
+	return xdir, ydir
+end
+
+function _M:tunnel(x1, y1, x2, y2)
+	local xdir, ydir = self:tunnelDir(x1, y1, x2, y2)
+	print("tunneling from",x1, y1, "to", x2, y2, "initial dir", xdir, ydir)
+
+	local startx, starty = x1, y1
+	local tun = {}
+
+	local tries = 2000
+	while tries > 0 do
+		if rng.percent(self.data.tunnel_change) then
+			if rng.percent(self.data.tunnel_random) then
+				xdir, ydir = self:randDir()
+			else
+				xdir, ydir = self:tunnelDir(x1, y1, x2, y2)
+			end
+		end
+
+		local nx, ny = x1 + xdir, y1 + ydir
+		while true do
+			if self.map:isBound(nx, ny) and (self.room_map[nx][ny].room or not self.room_map[nx][ny].tunnel) then break end
+
+			if rng.percent(self.data.tunnel_random) then
+				xdir, ydir = self:randDir()
+			else
+				xdir, ydir = self:tunnelDir(x1, y1, x2, y2)
+			end
+			nx, ny = x1 + xdir, y1 + ydir
+		end
+
+		if self.room_map[nx][ny].room and not self.room_map[nx][ny].can_open then
+		else
+			for i = -1, 1 do for j = -1, 1 do
+				if self.map:isBound(nx + i, ny + j) then
+					self.room_map[nx + i][ny + j].can_open = false
+				end
+			end end
+
+			tun[#tun+1] = {nx,ny}
+		end
+		x1, y1 = nx, ny
+
+		if x1 == x2 and y1 == y2 then break end
+
+		tries = tries - 1
+	end
+
+	for _, t in ipairs(tun) do
+		local nx, ny = t[1], t[2]
+--		self.room_map[nx][ny].tunnel = true
+		self.map(nx, ny, Map.TERRAIN, self.grid_list[self:resolve("<")])
+	end
+end
+
+function _M:generate()
+	for i = 0, self.map.w - 1 do for j = 0, self.map.h - 1 do
+		self.map(i, j, Map.TERRAIN, self.grid_list[self:resolve("#")])
+	end end
+
+	local nb_room = 10
+	local rooms = {}
+	while nb_room > 0 do
+		local r = self:roomAlloc(self.rooms[rng.range(1, #self.rooms)], #rooms+1)
+		if r then rooms[#rooms+1] = r end
+		nb_room = nb_room - 1
+	end
+
+	-- Tunnels !
+	print("Begin tunnel", #rooms, rooms[1])
+	local tx, ty = rooms[1].cx, rooms[1].cy
+	for i = 2, #rooms do
+		self:tunnel(tx, ty, rooms[i].cx, rooms[i].cy)
+		tx, ty = rooms[i].cx, rooms[i].cy
+	end
+
+	-- Always starts at 1, 1
+	self.map(1, 1, Map.TERRAIN, self.up)
+	return 1, 1
+end
diff --git a/game/engine/generator/map/TileSet.lua b/game/engine/generator/map/TileSet.lua
index 086c55d7a0326fe08b5ae90c8b09d0f039c1caa5..2a6a2cac46e9cc28b54e000ab4de008548ff3568 100644
--- a/game/engine/generator/map/TileSet.lua
+++ b/game/engine/generator/map/TileSet.lua
@@ -8,6 +8,7 @@ function _M:init(map, grid_list, data)
 	self.data = data
 	self.grid_list = grid_list
 	self.tiles, self.raw = self:loadTiles(data.tileset)
+	self.matching_tiles = {}
 
 	self.block = self.raw.base
 	self.cols = math.floor(self.map.w / self.block.w)
@@ -96,25 +97,69 @@ function _M:matchTile(t1, t2)
 end
 
 function _M:findMatchingTiles(st, dir)
+	if self.matching_tiles[st] and self.matching_tiles[st][dir] then return self.matching_tiles[st][dir] end
+
 	local m = {}
 
 	for _, dt in ipairs(self.tiles) do
 		local ok = true
+		print("try match ", st.id, dt.id)
 		if dir == 8 then
-			for i = 1, self.block.w do
-				if not self:matchTile(st[i][1], dt[i][self.block.h]) then ok = false end
+			local sw = #st
+			local dw = #dt
+			print("sw/dw", sw,dw,#st[1],#dt[1])
+			if dw < sw then
+				print("dest 8 smaller")
+				for offset = 0, sw - dw do
+					ok = true
+					for i = 1, dw do
+						print("","try offset",offset,i,"::",st[i+offset] and st[i+offset][1], dt[i] and dt[i][self.block.h])
+						if not st[i+offset] or not dt[i] or not self:matchTile(st[i+offset][1], dt[i][self.block.h]) then ok = false end
+					end
+					print("tried ",offset,"result",ok)
+					if ok then break end
+				end
+			else
+				print("dest 8 bigger")
+				for offset = 0, dw - sw do
+					ok = true
+					for i = 1, sw do
+						print("","try offset",offset,i,"::",st[i] and st[i][1], dt[i+offset] and dt[i+offset][self.block.h])
+						if not st[i] or not dt[i+offset] or not self:matchTile(st[i][1], dt[i+offset][self.block.h]) then ok = false end
+					end
+					print("tried ",offset,"result",ok)
+					if ok then break end
+				end
 			end
 		elseif dir == 2 then
-			for i = 1, self.block.w do
-				if not self:matchTile(st[i][self.block.h], dt[i][1]) then ok = false end
+--[[
+			local sw = #st
+			local dw = #dt
+			if dw < sw then
+				print("dest 2 smaller")
+				for offset = 0, sw - dw do
+					for i = offset + 1, offset + dw do
+						if not st[i] or not dt[i] or not self:matchTile(st[i][self.block.h], dt[i][1]) then ok = false end
+					end
+				print("tried ",offset,"result",ok)
+				end
+			else
+				print("dest 2 bigger")
+				for offset = 0, dw - sw do
+					for i = offset + 1, offset + sw do
+						if not self:matchTile(st[i][self.block.h], dt[i][1]) then ok = false end
+					end
+				print("tried ",offset,"result",ok)
+				end
 			end
+]]
 		elseif dir == 4 then
 			for j = 1, self.block.h do
-				if not self:matchTile(st[1][j], dt[self.block.w][j]) then ok = false end
+				if not self:matchTile(st[1][j], dt[#dt][j]) then ok = false end
 			end
 		elseif dir == 6 then
 			for j = 1, self.block.h do
-				if not self:matchTile(st[self.block.w][j], dt[1][j]) then ok = false end
+				if not self:matchTile(st[#dt][j], dt[1][j]) then ok = false end
 			end
 		end
 		if ok then
@@ -123,6 +168,9 @@ function _M:findMatchingTiles(st, dir)
 		end
 	end
 
+	self.matching_tiles[st] = self.matching_tiles[st] or {}
+	self.matching_tiles[st][dir] = m
+
 	return m
 end
 
@@ -142,8 +190,9 @@ function _M:buildTile(tile, bx, by, rid)
 
 	if not self:roomAlloc(bx, by, bw, bh, rid) then return false end
 
-	for i = 1, self.block.w do
-		for j = 1, self.block.h do
+	print("building tile", tile.id, #tile, #tile[1])
+	for i = 1, #tile do
+		for j = 1, #tile[1] do
 			self.map(bx * self.block.w + i - 1, by * self.block.h + j - 1, Map.TERRAIN, self.grid_list[self:resolve(tile[i][j])])
 		end
 	end
@@ -152,8 +201,10 @@ function _M:buildTile(tile, bx, by, rid)
 		local coord = dir_to_coord[o[3]]
 		local mts = self:findMatchingTiles(tile, o[3])
 
-		opens[#opens+1] = {bx + coord[1], by + coord[2], tile=mts[rng.range(1, #mts)]}
-		print("room at ",bx,by,"opens to",o[3],"::",bx + coord[1], by + coord[2])
+		if #mts > 0 then
+			opens[#opens+1] = {bx + coord[1], by + coord[2], tile=mts[rng.range(1, #mts)]}
+			print("room at ",bx,by,"opens to",o[3],"::",bx + coord[1], by + coord[2])
+		end
 	end
 
 	return opens
@@ -167,7 +218,7 @@ function _M:generate()
 	local first = true
 	local process = {}
 	local id = 1
-	process[#process+1] = {math.floor(self.cols / 2), math.floor(self.rows / 2), tile=self.tiles[rng.range(1, #self.tiles)]}
+	process[#process+1] = {math.floor(self.cols / 2), math.floor(self.rows / 2), tile=self.tiles[2 or rng.range(1, #self.tiles)]}
 	while #process > 0 do
 		local b = table.remove(process)
 		local type = "room"
diff --git a/game/modules/tome/data/rooms/pilar.lua b/game/modules/tome/data/rooms/pilar.lua
new file mode 100644
index 0000000000000000000000000000000000000000..410293a4100024a259d02cee63fc97d36d132796
--- /dev/null
+++ b/game/modules/tome/data/rooms/pilar.lua
@@ -0,0 +1,7 @@
+return {
+[[#!!!!!!!!#]],
+[[!........!]],
+[[!...##...!]],
+[[!........!]],
+[[#!!!!!!!!#]],
+}
diff --git a/game/modules/tome/data/rooms/simple.lua b/game/modules/tome/data/rooms/simple.lua
new file mode 100644
index 0000000000000000000000000000000000000000..fc78931335db48bf7c13f0028360fc50698dfc2b
--- /dev/null
+++ b/game/modules/tome/data/rooms/simple.lua
@@ -0,0 +1,9 @@
+return {
+[[#!!!!!!!!!!#]],
+[[!..........!]],
+[[!..........!]],
+[[!..........!]],
+[[!..........!]],
+[[!..........!]],
+[[#!!!!!!!!!!#]],
+}
diff --git a/game/modules/tome/data/tilesets/dungeon.lua b/game/modules/tome/data/tilesets/dungeon.lua
index 04815cf92d1e97784812e77bd53cbf9bbf6f4ea0..ceb647b429774aa0be46699971626a54d369c2e2 100644
--- a/game/modules/tome/data/tilesets/dungeon.lua
+++ b/game/modules/tome/data/tilesets/dungeon.lua
@@ -8,24 +8,16 @@ matcher = function(t1, t2)
 end
 
 tiles =
-{type="tunnel",
 {
-[[#####]],
-[[#####]],
-[[.....]],
-[[#####]],
-[[#####]],
-},
 
 {type="tunnel",
-[[##.##]],
-[[##.##]],
-[[##.##]],
-[[##.##]],
-[[##.##]],
+[[#.#]],
+[[#.#]],
+[[#<#]],
+[[#.#]],
+[[#.#]],
 },
 
-
 {type="tunnel",
 [[##.##]],
 [[##.##]],
@@ -34,6 +26,14 @@ tiles =
 [[##.##]],
 },
 
+--[=[
+{type="tunnel",
+{
+[[#####]],
+[[.....]],
+[[#####]],
+},
+
 {type="tunnel",
 [[##.##]],
 [[##.##]],
@@ -65,7 +65,8 @@ tiles =
 [[##.##]],
 [[##.##]],
 },
-
+]=]
+--[=[
 {type="tunnel",
 [[##.##]],
 [[##.##]],
@@ -157,7 +158,7 @@ tiles =
 [[....#]],
 [[#####]],
 },
-
+]=]
 
 --[=[
 {type="room",
diff --git a/game/modules/tome/data/zones/ancient_ruins/zone.lua b/game/modules/tome/data/zones/ancient_ruins/zone.lua
index a13e028da842791bb1f7349cad50768a77caccb2..13c29e2b696979dd4ba928617c62e8ebd06d697b 100644
--- a/game/modules/tome/data/zones/ancient_ruins/zone.lua
+++ b/game/modules/tome/data/zones/ancient_ruins/zone.lua
@@ -2,16 +2,17 @@ return {
 	name = "ancient ruins",
 	level_range = {1, 5},
 	max_level = 5,
-	width = 50, height = 30,
+	width = 100, height = 100,
 	all_remembered = true,
 	all_lited = true,
 --	persistant = true,
 	generator =  {
 		map = {
-			class = "engine.generator.map.TileSet",
-			tileset = "dungeon",
+			class = "engine.generator.map.Roomer",
+			rooms = {"simple","pilar"},
 			['.'] = "FLOOR",
 			['#'] = "WALL",
+			['<'] = "UP",
 			up = "UP",
 			down = "DOWN",
 			['+'] = "DOOR",