15-puzzle: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: 15-puzzle
| Old | New | Line | |
|---|---|---|---|
| 1 | + | ## 15-puzzle | |
| 2 | + | ||
| 3 | + | <figure> | |
| 4 | + | <img src="puzzle15.png" title="puzzle15.png" /> | |
| 5 | + | <figcaption>puzzle15.png</figcaption> | |
| 6 | + | </figure> | |
| 7 | + | ||
| 8 | + | The 15-puzzle is a sliding puzzle that consists of a frame of numbered square tiles in random order with one tile missing. The object of the puzzle is to place the tiles in order by making sliding moves that use the empty space. | |
| 9 | + | ||
| 10 | + | ## Create code | |
| 11 | + | ||
| 12 | + | - Create a new blank GUI application with the form [[TForm|Form1]] | |
| 13 | + | - Change caption Form1 to *15Puzzle*'. | |
| 14 | + | - Add New [[Unit]] [[15-puzzle|unitGameBoard.pas]] | |
| 15 | + | - Create the OnCreate event handler for the form, by clicking on your form, use the Object Inspector, the tab events, select the OnCreate event and click the button \[...\] or double click the button in the form. | |
| 16 | + | - Add following code: | |
| 17 | + | ||
| 18 | + | ``` pascal | |
| 19 | + | procedure TForm1.FormCreate(Sender: TObject); | |
| 20 | + | var | |
| 21 | + | i : Integer; | |
| 22 | + | aButton: TButton; | |
| 23 | + | point:TPoint; | |
| 24 | + | begin | |
| 25 | + | Randomize; | |
| 26 | + | Puzzle15:= TGameBoard.Create; | |
| 27 | + | i := 1; | |
| 28 | + | while i < 16 do | |
| 29 | + | begin //create 15 Buttons | |
| 30 | + | aButton:=TButton.Create(Self); //create Button, Owner is Form1, where the button is released later | |
| 31 | + | aButton.Parent:=Self; //determine where it is to be displayed | |
| 32 | + | aButton.Caption:=IntToStr(i); //Captions of the buttons | |
| 33 | + | aButton.Width:=aButton.Height; //Width should correspond to the height of the buttons | |
| 34 | + | point := Puzzle15.ButtonPlace(aButton.Caption); | |
| 35 | + | aButton.Left:= point.x* aButton.Width; //Distance from left | |
| 36 | + | aButton.Top := point.y* aButton.Height; | |
| 37 | + | ||
| 38 | + | aButton.OnClick:=@aButtonClick; //the event handler for the button -> will be created yet | |
| 39 | + | inc(i); | |
| 40 | + | end; | |
| 41 | + | Self.Height:=aButton.Height*6; //Height of the form should correspond to the height of the buttons | |
| 42 | + | Self.Width:=aButton.Width*6; //Width of the form to match the width of all buttons | |
| 43 | + | end; | |
| 44 | + | ``` | |
| 45 | + | ||
| 46 | + | - Create in the Object Inspector the event handler for the event FormDestroy, by clicking on the button \[...\]. | |
| 47 | + | - Add the following code to the handler: | |
| 48 | + | ||
| 49 | + | ``` pascal | |
| 50 | + | procedure TForm1.FormDestroy(Sender: TObject); | |
| 51 | + | begin | |
| 52 | + | FreeAndNil(Puzzle15); | |
| 53 | + | end; | |
| 54 | + | ``` | |
| 55 | + | ||
| 56 | + | - Now you must create the event handler for the button clicks. | |
| 57 | + | - In the source editor, entering your [[Class|class]] TForm1 in the section [[Private|private]]. | |
| 58 | + | - Add [[Procedure|procedure]] `procedure aButtonClick(Sender: TObject);` and then press the keys <span class="kbd">Ctrl</span>+<span class="kbd">Shift</span>+<span class="kbd">c</span> (the code completion becomes active and creates the `procedure TForm1.aButtonClick(Sender: TObject);`. | |
| 59 | + | - Paste following code: | |
| 60 | + | ||
| 61 | + | ``` pascal | |
| 62 | + | procedure TForm1.aButtonClick(Sender: TObject); //the event handler for the button | |
| 63 | + | var | |
| 64 | + | i:integer; | |
| 65 | + | point : TPoint; | |
| 66 | + | begin | |
| 67 | + | if (Sender is TButton) and //called the event handler of a button out? | |
| 68 | + | TryStrToInt(TButton(Sender).Caption, i) //then try to convert the label in a integer | |
| 69 | + | then | |
| 70 | + | begin | |
| 71 | + | if Puzzle15.CanMove(TButton(Sender).Caption) then | |
| 72 | + | begin | |
| 73 | + | point := Puzzle15.Change(TButton(Sender).Caption); | |
| 74 | + | TButton(Sender).Left:= point.x* TButton(Sender).Width; //Distance from left | |
| 75 | + | TButton(Sender).Top := point.y* TButton(Sender).Height; | |
| 76 | + | end; | |
| 77 | + | end; | |
| 78 | + | end; | |
| 79 | + | ``` | |
| 80 | + | ||
| 81 | + | ## Source code | |
| 82 | + | ||
| 83 | + | ### unit1.pas | |
| 84 | + | ||
| 85 | + | ``` pascal | |
| 86 | + | unit Unit1; | |
| 87 | + | ||
| 88 | + | {$mode objfpc}{$H+} | |
| 89 | + | ||
| 90 | + | interface | |
| 91 | + | ||
| 92 | + | uses | |
| 93 | + | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, | |
| 94 | + | unitGameBoard; | |
| 95 | + | ||
| 96 | + | type | |
| 97 | + | ||
| 98 | + | { TForm1 } | |
| 99 | + | ||
| 100 | + | TForm1 = class(TForm) | |
| 101 | + | procedure FormCreate(Sender: TObject); | |
| 102 | + | procedure FormDestroy(Sender: TObject); | |
| 103 | + | private | |
| 104 | + | { private declarations } | |
| 105 | + | Puzzle15:TGameBoard; | |
| 106 | + | procedure aButtonClick(Sender: TObject); | |
| 107 | + | ||
| 108 | + | public | |
| 109 | + | { public declarations } | |
| 110 | + | end; | |
| 111 | + | ||
| 112 | + | var | |
| 113 | + | Form1: TForm1; | |
| 114 | + | ||
| 115 | + | implementation | |
| 116 | + | ||
| 117 | + | {$R *.lfm} | |
| 118 | + | ||
| 119 | + | { TForm1 } | |
| 120 | + | ||
| 121 | + | procedure TForm1.FormCreate(Sender: TObject); | |
| 122 | + | var | |
| 123 | + | i : Integer; | |
| 124 | + | aButton: TButton; | |
| 125 | + | point:TPoint; | |
| 126 | + | begin | |
| 127 | + | Randomize; | |
| 128 | + | Puzzle15:= TGameBoard.Create; | |
| 129 | + | i := 1; | |
| 130 | + | while i < 16 do | |
| 131 | + | begin //create 15 Buttons | |
| 132 | + | aButton:=TButton.Create(Self); //create Button, Owner is Form1, where the button is released later | |
| 133 | + | aButton.Parent:=Self; //determine where it is to be displayed | |
| 134 | + | aButton.Caption:=IntToStr(i); //Captions of the buttons | |
| 135 | + | aButton.Width:=aButton.Height; //Width should correspond to the height of the buttons | |
| 136 | + | point := Puzzle15.ButtonPlace(aButton.Caption); | |
| 137 | + | aButton.Left:= point.x* aButton.Width; //Distance from left | |
| 138 | + | aButton.Top := point.y* aButton.Height; | |
| 139 | + | aButton.OnClick:=@aButtonClick; //the event handler for the button -> will be created yet | |
| 140 | + | inc(i); | |
| 141 | + | end; | |
| 142 | + | Self.Height:=aButton.Height*6; //Height of the form should correspond to the height of the buttons | |
| 143 | + | Self.Width:=aButton.Width*6; //Width of the form to match the width of all buttons | |
| 144 | + | end; | |
| 145 | + | ||
| 146 | + | procedure TForm1.FormDestroy(Sender: TObject); | |
| 147 | + | begin | |
| 148 | + | FreeAndNil(Puzzle15); | |
| 149 | + | end; | |
| 150 | + | ||
| 151 | + | procedure TForm1.aButtonClick(Sender: TObject); //the event handler for the button | |
| 152 | + | var | |
| 153 | + | i:integer; | |
| 154 | + | point : TPoint; | |
| 155 | + | begin | |
| 156 | + | if (Sender is TButton) and //called the event handler of a button out? | |
| 157 | + | TryStrToInt(TButton(Sender).Caption, i) //then try to convert the label in a integer | |
| 158 | + | then | |
| 159 | + | begin | |
| 160 | + | if Puzzle15.CanMove(TButton(Sender).Caption) then | |
| 161 | + | begin | |
| 162 | + | point := Puzzle15.Change(TButton(Sender).Caption); | |
| 163 | + | TButton(Sender).Left:= point.x* TButton(Sender).Width; //Distance from left | |
| 164 | + | TButton(Sender).Top := point.y* TButton(Sender).Height; | |
| 165 | + | end; | |
| 166 | + | end; | |
| 167 | + | end; | |
| 168 | + | ||
| 169 | + | end. | |
| 170 | + | ``` | |
| 171 | + | ||
| 172 | + | ### Unit1.lfm | |
| 173 | + | ||
| 174 | + | ``` pascal | |
| 175 | + | object Form1: TForm1 | |
| 176 | + | Left = 362 | |
| 177 | + | Height = 184 | |
| 178 | + | Top = 162 | |
| 179 | + | Width = 432 | |
| 180 | + | Caption = '15Puzzle' | |
| 181 | + | OnCreate = FormCreate | |
| 182 | + | OnDestroy = FormDestroy | |
| 183 | + | LCLVersion = '1.6.0.4' | |
| 184 | + | end | |
| 185 | + | ``` | |
| 186 | + | ||
| 187 | + | ### unitGameBoard.pas | |
| 188 | + | ||
| 189 | + | ``` pascal | |
| 190 | + | unit unitGameBoard; | |
| 191 | + | ||
| 192 | + | {$mode objfpc}{$H+} | |
| 193 | + | ||
| 194 | + | interface | |
| 195 | + | ||
| 196 | + | uses | |
| 197 | + | Classes, SysUtils; | |
| 198 | + | ||
| 199 | + | const | |
| 200 | + | GB_MAX_BUTTONS = 15; | |
| 201 | + | GB_MAX_X = 4; | |
| 202 | + | GB_MAX_Y = 4; | |
| 203 | + | GB_EMPTY_PLACE ='-'; | |
| 204 | + | type | |
| 205 | + | ||
| 206 | + | { TGameBoard } | |
| 207 | + | ||
| 208 | + | TGameBoard = class | |
| 209 | + | private | |
| 210 | + | GameArray:array[1..GB_MAX_X, 1..GB_MAX_Y] of string; | |
| 211 | + | function ValidPoint(a_point:TPoint):boolean; | |
| 212 | + | public | |
| 213 | + | constructor Create; | |
| 214 | + | function ButtonPlace(a_name:string):TPoint; | |
| 215 | + | function CanMove(a_name:string):Boolean; | |
| 216 | + | function Change(a_name: string): TPoint; | |
| 217 | + | procedure RandomArray; | |
| 218 | + | destructor Destroy; override; | |
| 219 | + | end; | |
| 220 | + | implementation | |
| 221 | + | ||
| 222 | + | { TGameBoard } | |
| 223 | + | ||
| 224 | + | function TGameBoard.ValidPoint(a_point: TPoint): boolean; | |
| 225 | + | begin | |
| 226 | + | result := true; | |
| 227 | + | if a_point.y > GB_MAX_Y then result := false; | |
| 228 | + | if a_point.x > GB_MAX_X then result := false; | |
| 229 | + | if a_point.y < 1 then result := false; | |
| 230 | + | if a_point.x < 1 then result := false; | |
| 231 | + | end; | |
| 232 | + | ||
| 233 | + | constructor TGameBoard.Create; | |
| 234 | + | var | |
| 235 | + | i, x, y : integer; | |
| 236 | + | begin | |
| 237 | + | i := 1; | |
| 238 | + | for y := 1 to GB_MAX_Y do | |
| 239 | + | for x := 1 to GB_MAX_X do | |
| 240 | + | begin | |
| 241 | + | if i < GB_MAX_BUTTONS+1 | |
| 242 | + | then GameArray[x,y] := IntToStr(i) | |
| 243 | + | else GameArray[x,y] := GB_EMPTY_PLACE; | |
| 244 | + | inc(i); | |
| 245 | + | end; | |
| 246 | + | RandomArray; | |
| 247 | + | end; | |
| 248 | + | ||
| 249 | + | function TGameBoard.ButtonPlace(a_name: string): TPoint; | |
| 250 | + | var | |
| 251 | + | x,y:integer; | |
| 252 | + | begin | |
| 253 | + | result.x := 0; | |
| 254 | + | result.y := 0; | |
| 255 | + | for y := 1 to GB_MAX_Y do | |
| 256 | + | for x := 1 to GB_MAX_X do | |
| 257 | + | if GameArray[x,y] = a_name then | |
| 258 | + | begin | |
| 259 | + | result.x := x; | |
| 260 | + | result.y := y; | |
| 261 | + | end; | |
| 262 | + | end; | |
| 263 | + | ||
| 264 | + | function TGameBoard.CanMove(a_name: string): Boolean; | |
| 265 | + | var | |
| 266 | + | empty, point:TPoint; | |
| 267 | + | begin | |
| 268 | + | result := false; | |
| 269 | + | empty := ButtonPlace( GB_EMPTY_PLACE ); | |
| 270 | + | point := ButtonPlace(a_name); | |
| 271 | + | if (abs(empty.x-point.x)=1) and (empty.y=point.y) then result := true; | |
| 272 | + | if (abs(empty.y-point.y)=1) and (empty.x=point.x) then result := true; | |
| 273 | + | end; | |
| 274 | + | ||
| 275 | + | function TGameBoard.Change(a_name: string): TPoint; | |
| 276 | + | var | |
| 277 | + | empty, point:TPoint; | |
| 278 | + | begin | |
| 279 | + | empty := ButtonPlace( GB_EMPTY_PLACE ); | |
| 280 | + | result := empty; | |
| 281 | + | point := ButtonPlace(a_name); | |
| 282 | + | GameArray[point.x, point.y] := '-'; | |
| 283 | + | GameArray[empty.x, empty.y] := a_name; | |
| 284 | + | end; | |
| 285 | + | ||
| 286 | + | procedure TGameBoard.RandomArray; | |
| 287 | + | var | |
| 288 | + | j,i:integer; | |
| 289 | + | name:string; | |
| 290 | + | point,point2:TPoint; | |
| 291 | + | begin | |
| 292 | + | for i := 0 to 1000 do | |
| 293 | + | begin | |
| 294 | + | j := random(4); | |
| 295 | + | point := ButtonPlace( GB_EMPTY_PLACE ); | |
| 296 | + | point2 := point; | |
| 297 | + | case j of | |
| 298 | + | 0: point2.x := point.x-1; | |
| 299 | + | 1: point2.x := point.x+1; | |
| 300 | + | 2: point2.y := point.y-1; | |
| 301 | + | 3: point2.y := point.y+1; | |
| 302 | + | end; | |
| 303 | + | if ValidPoint( point2 ) then | |
| 304 | + | begin | |
| 305 | + | name := GameArray[point2.x, point2.y]; | |
| 306 | + | GameArray[point.x, point.y] := name; | |
| 307 | + | GameArray[point2.x, point2.y] := GB_EMPTY_PLACE; | |
| 308 | + | end; | |
| 309 | + | end; | |
| 310 | + | end; | |
| 311 | + | ||
| 312 | + | destructor TGameBoard.Destroy; | |
| 313 | + | begin | |
| 314 | + | inherited Destroy; | |
| 315 | + | end; | |
| 316 | + | ||
| 317 | + | end. | |
| 318 | + | ``` | |
| 319 | + | ||
| 320 | + | ## See also | |
| 321 | + | ||
| 322 | + | - [[TButton]] | |
| 323 | + | ||
| 324 | + | --- | |
| 325 | + | *From the Free Pascal wiki, page "15-puzzle", as it stood on 2022-08-06 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
