I originally used an array to store the x and y positions of the snake segments. This worked really well with a size 5 snake but by size 10 it slowed to a crawl. That's when I decided to store it in memory locations and use some simple machine code to copy the positions.
How hard could machine code be anyway? Well after several crashes (including spin itself crashing) and much swearing I managed to get something that actually worked!
No expense was spared on the graphics. The name was the hardest part. I was gonna call it Advanced Nokia Simulator but that wasn't puerile enough! And that ladies and gentlemen is how I played around with my snake until I was happy enough to share it with the public! :razz:
Aye I was surprised that it didn't slow down as the snake grew, still am actually as I thought drawing that many characters from BASIC would take up most of the time rather than the data. Good (!) work!
This plays really well! You fool! It makes me think of LumASCII, too. I have always wondered how these snake games work under the hood so I shall be examining this in earnest.
Aye I was surprised that it didn't slow down as the snake grew, still am actually as I thought drawing that many characters from BASIC would take up most of the time rather than the data. Good (!) work!
The snake only gets drawn in full at the start of the game. All that happens when you move is the head gets redrawn at it's new position and the tail erased. So the only thing slowing it down is copying the coordinates of the segments.
The snake only gets drawn in full at the start of the game. All that happens when you move is the head gets redrawn at it's new position and the tail erased. So the only thing slowing it down is copying the coordinates of the segments.
That's so simple, and obvious when you think about it! I don't have to bother looking now :)
You still have to implement a circular buffer though (since it is a double ended queue and you don't want to have to shift the array entries down a position every move). And there's no MOD (modulo arithmetic) in ZX Basic is there (and you can't do bitwise-ANDs either [since you would use a power of 2 sized array] or can you?). You have to do a tedious divide, subtract the integer part, and multiply up again to do modulo arithmetic.
Probably easier to do in machine code anyway (bitwise AND is your friend).
Some snake games are super-clever and draw different graphics for the head, tail and the 3 different possible turn directions as well. (ok, there are 4 different turn direction graphics but only 3 possibilities at each turn [straight on, clockwise, anticlockwise]). You can do that without traversing the entire buffer though.
But since when do snakes die when they run into themselves? And don't they have to shed their skin to get longer rather than just by eating "ampersats"?
EDIT: Sounds like Rebelstar IS copying all the segments down the array. LOL NOOB!
EDIT2: Easiest thing to do in basic is probably just use a counter (for start element, end element) which you manually wrap around when it overflows. Or do memory contents wrap when you poke them with values > 255? Cos you could do POKE(address, PEEK(address) + 1) to wrap around mod 256 and use a 256 length array in that case. EDIT3: I hope they wrap around. It would be retarded if you get an out of range error...
EDIT4: Moral of the story - the obvious way (use an array and copy the positions down each time you erase the first element) is probably the worst way to actually do the snake movement code [it's O(N) on the length of the snake (big-O algorithmic complexity) rather than the circular buffer O(1) case]. In a language as slow as ZX Basic you'd better pick a decent algorithm to start with.
Hmm, maybe I should put my proverbial money where my proverbial mouth is and actually do an all-ZX Basic snake CGC game then. With multiple implementations of the game loop, I could call it algorithmic complexisnake or something.
What do I need to download to not have to use stoopid keyword basic (I know I could do it on a 128K but I've never used the editor I bet it sucks balls and is probably slow as hell anyway?).
I'll give the 128K basic a try then... BASIC programs do run on a 48K if you save them from a 128K? What are the keys for the editor? I bet it's not as good an editor as DevStudio ;)
Before I embark on this quest could do with a refresher course... not programmed ZX Basic since 1988 lol.
You can't read the screen if you use UDGs or can you? SCREEN$ AT would that be? I suppose I could read the attributes and colour code things... how do you do that, is it ATTR(row, col)? May just be a memory hog and use a 32*22 string array instead (gonna be faster to do lookups from that I expect anyway).
You can't use all the UDGs from 128K basic I believe? (How do you POKE UDGs anyway?, can't remember).
How do you print to the bottom 2 lines of the screen (PRINT #0 rings a bell?).
Do I have to do a CLEAR some address to have a good place to POKE a 1 bye counter or is there an address that is always safe to use? I never worked out how CLEAR worked anyway ;) I suppose I could use 8 bytes from a UDG as scratch memory anyway if I wanted to be flash.
I'm thinking multiple food items on screen, bonus items, multiple levels, a moving enemy, maybe even some scissors that cut your snake in half as well if I want to be really clever (that's probably quite helpful although will probably leave the dead half of the snake on the screen as a hazard, so could block you in). Power-ups/snake that shoots frickin' laser beams from its eyes would be on my wish list too but I'm getting ahead of myself here.
Maybe "My snake is longer and harder than yours (100% BASIC edition)" for a working title?
Don't forget to explain how the snake grows in length just by eating stuff! And yeah ATTR is better than SCREEN$ for reading the screen.
And one of my games (Can't remember which one) has a UDG designer built in!
man oh man, i can?t wait to see that :)
didn?t the spectrum manual had some snake game tutorial in it?
i know what you mean i also haven?t done something in the real editor since 88 or 89,
i have great respect for people who coded games back then, it was a great pain in the "#$"!, not that they know it was obviouslly :D
Original 48K manual didn't have a snake game in it. Some crappy I Ching "simulator", and something which drew a flag on the screen. Also the BEEP example tune was the Funeral March, lulz.
I bet they didn't use a circular buffer either if there was a snake example! Either that or they were lazy and never erased the end of the snake (like some game I spent ages typing in from Sinclair User once).
oh yes, i remeber typing that flag example :)
well i must have seen a snake one somewere too, it probably didn?t erase the end of the snake too.
That?s some calculations that would make a basic game from slow to really unplayable?
Nah, I think it will be OK if I use a circular buffer (I guess I'll find out?). Only need to erase the element at the start of the array, increment the start, add the new head at end element, increment that, wrapping the numbers around. As long as start never passes the end (and by having a limited amount of food to eat per level, that can be guaranteed), LET bob = uncle. All I need is 2 arrays DIM r(256) : DIM c(256) for the row, column of each segment, and I can use the POKE trick to wrap a counter round mod 256 probably. May want to make the dimensions 255 instead since basic starts counting array indices at 1 instead of 0 :( I'd have to treat 0 as a special case then, probably better than adding 1 to the index everywhere though...
EDIT: Might just be easier to have a 32-length array and do something like
init: start = 1 : end = initial snake length + 1
set initial coordinates for snake segments in r, c arrays. end not yet filled in, that's where the new segment will be drawn
loop:
erase at r(start), c(start)
start = start + 1: if start = 33 then start = 1
keyboard stuff goes here, set new head position at r(end), c(end)
draw at r(end), c(end)
end = end + 1: if end = 33 then end = 1
The snake only gets drawn in full at the start of the game. All that happens when you move is the head gets redrawn at it's new position and the tail erased. So the only thing slowing it down is copying the coordinates of the segments.
You see it's this kind of problem solving that's bringing this competition into disrepute! I would have deleted and drawn the whole snake each loop. Tsk.
(I'm embarrassed I didn't quite spot it in the code though, I'm too stuck in my ways :-D)
aghh, using 2 arrays in basic will slow down the hole thing.
maybe use poking and peeking values in memory, its the same and ends up faster.
Yeah that would probably be best (but then, is it 100% BASIC??? UDGs don't count!), I take it arrays are really slow then? As I said in the edit I will only be touching 2 elements from each array each tick (I will be touching the end and the start of my snake as fast as possible). EDIT: I will erase 1 cell (if I didn't just eat any food) and draw 1 cell each tick, hence it is O(1) algorithm (independent of how large my snake is). EDIT2: And the only writing that gets done to the arrays is the new head row, column position. I read the start position to erase it in the loop as well.
EDIT2: Is it better to use a 2D array p(32, 2) rather than 2 arrays then? That's like using a struct! This could be a new programming paradigm! I'm guessing all the index offsets are worked out every array access so there would be no difference between p(32, 2) and p(2, 32) for speed (AoS [array of structs] vs. SoA [structure of arrays]). How big is the L1 and L2 cache on these ZX Spectrums anyway??? Does the JIT compiler for BASIC do any peephole optimisations and instruction reordering? ;)
I think you'll be fine with an array. Mine started out with an array and was quick enough to begin with. As you're not going to be copying large chunks of it you shouldn't lose any speed like I did.
They are slow, but only if you use the entire array, if you only work with one value each one at a time i guess it wont be a problem, and i agree that?s the best way to to do something like this.
But what do you mean 100% basic? poke and peek are also part of basic too :)
Comments
Teach yerself Australian.
By Lee Spoons.
Hope the French are next :D
Can anyone convert the WAV to a TZX or TAP? I failed with MakeTZX and can't get WAV2TZX to run.
Actually it's not such a non-standard loader after all (even though I saved it in SPIN) so I should be able to provide a TZX tonight.
Cheers, while we're waiting here's an alternative translating engine:
By the way, back to back ashes coming up, crap cricket games most welcome :smile:
Is that a snake or are you just pleased to see me?
By Rebelstar without a cause.
Worryingly fault free this one :-P
How hard could machine code be anyway? Well after several crashes (including spin itself crashing) and much swearing I managed to get something that actually worked!
No expense was spared on the graphics. The name was the hardest part. I was gonna call it Advanced Nokia Simulator but that wasn't puerile enough! And that ladies and gentlemen is how I played around with my snake until I was happy enough to share it with the public! :razz:
The snake only gets drawn in full at the start of the game. All that happens when you move is the head gets redrawn at it's new position and the tail erased. So the only thing slowing it down is copying the coordinates of the segments.
That was an inspiration as well as my desire to avoid designing UDGs!
That's so simple, and obvious when you think about it! I don't have to bother looking now :)
Probably easier to do in machine code anyway (bitwise AND is your friend).
Some snake games are super-clever and draw different graphics for the head, tail and the 3 different possible turn directions as well. (ok, there are 4 different turn direction graphics but only 3 possibilities at each turn [straight on, clockwise, anticlockwise]). You can do that without traversing the entire buffer though.
But since when do snakes die when they run into themselves? And don't they have to shed their skin to get longer rather than just by eating "ampersats"?
EDIT: Sounds like Rebelstar IS copying all the segments down the array. LOL NOOB!
EDIT2: Easiest thing to do in basic is probably just use a counter (for start element, end element) which you manually wrap around when it overflows. Or do memory contents wrap when you poke them with values > 255? Cos you could do POKE(address, PEEK(address) + 1) to wrap around mod 256 and use a 256 length array in that case. EDIT3: I hope they wrap around. It would be retarded if you get an out of range error...
EDIT4: Moral of the story - the obvious way (use an array and copy the positions down each time you erase the first element) is probably the worst way to actually do the snake movement code [it's O(N) on the length of the snake (big-O algorithmic complexity) rather than the circular buffer O(1) case]. In a language as slow as ZX Basic you'd better pick a decent algorithm to start with.
It's not called the crap games competition for nothing! :D
What do I need to download to not have to use stoopid keyword basic (I know I could do it on a 128K but I've never used the editor I bet it sucks balls and is probably slow as hell anyway?).
Sometimes I miss those line numbers!
or use a compiler and do it in a text editor anyway :D
Before I embark on this quest could do with a refresher course... not programmed ZX Basic since 1988 lol.
You can't read the screen if you use UDGs or can you? SCREEN$ AT would that be? I suppose I could read the attributes and colour code things... how do you do that, is it ATTR(row, col)? May just be a memory hog and use a 32*22 string array instead (gonna be faster to do lookups from that I expect anyway).
You can't use all the UDGs from 128K basic I believe? (How do you POKE UDGs anyway?, can't remember).
How do you print to the bottom 2 lines of the screen (PRINT #0 rings a bell?).
Do I have to do a CLEAR some address to have a good place to POKE a 1 bye counter or is there an address that is always safe to use? I never worked out how CLEAR worked anyway ;) I suppose I could use 8 bytes from a UDG as scratch memory anyway if I wanted to be flash.
I'm thinking multiple food items on screen, bonus items, multiple levels, a moving enemy, maybe even some scissors that cut your snake in half as well if I want to be really clever (that's probably quite helpful although will probably leave the dead half of the snake on the screen as a hazard, so could block you in). Power-ups/snake that shoots frickin' laser beams from its eyes would be on my wish list too but I'm getting ahead of myself here.
Maybe "My snake is longer and harder than yours (100% BASIC edition)" for a working title?
And one of my games (Can't remember which one) has a UDG designer built in!
didn?t the spectrum manual had some snake game tutorial in it?
i know what you mean i also haven?t done something in the real editor since 88 or 89,
i have great respect for people who coded games back then, it was a great pain in the "#$"!, not that they know it was obviouslly :D
I bet they didn't use a circular buffer either if there was a snake example! Either that or they were lazy and never erased the end of the snake (like some game I spent ages typing in from Sinclair User once).
well i must have seen a snake one somewere too, it probably didn?t erase the end of the snake too.
That?s some calculations that would make a basic game from slow to really unplayable?
EDIT: Might just be easier to have a 32-length array and do something like
init: start = 1 : end = initial snake length + 1
set initial coordinates for snake segments in r, c arrays. end not yet filled in, that's where the new segment will be drawn
loop:
erase at r(start), c(start)
start = start + 1: if start = 33 then start = 1
keyboard stuff goes here, set new head position at r(end), c(end)
draw at r(end), c(end)
end = end + 1: if end = 33 then end = 1
maybe use poking and peeking values in memory, its the same and ends up faster.
You see it's this kind of problem solving that's bringing this competition into disrepute! I would have deleted and drawn the whole snake each loop. Tsk.
(I'm embarrassed I didn't quite spot it in the code though, I'm too stuck in my ways :-D)
This sounds great, if the coding doesn't work out enter it to the comp :razz:
Yeah that would probably be best (but then, is it 100% BASIC??? UDGs don't count!), I take it arrays are really slow then? As I said in the edit I will only be touching 2 elements from each array each tick (I will be touching the end and the start of my snake as fast as possible). EDIT: I will erase 1 cell (if I didn't just eat any food) and draw 1 cell each tick, hence it is O(1) algorithm (independent of how large my snake is). EDIT2: And the only writing that gets done to the arrays is the new head row, column position. I read the start position to erase it in the loop as well.
EDIT2: Is it better to use a 2D array p(32, 2) rather than 2 arrays then? That's like using a struct! This could be a new programming paradigm! I'm guessing all the index offsets are worked out every array access so there would be no difference between p(32, 2) and p(2, 32) for speed (AoS [array of structs] vs. SoA [structure of arrays]). How big is the L1 and L2 cache on these ZX Spectrums anyway??? Does the JIT compiler for BASIC do any peephole optimisations and instruction reordering? ;)
But what do you mean 100% basic? poke and peek are also part of basic too :)