Believe it or not, that small bit of coding makes up a very playable little game...
Flood - Geza Lucz
I typed in and saved the game, you can get a D64 Image Here.
Just for geeky fun, I'm going to try to understand how it does its magic. Its been a long time since I've delved into C=64/Microsoft BASIC, so wish me luck...
The next part I'm not sure about:
The next block is so cute:
This is using a single FOR loop to set all 4 borders. According to the Commodore-64 Memory Map, 1024 is the default start of screen memory, and according to the C64 screen codes 42 is the code for "*". So line 150 POKEs in a * for the top row, and then the bottom row (1984 = 1024 memory start + (40 characters across * 24 rows down)).
Lines 160 and 170 are the really amusing bits (er, for people who are amused by this kind of thing) - instead of going from 0 to 39, the vertical sides are 0 to 23, so Lucz uses some division and rounding... that means some characters are being set twice, but that doesn't hurt anything, and is more efficient than conditionals to see if the setting is necessary.
Next is some more initialization, this time of the two starting water drops:
So this is an important hint at how A() will be used- it's not a simple memory map, it's a set of offsets INTO the screen memory, I'm assuming the use will be a flattened X,Y coordinate, one for each water drop.
Still initializing, we get
Line 250 pokes a checkerboard (102) into the current player position, again according the the C64 screen codes.
Ok, time for our main game loop:
270-330 is the main loop for updating water. Inside this loop, there's that GOSUB 370 which as we'll see is the keyboard reading/player moving. It's important to see that that call is nested in the outer FOR loop, rather than just once per "infinite" GOTO loop... otherwise the game would be feel very unresponsive, and the player could only move once per global update.
So, I'm conjecturing "A" is the first water drop to inspect, and "B" is the final one. A(), remember, is the array of offsets to screenlocations (one for each drop) and B(1-4) is a series of offsets to look up, down, left, and right. So "I" is the offset of the drop we're inspecting. 290 says "if the location of the current drop adjusted for the up/down/left/right offset is NOT a space, then goto 310" (i.e. skip line 300).
So line 300 represents the addition of the drop. I'm thinking "L" represents how many drops we've added to this "GOTO loop". Into A(B+L) (i.e. the next available space in A() for a drop to sit) we push the screen memory location of this drop - A(I) - plus the up/down/left/right screen memory offset from B(). And to end the line we poke a 42 "*" character into that new screen location. (And because we started with those solid borders, we don't have to worry about poke'ing off of the screen.)
310 finishes looping for looking up/down/left/right, 320 reads and reacts to the player input, line 330 finishes looping through all the droplets that were known to be needed to be checked.
340 then sets up the variables for the next run of the GOTO loop, A, the start of the search, is set to B, where we ended the search this time. B, the end of the search for next time, is then increased by how many drops we added in.
In other words, each turn, the up/down/left/right neighbors of all the drops we added last time are inspected, and any blanks there are made new drops, and then next GOTO loop those new drops will be inspected in turn, and so on. Thus, the floodfill happens.
350 looks for the win condition. If L is zero, that mean we added in zero drops... i.e. there are no drops with empty neighbors, and the game is done. And B is the offset of the last drop, so we get a score by subtracting that from the total number of drops possible, if the player did nothing but stand there.
All we have left is the keyboard and player movement subroutine:
370 reads in the keyboard. Not sure about reasoning of appending a space, probably to prevent errors if no key is pressed?
380 says if the key pressed was the return key, than R will be 91 - this corresponds with the wall marking character, a big cross shape.
With line 390, the use of the second set of values for B() (as set in line 130) becomes clear: those magic numbers are character codes corresponding to the cursor movement keys. So we look at the screen map of the current player location H, treating the ASCII code of the pressed key as an offset into the up/down/left/right offset in B(), and if it's not a space there, we return; the player can't move there, whether its wall or water blocking them. (So in short, B() has 8 values, 2 sets that point to up/down/left/right offsets, the first set is index making it easy to loop through when looking for drop neighbors, and the second set has indexes lining up with keyboard codes. We never initialize B(), I guess other pressed keycodes could create odd movement jumps if they had old data.)
400 pokes the players current location with R - this looks to be a wall piece if they've hit return since we last tried to move.
410 uses the same B() offset trick to change the player's known location.
420 says R gets to be what is in the space the player is about to be moved to... i.e. I'm pretty sure that will always be a space. And having read what was there, we go ahead and put a 102 checkerboard in that new location.
Wow! That is a neat bit of coding.
You can make a harder variation by increasing the number of starting droplets, you just have to change what W loops up to in 190 and then the initial value of B in 240. And of course, being able to make that kind of change is part of the charm of this era of BASIC programming!