Parcourir la Source

Initial commit.

Eiyeron Fulmincendii il y a 9 ans
Parent
commit
2d68f7ce94
11 fichiers modifiés avec 558 ajouts et 0 suppressions
  1. 6 0
      .gitmodules
  2. 177 0
      DungeonGenerator.lua
  3. 75 0
      Graph/Graph.lua
  4. 85 0
      Graph/MST.lua
  5. 38 0
      Graph/Node.lua
  6. 71 0
      Map.lua
  7. 43 0
      Room.lua
  8. 1 0
      bresenham
  9. 1 0
      delaunay
  10. 19 0
      func.lua
  11. 42 0
      generate_dataset.lua

+ 6 - 0
.gitmodules

@@ -0,0 +1,6 @@
+[submodule "bresenham"]
+	path = bresenham
+	url = https://github.com/kikito/bresenham.lua.git
+[submodule "delaunay"]
+	path = delaunay
+	url = http://github.com/Yonaba/delaunay.git

+ 177 - 0
DungeonGenerator.lua

@@ -0,0 +1,177 @@
+local Bresenham = require("bresenham/bresenham")
+local Delaunay = require("delaunay/delaunay")
+local Point    = Delaunay.Point
+local Map = require("Map")
+local Room = require("Room")
+
+local Node = require("Graph/Node")
+local Graph = require("Graph/Graph")
+local MST = require("Graph/MST")
+
+require("func")
+
+local Generator = {
+  _VERSION = "0",
+
+  DIMENSION=64,
+  NB_ROOMS = 400
+
+}
+
+
+
+function roundm(n, m) return math.floor(((n + m - 1)/m))*m end
+
+function getRandomPointInCircle(radius)
+  local t = 2*math.pi*math.random()
+  local u = math.random()+math.random()
+  local r = nil
+  if u > 1 then r = 2-u else r = u end
+  return radius*r*math.cos(t), radius*r*math.sin(t)
+end
+
+function trim_table(t, indices)
+  function reverse(tbl)
+    local r = {}
+    for i=1, math.floor(#tbl / 2) do
+      r[i], r[#tbl - i + 1] = tbl[#tbl - i + 1], tbl[i]
+    end
+    return r
+  end
+
+  -- We're reversing the indcies to avoid removing the wrong rooms by popping the items.
+  for _,i in pairs(reverse(indices)) do
+    table.remove(t, i)
+  end
+end
+
+
+-- Dirty and unoptimized room generation
+function generate_rooms()
+  local rooms = {}
+  for i=1, Generator.NB_ROOMS do 
+    width,height = roundm(math.pow(math.random(), 2)*10+2,1), roundm(math.pow(math.random(), 2)*10+2,1)
+    local x,y= getRandomPointInCircle(16-math.max(width, height))
+    x = x+Generator.DIMENSION/2
+    y = y+Generator.DIMENSION/2
+    local r = Room(x, y, width, height)
+    table.insert(rooms, r)
+  end
+
+  local done = false
+  local pass = 0
+  while not done do
+    -- Repulsion calculation
+    for a_i, a in pairs(rooms) do
+      a.vx, a.vy = 0,0
+      for b_i, b in pairs(rooms) do
+        if (a_i ~= b_i) and a:intersects(b) then
+          local dx = a.x - b.x
+          local dy = a.y - b.y
+          local l = math.sqrt(dx*dx + dy*dy)
+          a.vx = a.vx + dx/l
+          a.vy = a.vy + dy/l
+          break
+        end
+      end
+    end
+
+    -- Repulsion applicaiton and room carving
+    for _, room in pairs(rooms) do
+      local l = math.sqrt(room.vx*room.vx + room.vy*room.vy)
+      if l >= 0.01 then
+        room.x = room.x + room.vx/l
+        room.y = room.y + room.vy/l
+      end
+    end
+
+    -- triming out of bounds rooms
+    for i=#rooms,1,-1 do
+       local room = rooms[i]
+        if room.x < 1 or room.y < 1 or room.x >= Generator.DIMENSION-room.width-1 or room.y >= Generator.DIMENSION-room.height-1 then
+         table.remove(rooms, i)
+       end
+    end     
+
+    -- Exit check
+    done = true
+    for a_i, a in pairs(rooms) do
+      for b_i, b in pairs(rooms) do
+        if a_i ~= b_i and a:intersects(b) then
+          done = false
+          break
+        end
+      end
+      if not done then break end
+    end
+    pass = pass+1
+  end
+  return rooms
+end
+
+
+function Generator.generate(seed)
+    math.randomseed(seed) 
+
+    local rooms = generate_rooms()
+    -- Keeping the biggest rooms
+    local biggest_rooms = filter(function(r) return r.width >= 4 and r.height >= 4 end, rooms)
+
+    -- Generating their centers
+    local centers = map(function(r) return Point(r.x+r.width/2, r.y+r.height/2) end, biggest_rooms)
+
+
+
+    local triangles = Delaunay.triangulate(unpack(centers))
+    local edges = {}
+    for i, triangle in ipairs(triangles) do
+      for _,edge in pairs({triangle.e1, triangle.e2, triangle.e3}) do
+        local found = false
+        for _,v in pairs(edges) do
+          if v == edge then
+            found = true
+            break
+          end
+        end
+        if not found then
+          table.insert(edges, edge)
+        end
+      end
+    end
+
+    local graph = Graph(centers, edges)
+    local res = graph
+    if #edges > 3 then
+      res = MST.search(graph,
+        function(edge)
+          local dx = edge.p1.x - edge.p2.x
+          local dy = edge.p1.y - edge.p2.y
+          return math.sqrt(dx*dx+dy*dy)
+        end
+        )
+    end
+
+    local m = Map(Generator.DIMENSION, Generator.DIMENSION)
+    for _, r in pairs(biggest_rooms) do
+      m:carve(r)
+    end
+
+    for _,edge in pairs(res.edges) do
+      local vertices_ok = 0
+      for _,c in pairs(centers) do
+        if edge.p1.id ==c.id or edge.p2.id == c.id then
+          vertices_ok = vertices_ok + 1
+        end 
+      end
+      if vertices_ok == 2 then
+        Bresenham.los(math.floor(edge.p1.x), math.floor(edge.p1.y), math.floor(edge.p2.x), math.floor(edge.p2.y),
+          function(x, y) 
+            m.tiles[y][x] = 0 
+            return true 
+          end
+          )
+      end
+    end
+    return m
+end
+return Generator

+ 75 - 0
Graph/Graph.lua

@@ -0,0 +1,75 @@
+-- ================
+-- Private helpers
+-- ================
+local setmetatable = setmetatable
+local Node = require("Graph/Node")
+
+
+-- Internal class constructor
+local class = function(...)
+local klass = {}
+klass.__index = klass
+klass.__call = function(_,...) return klass:new(...) end
+function klass:new(...)
+  local instance = setmetatable({}, klass)
+  klass.__init(instance, ...)
+  return instance
+end
+return setmetatable(klass,{__call = klass.__call})
+end
+
+local Graph = class()
+Graph.__tostring = function(g) return ("Graph") end
+
+function Graph:__init(vertices, edges)
+  self.vertices = vertices
+  self.edges = edges
+  self.nodes = {}
+
+  for _,e in pairs(edges) do 
+    if self.nodes[e.p1.id] == nil then
+      self.nodes[e.p1.id] = Node(e.p1.id)
+    end
+    if self.nodes[e.p2.id] == nil then
+      self.nodes[e.p2.id] = Node(e.p2.id)
+    end
+    self.nodes[e.p1.id]:link(self.nodes[e.p2.id])
+    self.nodes[e.p2.id]:link(self.nodes[e.p1.id])
+  end
+  for _,v in pairs(vertices) do
+    if self.nodes[v.id] == nil then
+      self.nodes[v.id] = Node(v.id)
+    end
+  end
+end
+
+function Graph:contains_vertex(v)
+  if self.vertices[v.id] then return true end
+  return false
+end
+
+function Graph:append_vertex(v)
+  assert(v.id ~= nil, "vertex not configured")
+  assert(self.vertices[v.id] == nil, "vertex already existing")
+  self.vertices[v.id] = v
+  self.nodes[v.id] = Node(v.id)
+end
+
+function Graph:contains_edge(e)
+  for _,edge in pairs(self.edges) do
+    if e == edge then return true end
+  end
+  return false
+end
+
+
+function Graph:append_edge(e)
+for _,edge in pairs(self.edges) do
+  assert(e ~= edge, "Edge already existing")
+end
+table.insert(self.edges, e)
+self.nodes[e.p1.id]:link(self.nodes[e.p2.id])
+self.nodes[e.p2.id]:link(self.nodes[e.p1.id])
+end
+
+return Graph

+ 85 - 0
Graph/MST.lua

@@ -0,0 +1,85 @@
+-- ================
+-- Private helpers
+-- ================
+local setmetatable = setmetatable
+local Graph = require("Graph/Graph")
+local Node = require("Graph/Node")
+require("func")
+
+local MST = {
+_VERSION = "0"
+}
+
+function xor(a, b)
+  return (a and not b) or (b and not a)
+end
+
+-- cost = function taking an edge as argument and returning its cost
+function MST.search(g, cost)
+  local res = Graph({g.vertices[1]}, {})
+  for k,v in pairs(g.vertices) do
+
+  end
+
+  local nb_vertices = 1
+  while nb_vertices < #(g.vertices) do
+    local edges_not_in_res = filter(function(e)
+      if res:contains_edge(e) then
+        return false
+      end
+      return true
+      end, g.edges)
+    
+
+    for _,e in pairs(edges_not_in_res) do
+
+    end
+
+    local edges_exiting_res = filter(function (e)
+      local in_graph = 0
+      for _,v in pairs(res.vertices) do
+        if v.id == e.p1.id then
+          in_graph = in_graph + 1
+        end
+        if v.id == e.p2.id then
+          in_graph = in_graph + 1
+        end
+      end
+      if in_graph == 1 then return true end
+      return false
+      end, edges_not_in_res)
+
+
+    local min = nil
+    local selected_edge = nil
+    for _,e in pairs(edges_exiting_res) do
+      c = cost(e)
+      if min == nil or min > c then
+        min = c
+        selected_edge = e
+      end
+    end
+
+
+    if not res:contains_vertex(selected_edge.p1) then
+      res:append_vertex(selected_edge.p1)
+      nb_vertices = nb_vertices + 1 
+    end
+    if not res:contains_vertex(selected_edge.p2) then
+      res:append_vertex(selected_edge.p2)
+      nb_vertices = nb_vertices + 1 
+    end
+    if not res:contains_edge(selected_edge) then
+      res:append_edge(selected_edge)
+    end
+
+  end
+
+
+  for k,v in pairs(res.vertices) do
+
+  end
+  return res
+end
+
+return MST

+ 38 - 0
Graph/Node.lua

@@ -0,0 +1,38 @@
+-- ================
+-- Private helpers
+-- ================
+local setmetatable = setmetatable
+
+-- Internal class constructor
+local class = function(...)
+  local klass = {}
+  klass.__index = klass
+  klass.__call = function(_,...) return klass:new(...) end
+  function klass:new(...)
+    local instance = setmetatable({}, klass)
+    klass.__init(instance, ...)
+    return instance
+  end
+  return setmetatable(klass,{__call = klass.__call})
+end
+
+local Node = class()
+Node.__tostring = function(n)
+	local res = 'Node %d (%d neighbours)'
+	res = res:format(n.id, #n.neighbours)
+	for _,nn in pairs(n.neighbours) do
+		res = res..('\n=> %d'):format(nn.id)
+	end
+	return res
+end
+
+function Node:__init( id )
+	self.id = id
+	self.neighbours = {}
+end
+
+function Node:link( node )
+	table.insert(self.neighbours, node)
+end
+
+return Node

+ 71 - 0
Map.lua

@@ -0,0 +1,71 @@
+-- ================
+-- Private helpers
+-- ================
+local setmetatable = setmetatable
+
+-- Internal class constructor
+local class = function(...)
+  local klass = {}
+  klass.__index = klass
+  klass.__call = function(_,...) return klass:new(...) end
+  function klass:new(...)
+    local instance = setmetatable({}, klass)
+    klass.__init(instance, ...)
+    return instance
+  end
+  return setmetatable(klass,{__call = klass.__call})
+end
+
+local Map = class()
+Map.__tostring = function(m) return ('Map %dx%d'):format(m.width, m.height) end
+
+function Map:__init(w, h)
+	self.width, self.height = w, h
+	self.tiles = {}
+	for y=0,h-1 do
+		self.tiles[y] = {}
+		for x=0,w-1 do
+			self.tiles[y][x] = 1
+		end
+	end
+end
+
+function Map:print()
+	for y=0,self.height-1 do
+		line = ''
+		for x=0,self.width-1 do
+			if self.tiles[y][x] == 1 then
+				line = line..'#'
+			elseif self.tiles[y][x] == 2 then
+				line = line..'.'
+			else
+				line = line..' '
+			end 
+		end
+		print(line)
+	end
+end
+
+function Map:carve(room) 
+	local rx, ry = math.floor(room.x), math.floor(room.y)
+	if rx <= 0 or ry <= 0 or rx >= self.width-room.width-1 or ry >= self.height-room.height-1 then return end
+	self.tiles[ry][rx] = 2
+
+	for y=ry, ry+room.height-1 do
+		for x=rx, rx+room.width-1 do
+			self.tiles[y][x] = 0
+		end
+	end
+end
+
+function Map:toArray()
+	local a = {}
+	for y=0,self.height do
+		for x=0,self.width do
+			table.insert(a, self.tiles[y][x])
+		end
+	end
+	return a
+end
+
+return Map

+ 43 - 0
Room.lua

@@ -0,0 +1,43 @@
+-- ================
+-- Private helpers
+-- ================
+local setmetatable = setmetatable
+
+-- Internal class constructor
+local class = function(...)
+  local klass = {}
+  klass.__index = klass
+  klass.__call = function(_,...) return klass:new(...) end
+  function klass:new(...)
+    local instance = setmetatable({}, klass)
+    klass.__init(instance, ...)
+    return instance
+  end
+  return setmetatable(klass,{__call = klass.__call})
+end
+
+local Room = class()
+Room.__tostring = function(r) return ('Room  x=%f y=%f w=%d h=%d'):format(r.x, r.y, r.width, r.height) end
+
+function Room:__init(x, y, width, height)
+	self.x, self.y, self.width, self.height = x, y, width, height
+	self.vx, self.vy = 0,0
+end
+
+function Room:intersects(r)
+	local a = {
+		x=self.x+self.width/2,
+		y=self.y+self.height/2,
+		wh = self.width/2,
+		hh = self.height/2
+	}
+	local b = {
+		x=r.x+r.width/2,
+		y=r.y+r.height/2,
+		wh = r.width/2+1,
+		hh = r.height/2+1
+	}
+	return not (math.abs(a.x - b.x) > (a.wh + b.wh) or math.abs(a.y - b.y) > (a.hh + b.hh))
+end
+
+return Room

+ 1 - 0
bresenham

@@ -0,0 +1 @@
+Subproject commit 2e060144d448d3be16cf2042f5a297011c0a60a9

+ 1 - 0
delaunay

@@ -0,0 +1 @@
+Subproject commit 615b6214e4ffbbbf95efb7776e55f3440eb70b75

+ 19 - 0
func.lua

@@ -0,0 +1,19 @@
+ function map(func, tbl)
+   local newtbl = {}
+   for i,v in pairs(tbl) do
+       newtbl[i] = func(v)
+   end
+   return newtbl
+end
+
+ -- filter(function, table)
+ -- e.g: filter(is_even, {1,2,3,4}) -> {2,4}
+ function filter(func, tbl)
+   local newtbl= {}
+   for _,v in pairs(tbl) do
+       if func(v) then
+        table.insert(newtbl, v)
+    end
+end
+return newtbl
+end

+ 42 - 0
generate_dataset.lua

@@ -0,0 +1,42 @@
+#!/usr/bin/env th
+
+local threads = require 'threads'
+local nthread = 8
+local njob = 100000
+
+
+local pool = threads.Threads(
+   nthread,
+   function(threadid)
+   end
+)
+
+local jobdone = 56530
+for i=jobdone,njob do
+   pool:addjob(
+      function()
+         require "image"
+         gen = require("DungeonGenerator")
+         gen.DIMENSION=64
+
+         tensor = torch.Tensor(1, gen.DIMENSION, gen.DIMENSION)
+         m = gen.generate(i)
+         index = 1
+         for y=0,gen.DIMENSION-1 do
+         	for x=0,gen.DIMENSION-1 do
+         		tensor:storage()[index] = m.tiles[y][x]
+         		index = index+1
+         	end
+         end
+         image.save(("out/%06d.png"):format(i), tensor)
+
+         return __threadid
+      end,
+      function(id)
+         jobdone = jobdone + 1
+         print(("%d pictures generated"):format(jobdone))
+      end   )
+end
+
+pool:synchronize()
+pool:terminate()