/ misc_txt_files / sotlvl_v3_poc.py
sotlvl_v3_poc.py
  1  from enum import Enum
  2  import collections
  3  import random
  4  import pygame
  5  from pygame import Rect
  6  from pygame.math import Vector2
  7  
  8  class VaultColour(Enum):
  9      RED = 1
 10      GREEN = 2
 11      BLUE = 3
 12      GOLD = 4
 13  
 14  class Schematic:
 15      def __init__(self, name: str, size: Vector2, connectors: [Vector2]):
 16          self.name = name
 17          self.size = size
 18          self.connectors = connectors
 19  
 20      def __repr__(self):
 21          return self.name + " (" + str(self.size) + ")"
 22  
 23      def __hash__(self):
 24          return hash((tuple(self.size), tuple(map(tuple, self.connectors))))
 25  
 26      def can_place(self, topleft: Vector2, connecting_to: Vector2) -> bool:
 27          boundingbox = Rect(topleft, self.size)
 28          for room in placed_rooms:
 29              if boundingbox.colliderect(room.boundingbox):
 30                  return False
 31  
 32          our_connectors = [c + topleft for c in self.connectors]
 33          for location in free_connectors:
 34              for offset in [(x,y) for x in range(-1, 2) for y in range(-1, 2)]:
 35                  if boundingbox.collidepoint(location + offset):
 36                      # Overlaps space adjacent to a connector
 37                      # And is not connecting to it
 38                      if location + offset not in our_connectors:
 39                          return False
 40  
 41          for connector in our_connectors:
 42              if not any(connector.distance_squared_to(other_conn) <= 1 for other_conn in free_connectors + [connecting_to]):
 43                  # Not touching another connector
 44                  for room in placed_rooms:
 45                      for offset in [(x,y) for x in range(-1, 2) for y in range(-1, 2)]:
 46                          if room.boundingbox.collidepoint(connector + offset):
 47                              # And space adjacent to our connector is blocked...
 48                              # which means that we've blocked ourselves off
 49                              return False
 50  
 51          return True
 52  
 53      def place(self, topleft: Vector2, parentrooms: list):
 54          boundingbox = Rect(topleft, self.size)
 55  
 56          connectors_to_connect = [c + topleft for c in self.connectors]
 57          other_connectors_connected = []
 58          for location in free_connectors:
 59              for offset in [(x,y) for x in range(-1, 2) for y in range(-1, 2)]:
 60                  if boundingbox.collidepoint(location + offset):
 61                      # Overlaps space adjacent to a connector
 62                      if location + offset in connectors_to_connect:
 63                          connectors_to_connect.remove(location + offset)  # Connect our end
 64                          other_connectors_connected.append(location)  # Connect their end
 65                          # Connect it!
 66                          
 67          for connected in other_connectors_connected:free_connectors.remove(connected)  # Connector is no longer free
 68  
 69          placed = Room(self, boundingbox, [c + topleft for c in self.connectors], parentrooms)
 70          placed_rooms.append(placed)
 71  
 72          for connector in connectors_to_connect:
 73              free_connectors.append(connector)
 74              upcoming_calculations.append((connector, parentrooms + [placed]))
 75              
 76          return placed
 77  
 78      def rotatedClockwise90(self):
 79          size = Vector2(self.size.y, self.size.x)
 80  
 81          connectors = []
 82          for conn in self.connectors:
 83              rotated = Vector2(conn.y, self.size.x-1-conn.x)
 84              connectors.append(rotated)
 85  
 86          return Schematic(self.name + "-rotated", size, connectors)
 87  
 88  class Room:
 89      def __init__(self, schematic: Schematic, boundingbox: Rect, connectors: [Vector2], parentrooms):
 90          self.schematic = schematic
 91          self.boundingbox = boundingbox
 92          self.connectors = connectors
 93          self.parentrooms = parentrooms
 94          self.colour = None
 95  
 96      def __repr__(self):
 97          return str(self.schematic) + " placed at " + str(self.boundingbox)
 98  
 99  print("Loading schematics...")
100  # Schematics
101  schematics = {
102      Schematic("Boring Hallway 1", Vector2(9, 3), [Vector2(0, 1), Vector2(8, 1)]),
103      Schematic("Boring Turning 1", Vector2(3, 3), [Vector2(0, 1), Vector2(1, 0)]),
104      Schematic("Boring T-Junction 1", Vector2(4, 3), [Vector2(0, 1), Vector2(2, 0), Vector2(2, 2)]),
105      }
106  vault_schematics = {
107      VaultColour.RED: {Schematic("Vault", Vector2(18, 9), [Vector2(0, 4)])},
108      VaultColour.GREEN: {Schematic("Vault", Vector2(18, 9), [Vector2(0, 4)])},
109      VaultColour.BLUE: {Schematic("Vault", Vector2(18, 9), [Vector2(0, 4)])},
110      VaultColour.GOLD: {Schematic("Vault", Vector2(20, 9), [Vector2(0, 4)])}
111      }
112  key_schematics = {
113      VaultColour.RED: {Schematic("Key Room", Vector2(9, 12), [Vector2(4, 0)])},
114      VaultColour.GREEN: {Schematic("Key Room", Vector2(5, 5), [Vector2(0, 2)])},
115      VaultColour.BLUE: set(),
116      VaultColour.GOLD: {Schematic("Key Room", Vector2(20, 8), [Vector2(0, 4)])}
117      }
118  # Permutations
119  for sch in schematics.copy():
120      # Rotation
121      for i in range(3):
122          sch = sch.rotatedClockwise90()
123          schematics.add(sch)
124  for colour, schems in vault_schematics.items():
125      for sch in schems.copy():
126          # Rotation
127          for i in range(3):
128              sch = sch.rotatedClockwise90()
129              vault_schematics[colour].add(sch)
130  for colour, schems in key_schematics.items():
131      for sch in schems.copy():
132          # Rotation
133          for i in range(3):
134              sch = sch.rotatedClockwise90()
135              key_schematics[colour].add(sch)
136  
137  invalid = True
138  while invalid:
139      # Datamodel
140      placed_rooms = []
141      free_connectors = []
142  
143      upcoming_calculations = []
144  
145      vault_depths = {
146          VaultColour.RED: random.randint(7,9),
147          VaultColour.GREEN: random.randint(2,4),
148          VaultColour.BLUE: random.randint(7,9),
149          VaultColour.GOLD: random.randint(10,12)
150          }
151      key_depths = {
152          VaultColour.RED: random.randint(1,2),
153          VaultColour.GREEN: random.randint(5,7),
154          VaultColour.BLUE: None,
155          VaultColour.GOLD: random.randint(7,9)
156          }
157  
158      # Hub room
159      hub_schem = Schematic("Hub Room", Vector2(51,51), [Vector2(0, 26), Vector2(0, 10), Vector2(0, 42),                  Vector2(50, 10), Vector2(50, 42),
160                                                         Vector2(26, 0), Vector2(10, 0), Vector2(42, 0), Vector2(26, 50), Vector2(10, 50), Vector2(42, 50)])
161      hub_schem.place(Vector2(-25, -25), [])
162      # schematics[0].place(Vector2(-25-9, 0))
163      # schematics[0].rotatedClockwise90()
164  
165      # Place rooms
166      print("Calculating...")
167      arrangement = collections.namedtuple("arrangement", "schematic connector topleft")
168      depth = 0
169      MAX_DEPTH = 12
170      while free_connectors and depth < MAX_DEPTH:
171          depth += 1
172          # print("Calculating at depth = " + str(depth) + "...")
173          
174          calculations = upcoming_calculations.copy()
175          for connector, parentrooms in calculations:
176              if connector not in free_connectors: continue
177  
178              free_connectors.remove(connector)
179  
180              # Check for vault/keyroom
181              additional_schems = set()
182              chosen_vault = None
183              chosen_type = None
184              for colour in VaultColour:
185                  if vault_depths[colour] is not None and depth >= vault_depths[colour]:
186                      additional_schems = vault_schematics[colour]
187                      chosen_vault = colour
188                      chosen_type = "vault"
189                      break
190                  elif key_depths[colour] is not None and depth >= key_depths[colour]:
191                      additional_schems = key_schematics[colour]
192                      chosen_vault = colour
193                      chosen_type = "key"
194                      break
195              
196              # Find suitable schematic
197              suitable_arrangements = []
198              for schematic in schematics.union(additional_schems):
199                  for other_con in schematic.connectors:
200                      for offset in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
201                          con_pos = connector + offset
202                          schem_tl = con_pos - other_con
203                          if schematic.can_place(schem_tl, connector): suitable_arrangements.append(arrangement(schematic=schematic, connector=other_con, topleft=schem_tl))
204  
205              if not suitable_arrangements:
206                  # Nothing suitable
207                  # TODO
208                  continue
209              else:
210                  chosen_arrangement = random.choice(suitable_arrangements)
211                  placed_room = chosen_arrangement.schematic.place(chosen_arrangement.topleft, parentrooms)
212  
213                  # Handle vault/keyroom placement
214                  if chosen_arrangement.schematic in additional_schems:
215                      if chosen_type == "vault":
216                          placed_room.colour = chosen_vault
217                          parent = parentrooms[1]
218                          parent.colour = chosen_vault
219                          for pchild in placed_rooms:
220                              if parent in pchild.parentrooms and pchild.colour is None:
221                                  pchild.colour = chosen_vault
222                                  
223                          vault_depths[chosen_vault] = None
224                      elif chosen_type == "key":
225                          placed_room.colour = chosen_vault
226                          key_depths[chosen_vault] = None
227  
228      # Cap off last bits
229      # TODO
230  
231      # Check validity
232      invalid = any((vault_depths[colour] is not None for colour in VaultColour)) or any((key_depths[colour] is not None for colour in VaultColour))
233  
234  # Visualise
235  print("Visualising...")
236  min_x = min(map(lambda r: r.boundingbox.left, placed_rooms))-1
237  min_y = min(map(lambda r: r.boundingbox.top, placed_rooms))-1
238  max_x = max(map(lambda r: r.boundingbox.right, placed_rooms))+1
239  max_y = max(map(lambda r: r.boundingbox.bottom, placed_rooms))+1
240  
241  min_coords = min_x, min_y
242  
243  WIN_SIZE = 700, 700
244  pixels_per_unit = WIN_SIZE[0] / (max_x - min_x), WIN_SIZE[1] / (max_y - min_y)
245  
246  import pygame
247  pygame.init()
248  pygame.display.set_mode(WIN_SIZE)
249  win = pygame.display.get_surface()
250  win.fill((0, 0, 0))
251  
252  # Draw rooms
253  for room in placed_rooms:
254      if room.colour == VaultColour.RED: colour = 255, 0, 0
255      elif room.colour == VaultColour.GREEN: colour = 0, 255, 0
256      elif room.colour == VaultColour.BLUE: colour = 0, 0, 255
257      elif room.colour == VaultColour.GOLD: colour = 255, 255, 0
258      else: colour = 255, 255, 255
259      pygame.draw.rect(win, colour, Rect((room.boundingbox.left - min_x) * pixels_per_unit[0],
260                                                  (room.boundingbox.top - min_y) * pixels_per_unit[1],
261                                                  *(room.boundingbox.size[x] * pixels_per_unit[x] for x in range(2))))
262  
263  # Draw connectors
264  for room in placed_rooms:
265      for connector in room.connectors:
266          r = Rect(0, 0, *(1 * pixels_per_unit[x] for x in range(2)))
267          r.center = tuple((connector[x] - min_coords[x] + 0.5) * pixels_per_unit[x] for x in range(2))
268          pygame.draw.rect(win, (255, 127, 0), r)
269  
270  pygame.display.update()