home · contact · privacy
Add Hex map capabilities.
[plomrogue2-experiments] / new2 / plomrogue / mapping.py
1 import collections
2 from plomrogue.errors import ArgError
3
4
5
6 class YX(collections.namedtuple('YX', ('y', 'x'))):
7
8     def __add__(self, other):
9         return YX(self.y + other.y, self.x + other.x)
10
11     def __sub__(self, other):
12         return YX(self.y - other.y, self.x - other.x)
13
14     def __str__(self):
15         return 'Y:%s,X:%s' % (self.y, self.x)
16
17
18
19 class MapGeometry():
20
21     def __init__(self, size):
22         self.size = size
23
24     def get_directions(self):
25         directions = []
26         for name in dir(self):
27             if name[:5] == 'move_':
28                 directions += [name[5:]]
29         return directions
30
31     def get_neighbors(self, pos):
32         neighbors = {}
33         for direction in self.get_directions():
34             neighbors[direction] = self.move(pos, direction)
35         return neighbors
36
37     def move(self, start_pos, direction):
38         mover = getattr(self, 'move_' + direction)
39         target = mover(start_pos)
40         if target.y < 0 or target.x < 0 or \
41                 target.y >= self.size.y or target.x >= self.size.x:
42             return None
43         return target
44
45
46
47 class MapGeometryWithLeftRightMoves(MapGeometry):
48
49     def move_LEFT(self, start_pos):
50         return YX(start_pos.y, start_pos.x - 1)
51
52     def move_RIGHT(self, start_pos):
53         return YX(start_pos.y, start_pos.x + 1)
54
55
56
57 class MapGeometrySquare(MapGeometryWithLeftRightMoves):
58
59     def move_UP(self, start_pos):
60         return YX(start_pos.y - 1, start_pos.x)
61
62     def move_DOWN(self, start_pos):
63         return YX(start_pos.y + 1, start_pos.x)
64
65
66
67 class MapGeometryHex(MapGeometryWithLeftRightMoves):
68
69     def move_UPLEFT(self, start_pos):
70         start_indented = start_pos.y % 2
71         if start_indented:
72             return YX(start_pos.y - 1, start_pos.x)
73         else:
74             return YX(start_pos.y - 1, start_pos.x - 1)
75
76     def move_UPRIGHT(self, start_pos):
77         start_indented = start_pos.y % 2
78         if start_indented:
79             return YX(start_pos.y - 1, start_pos.x + 1)
80         else:
81             return YX(start_pos.y - 1, start_pos.x)
82
83     def move_DOWNLEFT(self, start_pos):
84         start_indented = start_pos.y % 2
85         if start_indented:
86             return YX(start_pos.y + 1, start_pos.x)
87         else:
88             return YX(start_pos.y + 1, start_pos.x - 1)
89
90     def move_DOWNRIGHT(self, start_pos):
91         start_indented = start_pos.y % 2
92         if start_indented:
93             return YX(start_pos.y + 1, start_pos.x + 1)
94         else:
95             return YX(start_pos.y + 1, start_pos.x)
96
97
98
99 class Map():
100
101     def __init__(self, map_size):
102         self.size = map_size
103         self.terrain = '.' * self.size_i
104
105     def __getitem__(self, yx):
106         return self.terrain[self.get_position_index(yx)]
107
108     def __setitem__(self, yx, c):
109         pos_i = self.get_position_index(yx)
110         if type(c) == str:
111             self.terrain = self.terrain[:pos_i] + c + self.terrain[pos_i + 1:]
112         else:
113             self.terrain[pos_i] = c
114
115     @property
116     def size_i(self):
117         return self.size.y * self.size.x
118
119     def set_line(self, y, line):
120         height_map = self.size.y
121         width_map = self.size.x
122         if y >= height_map:
123             raise ArgError('too large row number %s' % y)
124         width_line = len(line)
125         if width_line != width_map:
126             raise ArgError('map line width %s unequal map width %s' % (width_line, width_map))
127         self.terrain = self.terrain[:y * width_map] + line +\
128                        self.terrain[(y + 1) * width_map:]
129
130     def get_position_index(self, yx):
131         return yx.y * self.size.x + yx.x
132
133     def lines(self):
134         width = self.size.x
135         for y in range(self.size.y):
136             yield (y, self.terrain[y * width:(y + 1) * width])