Rev 23 | Rev 25 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 23 | Rev 24 | ||
---|---|---|---|
Line 11... | Line 11... | ||
11 | Y: integer; |
11 | Y: integer; |
12 | end; |
12 | end; |
13 | 13 | ||
14 | TFieldType = (ftUndefined, ftFullSpace, ftEmpty, ftRed, ftYellow, ftGreen); |
14 | TFieldType = (ftUndefined, ftFullSpace, ftEmpty, ftRed, ftYellow, ftGreen); |
15 | 15 | ||
16 | TFieldProperties = record |
- | |
17 | Typ: TFieldType; |
- | |
18 | Goal: Boolean; |
- | |
19 | end; |
- | |
20 | - | ||
21 | TGameMode = (gmUndefined, gmNormal, gmDiagonal); |
16 | TGameMode = (gmUndefined, gmNormal, gmDiagonal); |
22 | 17 | ||
23 | TRow = record |
- | |
24 | Indent: integer; |
- | |
25 | Fields: array of TFieldProperties; |
- | |
26 | end; |
- | |
27 | TLevelArray = array of TRow; |
- | |
28 | - | ||
29 | TLevelError = (leUndefined, leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength, |
18 | TLevelError = (leUndefined, leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength, |
30 | leUnsupportedVersion, leUnsupportedMode); |
19 | leUnsupportedVersion, leUnsupportedMode); |
31 | 20 | ||
32 | TLevel = class(TObject) |
- | |
33 | private |
- | |
34 | FStringList: TStringList; |
- | |
35 | procedure Load(ABoardFile: string); |
- | |
36 | function GetGameMode: TGameMode; |
- | |
37 | public |
- | |
38 | constructor Create(ABoardFile: string); |
- | |
39 | destructor Destroy; override; |
- | |
40 | function LevelStringToLevelArray(ShowErrors: boolean): TLevelArray; |
- | |
41 | function CheckLevelIntegrity: TLevelError; overload; |
- | |
42 | function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload; |
- | |
43 | property GameMode: TGameMode read GetGameMode; |
- | |
44 | end; |
- | |
45 | - | ||
46 | TField = record |
21 | TField = record |
- | 22 | Indent: integer; |
|
47 | FieldType: TFieldType; |
23 | FieldType: TFieldType; |
48 | Goal: Boolean; |
24 | Goal: Boolean; |
49 | Panel: TPanel; |
25 | Panel: TPanel; |
50 | Stone: TImage; |
26 | Stone: TImage; |
51 | end; |
27 | end; |
52 | 28 | ||
53 | TGoalStatus = (gsUndefined, gsNoGoal, gsMultipleStonesRemaining, gsLastStoneInGoalRed, gsLastStoneInGoalYellow, gsLastStoneInGoalGreen, gsLastStoneOutsideGoal); |
29 | TGoalStatus = (gsUndefined, gsNoGoal, gsMultipleStonesRemaining, gsLastStoneInGoalRed, gsLastStoneInGoalYellow, gsLastStoneInGoalGreen, gsLastStoneOutsideGoal); |
54 | 30 | ||
55 | TFieldState = (fsUndefined, fsError, fsLocked, fsAvailable, fsStone); |
31 | TFieldState = (fsUndefined, fsLocked, fsAvailable, fsOccupied); |
56 | 32 | ||
57 | TPlayGroundMatrix = record |
33 | TPlayGroundMatrix = record |
58 | Fields: array of array of TField; |
34 | Fields: array of array of TField; |
59 | public |
35 | public |
60 | procedure InitFieldArray(width, height: integer); |
36 | procedure InitFieldArray(width, height: integer); |
Line 65... | Line 41... | ||
65 | function CloneMatrix: TPlayGroundMatrix; |
41 | function CloneMatrix: TPlayGroundMatrix; |
66 | class function FieldState(t: TFieldType): TFieldState; overload; static; |
42 | class function FieldState(t: TFieldType): TFieldState; overload; static; |
67 | function FieldState(f: TField): TFieldState; overload; |
43 | function FieldState(f: TField): TFieldState; overload; |
68 | function FieldState(x, y: integer): TFieldState; overload; |
44 | function FieldState(x, y: integer): TFieldState; overload; |
69 | function CanJump(SourceX, SourceY, DestX, DestY: integer; DiagonalOK: boolean): boolean; overload; |
45 | function CanJump(SourceX, SourceY, DestX, DestY: integer; DiagonalOK: boolean): boolean; overload; |
- | 46 | function CanJump(Source, Dest: TCoord; DiagonalOK: boolean): boolean; overload; |
|
70 | function CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; overload; |
47 | function CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; overload; |
- | 48 | function CanJump(Source: TCoord; DiagonalOK: boolean): boolean; overload; |
|
71 | function CanJump(DiagonalOK: boolean): boolean; overload; |
49 | function CanJump(DiagonalOK: boolean): boolean; overload; |
72 | function IndexToCoord(index: integer): TCoord; |
50 | function IndexToCoord(index: integer): TCoord; |
73 | function CoordToIndex(coord: TCoord): integer; overload; |
51 | function CoordToIndex(coord: TCoord): integer; overload; |
74 | function CoordToIndex(x, y: integer): integer; overload; |
52 | function CoordToIndex(x, y: integer): integer; overload; |
75 | function Width: integer; |
53 | function Width: integer; |
76 | function Height: integer; |
54 | function Height: integer; |
77 | end; |
55 | end; |
78 | 56 | ||
- | 57 | TLevel = class(TObject) |
|
- | 58 | private |
|
- | 59 | FStringList: TStringList; |
|
- | 60 | procedure Load(ABoardFile: string); |
|
- | 61 | function GetGameMode: TGameMode; |
|
- | 62 | public |
|
- | 63 | constructor Create(ABoardFile: string); |
|
- | 64 | destructor Destroy; override; |
|
- | 65 | procedure FillPlaygroundMatrix(var matrix: TPlayGroundMatrix; ShowErrors: boolean); |
|
- | 66 | function CheckLevelIntegrity: TLevelError; overload; |
|
- | 67 | function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload; |
|
- | 68 | property GameMode: TGameMode read GetGameMode; |
|
- | 69 | end; |
|
- | 70 | ||
79 | function FieldTypeWorth(t: TFieldType): integer; |
71 | function FieldTypeWorth(t: TFieldType): integer; |
80 | 72 | ||
81 | implementation |
73 | implementation |
82 | 74 | ||
83 | function FieldTypeWorth(t: TFieldType): integer; |
75 | function FieldTypeWorth(t: TFieldType): integer; |
Line 139... | Line 131... | ||
139 | SetLength(Fields, width, height); |
131 | SetLength(Fields, width, height); |
140 | for x := Low(Fields) to High(Fields) do |
132 | for x := Low(Fields) to High(Fields) do |
141 | begin |
133 | begin |
142 | for y := Low(Fields[x]) to High(Fields[x]) do |
134 | for y := Low(Fields[x]) to High(Fields[x]) do |
143 | begin |
135 | begin |
144 | Fields[x,y].FieldType := ftUndefined; |
136 | Fields[x,y].FieldType := ftUndefined |
145 | end; |
137 | end; |
146 | end; |
138 | end; |
147 | end; |
139 | end; |
148 | 140 | ||
149 | function TPlayGroundMatrix.MatrixWorth: integer; |
141 | function TPlayGroundMatrix.MatrixWorth: integer; |
Line 163... | Line 155... | ||
163 | function TPlayGroundMatrix.Width: integer; |
155 | function TPlayGroundMatrix.Width: integer; |
164 | begin |
156 | begin |
165 | result := Length(Fields); |
157 | result := Length(Fields); |
166 | end; |
158 | end; |
167 | 159 | ||
- | 160 | function TPlayGroundMatrix.CanJump(Source: TCoord; |
|
- | 161 | DiagonalOK: boolean): boolean; |
|
- | 162 | begin |
|
- | 163 | result := CanJump(Source.X, Source.Y, DiagonalOK); |
|
- | 164 | end; |
|
- | 165 | ||
- | 166 | function TPlayGroundMatrix.CanJump(Source, Dest: TCoord; |
|
- | 167 | DiagonalOK: boolean): boolean; |
|
- | 168 | begin |
|
- | 169 | result := CanJump(Source.X, Source.Y, Dest.X, Dest.Y, DiagonalOK); |
|
- | 170 | end; |
|
- | 171 | ||
168 | procedure TPlayGroundMatrix.ClearMatrix(FreeVCL: boolean); |
172 | procedure TPlayGroundMatrix.ClearMatrix(FreeVCL: boolean); |
169 | var |
173 | var |
170 | x, y: integer; |
174 | x, y: integer; |
171 | begin |
175 | begin |
- | 176 | if FreeVCL then |
|
- | 177 | begin |
|
172 | for x := Low(Fields) to High(Fields) do |
178 | for x := Low(Fields) to High(Fields) do |
173 | begin |
179 | begin |
174 | for y := Low(Fields[x]) to High(Fields[x]) do |
180 | for y := Low(Fields[x]) to High(Fields[x]) do |
175 | begin |
181 | begin |
176 | if FreeVCL then |
- | |
177 | begin |
- | |
178 | if Assigned(Fields[x,y].Stone) then Fields[x,y].Stone.Free; |
182 | if Assigned(Fields[x,y].Stone) then Fields[x,y].Stone.Free; |
179 | if Assigned(Fields[x,y].Panel) then Fields[x,y].Panel.Free; |
183 | if Assigned(Fields[x,y].Panel) then Fields[x,y].Panel.Free; |
180 | end; |
184 | end; |
181 | end; |
185 | end; |
182 | end; |
186 | end; |
Line 200... | Line 204... | ||
200 | end; |
204 | end; |
201 | end; |
205 | end; |
202 | end; |
206 | end; |
203 | 207 | ||
204 | function TPlayGroundMatrix.CoordToIndex(x, y: integer): integer; |
208 | function TPlayGroundMatrix.CoordToIndex(x, y: integer): integer; |
205 | var |
- | |
206 | c: TCoord; |
- | |
207 | begin |
209 | begin |
208 | c.X := x; |
- | |
209 | c.Y := y; |
- | |
210 | result := CoordToIndex(c); |
210 | result := x + y * Width; |
211 | end; |
211 | end; |
212 | 212 | ||
213 | function TPlayGroundMatrix.CoordToIndex(coord: TCoord): integer; |
213 | function TPlayGroundMatrix.CoordToIndex(coord: TCoord): integer; |
214 | begin |
214 | begin |
215 | result := coord.X + coord.Y * Width; |
215 | result := CoordToIndex(coord.X, coord.Y); |
216 | end; |
216 | end; |
217 | 217 | ||
218 | class function TPlayGroundMatrix.FieldState(t: TFieldType): TFieldState; |
218 | class function TPlayGroundMatrix.FieldState(t: TFieldType): TFieldState; |
219 | begin |
219 | begin |
220 | result := fsError; |
220 | result := fsUndefined; |
221 | case t of |
221 | case t of |
222 | ftFullSpace: result := fsLocked; |
222 | ftFullSpace: result := fsLocked; |
223 | ftEmpty: result := fsAvailable; |
223 | ftEmpty: result := fsAvailable; |
224 | ftGreen: result := fsStone; |
224 | ftGreen: result := fsOccupied; |
225 | ftYellow: result := fsStone; |
225 | ftYellow: result := fsOccupied; |
226 | ftRed: result := fsStone; |
226 | ftRed: result := fsOccupied; |
227 | end; |
227 | end; |
228 | end; |
228 | end; |
229 | 229 | ||
230 | function TPlayGroundMatrix.FieldState(f: TField): TFieldState; |
230 | function TPlayGroundMatrix.FieldState(f: TField): TFieldState; |
231 | begin |
231 | begin |
232 | result := FieldState(f.FieldType); |
232 | result := FieldState(f.FieldType); |
233 | end; |
233 | end; |
234 | 234 | ||
235 | function TPlayGroundMatrix.FieldState(x, y: integer): TFieldState; |
235 | function TPlayGroundMatrix.FieldState(x, y: integer): TFieldState; |
236 | begin |
236 | begin |
237 | result := fsError; |
237 | result := fsUndefined; |
238 | if (x < Low(Fields)) or (x > High(Fields)) then exit; |
238 | if (x < Low(Fields)) or (x > High(Fields)) then exit; |
239 | if (y < Low(Fields[x])) or (y > High(Fields[x])) then exit; |
239 | if (y < Low(Fields[x])) or (y > High(Fields[x])) then exit; |
240 | 240 | ||
241 | result := FieldState(Fields[x,y]); |
241 | result := FieldState(Fields[x,y]); |
242 | end; |
242 | end; |
Line 249... | Line 249... | ||
249 | if FieldState(DestX, DestY) <> fsAvailable then exit; |
249 | if FieldState(DestX, DestY) <> fsAvailable then exit; |
250 | 250 | ||
251 | // Check 2: Befindet sich ein Stein zwischen Source und Destination und ist der Abstand 2? |
251 | // Check 2: Befindet sich ein Stein zwischen Source und Destination und ist der Abstand 2? |
252 | if DiagonalOK then |
252 | if DiagonalOK then |
253 | begin |
253 | begin |
254 | if (SourceX-2 = DestX) and (SourceY-2 = DestY) and (FieldState(SourceX-1, SourceY-1) = fsStone) then result := true; |
254 | if (SourceX-2 = DestX) and (SourceY-2 = DestY) and (FieldState(SourceX-1, SourceY-1) = fsOccupied) then result := true; |
255 | if (SourceX-2 = DestX) and (SourceY+2 = DestY) and (FieldState(SourceX-1, SourceY+1) = fsStone) then result := true; |
255 | if (SourceX-2 = DestX) and (SourceY+2 = DestY) and (FieldState(SourceX-1, SourceY+1) = fsOccupied) then result := true; |
256 | if (SourceX+2 = DestX) and (SourceY-2 = DestY) and (FieldState(SourceX+1, SourceY-1) = fsStone) then result := true; |
256 | if (SourceX+2 = DestX) and (SourceY-2 = DestY) and (FieldState(SourceX+1, SourceY-1) = fsOccupied) then result := true; |
257 | if (SourceX+2 = DestX) and (SourceY+2 = DestY) and (FieldState(SourceX+1, SourceY+1) = fsStone) then result := true; |
257 | if (SourceX+2 = DestX) and (SourceY+2 = DestY) and (FieldState(SourceX+1, SourceY+1) = fsOccupied) then result := true; |
258 | end; |
258 | end; |
259 | 259 | ||
260 | if (SourceX+2 = DestX) and (SourceY = DestY) and (FieldState(SourceX+1, SourceY ) = fsStone) then result := true; |
260 | if (SourceX+2 = DestX) and (SourceY = DestY) and (FieldState(SourceX+1, SourceY ) = fsOccupied) then result := true; |
261 | if (SourceX-2 = DestX) and (SourceY = DestY) and (FieldState(SourceX-1, SourceY ) = fsStone) then result := true; |
261 | if (SourceX-2 = DestX) and (SourceY = DestY) and (FieldState(SourceX-1, SourceY ) = fsOccupied) then result := true; |
262 | if (SourceX = DestX) and (SourceY+2 = DestY) and (FieldState(SourceX , SourceY+1) = fsStone) then result := true; |
262 | if (SourceX = DestX) and (SourceY+2 = DestY) and (FieldState(SourceX , SourceY+1) = fsOccupied) then result := true; |
263 | if (SourceX = DestX) and (SourceY-2 = DestY) and (FieldState(SourceX , SourceY-1) = fsStone) then result := true; |
263 | if (SourceX = DestX) and (SourceY-2 = DestY) and (FieldState(SourceX , SourceY-1) = fsOccupied) then result := true; |
264 | end; |
264 | end; |
265 | 265 | ||
266 | function TPlayGroundMatrix.CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; |
266 | function TPlayGroundMatrix.CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; |
267 | begin |
267 | begin |
268 | if FieldState(SourceX, SourceY) <> fsStone then |
268 | if FieldState(SourceX, SourceY) <> fsOccupied then |
269 | begin |
269 | begin |
270 | result := false; |
270 | result := false; |
271 | exit; |
271 | exit; |
272 | end; |
272 | end; |
273 | 273 | ||
Line 349... | Line 349... | ||
349 | FStringList.Strings[i] := StringReplace(FStringList.Strings[i], ' ', '', [rfReplaceAll]); |
349 | FStringList.Strings[i] := StringReplace(FStringList.Strings[i], ' ', '', [rfReplaceAll]); |
350 | if FStringList.Strings[i] = '' then FStringList.Delete(i); |
350 | if FStringList.Strings[i] = '' then FStringList.Delete(i); |
351 | end; |
351 | end; |
352 | end; |
352 | end; |
353 | 353 | ||
354 | function TLevel.LevelStringToLevelArray(ShowErrors: boolean): TLevelArray; |
354 | procedure TLevel.FillPlaygroundMatrix(var matrix: TPlayGroundMatrix; ShowErrors: boolean); |
355 | var |
355 | var |
356 | i: integer; |
356 | i: integer; |
357 | t: TFieldType; |
357 | t: TFieldType; |
358 | err: TLevelError; |
358 | err: TLevelError; |
359 | y: Integer; |
359 | y: Integer; |
360 | x: Integer; |
360 | x: Integer; |
361 | Line: string; |
361 | Line: string; |
362 | lch, uch: char; |
362 | lch, uch: char; |
363 | ch: char; |
363 | ch: char; |
- | 364 | width: Integer; |
|
- | 365 | height: Integer; |
|
- | 366 | lineIndent: Integer; |
|
364 | begin |
367 | begin |
365 | // Zuerst nach Fehlern suchen |
368 | // Zuerst nach Fehlern suchen |
366 | err := CheckLevelIntegrity(ShowErrors); |
369 | err := CheckLevelIntegrity(ShowErrors); |
367 | if err <> leNone then exit; |
370 | if err <> leNone then exit; |
368 | 371 | ||
- | 372 | // Breite feststellen |
|
- | 373 | if FStringList.Count > NUM_HEADERS then |
|
- | 374 | begin |
|
- | 375 | Line := FStringList.Strings[NUM_HEADERS]; |
|
- | 376 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
|
- | 377 | width := Length(Line); |
|
- | 378 | end |
|
- | 379 | else width := 0; |
|
- | 380 | ||
- | 381 | // Höhe feststellen |
|
- | 382 | height := FStringList.Count - NUM_HEADERS; |
|
- | 383 | ||
369 | // Nun Matrix aufbauen |
384 | // Nun Matrix aufbauen |
370 | SetLength(result, 0); |
385 | matrix.ClearMatrix(true); |
- | 386 | matrix.InitFieldArray(width, height); |
|
371 | for i := NUM_HEADERS to FStringList.Count-1 do |
387 | for i := NUM_HEADERS to FStringList.Count-1 do |
372 | begin |
388 | begin |
373 | y := i - NUM_HEADERS; |
389 | y := i - NUM_HEADERS; |
374 | 390 | ||
375 | SetLength(result, Length(result)+1); // add line to matrix |
- | |
376 | - | ||
377 | Line := FStringList.Strings[i]; |
391 | Line := FStringList.Strings[i]; |
378 | result[y].Indent := DotsAtBeginning(Line) - DotsAtEnd(Line); |
392 | lineIndent := DotsAtBeginning(Line) - DotsAtEnd(Line); |
379 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
393 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
380 | SetLength(result[y].Fields, Length(Line)); |
- | |
381 | 394 | ||
382 | for x := 0 to Length(Line)-1 do |
395 | for x := 0 to Length(Line)-1 do |
383 | begin |
396 | begin |
384 | ch := Line[x+1]; |
397 | ch := Line[x+1]; |
385 | lch := LowerCase(ch)[1]; |
398 | lch := LowerCase(ch)[1]; |
Line 392... | Line 405... | ||
392 | 'r': t := ftRed; |
405 | 'r': t := ftRed; |
393 | 'y': t := ftYellow; |
406 | 'y': t := ftYellow; |
394 | 'g': t := ftGreen; |
407 | 'g': t := ftGreen; |
395 | end; |
408 | end; |
396 | 409 | ||
- | 410 | matrix.Fields[x,y].Indent := lineIndent; |
|
397 | result[y].Fields[x].Typ := t; |
411 | matrix.Fields[x,y].FieldType := t; |
398 | result[y].Fields[x].Goal := (ch = uch) and (ch <> lch); |
412 | matrix.Fields[x,y].Goal := (ch = uch) and (ch <> lch); |
399 | end; |
413 | end; |
400 | end; |
414 | end; |
401 | end; |
415 | end; |
402 | 416 | ||
403 | function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError; |
417 | function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError; |
Line 438... | Line 452... | ||
438 | begin |
452 | begin |
439 | result := leUnsupportedVersion; |
453 | result := leUnsupportedVersion; |
440 | exit; |
454 | exit; |
441 | end; |
455 | end; |
442 | 456 | ||
- | 457 | if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and |
|
443 | if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then |
458 | (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then |
444 | begin |
459 | begin |
445 | result := leUnsupportedMode; |
460 | result := leUnsupportedMode; |
446 | exit; |
461 | exit; |
447 | end; |
462 | end; |
448 | 463 | ||
Line 462... | Line 477... | ||
462 | for i := NUM_HEADERS to FStringList.Count-1 do |
477 | for i := NUM_HEADERS to FStringList.Count-1 do |
463 | begin |
478 | begin |
464 | thisLine := StringReplace(FStringList.Strings[i], '.', '', [rfReplaceAll]); |
479 | thisLine := StringReplace(FStringList.Strings[i], '.', '', [rfReplaceAll]); |
465 | if Length(thisLine) <> Length(firstLine) then |
480 | if Length(thisLine) <> Length(firstLine) then |
466 | begin |
481 | begin |
467 | result := leRowInvalidLength; // at row y-NUM_HEADERS |
482 | result := leRowInvalidLength; // at row y = i-NUM_HEADERS |
468 | exit; |
483 | exit; |
469 | end; |
484 | end; |
470 | end; |
485 | end; |
471 | 486 | ||
472 | // Check 4: Gibt es ungültige Elemente in den Zeilen? |
487 | // Check 4: Gibt es ungültige Elemente in den Zeilen? |
Line 482... | Line 497... | ||
482 | Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]); |
497 | Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]); |
483 | Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]); |
498 | Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]); |
484 | 499 | ||
485 | if Length(Line) > 0 then |
500 | if Length(Line) > 0 then |
486 | begin |
501 | begin |
487 | result := leInvalidElement; // at row y-NUM_HEADERS |
502 | result := leInvalidElement; // at row y = i-NUM_HEADERS |
488 | Exit; |
503 | Exit; |
489 | end; |
504 | end; |
490 | end; |
505 | end; |
491 | 506 | ||
492 | // Check 5: Kann im Level gesprungen werden? |
507 | // Check 5: Kann im Level gesprungen werden? |