Merge of doom
[koperkapel.git] / koperkapel / generators / maps.py
index 4647fb003f07df84b96c475e96c570e810dfb3a0..6e6bdbdc1915f61627fb1c5872979bfe75fa5d54 100644 (file)
@@ -5,29 +5,135 @@ import math
 import json
 import os
 
-i = random.randint(0,100)
-
-
 ATTRIBUTE_MAP = {
-    '#': {'floor': {'base': 'cwall',
-                      'behaviour': [],
-                   },
-         },
     ' ': {'floor': {'base': 'floor',
-                      'behaviour': ['walk', 'fly'],
-                   },
-         },
+                    'behaviour': ['walk', 'fly'],
+                    },
+          },
     'o': {'tunnels': {'base': 'underground',
                       'behaviour': [],
-                     }
-         },
+                      }
+          },
     '-': {'tunnels': {'base': 'tunnel',
-                      'behaviour': ['walk',],
-                   },
-         },
+                      'behaviour': ['walk', ],
+                      },
+          },
 }
 
 
+class Room:
+    def __init__(self, coordinates, region):
+        """
+        """
+        self.coordinates = [coordinates]
+        self.region = region
+        self.max_connections = 1
+        self.passages = []
+        self.tunnels = []
+
+    def is_linked(self):
+        """
+        Check if the room is linked to another room
+        :return: Whether the room has any links or not
+        """
+        return len(self.passages) + len(self.tunnels) > 0
+
+    def add_region(self, coordinates):
+        """
+        Add a new region into an existing room
+        :param coordinates: region coordinates to be added to room
+        :return:
+        """
+        self.coordinates.append(coordinates)
+
+    def connect_rooms(self, other_rooms):
+        """ Find the nearest rooms to this room
+        :param other_rooms: list of Rooms objects that we are searching
+        :return:
+        """
+        distance = []
+        other_tile = []
+        this_tile = []
+        target_rooms = []
+        for coord in self.coordinates:
+            for room in other_rooms:
+                if self == room:
+                    continue
+                for new_coord in room.coordinates:
+                    distance.append(
+                        math.sqrt((coord[0] - new_coord[0]) ** 2 +
+                                  (coord[1] - new_coord[1]) ** 2))
+                    other_tile.append(new_coord)
+                    this_tile.append(coord)
+                    target_rooms.append(room)
+
+        sorted_indices = [i[0] for i in sorted(enumerate(distance),
+                                               key=lambda x:x[0])]
+        for index in sorted_indices:
+            if len(self.passages) + len(self.tunnels) >= self.max_connections:
+                break
+            if not target_rooms[index].is_linked():
+                self.link_passage(this_tile[index], other_tile[index])
+                target_rooms[index].link_passage(
+                    other_tile[index], this_tile[index])
+
+    def link_passage(self, local_tile, foreign_tile):
+        """ Link a passage between two rooms
+        :param local_tile: tile in this room to which we wish to link
+        :param foreign_tile: tile in another room to which we wish to link
+        :return:
+        """
+        self.passages.append([local_tile, foreign_tile])
+
+    def render_region(self, region, room_dist, region_size, tile_map, x, y):
+        """ Check if a region is in this room and return the required tiles
+        :param region: Region that we wish to render
+        :param room_dist: Tile separation distance from other rooms
+        :param region_size: Region size in tiles
+        :param tile_map: Tile map to update
+        :param x: X coordinate
+        :param y: Y coordinate
+        :return:
+        """
+        if region in self.coordinates:
+            print(self.coordinates)
+            for ht in range(room_dist, region_size - room_dist):
+                for wt in range(room_dist, region_size - room_dist):
+                    tile_map[x + ht][y + wt] = ' '
+
+                    # if w == 0:
+                    #     w_dist = self.dist_from_other_rooms
+                    # elif self.region_map[h][w-1] == region_selected:
+                    #     w_dist = 0
+                    # else:
+                    #     w_dist = self.dist_from_other_rooms
+                    #
+                    # if w + 1 == self.width:
+                    #     e_dist = self.region_size - self.dist_from_other_rooms
+                    # elif self.region_map[h][w+1] == region_selected:
+                    #     e_dist = self.region_size
+                    # else:
+                    #     e_dist = self.region_size - self.dist_from_other_rooms
+                    #
+                    # if h == 0:
+                    #     n_dist = self.dist_from_other_rooms
+                    # elif self.region_map[h-1][w] == region_selected:
+                    #     n_dist = 0
+                    # else:
+                    #     n_dist = self.dist_from_other_rooms
+                    #
+                    # if h + 1 == self.height:
+                    #     s_dist = self.region_size - self.dist_from_other_rooms
+                    # elif self.region_map[h+1][w] == region_selected:
+                    #     s_dist = self.region_size
+                    # else:
+                    #     s_dist = self.region_size - self.dist_from_other_rooms
+                    #
+                    # for wt in range(w_dist, e_dist):
+                    #     for ht in range(n_dist, s_dist):
+                    #         self.map[h * self.region_size + ht]\
+                    #             [w * self.region_size + wt] = ' '
+
 def random_cardinal():
     """Return a random cardinal direction for random walks."""
     return random.choice([(0, 1), (0, -1), (1, 0), (-1, 0)])
@@ -36,76 +142,94 @@ def random_cardinal():
 class LevelGenerator:
     width = 0
     height = 0
-    rooms = 0
+    no_rooms = 0
+    rooms = []
     map = None
-    min_room_size = 0
-    max_room_size = 0
-    dist_from_edge = 0
+    map2 = None
     dist_from_other_rooms = 0
-    regions = None
-    region = 0
-    region_size_in_tiles = 0
+    region_map = None
+    regions = 0
+    region_size = 0
 
-    def __init__(self, width, height, rooms, min_room_size, max_room_size, dist_from_edge,
-                 dist_from_other_rooms, region_size_in_tiles):
+    def __init__(self, width, height, no_rooms, dist_from_other_rooms,
+                 region_size):
         """ Initialize the level parameters
         """
         self.width = width
         self.height = height
-        self.rooms = rooms
-        self.min_room_size = min_room_size
-        self.max_room_size = max_room_size
-        self.dist_from_edge = dist_from_edge
+        self.no_rooms = no_rooms
         self.dist_from_other_rooms = dist_from_other_rooms
-        self.region_size_in_tiles = region_size_in_tiles
+        self.region_size = region_size
+        self.region_coordinates = []
 
     def generate(self):
         """ Generate a random level map
         """
-        self.generate_regions()
-        row = ['#' for x in range(self.width * self.region_size_in_tiles)]
-        self.map = [row[:] for x in range(self.height * self.region_size_in_tiles)]
-        regions_selected = random.sample(range(self.region), min(self.region, self.rooms))
+        self.generate_rooms()
+        regions_selected = random.sample(range(self.regions),
+                                         min(self.regions, self.no_rooms))
+        row = ['#' for x in range(self.width * self.region_size)]
+        self.map = [row[:] for x in range(self.height * self.region_size)]
+        self.map2 = [row[:] for x in range(self.height * self.region_size)]
         print('Regions: %s' % str(regions_selected))
         for region in regions_selected:
+            self.rooms[region].connect_rooms(
+                [self.rooms[i] for i in regions_selected])
             self.generate_room(region)
-        self.generate_underlayer()
+        region_coordinates_selected = [p for p in self.region_coordinates if
+                                       p[0] in regions_selected]
+        print('Coords: %s' % str(region_coordinates_selected))
+        for coord in region_coordinates_selected:
+            print(str(coord))
+            self.rooms[coord[0]].render_region(
+                coord[1], self.dist_from_other_rooms, self.region_size,
+                self.map2, coord[1][0] * self.region_size,
+                coord[1][1] * self.region_size)
+        # self.generate_underlayer()
 
-    def generate_regions(self):
+    def generate_rooms(self):
         """ Generate a random level region map
         """
-        row = ['#' for x in range(self.width)]
-        self.regions = [row[:] for x in range(self.height)]
+        row = [0 for x in range(self.width)]
+        self.region_map = [row[:] for x in range(self.height)]
         for h in range(self.height):
             for w in range(self.width):
                 random_number = random.randint(0, 2)
+                increment_region = False
                 if w == h == 0:
-                    self.regions[h][w] = self.region
-                    self.region += 1
+                    update_value = self.regions
+                    increment_region = True
                 elif h == 0:
                     if random_number > 1:
-                        try:
-                            self.regions[h][w] = self.regions[h][w - 1]
-                        except:
-                            print(h, w)
-                            raise
+                        update_value = self.region_map[h][w - 1]
                     else:
-                        self.regions[h][w] = self.region
-                        self.region += 1
+                        update_value = self.regions
+                        increment_region = True
                 elif w == 0:
                     if random_number > 1:
-                        self.regions[h][w] = self.regions[h - 1][w]
+                        update_value = self.region_map[h - 1][w]
                     else:
-                        self.regions[h][w] = self.region
-                        self.region += 1
+                        update_value = self.regions
+                        increment_region = True
                 else:
                     if random_number > 1:
-                        self.regions[h][w] = self.regions[h - 1][w]
+                        update_value = self.region_map[h - 1][w]
                     elif random_number > 0:
-                        self.regions[h][w] = self.regions[h][w - 1]
+                        update_value = self.region_map[h][w - 1]
                     else:
-                        self.regions[h][w] = self.region
-                        self.region += 1
+                        update_value = self.regions
+                        increment_region = True
+                self.region_map[h][w] = update_value
+                if increment_region:
+                    r = Room([h, w], update_value)
+                    self.rooms.append(r)
+                    self.region_coordinates.append([update_value, [h, w]])
+                    self.regions += 1
+                else:
+                    for r in self.rooms:
+                        if r.region == update_value:
+                            r.add_region([h, w])
+                    self.region_coordinates.append([update_value, [h, w]])
 
     def generate_underlayer(self):
         """Generate a small mess of tunnels to have something."""
@@ -128,58 +252,91 @@ class LevelGenerator:
                 x += dir_x
                 y += dir_y
 
+    def generate_tiles(self, region_selected):
+        """Generate a small mess of tunnels to have something."""
+        width = len(self.map[0])
+        height = len(self.map)
+        row = ['o' for x in range(width)]
+        self.underlayer = [row[:] for x in range(height)]
+        # we create a set of biased random walks to create the tunnel network
+        for walk in range(random.randint(3, 6)):
+            x = width // 2 + random.randint(-8, 8)
+            y = height // 2 + random.randint(-8, 8)
+            dir_x, dir_y = random_cardinal()
+            max_steps = random.randint(40, width * height // 4)
+            for step in range(20, max_steps):
+                if 0 < x < width - 1:
+                    if 0 < y < height - 1:
+                        self.underlayer[y][x] = '-'
+                if random.random() > 0.7:
+                   dir_x, dir_y = random_cardinal()
+                x += dir_x
+                y += dir_y
+
     def generate_room(self, region_selected):
         """
+        :param region_selected:
+        :return:
         """
         for h in range(self.height):
             for w in range(self.width):
-                if self.regions[h][w] == region_selected:
+                if self.region_map[h][w] == region_selected:
                     if w == 0:
                         w_dist = self.dist_from_other_rooms
-                    elif self.regions[h][w-1] == region_selected:
+                    elif self.region_map[h][w-1] == region_selected:
                         w_dist = 0
                     else:
                         w_dist = self.dist_from_other_rooms
 
                     if w + 1 == self.width:
-                        e_dist = self.region_size_in_tiles - self.dist_from_other_rooms
-                    elif self.regions[h][w+1] == region_selected:
-                        e_dist = self.region_size_in_tiles
+                        e_dist = self.region_size - self.dist_from_other_rooms
+                    elif self.region_map[h][w+1] == region_selected:
+                        e_dist = self.region_size
                     else:
-                        e_dist = self.region_size_in_tiles - self.dist_from_other_rooms
+                        e_dist = self.region_size - self.dist_from_other_rooms
 
                     if h == 0:
                         n_dist = self.dist_from_other_rooms
-                    elif self.regions[h-1][w] == region_selected:
+                    elif self.region_map[h-1][w] == region_selected:
                         n_dist = 0
                     else:
                         n_dist = self.dist_from_other_rooms
 
                     if h + 1 == self.height:
-                        s_dist = self.region_size_in_tiles - self.dist_from_other_rooms
-                    elif self.regions[h+1][w] == region_selected:
-                        s_dist = self.region_size_in_tiles
+                        s_dist = self.region_size - self.dist_from_other_rooms
+                    elif self.region_map[h+1][w] == region_selected:
+                        s_dist = self.region_size
                     else:
-                        s_dist = self.region_size_in_tiles - self.dist_from_other_rooms
+                        s_dist = self.region_size - self.dist_from_other_rooms
 
                     for wt in range(w_dist, e_dist):
                         for ht in range(n_dist, s_dist):
-                            self.map[h * self.region_size_in_tiles + ht][w * self.region_size_in_tiles + wt] = ' '
+                            self.map[h * self.region_size + ht]\
+                                [w * self.region_size + wt] = str(region_selected)
 
     def display(self):
         file = open('map.txt', 'w')
+        print('-----------------')
         for l in self.map:
             print(''.join(l))
             file.write(''.join(l))
             file.write('\n')
-        print('')
-        for l in self.underlayer:
+        print('-----------------')
+        for l in self.map2:
             print(''.join(l))
             file.write(''.join(l))
             file.write('\n')
+        print('-----------------')
+        try:
+            for l in self.underlayer:
+                print(''.join(l))
+                file.write(''.join(l))
+                file.write('\n')
+        except AttributeError:
+            pass
         file.close()
-        self._to_json()
-        for l in self.regions:
+        for l in self.region_map:
+            # self._to_json()
             print(l)
 
     def _to_json(self):
@@ -203,8 +360,8 @@ class LevelGenerator:
 
 if __name__ == '__main__':
     while True:
-        level = LevelGenerator(width=8, height=5, rooms=12, min_room_size=5, max_room_size=20,
-                               dist_from_edge=2, dist_from_other_rooms=1, region_size_in_tiles=6)
+        level = LevelGenerator(width=4, height=3, no_rooms=4,
+                               dist_from_other_rooms=0, region_size=3)
         level.generate()
         level.display()
         input("Press Enter to continue...")