ateubert преди 7 години
родител
ревизия
a48c40e071
променени са 1 файла, в които са добавени 249 реда и са изтрити 0 реда
  1. 249 0
      maze1.py

+ 249 - 0
maze1.py

@@ -0,0 +1,249 @@
1
+#!/usr/bin/env python
2
+# -*- coding: utf-8 -*-
3
+
4
+"""
5
+Author: Leo Vidarte <http://nerdlabs.com.ar>
6
+This is free software,
7
+you can redistribute it and/or modify it
8
+under the terms of the GPL version 3
9
+as published by the Free Software Foundation.
10
+"""
11
+
12
+import random
13
+import Tkinter as tk
14
+import sys
15
+
16
+
17
+class Application(tk.Frame):
18
+
19
+    def __init__(self, width=21, height=21, size=10):
20
+        tk.Frame.__init__(self)
21
+        self.maze = Maze(width, height)
22
+        self.size = size
23
+        self.steps = 0
24
+        self.grid()
25
+        self.create_widgets()
26
+        self.draw_maze()
27
+        self.create_events()
28
+
29
+    def create_widgets(self):
30
+        width = self.maze.width * self.size
31
+        height = self.maze.height * self.size
32
+        self.canvas = tk.Canvas(self, width=width, height=height)
33
+        self.canvas.grid()
34
+        self.status = tk.Label(self)
35
+        self.status.grid()
36
+
37
+    def draw_maze(self):
38
+        for i, row in enumerate(self.maze.maze):
39
+            for j, col in enumerate(row):
40
+                x0 = j * self.size
41
+                y0 = i * self.size
42
+                x1 = x0 + self.size
43
+                y1 = y0 + self.size
44
+                color = self.get_color(x=j, y=i)
45
+                id = self.canvas.create_rectangle(x0, y0, x1, y1, width=0, fill=color)
46
+                if self.maze.start_cell == (j, i):
47
+                    self.cell = id
48
+
49
+        self.canvas.tag_raise(self.cell) # bring to front
50
+        self.status.config(text='Movidas mínimas: %d' % self.maze.steps)
51
+
52
+    def create_events(self):
53
+        self.canvas.bind_all('<KeyPress-Up>', self.move_cell)
54
+        self.canvas.bind_all('<KeyPress-Down>', self.move_cell)
55
+        self.canvas.bind_all('<KeyPress-Left>', self.move_cell)
56
+        self.canvas.bind_all('<KeyPress-Right>', self.move_cell)
57
+
58
+    def move_cell(self, event):
59
+        if event.keysym == 'Up':
60
+            if self.check_move(0, -1):
61
+                self.canvas.move(self.cell, 0, -self.size)
62
+                self.steps += 1
63
+        if event.keysym == 'Down':
64
+            if self.check_move(0, 1):
65
+                self.canvas.move(self.cell, 0, self.size)
66
+                self.steps += 1
67
+        if event.keysym == 'Left':
68
+            if self.check_move(-1, 0):
69
+                self.canvas.move(self.cell, -self.size, 0)
70
+                self.steps += 1
71
+        if event.keysym == 'Right':
72
+            if self.check_move(1, 0):
73
+                self.canvas.move(self.cell, self.size, 0)
74
+                self.steps += 1
75
+
76
+        args = (self.steps, self.maze.steps)
77
+        self.status.config(text='Movidas: %d/%d' % args)
78
+        self.check_status()
79
+
80
+    def check_move(self, x, y):
81
+        x0, y0 = self.get_cell_coords()
82
+        x1 = x0 + x
83
+        y1 = y0 + y
84
+        return self.maze.maze[y1][x1] == 0
85
+
86
+    def get_cell_coords(self):
87
+        position = self.canvas.coords(self.cell)
88
+        x = int(position[0] / self.size)
89
+        y = int(position[1] / self.size)
90
+        return (x, y)
91
+
92
+    def check_status(self):
93
+        if self.maze.exit_cell == self.get_cell_coords():
94
+            args = (self.steps, self.maze.steps)
95
+            self.status.config(text='Resuelto en %d/%d movidas!' % args)
96
+
97
+    def get_color(self, x, y):
98
+        if self.maze.start_cell == (x, y):
99
+            return 'red'
100
+        if self.maze.exit_cell == (x, y):
101
+            return 'green'
102
+        if self.maze.maze[y][x] == 1:
103
+            return 'black'
104
+
105
+
106
+class Maze(object):
107
+
108
+    """
109
+    ## MAZE GENERATOR ##
110
+    Based on Depth-first search algorithm:
111
+    http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search
112
+      1. Start at a particular cell and call it the "exit."
113
+      2. Mark the current cell as visited, and get a list of its neighbors.
114
+         For each neighbor, starting with a randomly selected neighbor:
115
+           1. If that neighbor hasn't been visited, remove the wall between
116
+              this cell and that neighbor, and then recurse with that neighbor as
117
+              the current cell.
118
+    __author__ = "Leonardo Vidarte"
119
+    """
120
+
121
+    def __init__(self, width=21, height=21, exit_cell=(1, 1)):
122
+        self.width = width
123
+        self.height = height
124
+        self.exit_cell = exit_cell
125
+        self.create()
126
+
127
+    def create(self):
128
+        self.maze = [[1] * self.width for _ in range(self.height)] # full of walls
129
+        self.start_cell = None
130
+        self.steps = None
131
+        self.recursion_depth = None
132
+        self._visited_cells = []
133
+        self._visit_cell(self.exit_cell)
134
+
135
+    def _visit_cell(self, cell, depth=0):
136
+        x, y = cell
137
+        self.maze[y][x] = 0 # remove wall
138
+        self._visited_cells.append(cell)
139
+        neighbors = self._get_neighbors(cell)
140
+        random.shuffle(neighbors)
141
+        for neighbor in neighbors:
142
+            if not neighbor in self._visited_cells:
143
+                self._remove_wall(cell, neighbor)
144
+                self._visit_cell(neighbor, depth+1)
145
+        self._update_start_cell(cell, depth)
146
+
147
+    def _get_neighbors(self, cell):
148
+        """
149
+        Get the cells next to the cell
150
+        Example:
151
+          Given the following mazes
152
+          The a neighbor's are b
153
+          # # # # # # #     # # # # # # #
154
+          # # # b # # #     # a # b # # #
155
+          # # # # # # #     # # # # # # #
156
+          # b # a # b #     # b # # # # #
157
+          # # # # # # #     # # # # # # #
158
+          # # # b # # #     # # # # # # #
159
+          # # # # # # #     # # # # # # #
160
+        """
161
+        x, y = cell
162
+        neighbors = []
163
+
164
+        # Left
165
+        if x - 2 > 0:
166
+            neighbors.append((x-2, y))
167
+        # Right
168
+        if x + 2 < self.width:
169
+            neighbors.append((x+2, y))
170
+        # Up
171
+        if y - 2 > 0:
172
+            neighbors.append((x, y-2))
173
+        # Down
174
+        if y + 2 < self.height:
175
+            neighbors.append((x, y+2))
176
+
177
+        return neighbors
178
+
179
+    def _remove_wall(self, cell, neighbor):
180
+        """
181
+        Remove the wall between two cells
182
+        Example:
183
+          Given the cells a and b
184
+          The wall between them is w
185
+          # # # # #
186
+          # # # # #
187
+          # a w b #
188
+          # # # # #
189
+          # # # # #
190
+        """
191
+        x0, y0 = cell
192
+        x1, y1 = neighbor
193
+        # Vertical
194
+        if x0 == x1:
195
+            x = x0
196
+            y = (y0 + y1) / 2
197
+        # Horizontal
198
+        if y0 == y1:
199
+            x = (x0 + x1) / 2
200
+            y = y0
201
+        self.maze[y][x] = 0 # remove wall
202
+
203
+    def _update_start_cell(self, cell, depth):
204
+        if depth > self.recursion_depth:
205
+            self.recursion_depth = depth
206
+            self.start_cell = cell
207
+            self.steps = depth * 2 # wall + cell
208
+
209
+    def show(self, verbose=False):
210
+        MAP = {0: ' ', # path
211
+               1: '#', # wall
212
+               2: 'B', # exit
213
+               3: 'A', # start
214
+              }
215
+        x0, y0 = self.exit_cell
216
+        self.maze[y0][x0] = 2
217
+        x1, y1 = self.start_cell
218
+        self.maze[y1][x1] = 3
219
+        for row in self.maze:
220
+            print ' '.join([MAP[col] for col in row])
221
+        if verbose:
222
+            print "Steps from A to B:", self.steps
223
+
224
+
225
+if __name__ == '__main__':
226
+
227
+    from optparse import OptionParser
228
+
229
+    parser = OptionParser(description="Random maze game")
230
+    parser.add_option('-W', '--width', type=int, default=21,
231
+                      help="maze width (default 21)")
232
+    parser.add_option('-H', '--height', type=int, default=21,
233
+                      help="maze height (default 21)")
234
+    parser.add_option('-s', '--size', type=int, default=10,
235
+                      help="cell size (default 10)")
236
+    args, _ = parser.parse_args()
237
+
238
+    for arg in ('width', 'height'):
239
+        if getattr(args, arg) % 2 == 0:
240
+            setattr(args, arg, getattr(args, arg) + 1)
241
+            print "Warning: %s must be odd, using %d instead" % \
242
+                (arg, getattr(args, arg))
243
+
244
+    sys.setrecursionlimit(5000)
245
+
246
+    app = Application(args.width, args.height, args.size)
247
+    app.master.title('Maze game')
248
+    app.mainloop()
249
+