See also: part 1, part 2, part 3, part 4, part 5, part 6 and part 7.
As we begin this part, let’s look at the BASIC Lights Out code as it stands currently:
5 REM SELECT GAME
6 GOSUB 6000
10 REM INITIALIZE GRID
20 GOSUB 4000
30 REM SHOW GRID
40 GOSUB 1000
47 IF LO=0 THEN PRINT "YOU WON!":END
48 PRINT "LIGHTS ON:";LO
50 REM INPUT SQUARE
60 GOSUB 2000
70 REM TOGGLE SQUARES
80 GOSUB 3000
90 REM REPEAT
100 GOTO 30
1000 REM SHOW GRID
1005 PRINT "GAME NUMBER:";GN
1010 FOR Y=0 TO 4
1020 FOR X=0 TO 4
1030 IF L(X,Y) THEN PRINT "X";:GOTO 1050
1040 PRINT ".";
1050 NEXT
1060 PRINT
1070 NEXT
1080 PRINT
1090 RETURN
2000 REM INPUT SQUARE
2010 INPUT "X,Y (0-4,0-4)";X,Y
2020 IF X<0 OR X>4 OR Y<0 OR Y>4 THEN 2010
2030 RETURN
3000 REM TOGGLE SQUARES
3010 L(X,Y)=NOT L(X,Y):LO=LO-(L(X,Y)*2+1)
3020 IF X>0 THEN L(X-1,Y)=NOT L(X-1,Y):LO=LO-(L(X-1,Y)*2+1)
3030 IF X<4 THEN L(X+1,Y)=NOT L(X+1,Y):LO=LO-(L(X+1,Y)*2+1)
3040 IF Y>0 THEN L(X,Y-1)=NOT L(X,Y-1):LO=LO-(L(X,Y-1)*2+1)
3050 IF Y<4 THEN L(X,Y+1)=NOT L(X,Y+1):LO=LO-(L(X,Y+1)*2+1)
3060 RETURN
4000 REM INITIALIZE GRID
4010 PRINT "INITIALIZING..."
4020 FOR A=1 TO 10
4030 Y=RND(5)-1
4040 X=RND(5)-1
4050 GOSUB 3000
4060 NEXT
4070 RETURN
6000 REM SELECT GAME
6010 PRINT "PLAY SPECIFIC GAME # (Y/N)?"
6020 S=S+1:A$=INKEY$:IF A$="" THEN 6020
6030 IF A$="Y" THEN 6060
6040 IF A$="N" THEN A=RND(-S)
6045 GN=RND(65535):A=RND(-GN)
6046 GOTO 6090
6050 GOTO 6020
6060 INPUT "PLAY GAME (1-65535)";GN
6070 IF GN<1 OR GN>65535 THEN 6060
6080 A=RND(-GN)
6090 RETURN
There are quite a few more things that could (and probably need to) be done:
- The number of moves taken should be counted and displayed.
- If the game is won, it just ENDs in line 47. It could ask the player if they want to play again.
- There is no way to quit a game in progress other than hitting the break key.
- The user interface (typing in coordinates such as “0,2”) is user-hostile. At the very least, we could label the rows/columns to show which values to type. Perhaps even labeling them “A B C D E …” across, and “1 2 3 4 5” vertically, similar to how chess boards are done.
- Options for grids larger than 5×5 could be implemented with very little change in the code.
Once the game is “code complete“, there is also be some cleanup and renumbering that should be done. (Having those odd line numbers like 47 bugs me.)
Score (Counting Moves)
The current code only knows that you won the game (the game ends) or you are still playing. If one person plays game number 16809 and solves it in 80 moves, and another person plays the same game and solves it in 10 moves, they are both treated to the same ending – the game exits.
Let’s add a move counter. We’ll reset it at the start of a game…
10 REM INITIALIZE GRID
15 MV=0
20 GOSUB 4000
…and increment it after every move:
90 REM REPEAT
95 MV=MV+1
100 GOTO 30
We should also display the move count each time the grid is display. This is a good time to also move the “number of lights on” in to the display grid routine, too.
48 PRINT "LIGHTS ON:";LO(removed)
1000 REM SHOW GRID
1005 PRINT "GAME NUMBER:";GN
1010 FOR Y=0 TO 4
1020 FOR X=0 TO 4
1030 IF L(X,Y) THEN PRINT "X";:GOTO 1050
1040 PRINT ".";
1050 NEXT
1060 PRINT
1070 NEXT
1080 PRINT "MOVES:";MV;"LIGHTS ON:";LO
1090 RETURN
This now gives us a display of our game number (which was already there), the number of moves made so far (new), and the current count of how many lights are on (already there).
DONE: The number of moves taken should be counted and displayed.
Game Over
The next thing I want to add is a game over screen. When the game is on, this screen should display how many moves it took. It could then prompt the user to see if they want to play agan.
It could be as simple as this:
47 IF LO=0 THEN 200
200 REM GAME WON
220 PRINT "YOU WON IN";MV;"MOVES."
230 INPUT "PLAY AGAIN (Y/N)";Q$
240 IF Q$="Y" THEN 5
250 PRINT "GAME OVER"
260 END
DONE: If the game is won, it just ENDs in line 47. It could ask the player if they want to play again.
I give up!
The user should also be able to quit. Currently, the awful “type in an X,Y coordinate” thing is yucky, but we could make typing “-1,-1” end the game. (We will fix the user interface later.)
2000 REM INPUT SQUARE
2010 INPUT "X,Y (0-4,0-4 OR -1,-1)";X,Y
2015 IF X=-1 THEN IF Y=-1 THEN 230
2020 IF X<0 OR X>4 OR Y<0 OR Y>4 THEN 2010
2030 RETURN
This is so yucky, but the goal here is to get the code fully functional. We can make it nice later.
DONE: There is no way to quit a game in progress other than hitting the break key.
The user interface sucks!
Okay, this one will take more work, but a quick enhancement might be to just print the columns and rows so the player knows what to type. Also, humans like counting from one instead of zero (base-1 humans) so typing in things to match the base-0 arrays is not very human friendly. We can fix both things here.
1000 REM SHOW GRID
1005 PRINT "GAME NUMBER:";GN
1006 PRINT " 12345"
1010 FOR Y=0 TO 4
1015 PRINT Y+1;
1020 FOR X=0 TO 4
1030 IF L(X,Y) THEN PRINT "X";:GOTO 1050
1040 PRINT ".";
1050 NEXT
1060 PRINT
1070 NEXT
1080 PRINT "MOVES:";MV;"LIGHTS ON:";LO
1090 RETURN
…and…
2000 REM INPUT SQUARE
2010 INPUT "X,Y (1-5,1-5 OR 0,0)";X,Y
2015 IF X=0 THEN IF Y=0 THEN 230
2020 IF X<1 OR X>5 OR Y<1 OR Y>5 THEN 2010
2025 X=X-1:Y=Y-1
2030 RETURN
Now the user will see numbers above the grid, and to the left of the grid, and be able to enter them using 1-5 rather than 0-4. I made 0,0 exit as well to save the use from having to type the minus signs.
But, this interface still sucks. After we get the “text and typing” version done, we can do one that is more modern and uses the arrow keys to cursor around and select squares.
DONE: The user interface (typing in coordinates such as “0,2”) is user-hostile.
Bigger grid, please
Hey, let’s just get the basic game working first, then we can worry about “Deluxe Lights Out.”
DEFERRED: Options for grids larger than 5×5 could be implemented with very little change in the code.
To be continued…