Python script to generate simple "dungeon maps"
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

procgen.py 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import random as r
  2. import sys, os
  3. from PIL import Image
  4. def createDungeon(x=None, y=None, seed=None):
  5. """ Initializes an x by y grid.
  6. x is width, y is height
  7. seed is the chance that a given cell will be "live" and should be an integer between 1-99.
  8. If True is equivalent to "wall", then higher seeds make more walls.
  9. """
  10. x = 10 if x == None else int(x)
  11. y = 10 if y == None else int(y)
  12. seed = 45 if seed == None else int(seed)
  13. new_map = []
  14. for j in range(y):
  15. new_row = []
  16. for i in range(x):
  17. new_row.append(True if r.randint(1,99) <= seed else False)
  18. new_map.append(new_row)
  19. return new_map
  20. def refineDungeon(d_map, d_lmt=None, a_lmt=None):
  21. """ Refines the grid.
  22. """
  23. d_lmt = 4 if d_lmt == None else int(d_lmt)
  24. a_lmt = 4 if a_lmt == None else int(a_lmt)
  25. new_map = []
  26. for j in range(len(d_map)):
  27. new_line = []
  28. for i in range(len(d_map[j])):
  29. x, y = i, j
  30. n_count = countAliveNeighbors(d_map, x, y)
  31. if d_map[y][x]:
  32. # It's a wall.
  33. if n_count < d_lmt:
  34. # It has too few wall neighbors, so kill it.
  35. new_line.append(False)
  36. else:
  37. # It has enough wall neighbors, so keep it.
  38. new_line.append(True)
  39. else:
  40. # It's a path.
  41. if n_count > a_lmt:
  42. # It has too many wall neighbors, so it becomes a wall.
  43. new_line.append(True)
  44. else:
  45. # It's not too crowded, so it stays a path.
  46. new_line.append(False)
  47. new_map.append(new_line)
  48. return new_map
  49. def countAliveNeighbors(d_map, x, y):
  50. count = 0
  51. for j in range(-1,2):
  52. for i in range(-1,2):
  53. n_x, n_y = x+i, y+j
  54. if i == 0 and j == 0:
  55. continue
  56. if n_x < 0 or n_x >= len(d_map[j]) or n_y == 0 or n_y >= len(d_map):
  57. # The target cell is at the edge of the map and this neighbor is off the edge.
  58. # So we make this neighbor count as a wall.
  59. count += 1
  60. #pass
  61. elif d_map[n_y][n_x]:
  62. # This neighbor is on the map and is a wall.
  63. count += 1
  64. return count
  65. def printDungeon(d_map, wall=None, path=None):
  66. wall = "II" if wall == None else wall
  67. path = " " if path == None else path
  68. for line in d_map:
  69. print("".join([wall if x else path for x in line]))
  70. print()
  71. def createImage(d_map, color=None, chunky=None, fn=None):
  72. color = False if color == None else bool(color)
  73. chunky = False if chunky == None else bool(chunky)
  74. fn = filename() if fn == None else fn
  75. x, y = len(d_map[0]), len(d_map)
  76. if chunky:
  77. true_x, true_y = x*2, y*2
  78. else:
  79. true_x, true_y = x, y
  80. img = Image.new("RGB",(true_x,true_y),(0,0,0))
  81. lst = []
  82. # Walls are black by default
  83. c_wall = [r.randint(0,255), r.randint(0,255), r.randint(0,255)] if color else [0,0,0]
  84. # Paths are white by default
  85. c_space = [255-x for x in c_wall]
  86. if chunky:
  87. for line in d_map:
  88. for _ in range(2):
  89. for val in line:
  90. for _ in range(2):
  91. lst.append(tuple(c_wall) if val else tuple(c_space))
  92. else:
  93. for line in d_map:
  94. for val in line:
  95. lst.append(tuple(c_wall) if val else tuple(c_space))
  96. img.putdata(lst)
  97. if not os.path.exists("maps"):
  98. os.makedirs("maps")
  99. img.save('maps/{}.png'.format(fn))
  100. print("Saved maps/{}.png".format(fn))
  101. def filename():
  102. hexes = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
  103. fn = []
  104. for _ in range(16):
  105. fn.append(r.choice(hexes))
  106. return "".join(fn)
  107. def main(x=None, y=None, seed=None, d_lmt=None, a_lmt=None, reps=None, out=None, color=None, chunky=None):
  108. # Initialize
  109. x = 20 if x == None else int(x)
  110. y = 20 if y == None else int(y)
  111. seed = 45 if seed == None else int(seed)
  112. d_lmt = 4 if d_lmt == None else int(d_lmt)
  113. a_lmt = 4 if a_lmt == None else int(a_lmt)
  114. reps = 2 if reps == None else int(reps)
  115. out = False if out == None else bool(out)
  116. color = False if color == None else bool(color)
  117. chunky = False if chunky == None else bool(chunky)
  118. my_map = createDungeon(x,y,seed)
  119. fn = filename()
  120. for _ in range(reps):
  121. my_map = refineDungeon(my_map, d_lmt, a_lmt)
  122. if out:
  123. createImage(my_map, color, chunky)
  124. else:
  125. printDungeon(my_map)
  126. def parseArgs(args):
  127. flags = {
  128. "--height" : 20,
  129. "--width" : 20,
  130. "--seed" : 45,
  131. "--death" : 4,
  132. "--birth" : 4,
  133. "--reps" : 2,
  134. "--out" : False,
  135. "--color" : False,
  136. "--chunky" : False,
  137. }
  138. for flag, default in flags.items():
  139. if flag in args:
  140. if flag == "--out":
  141. flags["--out"] = True
  142. elif flag == "--color":
  143. flags["--color"] = True
  144. elif flag == "--chunky":
  145. flags["--chunky"] = True
  146. else:
  147. flags[flag] = args[args.index(flag) + 1]
  148. return flags
  149. if __name__ == "__main__":
  150. flags = parseArgs(sys.argv)
  151. main(flags["--width"],
  152. flags["--height"],
  153. flags["--seed"],
  154. flags["--death"],
  155. flags["--birth"],
  156. flags["--reps"],
  157. flags["--out"],
  158. flags["--color"],
  159. flags["--chunky"])