source: trunk/Forms/ufrmmain1.pas

Last change on this file was 7, checked in by chronos, 11 years ago
  • Added: Allow terminate solving process.
  • Fixed: Disable actions which can change maze during solving.
File size: 10.0 KB
Line 
1{
2 Main form of the Lazarus Mazes program
3
4 Copyright (C) 2012 G.A. Nijland (eny @ lazarus forum http://www.lazarus.freepascal.org/)
5
6 This source is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
8 version.
9
10 This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
11 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 details.
13
14 A copy of the GNU General Public License is available on the World Wide Web at
15 <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing to the Free Software Foundation, Inc., 59
16 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17}
18unit ufrmMain1;
19
20interface
21
22uses
23 ufrmScaling,
24 LazesGlobals, MazeBuilderDepthFirst, MazePainter, Maze,
25 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ExtCtrls,
26 Menus, ActnList, StdActns, types;
27
28type
29
30 { TfrmMain1 }
31
32 TfrmMain1 = class(TForm)
33 ATerminate: TAction;
34 AMazeEnlarge: TAction;
35 AMazeShrink: TAction;
36 AMazeSolve: TAction;
37 AMazeNew: TAction;
38 AMetricsPopUp: TAction;
39 ActionList1: TActionList;
40 AFileExit: TFileExit;
41 imgBackground: TImage;
42 MainMenu1: TMainMenu;
43 MenuItem1: TMenuItem;
44 MenuItem10: TMenuItem;
45 MenuItem2: TMenuItem;
46 MenuItem3: TMenuItem;
47 MenuItem4: TMenuItem;
48 MenuItem5: TMenuItem;
49 MenuItem6: TMenuItem;
50 MenuItem7: TMenuItem;
51 MenuItem8: TMenuItem;
52 MenuItem9: TMenuItem;
53 pbMaze: TPaintBox;
54 TimerDraw: TTimer;
55
56 procedure AMazeEnlargeExecute(Sender: TObject);
57 procedure AMazeNewExecute(Sender: TObject);
58 procedure AMazeShrinkExecute(Sender: TObject);
59 procedure AMazeSolveExecute(Sender: TObject);
60 procedure AMetricsPopUpExecute(Sender: TObject);
61 procedure ATerminateExecute(Sender: TObject);
62 procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
63 procedure FormCreate(Sender: TObject);
64 procedure FormDestroy(Sender: TObject);
65 procedure FormMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
66 procedure FormMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
67 procedure pbMazePaint(Sender: TObject);
68 procedure TimerDrawTimer(Sender: TObject);
69
70 private
71 FMazeMetrics: TMazeUpdateInfo;
72 Maze: TMaze;
73 MazePainter: TMazePainter;
74 MazeIsSolved: boolean;
75 RedrawPending: Boolean;
76 DisableActions: Boolean;
77 Solved: boolean;
78 TerminateAction: Boolean;
79 procedure GenerateNewMaze;
80 procedure ResizeMaze(const pdx, pdy: integer);
81 procedure SetMazeMetrics(AValue: TMazeUpdateInfo);
82 procedure TravelMaze(CurPos: TCellPoint);
83 procedure TryMove(const pFrom: TCellPoint; const pDirection: TDirection);
84 public
85 property MazeMetrics: TMazeUpdateInfo read FMazeMetrics write SetMazeMetrics;
86 procedure Redraw;
87 procedure UpdateInterface;
88 end;
89
90var
91 frmMain1: TfrmMain1;
92
93implementation
94
95{$R *.lfm}
96
97{ TfrmMain1 }
98
99procedure TfrmMain1.FormCreate(Sender: TObject);
100begin
101 // Reduce flicker
102 Self.DoubleBuffered := true;
103
104 // Set alignment images
105 imgBackground.Align := alClient;
106 pbMaze.Align := alClient;
107
108 // Start with base maze set up
109 with MazeMetrics do
110 begin
111 MazeWidth := 20;
112 MazeHeight := 15;
113 DrawWidth := 15;
114 DrawHeight := 15;
115 end;
116
117 // Initialize the random generator, so mazes dont repeat (any time soon)
118 Randomize;
119
120 // And generate a demo maze
121 GenerateNewMaze;
122
123 UpdateInterface;
124end;
125
126procedure TfrmMain1.FormDestroy(Sender: TObject);
127begin
128 FreeAndNil(MazePainter);
129 FreeAndNil(Maze);
130end;
131
132procedure TfrmMain1.FormMouseWheelDown(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
133begin
134 AMazeShrink.Execute;
135end;
136
137procedure TfrmMain1.FormMouseWheelUp(Sender: TObject; Shift: TShiftState; MousePos: TPoint; var Handled: Boolean);
138begin
139 AMazeEnlarge.Execute;
140end;
141
142procedure TfrmMain1.TryMove(const pFrom: TCellPoint; const pDirection: TDirection);
143const
144 DirMoves: array[TDirection] of TPoint =
145 ((x: 0; y:-1), // North
146 (x: 0; y:+1), // South
147 (x:+1; y:0 ), // East
148 (x:-1; y:0 )); // West
149var
150 NewPos: TCellPoint;
151begin
152 if TerminateAction then Exit;
153
154 // Direction at all possible?
155 with Maze.GetCell(pFrom) do
156 if not CanGo[pDirection] then Exit;
157
158 // Movement is possible!
159 NewPos.Row := pFrom.Row + DirMoves[pDirection].y;
160 NewPos.Col := pFrom.Col + DirMoves[pDirection].x;
161
162 // Check if the next cell is not visited before.
163 with Maze.GetCell(NewPos) do
164 if State = csExit then
165 begin
166 Solved := true;
167 Exit;
168 end
169 else if State = csVisited then
170 Exit
171 else
172 begin
173 State := csVisited; // Been here
174 // Tag := 1; // Visited
175 // State := csStart;
176 MazePainter.IsDirty := true; // Force repaint with new cell populated
177 Redraw;
178 Application.ProcessMessages;
179 Sleep(10);
180
181 // Start travelling from here
182 if not Solved then
183 TravelMaze(NewPos);
184
185 if TerminateAction then Exit;
186
187 // If still not solved, backtrack
188 if not Solved then
189 begin
190 State := csEmpty;
191 // Tag := 0;
192 MazePainter.IsDirty := true; // Force repaint with new cell populated
193 Redraw;
194 Application.ProcessMessages;
195 Sleep(10);
196 end;
197 end;
198end;
199
200procedure TfrmMain1.TravelMaze(CurPos: TCellPoint);
201begin
202 // Check all 4 directions for possible moves
203 if not Solved then TryMove(CurPos, dirEast);
204 if not Solved then TryMove(CurPos, dirSouth);
205 if not Solved then TryMove(CurPos, dirWest);
206 if not Solved then TryMove(CurPos, dirNorth);
207end;
208
209procedure TfrmMain1.AMazeSolveExecute(Sender: TObject);
210begin
211 // If the maze was already solved, dont do it again
212 if MazeIsSolved then
213 begin
214 MessageDlg('As you can see this maze was already solved!', mtInformation, [mbOK], 0);
215 Exit;
216 end;
217
218 try
219 DisableActions := True;
220 TerminateAction := False;
221 UpdateInterface;
222
223 Maze.SetStartCell(0, 0);
224
225 // Reset the tags, they will be used for backtracking
226 Maze.ResetTags;
227 Maze.ResetState;
228
229 // Not done yet...
230 Solved := false;
231
232 // Travel all cells until the end is found
233 TravelMaze(Maze.GetStartPosition);
234 if not TerminateAction then begin
235 MazeIsSolved := true;
236
237 // Found, so give a victorious message!
238 MessageDlg('Hurrah! Found the exit!!!!', mtInformation, [mbOK], 0);
239 end;
240 finally
241 DisableActions := False;
242 UpdateInterface;
243 end;
244end;
245
246procedure TfrmMain1.AMazeNewExecute(Sender: TObject);
247begin
248 GenerateNewMaze;
249end;
250
251procedure TfrmMain1.AMazeShrinkExecute(Sender: TObject);
252begin
253 ResizeMaze(-1,-1);
254end;
255
256procedure TfrmMain1.AMazeEnlargeExecute(Sender: TObject);
257begin
258 ResizeMaze(1,1);
259end;
260
261procedure TfrmMain1.AMetricsPopUpExecute(Sender: TObject);
262begin
263 frmScaling.Show;
264end;
265
266procedure TfrmMain1.ATerminateExecute(Sender: TObject);
267begin
268 TerminateAction := True;
269end;
270
271procedure TfrmMain1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
272begin
273 ATerminate.Execute;
274end;
275
276procedure TfrmMain1.pbMazePaint(Sender: TObject);
277begin
278 // Paint the maze centered within the paint window
279 if assigned(MazePainter) then
280 MazePainter.Paint((pbMaze.ClientWidth - MazePainter.Width) div 2,
281 (pbMaze.ClientHeight - MazePainter.Height) div 2);
282end;
283
284procedure TfrmMain1.TimerDrawTimer(Sender: TObject);
285begin
286 if RedrawPending then begin
287 RedrawPending := False;
288 pbMaze.Repaint;
289 end;
290end;
291
292procedure TfrmMain1.GenerateNewMaze;
293var bld: TMazeBuilderDepthFirst;
294begin
295 // Clean up old maze and painter
296 FreeAndNil(MazePainter);
297 FreeAndNil(Maze);
298
299 // Build a new one, based on the given metrics
300 bld := TMazeBuilderDepthFirst.Create;
301 try
302 Maze := bld.BuildMaze(MazeMetrics.MazeWidth, MazeMetrics.MazeHeight);
303 finally
304 bld.Free;
305 end;
306
307 // This one is not solved yet
308 MazeIsSolved := false;
309
310 // Set lower right hand corner as exit (top left is start by default)
311 Maze.Cell[Maze.Height - 1, Maze.Width - 1].State := csExit;
312
313 // Paint the maze
314 MazePainter := TMazePainter.Create(Maze, pbMaze.Canvas);
315 MazePainter.CellDrawWidth := MazeMetrics.DrawWidth;
316 MazePainter.CellDrawHeight := MazeMetrics.DrawHeight;
317 Redraw;
318end;
319
320procedure TfrmMain1.ResizeMaze(const pdx, pdy: integer);
321begin
322 if ((MazeMetrics.MazeWidth + pdx) >= C_MIN_MAZE_SIZE)
323 and ((MazeMetrics.MazeWidth + pdx) <= C_MAX_MAZE_SIZE)
324 and ((MazeMetrics.MazeHeight + pdy) >= C_MIN_MAZE_SIZE)
325 and ((MazeMetrics.MazeHeight + pdy) <= C_MAX_MAZE_SIZE) then
326 begin
327 inc(MazeMetrics.MazeWidth, pdx);
328 inc(MazeMetrics.MazeHeight, pdy);
329 GenerateNewMaze;
330 end
331end;
332
333procedure TfrmMain1.SetMazeMetrics(AValue: TMazeUpdateInfo);
334begin
335 if FMazeMetrics = AValue then Exit;
336 // Any updates?
337 if (AValue.DrawHeight <> MazeMetrics.DrawHeight)
338 or (AValue.DrawWidth <> MazeMetrics.DrawWidth)
339 or (AValue.MazeHeight <> MazeMetrics.MazeHeight)
340 or (AValue.MazeWidth <> MazeMetrics.MazeWidth) then
341 begin
342 FMazeMetrics := AValue;
343 GenerateNewMaze;
344 end;
345end;
346
347procedure TfrmMain1.Redraw;
348begin
349 RedrawPending := True;
350end;
351
352procedure TfrmMain1.UpdateInterface;
353begin
354 AMazeNew.Enabled := not DisableActions;
355 AMazeSolve.Enabled := not DisableActions;
356 AMetricsPopUp.Enabled := not DisableActions;
357 if DisableActions then frmScaling.Hide;
358 AMazeEnlarge.Enabled := not DisableActions;
359 AMazeShrink.Enabled := not DisableActions;
360 ATerminate.Enabled := DisableActions;
361end;
362
363end.
364
Note: See TracBrowser for help on using the repository browser.