Subversion Repositories jumper

Rev

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