
Finishing a level is one thing; finishing it neatly is another. Every puzzle of this kind counts your moves, and the fewer the better. This section adds a counter under the board, and keeps each level's best score in LEVELS.DAT, so that it is still there tomorrow.
Moves counts the worker's steps: set to 0 when a level starts, one more each time he actually moves. A push counts as a move - it is one - and a bump into a wall does not, because Go only counts when he goes.
Status writes both numbers on the line under the board:
GotoXY(1, 23);
Write('Moves ', Moves, ' ');
The spaces after the number rub out whatever was there before, if the old number was longer (S23). Status is called by DrawAll and after every move - and nowhere else draws that line.
A best score of 0 means the level has never been finished.
When a level is won, the game compares: if there is no best yet, or this one is lower, it is a new best, and it goes in the file. SaveLevel is ReadLevel the other way round - Seek to the level's record and Write it.
But look at what Won does first:
ReadLevel;
L.Best := Moves;
SaveLevel;
By the end of a level, L is not the level as it was designed. The crates have moved - they are all on their goals. Save L as it stands and the file holds a finished level, and the next time you play it, it is over before you begin. So Won reads the level fresh from the file, changes only Best, and writes that back. The grid on the disc is never touched.
Start WARE6 from WARE5.
Add Moves to the variables:
N, R, C, Moves, Total: Integer;
After Squares, add Status:
procedure Status;
begin
GotoXY(1, 23);
Write('Moves ', Moves, ' ');
GotoXY(16, 23);
Write('Best ', L.Best, ' ');
end;
In DrawAll, after the Write('Level ', ...) line, add:
Status;
After ReadLevel, add SaveLevel:
procedure SaveLevel;
begin
Reset(F);
Seek(F, N - 1);
Write(F, L);
Close(F);
end;
In Start, after C := L.StartC;:
Moves := 0;
In Go, after Show(R, C);:
Moves := Moves + 1;
Status;
In Won, after Write('Level complete!');:
if (L.Best = 0) or
(Moves < L.Best) then
begin
ReadLevel;
L.Best := Moves;
SaveLevel;
GotoXY(17, 2);
Write('A new best!');
end;
Starting from
WARE5 from S31, read into a new work file WARE6. Make the changes, CTRL-K CTRL-D, S, R.
What you should see
Moves 0 and Best 0 under the board. The count goes up with every step. Finish the first level in five moves, the fewest there are:

Quit, and run the game again: level 1 now shows Best 5.
Won, change Moves < L.Best to Moves > L.Best. Finish level 1 in five moves; run the game again and finish it in seven. What does the game think of that?Won, take out the ReadLevel; line. Finish level 1 (on a level with no best yet), then run the game again. What does level 1 look like - and what happens when you take one step?Go, take out the Status; after Moves := Moves + 1;. Walk around. Is Moves still being counted?32.1 Count pushes as well as moves, and show Pushes on the same line as Moves and Best.
32.2 Show Best - instead of Best 0 for a level that has never been finished.
32.3 Write a separate program, CLEARBST, that sets every level's best score in LEVELS.DAT back to 0.
Worked solutions are in Appendix II.
| Symptom | Cause |
|---|---|
| A level starts already finished | Its finished grid was saved: Won must ReadLevel before changing Best and saving. Erase LEVELS.DAT to get the levels back. |
A worse finish is A new best! | The comparison is the wrong way round: it must be Moves < L.Best. |
| The best score is lost when the game is run again | SaveLevel is missing, or Won never calls it. |
Moves does not change | Status is not called after Moves := Moves + 1. |
Moves counts steps, reset by Start and shown by Status. A level's best score lives in its record in LEVELS.DAT; Won reads the level fresh, changes Best, and writes it back with SaveLevel, leaving the level's grid as it was designed.
S33, Undo - taking moves back, as many as you like, with a list that grows as you play.