Category Archives: Color BASIC

What are the odds?

Back in the early 1980s, a friend of mine had TRS-80 Model III in their house. His mother was a writer, and had gotten it to use for writing books.

I believe it was this TRS-80 where I saw a version of Monopoly that taught me a strategy I had never learned as a kid:

The computer bought EVERYTHING it landed on, even if it had to mortgage properties to do so.

This simple strategy was a gamble, since you could end up with too many mortgaged properties and no source of income from rent. But. by doing this, the computer would end up owning so many random pieces it made it difficult to own a monopoly yourself.

And since then, that is how I played Monopoly. When it worked, it created some awfully long games (if no one had a monopoly to quickly drive other players bankrupt when they landed on their hotels).

In more modern times, I have watched YouTube videos concerning Monopoly strategies. They will break down the statistical odds of landing on any particular piece of property. For example, you know a pair of dice can produce values from 2 to 12. If truly random, the odds of getting a “1 and 1” should be the same as getting a “3 and 5.” Rather than focus on dice rolls, the strategies take into consideration things that alter locations: Go To Jail, Advance to Go, Take a Ride on Reading, etc.

These cards existing means there are more chances for a player to end up on Go, Reading Railroad, Jail, etc. This means the property squares after those spots have more chances of being landed on.

Board with board games. Move along…

But I digress… As Internet Rabbit Holes do, this led me to watch other videos about statistics in things like card games. In a randomly shuffled deck, there should be as much chance for the first card to be Ace of Spaces as there is for it to be Three of Clubs. It is random, after all.

For that first card drawn, that is a 1 out of 52 chance to be any card in the deck. (52 cards in the deck.)

But as a game plays on, there are fewer cards, so the odds of getting any of the remaining cards increases. For the second card drawn, you now know there is a 0% chance of getting whatever the first card is, and a 1 in 51 chance of getting any of the rest.

And so it continues…

For games like Blackjack or 21, you do not really care if it is a Ten of Diamonds or a King of Hearts or a Queen of Clubs or a Jack of Spades. They all have the value of 10 in the game. Thus, the likelihood of drawing a ten card is much higher than any other card in the deck.

You have four suits (clubs, spades, hearts, diamonds) so there are four of each card – Aces, Two through Ten, Jacks, Queens, and Kings. This means there are 16 cards in the deck that could be a value of 10 in the game. When you draw the first card, you should have a 16 in 52 chance of it being a ten card. That is about a 33% chance!

If you pay attention to what cards have been seen (either by you having it, or seeing it face up with another player), you can eliminate those cards from the possibilities — changing the odds of what you will get.

This is basically what I understand card counting to be. If you play a game, and you know you’ve seen three Kings so far (either in your hand, or played by others), you now know instead of four chances to draw a King, you only have one.

Math is hard. Make the computer do it.

I know this has been done before, and quite possible even on a Radio Shack Color Computer, but I thought it might be fun to create a program that displays the percentage likelihood of drawing a particular card from a deck. I suppose it could have the Blackjack/21 rule, where it treads 10, Jack, Queen and King the same, versus a “whole deck” rule where each card is unique (where you really need an 8 of Clubs to complete some run in poker or whatever game that does that; I barely know blackjack, and have never played poker).

I plan to play with this when I get time, but I decided to post this now in case others might want to work on it as well.

I envision a program that displays all the cards on the screen with a percentage below it. As cards are randomly drawn, that card’s percentage goes to 0% and all the others are adjusted.

It might be fun to visualize.

More to come, when time allows…

Counting characters in a string in Color BASIC

And so it begins…

I want to visualize some playing card statistics, and the easiest way I could think of to do this was to write a program in BASIC. Of course.

There are many approaches I can take, so as I put figure it out I will “show my work” and experiments that help me figure out which one will work best.

For my task, I will greatly simplify a deck of cards by ignoring suits (I just care if it’s an Ace, a Five, a King or whatever). I figure I can store it as a 52-character string representing each card:

A123456789JQKA123456789JQKA123456789JQKA123456789JQK

I could “shuffle” this “deck” and end up with a string in “random” order. I believe I have touched on randomizing a list in the past, and had some great suggestions in comments on how to do it better. Forgetting all of that, let’s just say I end up with as string that gets randomized.

But I digress…

The next question will be: “How many Kings are left in the deck?”

Some of you will already see I am heading down a poor path for doing this, but let’s start there anyway.

One way of counting a specific character in a string is to loop through it and use MID$ to pull out an individual character. Something like this:

FOR A=1 TO LEN(A$)
IF MID$(A$,A,1)="S" THEN C=C+1
NEXT

That would count all the “S” characters that appear in the string. Since every time MID$ is used it has to build a new string representing that portion of the original string, this should be our slowest way to do this. On a system with tons of strings in use, string manipulation gets real slow. For such a simple program, this might be fast enough.

Another approach, which was originally shown to me during a word wrap article as a submission, would be to use VARPTR to get the memory location of the 5-byte string ID memory, and then go to the memory where the string bytes are stored and use PEEK to look for them. You can find details in my earlier article on VARTPR.

The memory location that VARPTR returns will have the length of string as the first byte (byte 0), then an empty byte (byte 1, always 0), then the next two bytes will be the address where the string is stored (bytes 2 and 3) followed by a zero (byte 4). Knowing this, something like this would do the same thing as MID$:

A=VARPTR(A$)
SL=PEEK(A)
SS=PEEK(A+2)*256+PEEK(A+3)
FOR A=SS TO SS+SL-1
IF PEEK(A)=ASC("S") THEN C=C+1
NEXT

And do it faster.

VARPTR is a legal BASIC function, but it still seems nasty to reach in to string memory to do this. Thus, I came up with the idea of using INSTR. This function returns the start location of a matching string in another string, or 0 if not found:

PRINT INSTR("ABCDEFG","D")

That should print 4, since a “D” is located at the 4th position in the string.

You can also add an additional parameter which is where in the string to start searching. Doing this:

PRINT INSTR(5,"ABCDEFG","D")

…would print 0, because it starts scanning at the 5th character (just past the D) of the string, and then won’t find anymore.

I could start using INSTR with a position of 1 (first character), and if it comes back with a value other than 0, I found one. That value will be the position of the found character. I could then loop back and use that position + 1 to scan again at the character after the match. Repeat until a 0 (no more found) is returned. That lets the scan for characters be done by the assembly code of the BASIC ROM and is even faster. It looks like this:

F=0
xxx F=INSTR(F+1,A$,T$):IF F>0 THEN C=C+1:GOTO xxx

And we put them all together in a benchmark test program…

10 ' COUNTSTR.BAS
20 A$="THIS IS A STRING I AM GOING TO USE FOR TESTING. I WANT IT TO BE VERY LONG SO IT TAKES A LONG TIME TO PARSE."
30 T$="S":T=ASC(T$)
40 '
50 ' MID$
60 '
70 PRINT "MID$:";TAB(9);
80 TIMER=0:C=0
90 FOR A=1 TO LEN(A$)
100 IF MID$(A$,A,1)=T$ THEN C=C+1
110 NEXT
120 PRINT C,TIMER

130 '
140 ' VARPTR
150 '
160 PRINT "VARPTR:";TAB(9);
170 TIMER=0:C=0
180 A=VARPTR(A$)
190 SL=PEEK(A)
200 SS=PEEK(A+2)*256+PEEK(A+3)
210 FOR A=SS TO SS+SL-1
220 IF PEEK(A)=T THEN C=C+1
230 NEXT
240 PRINT C,TIMER

250 '
260 ' INSTR
270 '
280 PRINT "INSTR:";TAB(9);
290 TIMER=0:C=0:F=0
300 F=INSTR(F+1,A$,T$):IF F>0 THEN C=C+1:GOTO 300
310 PRINT C,TIMER

And running prints:

Wow, using INSTR is six times faster than MID$? And four times faster than VARPTR. Nice.

Now you know a bit about what I need to do. I need to represent cards in a deck (and be able to “draw” cards from that deck) and calculate how many of a specific card remain in the deck.

Since I do not need to track or show the suits (hearts, spaced, clubs, diamonds), I figure I could use one byte in a string.

To be continued … but in the meantime, do you have a better approach? Comment away!

BASIC and many input options…

Earlier this year, Jason Pittman shared a BASIC program with me that drew representations of all the letters of the alphabet. It was a cute program, and did some fancy drawing.

To select a letter, there was this block of code:

130 PMODE3,1:A$=INKEY$
140 IFA$="A"THEN410
150 IFA$="B"THEN510
160 IFA$="C"THEN590
170 IFA$="D"THEN680
180 IFA$="E"THEN750
190 IFA$="F"THEN820
200 IFA$="G"THEN900
210 IFA$="H"THEN1020
220 IFA$="I"THEN1100
230 IFA$="J"THEN1130
240 IFA$="K"THEN1220
250 IFA$="L"THEN1340
260 IFA$="M"THEN1410
270 IFA$="N"THEN1490
280 IFA$="O"THEN1560
290 IFA$="P"THEN1680
300 IFA$="Q"THEN1780
310 IFA$="R"THEN1890
320 IFA$="S"THEN2020
330 IFA$="T"THEN2100
340 IFA$="U"THEN2190
350 IFA$="V"THEN2250
360 IFA$="W"THEN2360
370 IFA$="X"THEN2440
380 IFA$="Y"THEN2490
390 IFA$="Z"THEN2550
400 GOTO130

I thought it might be fun to ask you — in the comments — to tell me how YOU would have done this. I can think of one way, that uses an Extended BASIC keyword, and another way, that would work on Color BASIC.

For a series of options that are sequential (like “A to Z”) there are certainly some options.

As a part two … what if they were not sequential? What if it was for a menu that had options like “A, B, C, D, Q, Z” or whatever? That let me think of a third way to do it to work in Color BASIC.

Comment away!

Let’s write Lights Out in BASIC – part 7

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

Code cleanup in aisle 4.

Let me begin this installment by presenting the current Lights Out code with some renumbering done and a few minor improvements.

0 REM LITESOUT-P7.BAS

10 REM SETUP
20 DIM L(9,9)

100 REM START NEW GAME
110 GOSUB 5000
120 REM INITIALIZE GRID
130 MV=0
140 GOSUB 4000
150 REM SHOW GRID
160 GOSUB 1000
170 REM CHECK FOR WIN
180 IF LO=0 THEN 300
190 REM INPUT SQUARE
200 GOSUB 2000
210 REM TOGGLE SQUARES
220 GOSUB 3000
230 REM REPEAT
240 MV=MV+1
250 GOTO 150

300 REM GAME WON
310 PRINT "YOU WON IN";MV;"MOVES."
320 INPUT "PLAY AGAIN (Y/N)";Q$
330 IF Q$="Y" THEN 100
340 PRINT "GAME OVER"
350 END

1000 REM SHOW GRID
1010 PRINT "GAME NUMBER:";GN
1020 PRINT " ";
1030 FOR A=1 TO GS
1040 PRINT RIGHT$(STR$(A),2);
1050 NEXT:PRINT
1060 FOR Y=0 TO GS-1
1070 PRINT RIGHT$(STR$(Y+1),2);" ";
1080 FOR X=0 TO GS-1
1090 IF L(X,Y) THEN PRINT "X ";:GOTO 1110
1100 PRINT ". ";
1110 NEXT
1120 PRINT
1130 NEXT
1140 PRINT "MOVES:";MV;"LIGHTS ON:";LO
1150 RETURN

2000 REM INPUT SQUARE
2010 S$=MID$(STR$(GS),2):PRINT "X,Y (1-";S$;",1-";S$;" OR 0,0)";
2020 INPUT X,Y
2030 IF X=0 THEN IF Y=0 THEN 320
2040 IF X<1 OR X>GS OR Y<1 OR Y>GS THEN 2010
2050 X=X-1:Y=Y-1
2060 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<GS-1 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<GS-1 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 INPUT "GRID SIZE (3-10, ENTER=5)";GS
4020 IF GS=0 THEN GS=5
4030 IF GS<3 OR GS>10 THEN 4010
4040 PRINT "INITIALIZING..."
4050 FOR A=1 TO 10*GS
4060 Y=RND(GS)-1
4070 X=RND(GS)-1
4080 GOSUB 3000
4090 NEXT
4100 RETURN

5000 REM SELECT GAME
5010 PRINT "PLAY SPECIFIC GAME # (Y/N)?"
5020 S=S+1:A$=INKEY$:IF A$="" THEN 5020
5030 IF A$="Y" THEN 5070
5040 IF A$="N" THEN A=RND(-S)
5050 GN=RND(65535):A=RND(-GN)
5060 GOTO 5100
5070 INPUT "PLAY GAME (1-65535)";GN
5080 IF GN<1 OR GN>65535 THEN 5060
5090 A=RND(-GN)
5100 RETURN

As I tested this version, I noticed my random grid generator was not very good–at least on a large 10×10 grid. It really needed to do more random light toggling for larger grids. This change would go in this line – maybe just doing a “grid size * X”, like “10*GS” or something. I am still trying things, but for now I made it do more random toggles the larger the grid size is (line 4050).

I also made it so the default grid size was 5 (line 4010).

And I fixed some output where it printed a numeric variable. In Color BASIC, there is an extra space printed before and after printing a number:

PRINT "*";42;"*"
* 42 *

And, technically, it’s not a space before a number — it’s a place for the sign (none if positive, “-” if negative):

PRINT "*";-42;"*"
*-42 *

Since I knew I was only printing a positive number, I just got rid of that leading space, and the one after it. I did this by converting the number to a string using STR$. That will convert it to a string with the first space (a place for the sign) but no space after it:

PRINT "*";STR$(42);"*"
* 42*

Then all I needed to do was trim off that leading space. In the old days, I would have done this by getting the length of the string using LEN$ and then using RIGHT$ to get just the character(s) after that leading space:

N=42
N$=STR$(N)
L=LEN(N$)
PRINT RIGHT$(N$,L-1)

But in recent years, someone commented on this site (I believe) about how you could use MID$ without a size and just specify the starting position in a string, and it would return everything from that position to the end:

N=42
PRINT MID$(STR$(N),2)

Nice! “Wish I knew then what I know now…”

But I digress…

We now have a fairly “feature complete” version of Lights Out that runs in very basic BASIC — it doesn’t even make use of ELSE.

Before I move on to making this look a lot nicer on a CoCo, I thought it might be fun to port it to a VIC-20. I made use of the wonderful CBM prg Studio Windows-based tool that can build BASIC or assembly programs for all kinds of Commodore machines. I just pasted in my source, and then found a few minor things I would need to change to run on the VIC-20’s CBM BASIC.

The first thing I did after pasting the code in to the editor was to convert it all to lowercase. That seems to be how that BASIC works, as SHIFT characters turn in to PETSCII graphics characters.

Now I would be able to build the program.

It would build right away, but not run on the VIC yet until I fix two things that are different between Color BASIC and CBM BASIC:

  • A$=INKEY$ must be changed to GET A$
  • RND(x) must be changed to INT(RND(1)*X)-1 (Doing the random seed using RND(-X) works the same way on the VIC, and could be left alone.)
CoCo:
5020 S=S+1:A$=INKEY$:IF A$="" THEN 5020

VIC:
5020 S=S+1:GET A$:IF A$="" THEN 5020

CoCo:
4060 Y=RND(GS)-1

VIC:
4060 Y=INT(RND(1)*GS)

CoCo:
4070 X=RND(GS)-1

VIC:
4070 X=INT(RND(1)*GS)

After that, I tried to RUN it and ran in to a problem with INPUT:

From testing, it seems that long prompts on INPUT cause issues, possibly related to how the VIC’s full screen editor works. I sent a tweet to 8-Bit Show and Tell to see what he knew, and did a simple workaround by breaking up the prompt and the input on separate screen lines:

4000 rem initialize grid
4010 print "grid size (3-10, enter=5)":input gs

I thought I might have the same issue with the “select square” input, but that prompt does not overlap to the next line so it worked… or so I thought. With the default grid size of 5×5, the prompt looked like:

X,Y (0-5,0-5 OR 0,0)?(space)
*(cursor here)

That seemed to work just fine, but when I did a grid of 10×10, it made the prompt longer, wrapping the “?” of INPUT to the next line:

X,Y (0-10,0-10 OR 0,0)
? *(cursor here)

So I needed to fix that, too. I had already had to break that one up in to a PRINT and an INPUT since I was printing variables in the prompt, so I just took the semicolon off from the end of the PRINT:

2000 rem input square
2010 s$=mid$(str$(gs),2):print "x,y (1-";s$;",1-";s$;" or 0,0)"
2020 input x,y

Now I had what appeared to be a working version of the game for VIC-20. BUT, it would need some work to reformat the prompts to fit on a 22-column screen, versus the 32- column CoCo screen. For anyone who wants to work on that, here is the VIC-20 version with the lines in bold that have changes from the CoCo version:

0 rem litesout-p7.bas

10 rem setup
20 dim l(9,9)

100 rem start new game
110 gosub 5000
120 rem initialize grid
130 mv=0
140 gosub 4000
150 rem show grid
160 gosub 1000
170 rem check for win
180 if lo=0 then 300
190 rem input square
200 gosub 2000
210 rem toggle squares
220 gosub 3000
230 rem repeat
240 mv=mv+1
250 goto 150

300 rem game won
310 print "you won in";mv;"moves."
320 input "play again (y/n)";q$
330 if q$="y" then 100
340 print "game over"
350 end

1000 rem show grid
1010 print "game number:";gn
1020 print " ";
1030 for a=1 to gs
1040 print right$(str$(a),2);
1050 next:print
1060 for y=0 to gs-1
1070 print right$(str$(y+1),2);" ";
1080 for x=0 to gs-1
1090 if l(x,y) then print "x ";:goto 1110
1100 print ". ";
1110 next
1120 print
1130 next
1140 print "moves:";mv;"lights on:";lo
1150 return

2000 rem input square
2010 s$=mid$(str$(gs),2):print "x,y (1-";s$;",1-";s$;" or 0,0)"
2020 input x,y
2030 if x=0 then if y=0 then 320
2040 if x<1 or x>gs or y<1 or y>gs then 2010
2050 x=x-1:y=y-1
2060 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<gs-1 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<gs-1 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 "grid size (3-10, enter=5)":input gs
4020 if gs=0 then gs=5
4030 if gs<3 or gs>10 then 4010
4040 print "initializing..."
4050 for a=1 to 10*gs
4060 y=int(rnd(1)*gs)
4070 x=int(rnd(1)*gs)
4080 gosub 3000
4090 next
4100 return

5000 rem select game
5010 print "play specific game # (y/n)?"
5020 rem s=s+1:a$=inkey$:if a$="" then 5020
5021 s=s+1:get a$:if a$="" then 5020
5030 if a$="y" then 5070
5040 if a$="n" then a=rnd(-s)
5050 gn=rnd(65535):a=rnd(-gn)
5051 rem gn=int(rnd(1)*65535)-1:a=rnd(-gn)
5060 goto 5100
5070 input "play game (1-65535)";gn
5080 if gn<1 or gn>65535 then 5060
5090 a=rnd(-gn)
5100 return

The more I look at this code, the more optimizations I want to make to it.

But for now, that is a version for CoCo, and a version for Commodore. It should be easy to port to other flavors of BASIC. Let me know if you port it to something.

Now that I know how to write the game, the next goal will be to make it look a lot better on the CoCo (and maybe VIC-20 as well, if I feel ambitious).

But I think I need a brake from Lights Out for a bit…

Until then…

Let’s write Lights Out in BASIC – part 6

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

At this point, we have a nicely functional BASIC version of Lights Out. Some of its features include:

  • 5×5 grid of lights.
  • Random games.
  • Allows replaying a specific game.
  • Counts moves for scoring.
  • Ability to quite a game in progress.
  • Slightly less sucky user interface than the original version.

But we still have more to do!

Bigger grid, please

One of the enhancements I mentioned in the previous installment was the ability to specify larger (or smaller, I suppose), grid sizes. We know that the official Lights Out games from Tiger Electronics in the 1990s used a 5×5 and 6×6 grid. Others have since created variations, such as one that operates around a cube (maybe we write that version some day). Let’s start there…

For this simple version, we’ll just arbitrarily pick a maximum grid size of 10×10. This should still fit easily on the CoCo’s 32×16 text screen. For fun, we could also allow a grid size smaller than 5×5 to be chosen. Since the spiritual predecessor of Lights Out was a 1970s game that featured a 3×3 grid, I’ll use that size as the minimum.

The changes needed should be fairly minor. When starting a game, the user will be prompted for the grid size.

It seems we could allow rectangular grids (5×3, 10×5, etc.), but for now we’ll just stick to square grids where both sides are the same size. The array for the grid just needs to be large enough to hold the largest sized grid.

Wrong DIMension

An oversight I made when starting this program was not adding a DIM statement to specify the maximum size of the two-dimensional grid array! The current version really needs a line that contains this at the very top:

DIM L(4,4)

Reminder: DIM is base-0, so it starts counting entries at 0. A DIM X(4) gives entries X(0), X(1), X(2), X(3) and X(4).

The program only works because Color BASIC allows array entries 0-10 before the DIM is needed. You can do A(10)=42 without needing to DIM A(10) first. (I find it weird that Microsoft chose to default to 11 array entries. I bet whoever coded that was thinking “1-10” rather than “0-10”.)

To enhance the program to support up to a 10×10 grid, we’ll allocate an array that large.

4 DIM L(9,9)

Next we need to ask the user what size grid they want. This can be added to the “initialize grid” subroutine:

4000 REM INITIALIZE GRID
4005 INPUT "GRID SIZE (3-10)";GS
4006 IF GS<3 OR GS>10 THEN 4005
...

After that, any place that is currently hard coded to 4 will need to be changed to use the grid size variable. If the user types in 10 (for a 10×10), the array will be using 0-9. We must pay attention to that and know that a 10 grid is actually 0-9.

This is also a good time to clean up the printing of the grid a bit. Here is the full program with the latest changes in bold:

4 DIM L(9,9)
5 REM SELECT GAME
6 GOSUB 6000
10 REM INITIALIZE GRID
15 MV=0
20 GOSUB 4000
30 REM SHOW GRID
40 GOSUB 1000
47 IF LO=0 THEN 200
50 REM INPUT SQUARE
60 GOSUB 2000
70 REM TOGGLE SQUARES
80 GOSUB 3000
90 REM REPEAT
95 MV=MV+1
100 GOTO 30

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

1000 REM SHOW GRID
1005 PRINT "GAME NUMBER:";GN
1006 PRINT " ";
1007 FOR A=1 TO GS
1008 PRINT RIGHT$(STR$(A),2);
1009 NEXT:PRINT
1010 FOR Y=0 TO GS-1
1015 PRINT RIGHT$(STR$(Y+1),2);" ";
1020 FOR X=0 TO GS-1
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

2000 REM INPUT SQUARE
2010 PRINT "X,Y (1-";GS;",1-";GS;" OR 0,0)";
2011 INPUT X,Y
2015 IF X=0 THEN IF Y=0 THEN 230
2020 IF X<1 OR X>GS OR Y<1 OR Y>GS THEN 2010
2025 X=X-1:Y=Y-1
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<GS-1 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<GS-1 THEN L(X,Y+1)=NOT L(X,Y+1):LO=LO-(L(X,Y+1)*2+1)
3060 RETURN

4000 REM INITIALIZE GRID
4005 INPUT "GRID SIZE (3-10)";GS
4006 IF GS<3 OR GS>10 THEN 4005
4010 PRINT "INITIALIZING..."
4020 FOR A=1 TO 10
4030 Y=RND(GS)-1
4040 X=RND(GS)-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

This version will display the grid with nicely (?) formatted headers for the columns and rows, and less nicely formatted prompts for what values are allowed.

There is still so much work to do to make the user interface nicer to look at and interact with.

To be continued…

Let’s write Lights Out in BASIC – part 5

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…

Let’s write Lights Out in BASIC – part 4

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

Where where we?

Oh, right. Light Out, in very basic BASIC.

If Lights Out always started with the same lights on, once you figured out how to win, you could repeat those steps and win every time. This reminds me of a wooden golf tee game I was given as a child. (If you have ever eaten at a Cracker Barrel restaurant, you probably have seen this — they have/had one on every table.)

I never learned how to win that game intentionally, but sometimes I would get lucky. This made visits to Cracker Barrel sometimes frustrating. The game had a listing of rankings based on how many pegs you had left at the end of the game. I was often an “EG-NO-RA-MOOSE.” :)

But then the iPhone happened in 2007, and a year later there was an App Store and you could play games on the phone! I found a peg game app and decided to learn and memorize the steps that win the game. I took screen shots of every step so I could consult them later. Here is one of those tiny 320×480 screen shots taken in 2008 on my original 2007 iPhone:

While I no longer remember those steps, I did for awhile, and visits to the Cracker Barrel were never the same. I was the peg game master! But soon it become boring and not worth playing since I knew how to win it every time.

Oh well. At least the collard greens and fried okra were still great.

Side Note: During research for this article, I found that Cracker Barrel has a blog post with the history of the peg game, as well as all the steps to solve it.

Blog post on the steps that solve the peg game every time.

But I digress…

“Random” game variations

The current program has an initializing routine which turns random lights on. But does it really?

As I wrote in an earlier article about RND, RND is not really random. On my CoCo, each time I turn it on, I can print some random numbers. Each time I power cycle and try it again, I will get those same “random” numbers. Here is an example of the first eight random numbers between 1 and 50 on the CoCo:

You get these same random numbers every time you power up the CoCo.

Any programmer that writes something that relies on RND to generate levels or patterns should be aware that it will make the same level or pattern each time the program runs from a fresh power up. BUT, in Color BASIC, you can seed the random number generator to change that pattern. It works by passing in a negative value to RND. When you do that, it seeds the number generator to a specific and repeatable sequence for that seed value.

If you run this code on a CoCo, you will see it print the same set of eight random numbers ten times:

10 FOR A=1 TO 10
20 B=RND(-42)
30 FOR B=1 TO 8
40 PRINT RND(50);:
50 NEXT
60 PRINT
70 NEXT

This means that if I powered up my computer and ran this Lights Out program, I would get the same pattern the next time I powered up and ran the program.

This is probably not a problem that really needs to be solved, but since there is a simple solution, it makes for a good excuse to discuss it.

Seeding the random number generator

In Extended Color BASIC, there is a TIMER function that starts at 0 on power up, then increments every 60th of a second until it rolls over and starts again from 0. Since the amount of time between power up, typing in “CLOAD” or whatever, and “RUN” will vary, using that value makes for a simple seed:

A=RND(-TIMER)

Just doing something like that at the start of the program should be enough to reduce chances that the patterns are not the same each time the game is played from a fresh start.

BUT, Color BASIC does not have TIMER. It would need a different approach to creating that number. If the program has a title/splash screen, it might sit in a loop waiting for the user to press a key to start. Something like that could be used to create a seed number:

PRINT "PRESS ANY KEY TO BEGIN:"
xx S=S+1:IF INKEY$="" THEN xx
A=RND(-S)

It is not perfect since if the user types RUN and quickly hits a key, there is a good potential that the program would be using one of only a few seeds based on the low numbers that the counter got to. But, hey, better than nothing… And that’s something.

We could also keep incrementing that S variable inside the main program loop. If the user chose to play again (our program does not offer that feature, yet), the value could be used to create more random patterns. This is probably overkill, but it would be simple to add.

Predictable randomness could be a feature…

I recall some game that came with Microsoft Windows (maybe a Solitaire, or perhaps Minesweeper) that had a spot where you entered a “game number” or something. This let you replay a specific game. I did not realize it at the time, but this was probably just seeding a random number generator so the order of the cards or the mines (whatever game it was) would be the same for those that wanted to try again.

Update: I was close! It was apparently FreeCell for Windows 95. I asked BING’s AI and it told me:

I believe the game you are referring to is FreeCell. FreeCell is a solitaire card game that was first introduced in Windows 95. It is a popular game that allows players to replay specific games by entering a “game number” 1If you’re interested in playing FreeCell, you can download it from the Microsoft Store 1.

I hope this helps!

– BING AI

Thank you, large language model.

But I digress…

The game could try to randomize, but could also allow the user to pick a “game number” that would be repeatable. That could be a fun feature, since players could use it to practice different strategies.

It could be as simple as something like this (for Extended Color BASIC which has the TIMER function):

xx INPUT "PLAY GAME (1-65535, 0=RND)";S
IF S<0 OR S>65535 THEN xx
IF S=0 THEN S=TIMER
A=RND(-S)

Or if TIMER was not available, it could prompt the user then sit in a loop counting and use the count value as the seed. This is not a great approach, but might work…

PRINT "PLAY SPECIFIC GAME # (Y/N)?"
xx S=S+1:A$=INKEY$:IF A$="" THEN xx
IF A$="Y" THEN yy
IF A$="N" THEN A=RND(-S):GOTO zz
GOTO xx
yy INPUT "PLAY GAME (1-65535)";S
IF S<1 OR S>65535 THEN yy
A=RND(-S)
zz RETURN

I decided to add it to the program as a subroutine that can be called before the main game loop:

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):GOTO 6090
6050 GOTO 6020
6060 INPUT "PLAY GAME (1-65535)";S
6070 IF S<1 OR S>65535 THEN 6060
6080 A=RND(-S)
6090 RETURN

I could just call this before the game begins:

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

This would be simple enough to allow the user to replay a specific game. Here is a screen shot showing two instances of the XRoar emulator running the program, and generating the same pattern by using the same random seed:

This still is not 100% what we might want. While it does allow the user to request a specific game over and over, if they choose a random game, there is no way to get back to that game — it was chosen randomly.

To do that, we’d pick a random number for the “game number”, then use that as the seed value and display it as part of the game (so the user knows which number to use next time). We would still need to do some initial random seeding at the start so the game did not choose the same pattern of game numbers.

Here is a modification to the “select game” routine with some variable changes (GN for game number):

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

Then, we could print the game number before it prints the grid each time:

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

Now I can have the program generate a “random” game for me, and use that game number later to get the same puzzle.

At this point, we have a fairly playable (though ugly) version of a 5×5 Lights Out game.

There are a few more “nice to haves” we can add, so we’ll do those in the next installment.

Until then…

Let’s write Lights Out in BASIC – part 3

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 cannot 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.

– BING AI Chat

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 1000
45 REM CHECK FOR WIN
46 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

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 WIN
46 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…

The 9 colors of the CoCo’s high resolution screens…

I was today year’s old when I realized the CoCo’s high-resolution color values actually make sense.

Let me just get this right out in the open…

“I feel dumb I never realized the PMODE colors in the manual were correct.”

– Allen

You all probably knew this. If I did, I forgot I knew it. And I really don’t think I ever knew it. Because dumb.

My first CoCo was a grey TRS-80 Color Computer that I got around 1984. Today we call it a “CoCo 1” but back then it was the only CoCo.

The CoCo had eight colors (nine if you count black) that it could display:

Color Adjustment Test Display, Radio Shack TRS-80 Color Computer Quick Reference Guide, page 55.

The colors are listed as:

  • 0 – Black
  • 1- Green
  • 2 – Yellow
  • 3 – Blue
  • 4 – Red
  • 5 – Buff (I always called it white)
  • 6 – Cyan
  • 7 – Magenta
  • 8 – Orange

When you use the high resolution graphics (PMODEs), you have a choice of a higher high-resolution with 2 colors, or lower high-resolution with 4 colors. Each of these modes has two color sets, which select which 4 or 2 colors you can use on the screen. Here is how the Extended Color BASIC manual describe the PMODEs:

There are some high resolution graphics commands that let you specify a color — such as the COLOR, CIRCLE and DRAW — let you specify a color value to use. I found it odd that the color value was listed as “0-8” — as if you could use nine colors on a high resolution screen. You can’t! Only four, or two.

But the manual listed nine colors, anyway:

The first dumb thing I never realized until today was those high-resolution color numbers are the same as the ones for the text screen, as used by text commands CLS and SET. As I previously wrote about, I also did not realize that the colors available on the high resolution screens were actually the same as the ones on the text screen. I’d never seen them at the same time, and they always “looked different” to me.

But why did Microsoft let you specify color values 0-8 for a screen that can only have four colors? When I started playing with the CoCo again as a grown-up, I assumed Extended Color BASIC must have been ported from a system with more colors and they just left it as-is.

But the manual was weird about it. For example, it would tell you use use colors 5, 6, 7 and 8!

“If you want to try changing the dots’ colors, use buff (5), cyan (6), or magenta (7). Then change the color back to orange (8) before proceeding…”

– Extended Color BASIC manual, page 86

But I knew back then that this was just silly. If you used colors 1, 2, 3 and 4 in one mode, you could also use colors 1, 2, 3 and 4 in the other color set. You’d get the four unique colors in each mode, just different colors based on the mode. (Or you could use 0, 1, 2 and 3 just shifting the order over.)

The important part of the manual was listing which four colors you got when you used SCREEN to specify the color set to use:

SCREEN 1,0 gave you the green/yellow/blue/red colors on a green screen with green border, and SCREEN 1,1 gave you the buff/cyan/magenta/orange colors on a buff screen with a buff border.

Then, there were the higher high-resolution modes that only had two colors. The manual mentioned them (and the four color modes) later:

I guess they were introducing things slowly as you progressed through the chapters.

In a 2-color mode, SCREEN 1,0 gives you a green border with black background and you can draw in green. SCREEN 1,1 gives you a black screen with a white background and you can draw in white.

And my mind was blown when I realized that, though you could use 1-4 on any 4-color screen, or 1-2 on any 2-color screen, and get different colors, the way the manual tried to teach us was basically: “In this mode, use colors A to B. In that mode, use colors C to D” and so on.

Behold! PMODE 3,1:SCREEN 1,0 5-color mode showing what it draws for all 9 colors (0-8):

If you use the color chart, you can see you should be using colors 1-4:

  • 1- Green
  • 2 – Yellow
  • 3 – Blue
  • 4 – Red

If you go to PMODE 3,1:SCREEN 1,1 you get these colors:

And here is why the manual told you to use colors 5-8:

  • 5 – Buff
  • 6 – Cyan
  • 7 – Magenta
  • 8 – Orange

And in the 2-color mode, if you went PMODE 4,1:SCREEN 1,0 you would get the same two colors repeated over and over:

And that corresponds to using colors 0 and 1 in the chart:

  • 0 – Black
  • 1- Green

And PMODE 4,1:SCREEN 1,1 looks like this:

Here, you would want to use colors 0 and 5:

  • 0 – Black
  • 5 – Buff

I don’t know if ANY of us ever programmed using the numbers like that — mostly because the manual really did not make it clear which colors were part of which mode. You’d need a fancier chart that showed what you could do, like:

PMODESCREEN0
Black
1
Green
2
Yellow
3
Blue
4
Red
5
Buff
6
Cyan
7
Magenta
8
Orange
0 or 20XX
0 or 21XX
1 or 30XXXX
1 or 31XXXX
The “correct” color values to use in high-resolution graphics ;-)

I would have needed to keep this chart nearby for reference until I memorized it.

What color values did you learn to use when you learned Extended Color BASIC? Leave a comment and let me know.

Until next time…

Bonus Code

Here’s my sample program.

0 'COLORS.BAS
10 PMODE 3,1:PCLS:SCREEN 1,0
20 GOSUB 500
30 PMODE 3,1:PCLS:SCREEN 1,1
40 GOSUB 500
50 PMODE 4,1:PCLS:SCREEN 1,0
60 GOSUB 500
70 PMODE 4,1:PCLS:SCREEN 1,1
80 GOSUB 500

499 END

500 ' SHOW COLORS
505 DRAW"BM10,3 D8R8U8NL8 BR20 ND8 BR28 R8D4L8D4R8 BR20 R8U4NL8U4NL8 BR20 D4R8NU4D4 BR20 R8U4L8U4R8 BR20 NR8D8R8U4NL8BU4 BR20 R8D8 BR20 R8U8L8D4R8"
510 FOR C=0 TO 8
520 COLOR C
530 LINE (C*28,16)-(C*28+27,191),PSET,BF
540 NEXT
550 IF INKEY$="" THEN 550
560 RETURN