Willsy Posted October 25, 2012 Share Posted October 25, 2012 Would anyone fancy having a go at writing a snake routine in extended basic? You know, the standard snake thing where a snake (say, 10 segments long) moves horizontally until it bumps into something and then moves up/down and changes horizontal direction. I wrote one in Forth a while ago, and it seems to work just fine. But what I couldn't work out (in the time I had available) was the drawing/erasing of the snake segments. I'm pretty sure that *all* you have to do, to make the snake move, is draw the head, and erase the tail. You don't need to draw the segments inbetween. I couldn't make it work though. In the end, what I actually coded, was a 10 *one* segment snakes, each taking care of erasing itself, updating it's position and checking the screen to see if it has hit a character. So, it *looks* like one snake, but it's actually 10 "objects" that are next to each other, giving the illusion of a snake. Is that how's it's really done? There must be a more efficient way where just the head does the collision detection, and the tail is erased. Anyone fancy a go? I'll then take the code and port it to TF for fun. Quote Link to comment Share on other sites More sharing options...
samishal Posted October 25, 2012 Share Posted October 25, 2012 I think the way you were describing (draw head + erase tail) is probably the most efficient. I'd probably go with that method, what bit of it didnt work Willsy? Quote Link to comment Share on other sites More sharing options...
Tursi Posted October 25, 2012 Share Posted October 25, 2012 There was a TI BASIC game ages ago that did this call Worm of Bemer - one difference being that you steered the snake at will. To make it work it tracked the position of every body segment in an array, but yes, only erased the tail and drew the head. I need a quickie, let me see if I can put it together in XB before bed Doing it as separate objects, like you did, probably works better for a game like Centipede where it has to split up, though. Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 25, 2012 Author Share Posted October 25, 2012 Well, that was one advantage of how I did it, yes... You could actually split them up and send them in different directions, because it wasn't one object made of 10 segments, it was 10 segments behaving individually! Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 25, 2012 Author Share Posted October 25, 2012 I think the way you were describing (draw head + erase tail) is probably the most efficient. I'd probably go with that method, what bit of it didnt work Willsy? I seem to rememer it leaving trails all over the place, but I can't remember now! Quote Link to comment Share on other sites More sharing options...
Retrospect Posted October 25, 2012 Share Posted October 25, 2012 it was leaving trails because you needed to put character 32 behind the tail after every move the snake made. for example; y=y+1 call hchar(x,y,snake)::call hchar(x,y-1,32) that's only an example in reality you would have three chars defined , the tail, body, and head. Quote Link to comment Share on other sites More sharing options...
Tursi Posted October 25, 2012 Share Posted October 25, 2012 (edited) 100 RANDOMIZE 110 REM SNAKE THINGY 120 REM DEFINE ARRAYS FOR POSITION OF EACH PIECE 130 OPTION BASE 1 140 DIM R(10),C(10) 150 CNT=10 :: REM NUMBER OF PIECES 160 CALL CLEAR 170 REM SET UP SNAKE 180 FOR I=1 TO CNT 190 R(I)=1 :: C(I)=I+1 :: CALL HCHAR(1,I+1,42) 200 NEXT I 210 REM DRAW SOME WALLS 220 CALL VCHAR(1,1,35,24):: CALL VCHAR(1,32,35,24) 230 REM AND SOME RANDOM OBSTACLES TO HIT 240 FOR I=2 TO 24 :: CALL HCHAR(I,INT(RND*32)+1,35):: NEXT I 250 REM INITIALIZE DIRECTION AND RING BUFFER 260 DIR=1 :: REM MOVING RIGHT 270 HEAD=10 :: REM CURRENT HEAD IS ARRAY ENTRY 10 280 REM MAIN LOOP STARTS HERE 290 REM SAVE OLD HEAD 300 HR=R(HEAD):: HC=C(HEAD) 310 REM FIRST ROTATE THE RING BUFFER POINTERS 320 HEAD=HEAD+1 330 IF HEAD>CNT THEN HEAD=1 340 REM ERASE THE OLD TAIL (IN 'HEAD') 350 CALL HCHAR(R(HEAD),C(HEAD),32) 360 REM UPDATE THE POSITION 370 R(HEAD)=HR :: C(HEAD)=HC+DIR 380 REM CHECK FOR LEGAL MOVE 390 CALL GCHAR(R(HEAD),C(HEAD),X) 400 IF X=32 THEN 480 410 REM NOT LEGAL - DROP DOWN AND CHANGE DIRECTION 420 C(HEAD)=HC :: R(HEAD)=R(HEAD)+1 430 DIR=-DIR 440 REM CHECK FOR BOTTOM OF SCREEN AND WRAP 450 IF R(HEAD)>24 THEN R(HEAD)=1 460 REM CHECK LEGALITY AGAIN 470 GOTO 390 480 REM NEW POSITION IS OKAY, DRAW THE HEAD 490 CALL HCHAR(R(HEAD),C(HEAD),42) 500 REM AND LOOP 510 GOTO 280 Try that Edited October 25, 2012 by Tursi Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 25, 2012 Share Posted October 25, 2012 Something I always wondered about Centipede was the interaction when you have close groups of mushrooms. This example: #<****** snake enters here ## #* #*******> snake exits here In Tursi's code the snake exits two lines below the double mushroom ##. I suspect the logic is such that the snake changes direction every time it moves down, which would explain why the snake above exits a line below the ## instead of directly below it. The collision detection probably goes like this: 1. snake hits the left wall, moves down 1 line, changes direction (now going right) 2. snake immediately hits mushroom against the wall, moves down 1 line, changes direction (now going left) 3. snake hits the left wall again, moves down 1 line, changes direction (now going right) 4. snake travels right now that the path is clear Not that this is wrong, I'm just curious about how Centipede actually did it. Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 25, 2012 Author Share Posted October 25, 2012 Something I always wondered about Centipede was the interaction when you have close groups of mushrooms. This example: #<****** snake enters here ## #* #*******> snake exits here In Tursi's code the snake exits two lines below the double mushroom ##. I suspect the logic is such that the snake changes direction every time it moves down, which would explain why the snake above exits a line below the ## instead of directly below it. The collision detection probably goes like this: 1. snake hits the left wall, moves down 1 line, changes direction (now going right) 2. snake immediately hits mushroom against the wall, moves down 1 line, changes direction (now going left) 3. snake hits the left wall again, moves down 1 line, changes direction (now going right) 4. snake travels right now that the path is clear Not that this is wrong, I'm just curious about how Centipede actually did it. The way I did it in my Forth example was to change horizontal direction when hitting an object (that includes screen edge - I drew a frame around the edge of the screen) and change vertical direction at the top/bottom of the screen. Actually, I think I did it exactly the same as Tursi. The example is shown on the Chicago Faire video. (20 minutes into the video) Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 26, 2012 Share Posted October 26, 2012 Well, I spent entirely to much time today frame-stepping in MAME to see how the original Centipede moves. It is actually pretty interesting. The collision detection is not done until *after* the turn has been made and then the only test is to see what is now in front of the head. So in the case above, the # against the wall would not have caused any collision or direction change and the Centipede would just walk over it. 2 Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 26, 2012 Author Share Posted October 26, 2012 Well, I spent entirely to much time today frame-stepping in MAME to see how the original Centipede moves. It is actually pretty interesting. The collision detection is not done until *after* the turn has been made and then the only test is to see what is now in front of the head. So in the case above, the # against the wall would not have caused any collision or direction change and the Centipede would just walk over it. Eh? If it's not done until after thev turn is made then what prompts it to turn in the first place? Confused! Quote Link to comment Share on other sites More sharing options...
matthew180 Posted October 26, 2012 Share Posted October 26, 2012 The determination to turn is based on what is *in front* of the Centipede head only. So, in the example above: #<***** Centipede hits wall, moves down, reverses direction ##****> The mushroom against the wall is walked-over # # When the Centipede detects the wall, it will move down and reverse direction. However, once it gets to the next line, the mushroom that was directly below it would simple be *walked over*. This is because once the head have moved down and facing the opposite direction, that mushroom is not in front of the Centipede. Of course the Centipede body is done with sprites so the movement does not affect the tiled mushroom play field. To get the rapid back and forth of the Centipede moving straight down, you need something like this: #* #*# #* #*# #> Not that is matters, I just always wondered (for some unknown reason) about the mechanics of the original arcade game. It is also interesting to see the irregularity of the body segments while turning. The game was very organic and it really blows me away how much detail was put into the mechanics. Quote Link to comment Share on other sites More sharing options...
RXB Posted October 26, 2012 Share Posted October 26, 2012 My hope was with RXB that people would attack problems like these with the commands like CALL RMOTION(sprite#) that reverses sprite motion 180 degrees or CALL GMOTION(sprite#,row,col) gets the motion of a sprite and swapping the row and col would make a sprite turn 90 degrees. Negate the values and you get a 180 degree and if you use Motion you could mathematically get the sprite to turn in a circle. Also as I cleaned up other sprite calls like Distance, Coinc and Motion. 1 Quote Link to comment Share on other sites More sharing options...
Retrospect Posted October 26, 2012 Share Posted October 26, 2012 My hope was with RXB that people would attack problems like these with the commands like CALL RMOTION(sprite#) that reverses sprite motion 180 degrees or CALL GMOTION(sprite#,row,col) gets the motion of a sprite and swapping the row and col would make a sprite turn 90 degrees. Negate the values and you get a 180 degree and if you use Motion you could mathematically get the sprite to turn in a circle. Also as I cleaned up other sprite calls like Distance, Coinc and Motion. That reminds me , Rich .... I really need to make some sort of RXB game ....... yes .... that would be ineresting .... I'll come up with something. Sorry to interrupt! Quote Link to comment Share on other sites More sharing options...
Willsy Posted October 26, 2012 Author Share Posted October 26, 2012 Is there a manual for RXB? Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted October 27, 2012 Share Posted October 27, 2012 My hope was with RXB that people would attack problems like these with the commands like CALL RMOTION(sprite#) that reverses sprite motion 180 degrees or CALL GMOTION(sprite#,row,col) gets the motion of a sprite and swapping the row and col would make a sprite turn 90 degrees. Negate the values and you get a 180 degree and if you use Motion you could mathematically get the sprite to turn in a circle. Also as I cleaned up other sprite calls like Distance, Coinc and Motion. Any chance of ever getting RXB on a cartridge? Quote Link to comment Share on other sites More sharing options...
RXB Posted October 27, 2012 Share Posted October 27, 2012 Is there a manual for RXB? Yes it comes with a PDF and text version. Quote Link to comment Share on other sites More sharing options...
RXB Posted October 27, 2012 Share Posted October 27, 2012 RXB and REA 2012 Quote Link to comment Share on other sites More sharing options...
+Ksarul Posted October 27, 2012 Share Posted October 27, 2012 (edited) Interesting, it seems I no longer have permission to download from here. . .at least not in this thread, as others do work. Edited October 27, 2012 by Ksarul Quote Link to comment Share on other sites More sharing options...
Retrospect Posted October 27, 2012 Share Posted October 27, 2012 Interesting, it seems I no longer have permission to download from here. . .at least not in this thread, as others do work. Yeah, me too ? Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted October 27, 2012 Share Posted October 27, 2012 (edited) Interesting, it seems I no longer have permission to download from here. . .at least not in this thread, as others do work. My guess is that it has more to do with those specific files. Try downloading the attached dummy.txt file to see whether I might be right. Rich (@RXB), any ideas? ...lee dummy.txt Edited October 27, 2012 by Lee Stewart Quote Link to comment Share on other sites More sharing options...
+Ksarul Posted October 27, 2012 Share Posted October 27, 2012 The dummy file downloads, so it must be something with the way Rich tagged his files on upload. . .or a system glitch. Quote Link to comment Share on other sites More sharing options...
RXB Posted October 27, 2012 Share Posted October 27, 2012 Well this thread has all the copies of RXB and all my uploads. http://www.atariage.com/forums/topic/163627-rxb-rich-extended-basic/ Quote Link to comment Share on other sites More sharing options...
Willsy Posted December 4, 2012 Author Share Posted December 4, 2012 I was bored in my lunch break, so I finally got around to converting Tursi's snake code: 10 VALUE CNT \ number of snake segments (150) 0 VALUE DIR \ direction. 1=right -1=left (260) 0 VALUE HEAD \ pointer into array (270) 0 VALUE HR \ head row (300) 0 VALUE HC \ head column (300) CREATE ROW CNT CHARS ALLOT \ row array (140) CREATE COL CNT CHARS ALLOT \ column array (140) : SETUP ( -- ) 1 GMODE \ 32 column mode \ initialise snake array and draw snake... CNT 0 DO ( 180) 0 ROW I + C! \ initialise row (190) I 1+ COL I + C! \ initialise column (190) 0 I 1+ 42 1 HCHAR LOOP \ draw some walls... 0 0 35 24 VCHAR 0 31 35 24 VCHAR ( 220) \ and some random obstacles to hit... 23 1 DO I 32 RND 35 1 HCHAR LOOP ( 240) \ initialise direction and ring buffer... 1 TO DIR \ moving to the right (260) CNT 1- TO HEAD \ current head is the 10th entry (270) ; : CHECK-MOVE ( -- ) \ check for legal move... ROW HEAD + C@ COL HEAD + C@ GCHAR ( 390) 32 <> IF ( 400) \ not legal, change direction... HC COL HEAD + C! ( 420) ROW HEAD + C@ 1+ ROW HEAD + C! ( 420) DIR NEGATE TO DIR ( 430) \ check for bottom of screen and wrap... ROW HEAD + C@ 23 > IF ( 450) 0 ROW HEAD + C! ( 450) BREAK? \ check break key (fctn 4) THEN RECURSE \ check legality again (470) THEN ; : MAIN-LOOP ( -- ) BEGIN \ save old head... ROW HEAD + C@ TO HR COL HEAD + C@ TO HC \ rotate the ring buffer pointers... HEAD 1+ CNT MOD TO HEAD \ erase the old tail (in 'head')... ROW HEAD + C@ COL HEAD + C@ 32 1 HCHAR \ update the position... HR ROW HEAD + C! HC DIR + COL HEAD + C! CHECK-MOVE \ new position is okay; draw the head ROW HEAD + C@ COL HEAD + C@ 42 1 HCHAR AGAIN ; : GO ( -- ) SETUP MAIN-LOOP ; I've used the same comments as Tursi. Numbers in brackets are the equivalent lines in Tursi's XB program. My version uses a little bit of recursion. The XB program, having detected an illegal move changes direction and moves down one screen line. Having done that, it checks again that the move is legal (line 470). Well, there's no GOTO in Forth, so I factored the legality check into its own definition, then used RECURSE. RECURSE simply causes the definition to call itself again. When a legal move is finally determined the recursion un-winds itself, as one would expect. You can paste the above code directly into TF V1.1 (which comes built into classic99). Dyed-in-the-wool console die-hards can type the following more compact version directly in: 10 VALUE CNT 0 VALUE DIR 0 VALUE HEAD 0 VALUE HR 0 VALUE HC CREATE ROW CNT CHARS ALLOT CREATE COL CNT CHARS ALLOT : SETUP 1 GMODE CNT 0 DO 0 ROW I + C! I 1+ COL I + C! 0 I 1+ 42 1 HCHAR LOOP 0 0 35 24 VCHAR 0 31 35 24 VCHAR 23 1 DO I 32 RND 35 1 HCHAR LOOP 1 TO DIR CNT 1- TO HEAD ; : CHECK-MOVE ROW HEAD + C@ COL HEAD + C@ GCHAR 32 <> IF HC COL HEAD + C! ROW HEAD + C@ 1+ ROW HEAD + C! DIR NEGATE TO DIR ROW HEAD + C@ 23 > IF 0 ROW HEAD + C! BREAK? THEN RECURSE THEN ; : MAIN-LOOP BEGIN ROW HEAD + C@ TO HR COL HEAD + C@ TO HC HEAD 1+ CNT MOD TO HEAD ROW HEAD + C@ COL HEAD + C@ 32 1 HCHAR HR ROW HEAD + C! HC DIR + COL HEAD + C! CHECK-MOVE ROW HEAD + C@ COL HEAD + C@ 42 1 HCHAR AGAIN ; : GO SETUP MAIN-LOOP ; TurboForth takes on average approx 1.5 seconds to move the snake all the way down the screen. XB when I measured it required approx 41 seconds on average, making TF V1.1 approx. 27.3 times faster. TF V1.2 takes approx 1.3 1.1 seconds (as close as I could measure it) making it ~31.5 37 times faster. Like anyone needs convincing! We need some convenient way of instrumenting execution times! The human eye and a stopwatch is not very accurate! Plus it draws attention in the office! http://www.youtube.com/watch?v=Gxt11NDxsYA snake_tf.txt snake_xb.txt 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 4, 2012 Share Posted December 4, 2012 A Harry Wilhelm compiled version runs pretty close to the Forth version in the video. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.