Python script to generate simple "dungeon maps"
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

CellMap.py 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import random, sys, os
  2. from PIL import Image
  3. class CellMap:
  4. initial = []
  5. genmap = []
  6. treasurelist = []
  7. def __init__(self, height=None, width=None, seed=None, death=None,
  8. birth=None, reps=0, out=None, color=None, chunky=None,
  9. treasure=None):
  10. self.height = height if height != None else 0
  11. self.width = width if width != None else 0
  12. self.seed = seed if seed != None else 0
  13. self.death = death if death != None else 0
  14. self.birth = birth if birth != None else 0
  15. self.reps = reps if reps != None else 0
  16. self.out = out if out != None else False
  17. self.color = color if color != None else False
  18. self.chunky = chunky if chunky != None else False
  19. self.treasure = treasure if treasure != None else False
  20. self.id = filename()
  21. @property
  22. def height(self):
  23. return self.__height
  24. @height.setter
  25. def height(self, height):
  26. self.__height = int(height) if int(height) > 0 else 0
  27. @property
  28. def width(self):
  29. return self.__width
  30. @width.setter
  31. def width(self, width):
  32. self.__width = int(width) if int(width) > 0 else 0
  33. @property
  34. def seed(self):
  35. return self.__seed
  36. @ seed.setter
  37. def seed(self, seed):
  38. self.__seed = int(seed) if int(seed) > 0 else 0
  39. @property
  40. def death(self):
  41. return self.__death
  42. @death.setter
  43. def death(self, death):
  44. self.__death = int(death) if int(death) > 0 else 0
  45. @property
  46. def birth(self):
  47. return self.__birth
  48. @birth.setter
  49. def birth(self, birth):
  50. self.__birth = int(birth) if int(birth) > 0 else 0
  51. @property
  52. def reps(self):
  53. return self.__reps
  54. @reps.setter
  55. def reps(self, reps):
  56. self.__reps = int(reps) if int(reps) > 0 else 0
  57. @property
  58. def out(self):
  59. return self.__out
  60. @out.setter
  61. def out(self, out):
  62. self.__out = bool(out)
  63. @property
  64. def color(self):
  65. return self.__color
  66. @color.setter
  67. def color(self, color):
  68. self.__color = bool(color)
  69. @property
  70. def chunky(self):
  71. return self.__chunky
  72. @chunky.setter
  73. def chunky(self, chunky):
  74. self.__chunky = bool(chunky)
  75. @property
  76. def treasure(self):
  77. return self.__treasure
  78. @treasure.setter
  79. def treasure(self, treasure):
  80. self.__treasure = bool(treasure)
  81. def generateFullMap(self):
  82. """ Puts everything together.
  83. """
  84. self.createMap()
  85. for _ in range(self.reps):
  86. self.smoothMap()
  87. if self.out:
  88. if self.treasure:
  89. self.generateTreasure()
  90. self.createImage()
  91. else:
  92. self.printScreen()
  93. def resetMap(self):
  94. """ Resets the map to its initial state, allowing the user to experiment
  95. with death/birth limits and number of repetitions on a single map.
  96. """
  97. self.genmap = list(self.initial)
  98. def createMap(self):
  99. """ Initializes an x by y grid.
  100. x is width, y is height
  101. seed is the chance that a given cell will be "live" and should be an integer between 1-99.
  102. If True is equivalent to "wall", then higher seeds make more walls.
  103. """
  104. if self.__height == 0 or self.__width == 0 or self.__seed == 0:
  105. print("Height, width, and seed must be set before creating a map.")
  106. print("Current values: height: {}, width: {}, seed: {}".format(self.height, self.width, self.seed))
  107. return
  108. y = self.height
  109. x = self.width
  110. seed = self.seed
  111. new_map = []
  112. for j in range(y):
  113. new_row = []
  114. for i in range(x):
  115. new_row.append(True if random.randint(1,99) <= seed else False)
  116. new_map.append(new_row)
  117. self.initial = new_map
  118. self.genmap = new_map
  119. def smoothMap(self):
  120. """ Refines the grid.
  121. """
  122. if self.death == 0 or self.birth == 0:
  123. print("The 'death' limit is currently {} and the 'birth' limit is {}.".format(self.death,self.birth))
  124. print("Smoothing with the 'death' or 'birth' limit set to 0 is not recommended.")
  125. print("Do you want to proceed? (y/N) ", end="")
  126. cont = input().strip()
  127. if cont.lower() != "y":
  128. print("Aborting.")
  129. return
  130. d_lmt = self.death
  131. a_lmt = self.birth
  132. new_map = []
  133. for j in range(len(self.genmap)):
  134. new_line = []
  135. for i in range(len(self.genmap[j])):
  136. x, y = i, j
  137. n_count = self.countWalls(x, y)
  138. if self.genmap[y][x]:
  139. # It's a wall.
  140. if n_count < d_lmt:
  141. # It has too few wall neighbors, so kill it.
  142. new_line.append(False)
  143. else:
  144. # It has enough wall neighbors, so keep it.
  145. new_line.append(True)
  146. else:
  147. # It's a path.
  148. if n_count > a_lmt:
  149. # It has too many wall neighbors, so it becomes a wall.
  150. new_line.append(True)
  151. else:
  152. # It's not too crowded, so it stays a path.
  153. new_line.append(False)
  154. new_map.append(new_line)
  155. self.genmap = new_map
  156. def countWalls(self, x, y):
  157. count = 0
  158. for j in range(-1,2):
  159. for i in range(-1,2):
  160. n_x, n_y = x+i, y+j
  161. if i == 0 and j == 0:
  162. continue
  163. if n_x < 0 or n_x >= len(self.genmap[j]) or n_y == 0 or n_y >= len(self.genmap):
  164. # The target cell is at the edge of the map and this neighbor is off the edge.
  165. # So we make this neighbor count as a wall.
  166. count += 1
  167. #pass
  168. elif self.genmap[n_y][n_x] and self.genmap[n_y][n_x] != "Gold":
  169. # This neighbor is on the map and is a wall.
  170. count += 1
  171. return count
  172. def generateTreasure(self):
  173. self.treasurelist = []
  174. walledin = False
  175. for j in range(len(self.genmap)):
  176. for i in range(len(self.genmap[j])):
  177. if not self.genmap[j][i]:
  178. walledin = True if self.countWalls(i,j) >= 5 else False
  179. if walledin:
  180. self.genmap[j][i] = "Gold"
  181. walledin = False
  182. def printScreen(self):
  183. wall = "II"
  184. path = " "
  185. gold = "GG"
  186. for line in self.genmap:
  187. print("".join([path if not x else (gold if x == "Gold" else wall) for x in line]))
  188. print()
  189. def createImage(self):
  190. x, y = len(self.genmap[0]), len(self.genmap)
  191. if self.chunky:
  192. true_x, true_y = x*2, y*2
  193. else:
  194. true_x, true_y = x, y
  195. img = Image.new("RGB",(true_x,true_y),(0,0,0))
  196. lst = []
  197. # Walls are black by default
  198. c_wall = [random.randint(0,255), random.randint(0,255), random.randint(0,255)] if self.color else [0,0,0]
  199. # Paths are white by default
  200. c_space = [255-x for x in c_wall]
  201. c_gold = [(x+64)%255 for x in c_space]
  202. if self.chunky:
  203. for line in self.genmap:
  204. for _ in range(2):
  205. for val in line:
  206. for _ in range(2):
  207. lst.append(tuple(c_space) if not val else (tuple(c_gold) if val == "Gold" else tuple(c_wall)))
  208. else:
  209. for line in self.genmap:
  210. for val in line:
  211. lst.append(tuple(c_space) if not val else (tuple(c_gold) if val == "Gold" else tuple(c_wall)))
  212. img.putdata(lst)
  213. if not os.path.exists("maps"):
  214. os.makedirs("maps")
  215. fn = self.id
  216. i = 0
  217. while os.path.exists("maps/{}.png".format(fn)):
  218. i += 1
  219. fn = self.id + "-" + str(i)
  220. img.save('maps/{}.png'.format(fn))
  221. print("Saved maps/{}.png".format(fn))
  222. def printArray(self):
  223. print("[",end="\n")
  224. for line in self.genmap:
  225. print("\t{},".format(line))
  226. print("]")
  227. def filename():
  228. hexes = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
  229. fn = []
  230. for _ in range(16):
  231. fn.append(random.choice(hexes))
  232. return "".join(fn)
  233. def parseArgs(args):
  234. flags = {
  235. "--height" : 20,
  236. "--width" : 20,
  237. "--seed" : 45,
  238. "--death" : 4,
  239. "--birth" : 4,
  240. "--reps" : 2,
  241. "--out" : False,
  242. "--color" : False,
  243. "--chunky" : False,
  244. "--treas" : False,
  245. }
  246. for flag, default in flags.items():
  247. if flag in args:
  248. if flag == "--out":
  249. flags["--out"] = True
  250. elif flag == "--color":
  251. flags["--color"] = True
  252. elif flag == "--chunky":
  253. flags["--chunky"] = True
  254. else:
  255. flags[flag] = args[args.index(flag) + 1]
  256. return flags
  257. def main(args):
  258. flags = parseArgs(args)
  259. my_map = CellMap(flags["--height"],flags["--width"],flags["--seed"],flags["--death"],
  260. flags["--birth"],flags["--reps"],flags["--out"],flags["--color"],
  261. flags["--chunky"],flags["--treas"],)
  262. my_map.generateFullMap()
  263. if __name__ == "__main__":
  264. main(sys.argv)