Rev 22 | Rev 24 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 22 | Rev 23 | ||
---|---|---|---|
1 | unit LevelFunctions; |
1 | unit LevelFunctions; |
2 | 2 | ||
3 | interface |
3 | interface |
4 | 4 | ||
5 | uses |
5 | uses |
6 | SysUtils, Dialogs, Functions, ExtCtrls, Classes, Math; |
6 | SysUtils, Dialogs, Functions, ExtCtrls, Classes, Math; |
7 | 7 | ||
8 | type |
8 | type |
- | 9 | TCoord = record |
|
- | 10 | X: integer; |
|
- | 11 | Y: integer; |
|
- | 12 | end; |
|
- | 13 | ||
9 | TFieldType = (ftUndefined, ftFullSpace, ftEmpty, ftRed, ftYellow, ftGreen); |
14 | TFieldType = (ftUndefined, ftFullSpace, ftEmpty, ftRed, ftYellow, ftGreen); |
10 | 15 | ||
11 | TFieldProperties = record |
16 | TFieldProperties = record |
12 | Typ: TFieldType; |
17 | Typ: TFieldType; |
13 | Goal: Boolean; |
18 | Goal: Boolean; |
14 | end; |
19 | end; |
15 | 20 | ||
16 | TGameMode = (gmUndefined, gmNormal, gmDiagonal); |
21 | TGameMode = (gmUndefined, gmNormal, gmDiagonal); |
17 | 22 | ||
18 | TRow = record |
23 | TRow = record |
19 | Indent: integer; |
24 | Indent: integer; |
20 | Fields: array of TFieldProperties; |
25 | Fields: array of TFieldProperties; |
21 | end; |
26 | end; |
22 | TLevelArray = array of TRow; |
27 | TLevelArray = array of TRow; |
23 | 28 | ||
24 | TLevelError = (leUndefined, leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength, |
29 | TLevelError = (leUndefined, leNone, leInvalidElement, leEmptyBoard, leRowInvalidLength, |
25 | leUnsupportedVersion, leUnsupportedMode); |
30 | leUnsupportedVersion, leUnsupportedMode); |
26 | 31 | ||
27 | TLevel = class(TObject) |
32 | TLevel = class(TObject) |
28 | private |
33 | private |
29 | FStringList: TStringList; |
34 | FStringList: TStringList; |
30 | procedure Load(ABoardFile: string); |
35 | procedure Load(ABoardFile: string); |
31 | function GetGameMode: TGameMode; |
36 | function GetGameMode: TGameMode; |
32 | public |
37 | public |
33 | constructor Create(ABoardFile: string); |
38 | constructor Create(ABoardFile: string); |
34 | destructor Destroy; override; |
39 | destructor Destroy; override; |
35 | function LevelStringToLevelArray(ShowErrors: boolean): TLevelArray; |
40 | function LevelStringToLevelArray(ShowErrors: boolean): TLevelArray; |
36 | function CheckLevelIntegrity: TLevelError; overload; |
41 | function CheckLevelIntegrity: TLevelError; overload; |
37 | function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload; |
42 | function CheckLevelIntegrity(ShowErrors: boolean): TLevelError; overload; |
38 | property GameMode: TGameMode read GetGameMode; |
43 | property GameMode: TGameMode read GetGameMode; |
39 | end; |
44 | end; |
40 | 45 | ||
41 | TField = record |
46 | TField = record |
42 | FieldType: TFieldType; |
47 | FieldType: TFieldType; |
43 | Goal: Boolean; |
48 | Goal: Boolean; |
44 | Panel: TPanel; |
49 | Panel: TPanel; |
45 | Stone: TImage; |
50 | Stone: TImage; |
46 | end; |
51 | end; |
47 | 52 | ||
48 | TGoalStatus = (gsUndefined, gsNoGoal, gsMultipleStonesRemaining, gsLastStoneInGoalRed, gsLastStoneInGoalYellow, gsLastStoneInGoalGreen, gsLastStoneOutsideGoal); |
53 | TGoalStatus = (gsUndefined, gsNoGoal, gsMultipleStonesRemaining, gsLastStoneInGoalRed, gsLastStoneInGoalYellow, gsLastStoneInGoalGreen, gsLastStoneOutsideGoal); |
49 | 54 | ||
50 | TFieldState = (fsUndefined, fsError, fsLocked, fsAvailable, fsStone); |
55 | TFieldState = (fsUndefined, fsError, fsLocked, fsAvailable, fsStone); |
51 | 56 | ||
52 | TPlayGroundMatrix = record |
57 | TPlayGroundMatrix = record |
53 | Fields: array of array of TField; |
58 | Fields: array of array of TField; |
54 | public |
59 | public |
- | 60 | procedure InitFieldArray(width, height: integer); |
|
55 | function MatrixHasGoal: boolean; |
61 | function MatrixHasGoal: boolean; |
56 | function GoalFieldType: TFieldType; |
62 | function GoalFieldType: TFieldType; |
57 | function MatrixWorth: integer; |
63 | function MatrixWorth: integer; |
58 | procedure ClearMatrix(FreeVCL: boolean); |
64 | procedure ClearMatrix(FreeVCL: boolean); |
59 | function CloneMatrix: TPlayGroundMatrix; |
65 | function CloneMatrix: TPlayGroundMatrix; |
60 | function FieldState(t: TFieldType): TFieldState; overload; |
66 | class function FieldState(t: TFieldType): TFieldState; overload; static; |
61 | function FieldState(f: TField): TFieldState; overload; |
67 | function FieldState(f: TField): TFieldState; overload; |
62 | function FieldState(x, y: integer): TFieldState; overload; |
68 | function FieldState(x, y: integer): TFieldState; overload; |
63 | function CanJump(SourceX, SourceY, DestX, DestY: integer; DiagonalOK: boolean): boolean; overload; |
69 | function CanJump(SourceX, SourceY, DestX, DestY: integer; DiagonalOK: boolean): boolean; overload; |
64 | function CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; overload; |
70 | function CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; overload; |
65 | function CanJump(DiagonalOK: boolean): boolean; overload; |
71 | function CanJump(DiagonalOK: boolean): boolean; overload; |
- | 72 | function IndexToCoord(index: integer): TCoord; |
|
- | 73 | function CoordToIndex(coord: TCoord): integer; overload; |
|
- | 74 | function CoordToIndex(x, y: integer): integer; overload; |
|
- | 75 | function Width: integer; |
|
- | 76 | function Height: integer; |
|
66 | end; |
77 | end; |
67 | 78 | ||
68 | function FieldTypeWorth(t: TFieldType): integer; |
79 | function FieldTypeWorth(t: TFieldType): integer; |
69 | 80 | ||
70 | implementation |
81 | implementation |
71 | 82 | ||
72 | function FieldTypeWorth(t: TFieldType): integer; |
83 | function FieldTypeWorth(t: TFieldType): integer; |
73 | begin |
84 | begin |
74 | if t = ftGreen then result := 10 |
85 | if t = ftGreen then result := 10 |
75 | else if t = ftYellow then result := 20 |
86 | else if t = ftYellow then result := 20 |
76 | else if t = ftRed then result := 30 |
87 | else if t = ftRed then result := 30 |
77 | else result := 0; |
88 | else result := 0; |
78 | end; |
89 | end; |
79 | 90 | ||
80 | { TPlayGroundMatrix } |
91 | { TPlayGroundMatrix } |
81 | 92 | ||
82 | function TPlayGroundMatrix.MatrixHasGoal: boolean; |
93 | function TPlayGroundMatrix.MatrixHasGoal: boolean; |
83 | var |
94 | var |
84 | x, y: integer; |
95 | x, y: integer; |
85 | begin |
96 | begin |
86 | result := false; |
97 | result := false; |
87 | for x := Low(Fields) to High(Fields) do |
98 | for x := Low(Fields) to High(Fields) do |
88 | begin |
99 | begin |
89 | for y := Low(Fields[x]) to High(Fields[x]) do |
100 | for y := Low(Fields[x]) to High(Fields[x]) do |
90 | begin |
101 | begin |
91 | result := result or Fields[x][y].Goal; |
102 | result := result or Fields[x,y].Goal; |
92 | end; |
103 | end; |
93 | end; |
104 | end; |
94 | end; |
105 | end; |
95 | 106 | ||
96 | function TPlayGroundMatrix.GoalFieldType: TFieldType; |
107 | function TPlayGroundMatrix.GoalFieldType: TFieldType; |
97 | var |
108 | var |
98 | x, y: integer; |
109 | x, y: integer; |
99 | begin |
110 | begin |
100 | result := ftEmpty; // Damit der Compiler nicht meckert |
111 | result := ftEmpty; // Damit der Compiler nicht meckert |
101 | for x := Low(Fields) to High(Fields) do |
112 | for x := Low(Fields) to High(Fields) do |
102 | begin |
113 | begin |
103 | for y := Low(Fields[x]) to High(Fields[x]) do |
114 | for y := Low(Fields[x]) to High(Fields[x]) do |
104 | begin |
115 | begin |
105 | if Fields[x][y].Goal then result := Fields[x][y].FieldType |
116 | if Fields[x,y].Goal then result := Fields[x,y].FieldType |
- | 117 | end; |
|
- | 118 | end; |
|
- | 119 | end; |
|
- | 120 | ||
- | 121 | function TPlayGroundMatrix.Height: integer; |
|
- | 122 | begin |
|
- | 123 | if Length(Fields) = 0 then |
|
- | 124 | result := 0 |
|
- | 125 | else |
|
- | 126 | result := Length(Fields[0]); |
|
- | 127 | end; |
|
- | 128 | ||
- | 129 | function TPlayGroundMatrix.IndexToCoord(index: integer): TCoord; |
|
- | 130 | begin |
|
- | 131 | result.X := index mod Width; |
|
- | 132 | result.Y := index div Width; |
|
- | 133 | end; |
|
- | 134 | ||
- | 135 | procedure TPlayGroundMatrix.InitFieldArray(width, height: integer); |
|
- | 136 | var |
|
- | 137 | x, y: integer; |
|
- | 138 | begin |
|
- | 139 | SetLength(Fields, width, height); |
|
- | 140 | for x := Low(Fields) to High(Fields) do |
|
- | 141 | begin |
|
- | 142 | for y := Low(Fields[x]) to High(Fields[x]) do |
|
- | 143 | begin |
|
- | 144 | Fields[x,y].FieldType := ftUndefined; |
|
106 | end; |
145 | end; |
107 | end; |
146 | end; |
108 | end; |
147 | end; |
109 | 148 | ||
110 | function TPlayGroundMatrix.MatrixWorth: integer; |
149 | function TPlayGroundMatrix.MatrixWorth: integer; |
111 | var |
150 | var |
112 | x, y: integer; |
151 | x, y: integer; |
113 | begin |
152 | begin |
114 | result := 0; |
153 | result := 0; |
115 | for x := Low(Fields) to High(Fields) do |
154 | for x := Low(Fields) to High(Fields) do |
116 | begin |
155 | begin |
117 | for y := Low(Fields[x]) to High(Fields[x]) do |
156 | for y := Low(Fields[x]) to High(Fields[x]) do |
118 | begin |
157 | begin |
119 | Inc(result, FieldTypeWorth(Fields[x][y].FieldType)); |
158 | Inc(result, FieldTypeWorth(Fields[x,y].FieldType)); |
120 | end; |
159 | end; |
121 | end; |
160 | end; |
122 | end; |
161 | end; |
123 | 162 | ||
- | 163 | function TPlayGroundMatrix.Width: integer; |
|
- | 164 | begin |
|
- | 165 | result := Length(Fields); |
|
- | 166 | end; |
|
- | 167 | ||
124 | procedure TPlayGroundMatrix.ClearMatrix(FreeVCL: boolean); |
168 | procedure TPlayGroundMatrix.ClearMatrix(FreeVCL: boolean); |
125 | var |
169 | var |
126 | x, y: integer; |
170 | x, y: integer; |
127 | begin |
171 | begin |
128 | for x := Low(Fields) to High(Fields) do |
172 | for x := Low(Fields) to High(Fields) do |
129 | begin |
173 | begin |
130 | for y := Low(Fields[x]) to High(Fields[x]) do |
174 | for y := Low(Fields[x]) to High(Fields[x]) do |
131 | begin |
175 | begin |
132 | if FreeVCL then |
176 | if FreeVCL then |
133 | begin |
177 | begin |
134 | if Assigned(Fields[x][y].Stone) then Fields[x][y].Stone.Free; |
178 | if Assigned(Fields[x,y].Stone) then Fields[x,y].Stone.Free; |
135 | if Assigned(Fields[x][y].Panel) then Fields[x][y].Panel.Free; |
179 | if Assigned(Fields[x,y].Panel) then Fields[x,y].Panel.Free; |
136 | end; |
180 | end; |
137 | end; |
181 | end; |
138 | SetLength(Fields[x], 0); |
- | |
139 | end; |
182 | end; |
140 | SetLength(Fields, 0); |
183 | SetLength(Fields, 0, 0); |
141 | end; |
184 | end; |
142 | 185 | ||
143 | function TPlayGroundMatrix.CloneMatrix: TPlayGroundMatrix; |
186 | function TPlayGroundMatrix.CloneMatrix: TPlayGroundMatrix; |
144 | var |
187 | var |
145 | x, y: integer; |
188 | x, y: integer; |
146 | begin |
189 | begin |
147 | SetLength(result.Fields, Length(Fields)); |
190 | SetLength(result.Fields, Length(Fields)); |
148 | for x := Low(Fields) to High(Fields) do |
191 | for x := Low(Fields) to High(Fields) do |
149 | begin |
192 | begin |
150 | SetLength(result.Fields[x], Length(Fields[x])); |
193 | SetLength(result.Fields[x], Length(Fields[x])); |
151 | for y := Low(Fields[x]) to High(Fields[x]) do |
194 | for y := Low(Fields[x]) to High(Fields[x]) do |
152 | begin |
195 | begin |
153 | result.Fields[x][y].FieldType := Fields[x][y].FieldType; |
196 | result.Fields[x,y].FieldType := Fields[x,y].FieldType; |
154 | result.Fields[x][y].Goal := Fields[x][y].Goal; |
197 | result.Fields[x,y].Goal := Fields[x,y].Goal; |
155 | result.Fields[x][y].Panel := Fields[x][y].Panel; |
198 | result.Fields[x,y].Panel := Fields[x,y].Panel; |
156 | result.Fields[x][y].Stone := Fields[x][y].Stone; |
199 | result.Fields[x,y].Stone := Fields[x,y].Stone; |
157 | end; |
200 | end; |
158 | end; |
201 | end; |
159 | end; |
202 | end; |
160 | 203 | ||
- | 204 | function TPlayGroundMatrix.CoordToIndex(x, y: integer): integer; |
|
- | 205 | var |
|
- | 206 | c: TCoord; |
|
- | 207 | begin |
|
- | 208 | c.X := x; |
|
- | 209 | c.Y := y; |
|
- | 210 | result := CoordToIndex(c); |
|
- | 211 | end; |
|
- | 212 | ||
- | 213 | function TPlayGroundMatrix.CoordToIndex(coord: TCoord): integer; |
|
- | 214 | begin |
|
- | 215 | result := coord.X + coord.Y * Width; |
|
- | 216 | end; |
|
- | 217 | ||
161 | function TPlayGroundMatrix.FieldState(t: TFieldType): TFieldState; |
218 | class function TPlayGroundMatrix.FieldState(t: TFieldType): TFieldState; |
162 | begin |
219 | begin |
163 | result := fsError; |
220 | result := fsError; |
164 | case t of |
221 | case t of |
165 | ftFullSpace: result := fsLocked; |
222 | ftFullSpace: result := fsLocked; |
166 | ftEmpty: result := fsAvailable; |
223 | ftEmpty: result := fsAvailable; |
167 | ftGreen: result := fsStone; |
224 | ftGreen: result := fsStone; |
168 | ftYellow: result := fsStone; |
225 | ftYellow: result := fsStone; |
169 | ftRed: result := fsStone; |
226 | ftRed: result := fsStone; |
170 | end; |
227 | end; |
171 | end; |
228 | end; |
172 | 229 | ||
173 | function TPlayGroundMatrix.FieldState(f: TField): TFieldState; |
230 | function TPlayGroundMatrix.FieldState(f: TField): TFieldState; |
174 | begin |
231 | begin |
175 | result := FieldState(f.FieldType); |
232 | result := FieldState(f.FieldType); |
176 | end; |
233 | end; |
177 | 234 | ||
178 | function TPlayGroundMatrix.FieldState(x, y: integer): TFieldState; |
235 | function TPlayGroundMatrix.FieldState(x, y: integer): TFieldState; |
179 | begin |
236 | begin |
180 | result := fsError; |
237 | result := fsError; |
181 | if (x < Low(Fields)) or (x > High(Fields)) then exit; |
238 | if (x < Low(Fields)) or (x > High(Fields)) then exit; |
182 | 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; |
183 | 240 | ||
184 | result := FieldState(Fields[x][y]); |
241 | result := FieldState(Fields[x,y]); |
185 | end; |
242 | end; |
186 | 243 | ||
187 | function TPlayGroundMatrix.CanJump(SourceX, SourceY, DestX, DestY: integer; DiagonalOK: boolean): boolean; |
244 | function TPlayGroundMatrix.CanJump(SourceX, SourceY, DestX, DestY: integer; DiagonalOK: boolean): boolean; |
188 | begin |
245 | begin |
189 | result := false; |
246 | result := false; |
190 | 247 | ||
191 | // Check 1: Ist das Zielfeld überhaupt leer? |
248 | // Check 1: Ist das Zielfeld überhaupt leer? |
192 | if FieldState(DestX, DestY) <> fsAvailable then exit; |
249 | if FieldState(DestX, DestY) <> fsAvailable then exit; |
193 | 250 | ||
194 | // 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? |
195 | if DiagonalOK then |
252 | if DiagonalOK then |
196 | begin |
253 | begin |
197 | 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) = fsStone) then result := true; |
198 | 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) = fsStone) then result := true; |
199 | 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) = fsStone) then result := true; |
200 | 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) = fsStone) then result := true; |
201 | end; |
258 | end; |
202 | 259 | ||
203 | 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 ) = fsStone) then result := true; |
204 | 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 ) = fsStone) then result := true; |
205 | 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) = fsStone) then result := true; |
206 | 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) = fsStone) then result := true; |
207 | end; |
264 | end; |
208 | 265 | ||
209 | function TPlayGroundMatrix.CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; |
266 | function TPlayGroundMatrix.CanJump(SourceX, SourceY: integer; DiagonalOK: boolean): boolean; |
210 | begin |
267 | begin |
211 | if FieldState(SourceX, SourceY) <> fsStone then |
268 | if FieldState(SourceX, SourceY) <> fsStone then |
212 | begin |
269 | begin |
213 | result := false; |
270 | result := false; |
214 | exit; |
271 | exit; |
215 | end; |
272 | end; |
216 | 273 | ||
217 | result := true; |
274 | result := true; |
218 | 275 | ||
219 | if CanJump(SourceX, SourceY, SourceX+2, SourceY, DiagonalOK) then exit; |
276 | if CanJump(SourceX, SourceY, SourceX+2, SourceY, DiagonalOK) then exit; |
220 | if CanJump(SourceX, SourceY, SourceX-2, SourceY, DiagonalOK) then exit; |
277 | if CanJump(SourceX, SourceY, SourceX-2, SourceY, DiagonalOK) then exit; |
221 | if CanJump(SourceX, SourceY, SourceX, SourceY+2, DiagonalOK) then exit; |
278 | if CanJump(SourceX, SourceY, SourceX, SourceY+2, DiagonalOK) then exit; |
222 | if CanJump(SourceX, SourceY, SourceX, SourceY-2, DiagonalOK) then exit; |
279 | if CanJump(SourceX, SourceY, SourceX, SourceY-2, DiagonalOK) then exit; |
223 | 280 | ||
224 | if DiagonalOK then |
281 | if DiagonalOK then |
225 | begin |
282 | begin |
226 | if CanJump(SourceX, SourceY, SourceX-2, SourceY-2, DiagonalOK) then exit; |
283 | if CanJump(SourceX, SourceY, SourceX-2, SourceY-2, DiagonalOK) then exit; |
227 | if CanJump(SourceX, SourceY, SourceX+2, SourceY-2, DiagonalOK) then exit; |
284 | if CanJump(SourceX, SourceY, SourceX+2, SourceY-2, DiagonalOK) then exit; |
228 | if CanJump(SourceX, SourceY, SourceX-2, SourceY+2, DiagonalOK) then exit; |
285 | if CanJump(SourceX, SourceY, SourceX-2, SourceY+2, DiagonalOK) then exit; |
229 | if CanJump(SourceX, SourceY, SourceX+2, SourceY+2, DiagonalOK) then exit; |
286 | if CanJump(SourceX, SourceY, SourceX+2, SourceY+2, DiagonalOK) then exit; |
230 | end; |
287 | end; |
231 | 288 | ||
232 | result := false; |
289 | result := false; |
233 | end; |
290 | end; |
234 | 291 | ||
235 | function TPlayGroundMatrix.CanJump(DiagonalOK: boolean): boolean; |
292 | function TPlayGroundMatrix.CanJump(DiagonalOK: boolean): boolean; |
236 | var |
293 | var |
237 | x, y: integer; |
294 | x, y: integer; |
238 | begin |
295 | begin |
239 | result := false; |
296 | result := false; |
240 | for x := Low(Fields) to High(Fields) do |
297 | for x := Low(Fields) to High(Fields) do |
241 | begin |
298 | begin |
242 | for y := Low(Fields[x]) to High(Fields[x]) do |
299 | for y := Low(Fields[x]) to High(Fields[x]) do |
243 | begin |
300 | begin |
244 | if CanJump(x, y, DiagonalOK) then |
301 | if CanJump(x, y, DiagonalOK) then |
245 | begin |
302 | begin |
246 | result := true; |
303 | result := true; |
247 | break; |
304 | break; |
248 | end; |
305 | end; |
249 | if result then break; |
306 | if result then break; |
250 | end; |
307 | end; |
251 | end; |
308 | end; |
252 | end; |
309 | end; |
253 | 310 | ||
254 | { TLevel } |
311 | { TLevel } |
255 | 312 | ||
256 | const NUM_HEADERS = 2; |
313 | const NUM_HEADERS = 2; |
257 | 314 | ||
258 | constructor TLevel.Create(ABoardFile: string); |
315 | constructor TLevel.Create(ABoardFile: string); |
259 | begin |
316 | begin |
260 | inherited Create; |
317 | inherited Create; |
261 | FStringList := TStringList.Create; |
318 | FStringList := TStringList.Create; |
262 | Load(ABoardFile); |
319 | Load(ABoardFile); |
263 | end; |
320 | end; |
264 | 321 | ||
265 | destructor TLevel.Destroy; |
322 | destructor TLevel.Destroy; |
266 | begin |
323 | begin |
267 | FreeAndNil(FStringList); |
324 | FreeAndNil(FStringList); |
268 | 325 | ||
269 | inherited; |
326 | inherited; |
270 | end; |
327 | end; |
271 | 328 | ||
272 | function TLevel.GetGameMode: TGameMode; |
329 | function TLevel.GetGameMode: TGameMode; |
273 | begin |
330 | begin |
274 | if LowerCase(FStringList.Strings[1]) = 'mode: normal' then |
331 | if LowerCase(FStringList.Strings[1]) = 'mode: normal' then |
275 | result := gmNormal |
332 | result := gmNormal |
276 | else if LowerCase(FStringList.Strings[1]) = 'mode: diagonal' then |
333 | else if LowerCase(FStringList.Strings[1]) = 'mode: diagonal' then |
277 | result := gmDiagonal |
334 | result := gmDiagonal |
278 | else |
335 | else |
279 | result := gmUndefined; |
336 | result := gmUndefined; |
280 | end; |
337 | end; |
281 | 338 | ||
282 | procedure TLevel.Load(ABoardFile: string); |
339 | procedure TLevel.Load(ABoardFile: string); |
283 | var |
340 | var |
284 | i: Integer; |
341 | i: Integer; |
285 | begin |
342 | begin |
286 | FStringList.Clear; |
343 | FStringList.Clear; |
287 | FStringList.LoadFromFile(ABoardFile); |
344 | FStringList.LoadFromFile(ABoardFile); |
288 | 345 | ||
289 | // Remove whitespaces and empty lines |
346 | // Remove whitespaces and empty lines |
290 | for i := FStringList.Count-1 downto NUM_HEADERS do |
347 | for i := FStringList.Count-1 downto NUM_HEADERS do |
291 | begin |
348 | begin |
292 | FStringList.Strings[i] := StringReplace(FStringList.Strings[i], ' ', '', [rfReplaceAll]); |
349 | FStringList.Strings[i] := StringReplace(FStringList.Strings[i], ' ', '', [rfReplaceAll]); |
293 | if FStringList.Strings[i] = '' then FStringList.Delete(i); |
350 | if FStringList.Strings[i] = '' then FStringList.Delete(i); |
294 | end; |
351 | end; |
295 | end; |
352 | end; |
296 | 353 | ||
297 | function TLevel.LevelStringToLevelArray(ShowErrors: boolean): TLevelArray; |
354 | function TLevel.LevelStringToLevelArray(ShowErrors: boolean): TLevelArray; |
298 | var |
355 | var |
299 | i: integer; |
356 | i: integer; |
300 | t: TFieldType; |
357 | t: TFieldType; |
301 | err: TLevelError; |
358 | err: TLevelError; |
302 | y: Integer; |
359 | y: Integer; |
303 | x: Integer; |
360 | x: Integer; |
304 | Line: string; |
361 | Line: string; |
305 | lch, uch: char; |
362 | lch, uch: char; |
306 | ch: char; |
363 | ch: char; |
307 | begin |
364 | begin |
308 | // Zuerst nach Fehlern suchen |
365 | // Zuerst nach Fehlern suchen |
309 | err := CheckLevelIntegrity(ShowErrors); |
366 | err := CheckLevelIntegrity(ShowErrors); |
310 | if err <> leNone then exit; |
367 | if err <> leNone then exit; |
311 | 368 | ||
312 | // Nun Matrix aufbauen |
369 | // Nun Matrix aufbauen |
313 | SetLength(result, 0); |
370 | SetLength(result, 0); |
314 | for i := NUM_HEADERS to FStringList.Count-1 do |
371 | for i := NUM_HEADERS to FStringList.Count-1 do |
315 | begin |
372 | begin |
316 | y := i - NUM_HEADERS; |
373 | y := i - NUM_HEADERS; |
317 | 374 | ||
318 | SetLength(result, Length(result)+1); // add line to matrix |
375 | SetLength(result, Length(result)+1); // add line to matrix |
319 | 376 | ||
320 | Line := FStringList.Strings[i]; |
377 | Line := FStringList.Strings[i]; |
321 | result[y].Indent := DotsAtBeginning(Line) - DotsAtEnd(Line); |
378 | result[y].Indent := DotsAtBeginning(Line) - DotsAtEnd(Line); |
322 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
379 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
323 | SetLength(result[y].Fields, Length(Line)); |
380 | SetLength(result[y].Fields, Length(Line)); |
324 | 381 | ||
325 | for x := 0 to Length(Line)-1 do |
382 | for x := 0 to Length(Line)-1 do |
326 | begin |
383 | begin |
327 | ch := Line[x+1]; |
384 | ch := Line[x+1]; |
328 | lch := LowerCase(ch)[1]; |
385 | lch := LowerCase(ch)[1]; |
329 | uch := UpperCase(ch)[1]; |
386 | uch := UpperCase(ch)[1]; |
330 | 387 | ||
331 | t := ftUndefined; |
388 | t := ftUndefined; |
332 | case lch of |
389 | case lch of |
333 | '*': t := ftFullSpace; |
390 | '*': t := ftFullSpace; |
334 | 'e': t := ftEmpty; |
391 | 'e': t := ftEmpty; |
335 | 'r': t := ftRed; |
392 | 'r': t := ftRed; |
336 | 'y': t := ftYellow; |
393 | 'y': t := ftYellow; |
337 | 'g': t := ftGreen; |
394 | 'g': t := ftGreen; |
338 | end; |
395 | end; |
339 | 396 | ||
340 | result[y].Fields[x].Typ := t; |
397 | result[y].Fields[x].Typ := t; |
341 | result[y].Fields[x].Goal := (ch = uch) and (ch <> lch); |
398 | result[y].Fields[x].Goal := (ch = uch) and (ch <> lch); |
342 | end; |
399 | end; |
343 | end; |
400 | end; |
344 | end; |
401 | end; |
345 | 402 | ||
346 | function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError; |
403 | function TLevel.CheckLevelIntegrity(ShowErrors: boolean): TLevelError; |
347 | resourcestring |
404 | resourcestring |
348 | LNG_LVL_INVALID_ELEMENT = 'Level invalid: There are invalid elements in the file.'+#13#10#13#10+'Valid elements are r/R, y/Y, g/G, e/E, . and *.'; |
405 | LNG_LVL_INVALID_ELEMENT = 'Level invalid: There are invalid elements in the file.'+#13#10#13#10+'Valid elements are r/R, y/Y, g/G, e/E, . and *.'; |
349 | LNG_LVL_UNSUPPORTED_VERSION = 'Level format invalid: Version not supported.'; |
406 | LNG_LVL_UNSUPPORTED_VERSION = 'Level format invalid: Version not supported.'; |
350 | LNG_LVL_UNSUPPORTED_MODE = 'Level format invalid: Mode not supported.'; |
407 | LNG_LVL_UNSUPPORTED_MODE = 'Level format invalid: Mode not supported.'; |
351 | LNG_LVL_EMPTY_BOARD = 'Level invalid: Board is empty.'; |
408 | LNG_LVL_EMPTY_BOARD = 'Level invalid: Board is empty.'; |
352 | LNG_LVL_INVALID_LENGTH = 'Level invalid: Lines don''t have an equal amount of elements.'; |
409 | LNG_LVL_INVALID_LENGTH = 'Level invalid: Lines don''t have an equal amount of elements.'; |
353 | begin |
410 | begin |
354 | result := CheckLevelIntegrity; |
411 | result := CheckLevelIntegrity; |
355 | if ShowErrors then |
412 | if ShowErrors then |
356 | begin |
413 | begin |
357 | case result of |
414 | case result of |
358 | leNone: ; |
415 | leNone: ; |
359 | leInvalidElement: MessageDlg(LNG_LVL_INVALID_ELEMENT, mtError, [mbOk], 0); |
416 | leInvalidElement: MessageDlg(LNG_LVL_INVALID_ELEMENT, mtError, [mbOk], 0); |
360 | leUnsupportedVersion: MessageDlg(LNG_LVL_UNSUPPORTED_VERSION, mtError, [mbOk], 0); |
417 | leUnsupportedVersion: MessageDlg(LNG_LVL_UNSUPPORTED_VERSION, mtError, [mbOk], 0); |
361 | leUnsupportedMode: MessageDlg(LNG_LVL_UNSUPPORTED_MODE, mtError, [mbOk], 0); |
418 | leUnsupportedMode: MessageDlg(LNG_LVL_UNSUPPORTED_MODE, mtError, [mbOk], 0); |
362 | leEmptyBoard: MessageDlg(LNG_LVL_EMPTY_BOARD, mtError, [mbOk], 0); |
419 | leEmptyBoard: MessageDlg(LNG_LVL_EMPTY_BOARD, mtError, [mbOk], 0); |
363 | leRowInvalidLength: MessageDlg(LNG_LVL_INVALID_LENGTH, mtError, [mbOk], 0); |
420 | leRowInvalidLength: MessageDlg(LNG_LVL_INVALID_LENGTH, mtError, [mbOk], 0); |
364 | end; |
421 | end; |
365 | end; |
422 | end; |
366 | end; |
423 | end; |
367 | 424 | ||
368 | function TLevel.CheckLevelIntegrity: TLevelError; |
425 | function TLevel.CheckLevelIntegrity: TLevelError; |
369 | var |
426 | var |
370 | tmp: string; |
427 | tmp: string; |
371 | i: Integer; |
428 | i: Integer; |
372 | Line: string; |
429 | Line: string; |
373 | firstLine: string; |
430 | firstLine: string; |
374 | thisLine: string; |
431 | thisLine: string; |
375 | begin |
432 | begin |
376 | result := leNone; |
433 | result := leNone; |
377 | 434 | ||
378 | // Check 1: Ist der Header OK? |
435 | // Check 1: Ist der Header OK? |
379 | 436 | ||
380 | if LowerCase(FStringList.Strings[0]) <> 'version 2' then |
437 | if LowerCase(FStringList.Strings[0]) <> 'version 2' then |
381 | begin |
438 | begin |
382 | result := leUnsupportedVersion; |
439 | result := leUnsupportedVersion; |
383 | exit; |
440 | exit; |
384 | end; |
441 | end; |
385 | 442 | ||
386 | if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then |
443 | if ((LowerCase(FStringList.Strings[1]) <> 'mode: normal') and (LowerCase(FStringList.Strings[1]) <> 'mode: diagonal')) then |
387 | begin |
444 | begin |
388 | result := leUnsupportedMode; |
445 | result := leUnsupportedMode; |
389 | exit; |
446 | exit; |
390 | end; |
447 | end; |
391 | 448 | ||
392 | // Check 2: Ist das Brett leer? |
449 | // Check 2: Ist das Brett leer? |
393 | 450 | ||
394 | tmp := ''; |
451 | tmp := ''; |
395 | for i := NUM_HEADERS to FStringList.Count-1 do tmp := tmp + FStringList.Strings[i]; |
452 | for i := NUM_HEADERS to FStringList.Count-1 do tmp := tmp + FStringList.Strings[i]; |
396 | if Trim(StringReplace(tmp, '.', '', [rfReplaceAll])) = '' then |
453 | if Trim(StringReplace(tmp, '.', '', [rfReplaceAll])) = '' then |
397 | begin |
454 | begin |
398 | result := leEmptyBoard; |
455 | result := leEmptyBoard; |
399 | exit; |
456 | exit; |
400 | end; |
457 | end; |
401 | 458 | ||
402 | // Check 3: Geht das Level nicht in einem Quadrat oder Rechteck auf? |
459 | // Check 3: Geht das Level nicht in einem Quadrat oder Rechteck auf? |
403 | 460 | ||
404 | firstLine := StringReplace(FStringList.Strings[NUM_HEADERS], '.', '', [rfReplaceAll]); |
461 | firstLine := StringReplace(FStringList.Strings[NUM_HEADERS], '.', '', [rfReplaceAll]); |
405 | for i := NUM_HEADERS to FStringList.Count-1 do |
462 | for i := NUM_HEADERS to FStringList.Count-1 do |
406 | begin |
463 | begin |
407 | thisLine := StringReplace(FStringList.Strings[i], '.', '', [rfReplaceAll]); |
464 | thisLine := StringReplace(FStringList.Strings[i], '.', '', [rfReplaceAll]); |
408 | if Length(thisLine) <> Length(firstLine) then |
465 | if Length(thisLine) <> Length(firstLine) then |
409 | begin |
466 | begin |
410 | result := leRowInvalidLength; // at row y-NUM_HEADERS |
467 | result := leRowInvalidLength; // at row y-NUM_HEADERS |
411 | exit; |
468 | exit; |
412 | end; |
469 | end; |
413 | end; |
470 | end; |
414 | 471 | ||
415 | // Check 4: Gibt es ungültige Elemente in den Zeilen? |
472 | // Check 4: Gibt es ungültige Elemente in den Zeilen? |
416 | 473 | ||
417 | for i := NUM_HEADERS to FStringList.Count-1 do |
474 | for i := NUM_HEADERS to FStringList.Count-1 do |
418 | begin |
475 | begin |
419 | Line := FStringList.Strings[i]; |
476 | Line := FStringList.Strings[i]; |
420 | 477 | ||
421 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
478 | Line := StringReplace(Line, '.', '', [rfReplaceAll]); |
422 | Line := StringReplace(Line, '*', '', [rfReplaceAll]); |
479 | Line := StringReplace(Line, '*', '', [rfReplaceAll]); |
423 | Line := StringReplace(Line, 'r', '', [rfReplaceAll, rfIgnoreCase]); |
480 | Line := StringReplace(Line, 'r', '', [rfReplaceAll, rfIgnoreCase]); |
424 | Line := StringReplace(Line, 'y', '', [rfReplaceAll, rfIgnoreCase]); |
481 | Line := StringReplace(Line, 'y', '', [rfReplaceAll, rfIgnoreCase]); |
425 | Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]); |
482 | Line := StringReplace(Line, 'g', '', [rfReplaceAll, rfIgnoreCase]); |
426 | Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]); |
483 | Line := StringReplace(Line, 'e', '', [rfReplaceAll, rfIgnoreCase]); |
427 | 484 | ||
428 | if Length(Line) > 0 then |
485 | if Length(Line) > 0 then |
429 | begin |
486 | begin |
430 | result := leInvalidElement; // at row y-NUM_HEADERS |
487 | result := leInvalidElement; // at row y-NUM_HEADERS |
431 | Exit; |
488 | Exit; |
432 | end; |
489 | end; |
433 | end; |
490 | end; |
434 | 491 | ||
435 | // Check 5: Kann im Level gesprungen werden? |
492 | // Check 5: Kann im Level gesprungen werden? |
436 | 493 | ||
437 | { Wird hier nicht abgeprüft, da dafür zuerst der PlayGround gebaut sein muss. |
494 | { Wird hier nicht abgeprüft, da dafür zuerst der PlayGround gebaut sein muss. |
438 | Es ist außerdem eher ein logischer Fehler, kein Fehler in der Levelstruktur! } |
495 | Es ist außerdem eher ein logischer Fehler, kein Fehler in der Levelstruktur! } |
439 | end; |
496 | end; |
440 | 497 | ||
441 | end. |
498 | end. |
442 | 499 |