**See also:** part 1, part 2, part 3, part 4, part 5, part 6 and part 7.

The story so far… In the beginning, we learned a bit about the game Lights Out and its history. We then saw some very basic BASIC that implements the basic functionality of a basic game of this type. We still have a bit more work to do, such as initializing some of the squares to an “on” state and adding code to detect when the game has been won.

Let’s add that.

## Game Initialization

Since variables in BASIC initialize as 0, and 0 means FALSE (the light is “off”), the initial grid would be shown with all lights off. Congratulations. You win!

To make it a game, we need to have some or all of those lights on when the game starts. My initial attempts of just randomly setting some number of lights to on seemed to often produce puzzles that could not be solved — even when using lookup tables (i.e., “cheating”) that show the winning patterns. Are there unsolvable patterns in Lights Out???

I consulted the chat section of the BING search engine, and asked it…

Yes, there are some configurations of the Lights Out game that

– BING AI Chatcannot be solved. In fact, Marlow Anderson and Todd Feil proved in 1998 that not all configurations are solvable. However, it is important to note that these unsolvable configurations are not valid Lights Out puzzles.

Well then. Since unsolvable patterns are not valid for Lights Out, tust turning on random lights is not an acceptable way to start a game.

**Side Note: **Card games like Solitaire certainly can be unwinnable depending on the order of cards in the deck. I am pretty sure games like Minesweeper are like that, too. But Lights Out patterns are *always* supposed to be winnable.

It seemed if you started with all lights off, you could simply work backwards by toggling lights on randomly until you got to a stopping point. Whatever pattern that is, the player was assured that there was a way to win. Instead of just turning on specific lights, I could just select a square and allow it to do the normal toggle (that light, plus the adjacent lights).

For my version, I will just make a very simple “initialize game” routine that does this. To do this, I will use a FOR/NEXT loop and generate random X/Y coordinates and call the “toggle” subroutine. This should end up with a random pattern that is 100% solvable.

4000 REM INITIALIZE GRID

4010 PRINT "INITIALIZING..."

4020 FOR A=1 TO 10

4030 X=RND(5)-1

4040 Y=RND(5)-1

4050 GOSUB 3000

4060 NEXT

4070 RETURN

This is what it is doing:

**Line 4010**– Since the FOR/NEXT loop will cause a pause, I wanted to print out something to tell the player what was happening.**Line 4020**– A FOR/NEXT loop is done so the random grid selections can be done 10 times. (We’ll improve this later.)**Line 4030**– For X, RND is used to choose a number from 1 to 5. Since the DIM arrays are base-0 (0-4), we subtract 1 so Y will be a number form 0-4.**Line 4040**– Same thing, but for Y.**Line 4050**– We GOSUB to the routine that toggles the specific square (X,Y), and any adjacent squares.**Line 4060**– Continue the FOR/NEXT loop…**Line 4070**– We return back to wherever we were GOSUB’d from.

At the start of the game, we’d just “GOSUB 4000” to initialize the grid. Here is the complete code, so far:

10 REM INITIALIZE GRID

20 GOSUB 4000

30 REM SHOW GRID

40 GOSUB 1000

50 REM INPUT SQUARE

60 GOSUB 2000

70 REM TOGGLE SQUARES

80 GOSUB 3000

90 REM REPEAT

100 GOTO 30

1000 REM SHOW GRID

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)

3020 IF X>0 THEN L(X-1,Y)=NOT L(X-1,Y)

3030 IF X<4 THEN L(X+1,Y)=NOT L(X+1,Y)

3040 IF Y>0 THEN L(X,Y-1)=NOT L(X,Y-1)

3050 IF Y<4 THEN L(X,Y+1)=NOT L(X,Y+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

And it works! Even on a 1980 4K TRS-80 Color Computer (shown here, emulated in **XRoar**):

But the game still cannot be won because there is no code to check for a win ;-)

## Defining a “Win”

Since the goal of Lights Out is to turn all the lights out, winning means that the number of lights that are on is **zero**. One simple (but slow) way to test for this condition would be to scan through the entire grid and add up how many lights are on.

5000 REM COUNT LIGHTS ON

5005 LO=0

5010 FOR Y=0 TO 4

5020 FOR X=0 TO 4

5030 IF L(X,Y) THEN LO=LO+1

5040 NEXT

5050 NEXT

5060 RETURN

To check, that routine is called. If LO=0 then the game has been won. Since the routine counts the number of “on” lights,, the user could also be told how many lights are on. I would probably add this right after it draws the grid, so the final winning grid is shown before the game ends.

10 REM INITIALIZE GRID

20 GOSUB 4000

30 REM SHOW GRID

40 GOSUB 100045 REM CHECK FOR WIN50 REM INPUT SQUARE

46 GOSUB 5000

47 IF LO=0 THEN PRINT "YOU WON!":END

48 PRINT "LIGHTS ON:";LO

60 GOSUB 2000

70 REM TOGGLE SQUARES

80 GOSUB 3000

90 REM REPEAT

100 GOTO 30

For a small 5×5 grid, the CoCo seems plenty fast enough to do this check without much of a delay in between moves. If the machine were very slow, or the grid was very large (and thus, took much longer to check), this might not be the best approach.

Another option might be to start with a count of all lights that are on, and decrement that value each time a light is turned off. If a light is turned on, increment it. If the value reaches zero, the game has been won.

But how do we know which lights are on? We’d could just call our “count lights that are on” function after the grid is initialized and get that value.

Or, maybe we don’t use that routine at all. We could put the count routine directly in the routine that toggles the squares on and off:

3000 REM TOGGLE SQUARES

3010 L(X,Y)=NOT L(X,Y)

3020 IF X>0 THEN L(X-1,Y)=NOT L(X-1,Y)

3030 IF X<4 THEN L(X+1,Y)=NOT L(X+1,Y)

3040 IF Y>0 THEN L(X,Y-1)=NOT L(X,Y-1)

3050 IF Y<4 THEN L(X,Y+1)=NOT L(X,Y+1)

3060 RETURN

We could increment some variable every time a light is turned on, and decrement it any time a light is turned off.

It looks like our shortcut of using NOT is going to cause us more work. Since we aren’t manually setting the value to something meaning ON, and something else meaning OFF, we have no place add code to increment or decrement our Lights On count. Had we used 1 for on, and -1 for off, we could just add the value of the square (1 or -1) and been done.

But we didn’t.

We know after the **NOT** toggle, the value will be -1 or 0. Had it been 1 and -1, counting would be very straightforward. But with -1 and 0, we need to somehow make the -1 (meaning TRUE, a light was turned on) be an increment (+1), and the 0 (meaning FALSE, a light was turned off) to be a decrement (-1).

**Side Note:** There are many ways to write a program. In this one, I chose an easy way to toggle the lights. At the time, I was not considering counting the lights that were on. This NOT approach adds a bit more work to do the count. It could be that adding extra work here might be larger/slower than just doing a count routine that scans the whole grid. Depending on if we are going for speed or memory usage, the design decisions may need to be different.

Mapping this out, I find a simple solution. I can multiply the value (-1 or 0) by 2, and then add 1.

- If the value is -1, multiplying by 2 will make it -2. Then adding 1 will make it -1.
- If the value is 0, multiplying by 2 will be 0. Then adding 1 will make it 1.

ON: -1* 2= -2+ 1= -1

OFF: 0* 2= 0+ 1= 1

Well, that gets us a +/- 1, but it is backwards from what I would have preferred. To work around that, I can **subtract** the value. If you subtract -1, it is like adding one. And if you subtract 1, it is like adding a -1. Easy! :)

The updated routine looks like this:

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

Yuck. But hey, it works!

Now, instead of calling the “count all the squares” routine, we can just reset the LO “lights on” variable during the initialization routine, and it should be decremented/incremented as the game plays.

To test, let’s comment out the call that counts the squares:

10 REM INITIALIZE GRID

20 GOSUB 4000

30 REM SHOW GRID

40 GOSUB 1000

45 REM CHECK FOR WIN46 REM GOSUB 5000

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

I did a quick test, and this appears to work. It is also faster, since it bypasses the small delay that was happening each time it called the “count all 25 squares” routine. Nice.

Again, the overhead of adding the count in five locations (about 21 bytes each, so 100 bytes extra) does appear to be larger than the “count all the squares” routine. If code size were more important than speed, the count routine would make more sense.

But so far, it still fits on my 1980 4K CoCo with 1514 bytes left to spare (and more room once I remove the un-used “count all the squares” routine). We can add more code! :)

We now have a very primitive but functional version of Lights Out that should guarantee each “random” pattern can be won. The “random” patterns should keep someone from just learning the moves and repeating them to win the level if they play it over and over.

Except they may not be as random as we think.

To be continued…

William AstleAs a side note, for your count lights routine you could have used a similar trick to what you did for the running tally variation. That is, you could have just subtracted the array entry from your tally and not needed an IF at all. I only mention it in case someone else wants to do a similar tally. However, I’d stick with the running count you have implemented instead. It’s going to be much better on average.

Allen HuffmanPost authorOh yeah, that makes sense. I think Rick just counted in the display routine, since you always have to display anyway. I am not sure if I ended up doing that in mine in later parts.

Jason PittmanWow, I’ve never played it. It’s maddening!!! I thought it might make it easier for me to know what X/Y I wanted if I printed a header for them. Also, I think you could eliminate the IF/GOTO on line 1030 by just printing a semigraphics character and letting the L value either make it print CHR$(159) (a yellow square for “on”) or CHR$(160) (a black square for “off”). I’ll definitely be playing around with this more later.

`1000 REM SHOW GRID`

1010 PRINT " 01234"

1020 FOR Y=0 TO 4

1030 PRINT Y;

1040 FOR X=0 TO 4

1050 PRINT CHR$(L(X,Y)+160);

1060 NEXT

1070 PRINT

1080 NEXT

1090 PRINT

1100 RETURN

Allen HuffmanPost authorIt will indeed evolve to printing headers, but no demographics since by part 7 it runs on VIC too. But, once the logic is done it needs much better than “0,3” for input.

Jason PittmanAhh…I forgot all about VIC 20 when I was playing around with it. And I’m pretty sure that I won’t need the routine to see if I’ve won, because I’ll never win this one, but it’s a fun little game!

Allen HuffmanPost authorThere are some basic patterns explained on the wiki page. You can “chase” the lights from top to bottom, and then based on the final pattern, you start over with some sequence and do it again and can win. But memorizing that, man, I don’t know.

Allen HuffmanPost authorWhen the final engine is posted, do you want to take a stab at enhancing it for CoCo? Joystick or better keyboard controls, semi graphics, etc.?