sometimes99er Posted December 2, 2016 Share Posted December 2, 2016 (edited) parac.bin - - - - - In the footsteps of managing 32 sprites, without more than 4 sprites per horizontal line, as explored in Bubbles (demo). If you keep a number of sprites stacked vertically (no vertical overlap), they will only occupy 1 of the 4 sprites allowed horizontally. In Bubbles I made each stack move up with their own individual speed. This time I will try and move the 4 stacks in any direction. Here's a quick setup of 4 stacks. Oh, and instead of having 8 sprites in each stack (or plane), it's 6 + 7 + 9 + 10 = 32 sprites. The number of stars close to you are less than those far away. Edited May 8, 2021 by sometimes99er Cartridge made V9938/58 friendly. 5 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 3, 2016 Author Share Posted December 3, 2016 (edited) This has the vertical position slightly adjusted to get a better initial distribution.The different sized stars are evenly distributed vertically. The 6 biggest stars separated by 192 / 6 = 32 pixels. The smallest by 192 / 10 = roughly 19 pixels.Tursi supplied me with an excellent random number routine here. And I've been using it ever since. Distributing stars horizontally and randomly did however expose a weakness. Or maybe I'm doing something wrong.The numbers may seem random, but the bit manipulation appear weak.Generally I'd like to use fixed seeds to populate scenarios ( procedural level generation *) ). Then I know what is presented instead of getting perhaps somewhat "strange effects" from more random generation.Manually I changed the fixed seed about 20 times, and did find one with a good initial appearance. It still has exponential growth telltale though.*)1982 Pitfall for the Atari 2600: Innovative techniques were used to keep the code space within the 4K limit, including a polynomial counter to create 256 screens within 50 bytes of code. Edited December 8, 2016 by sometimes99er 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted December 3, 2016 Share Posted December 3, 2016 Yeah, I've had issues with the randomness of that algorithm at times as well. But I've often got better results when I don't use the entire word by taking bits from the middle of the word instead of the least significant ones. Might have been my imagination but I liked the results more. 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 5, 2016 Author Share Posted December 5, 2016 (edited) Stars Apart from the 32 hardware sprites, I'm adding 16 software sprites. That's 48 sprites in 5 planes. Randomness I tried to shift the random number 3, 4 and 5 places to the left, but still there's traces of exponential growth. More or less. Tried up to 10 different initial seeds without being fully satisfied. Now, I simply preset all sprites. To do list Splash screen. Music. Joystick input and movement of planes. Adjustments. Colors etc. Edited December 8, 2016 by sometimes99er 2 Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 5, 2016 Share Posted December 5, 2016 You might have better luck using the modulo operation on the values from your PRNG to determine x/y positions. Its more complex that a simple shift and bitwise AND, but you should get better results. 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 6, 2016 Author Share Posted December 6, 2016 (edited) You might have better luck using the modulo operation on the values from your PRNG to determine x/y positions. Its more complex that a simple shift and bitwise AND, but you should get better results. I did not use modulo operations (DIV), since y-positions are not random and x-positions use a width of 256 pixels. Using DIV with the random routine to get a range is however creme de la creme. Dividing the random word (range >0000 to >FFFF) by 256 returns the low byte as remainder, so no gain in some cases (power of 2). DIV may be a key ingredient to better random number generation. Simply adding contents from console ROM (same for all TI-99/4A) may be a quick and dirty fix. Somehow I think I may have to control randomness in my Restless II game to avoid a screen full of aliens all starting out in the same corner. I did some controlling in my Snake Plissken game. With my Minesweeper game you could get all the bombs in one corner, though the random routine is probably not that random. Edited December 6, 2016 by sometimes99er Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted December 6, 2016 Share Posted December 6, 2016 I do not know whether the PRNG I use in fbForth (same as TI Forth) is any better than what you are using, but here it is: The _RNDW function first retrieves the seed from >83C0. Then the result of seed * 6FE5h + 7AB9h is shifted right circularly by 5 bits. This result is stored at >83C0 as the new seed and also returned to the user: _RNDW LI R0,>6FE5 calculate new pseudo-random MPY @>83C0,R0 seed * >6FE5 AI R1,>7AB9 seed * >6FE5 + >7AB9 SRC R1,5 (seed * >6FE5 + >7AB9) SRC 5 MOV R1,@>83C0 store new seed B *R11 return to caller The _RND function returns a positive integer less than the number supplied to it, which includes 0. _RND invokes _RNDW , takes the absolute value of the result and divides by the user-supplied number and returns the remainder. Obviously, for your case, retrieving only the LSB would be a much more economical operation than division: _RND MOV R11,R15 save return BL @_RNDW get random number into R1 ABS R1 force positive CLR R0 set up for division DIV *R9,R0 divide number in R0--R1 by num on stack MOV R1,*R9 return remainder on stack B *R15 return to Forth inner interpreter The above done your way: RND256 MOV R11,R15 save return BL @_RNDW get random number into R1 ANDI R1,255 extract right byte (MOD 256) MOV R1,*R9 return remainder on stack B *R15 return to Forth inner interpreter I have puzzled over changing RND for fbForth 2.0 to not do the ABS and to just return all but the MSb, i.e., leftmost bit. I should compare the distribution of pseudo-random numbers done both ways, I suppose. One final note, randomizing the seed in both TI Forth and fbForth counts from zero until a VDP interrupt is detected. Though it sets up a race condition, I have never had any trouble with it: _RNDMZ MOVB @>8802,R0 get VDP status byte CLR R0 discard it CLR R1 clear counter RZLOOP INC R1 increment counter MOVB @>8802,R0 get VDP status byte ANDI R0,>8000 VDP interrupt? JEQ RZLOOP no, increment counter MOV R1,@>83C0 yes, store new seed B *R11 return to caller ...lee 2 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 7, 2016 Author Share Posted December 7, 2016 (edited) The _RNDW function first retrieves the seed from >83C0. Then the result of seed * 6FE5h + 7AB9h is shifted right circularly by 5 bits. The _RND function returns a positive integer less than the number supplied to it, which includes 0. _RND invokes _RNDW , takes the absolute value of the result and divides by the user-supplied number and returns the remainder. Excellent. At least I can switch to something else if there's a problem with patterns. Also found that Tursi's random function is the same as in Spectra2 from retroclouds. retroclouds avoids zero by adding one, while Tursi requires the seed to be anything but zero. I have puzzled over changing RND for fbForth 2.0 to not do the ABS and to just return all but the MSb, i.e., leftmost bit. I should compare the distribution of pseudo-random numbers done both ways, I suppose. Interesting. I have to get hold of my old homemade random routine, document it and run all through a few obvious tests. One final note, randomizing the seed in both TI Forth and fbForth counts from zero until a VDP interrupt is detected. Though it sets up a race condition, I have never had any trouble with it: Nice one. I'm a bit worried about emulators that could fail somewhat on that (with whatever conditions you have there). Adding it to the "system seed" (>83C0) should be more safe. And perhaps adding or solely relying on a race at a splash screen until the user hits Enter (insuring that the user is not already holding Enter before the race begins). Edited December 7, 2016 by sometimes99er 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted December 7, 2016 Share Posted December 7, 2016 (edited) For what it's worth, my routine is not mine, but borrowed from a fellow named Albert Veli who shared it with the Dreamcast homebrew group (and I thought he attributed some book before him ). One of its properties is that it never returns 0 (and also you can never seed with 0, it gets stuck)... another is that it returns /all/ values in the sequence before repeating. So for 16 bit numbers, you will see each value from 1 to 65535 in a pseudo-random order before they repeat -- but this is obviously not the same as each element being a random /number/ - for that you'd expect a duplicate to at least be possible. Psuedo-randomness is of course a huge field of research, but I've always been happy with simple enough, so I can't say much more about it. Edited December 7, 2016 by Tursi 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 7, 2016 Author Share Posted December 7, 2016 (edited) And perhaps adding or solely relying on a race at a splash screen until the user hits Enter (insuring that the user is not already holding Enter before the race begins). And then I'm worried about testing Enter directly (CRU) and constantly in a race on real hardware. The rather long time delay in the SCAN is invoked every time a key is pressed or released. If you hold a key, this time delay is not called. SCAN is the console ROM keyboard scanning routine. The loop counter in the delay (@>0498) is a staggering 1250. The interesting part is, that even though SCAN is run and all the lines are tested, the delay only runs when there's a new key state. I guess that's how they thought one could debounce. Since Enter is not used on the menu selection screen, and it's on different lines and columns, I think direct hits without delays could be okay. Within my games and demos, I test all keys of interest (CRU) once per frame update. Edited December 7, 2016 by sometimes99er Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 7, 2016 Author Share Posted December 7, 2016 For what it's worth, my routine is not mine, but borrowed from a fellow named Albert Veli who shared it with the Dreamcast homebrew group (and I thought he attributed some book before him ). Thought so. We could call it the B4 for now. One of its properties is that it never returns 0 (and also you can never seed with 0, it gets stuck)... another is that it returns /all/ values in the sequence before repeating. Excellent properties. It's short. It's quick (no DIV). Takes almost no space. With the long insured range, you can seed at a certain point and do "procedural level generation". You can separate level progress and other random stuff (like aliens attacking) with the same routine but handling different seeds. Later the same level could have another attack formation. So for 16 bit numbers, you will see each value from 1 to 65535 in a pseudo-random order before they repeat -- but this is obviously not the same as each element being a random /number/ - for that you'd expect a duplicate to at least be possible. With most use cases the return range would probably not be the full range. Rolling a dice should be safe / random. Wider ranges like screen height and width is something I'll include in future tests. Psuedo-randomness is of course a huge field of research, but I've always been happy with simple enough, so I can't say much more about it. Yeah, it's interesting but perhaps also a time consuming detour. Quote Link to comment Share on other sites More sharing options...
Tursi Posted December 7, 2016 Share Posted December 7, 2016 Thought so. We could call it the B4 for now. Hehe, but that would be limiting - the mask depends on how many bits you want out of it. I found the original message, the source was the book "Graphic Gems". For different widths just use a different mask. Below is a table of masks and widths. Mask (hex) Width (bits) 03 2 06 3 0C 4 14 5 30 6 60 7 B8 8 110 9 240 10 500 11 CA0 12 1B00 13 3500 14 6000 15 B400 16 12000 17 20400 18 72000 19 90000 20 140000 21 300000 22 400000 23 D80000 24 1200000 25 3880000 26 7200000 27 9000000 28 14000000 29 32800000 30 48000000 31 A3000000 32 Very handy little function 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 8, 2016 Author Share Posted December 8, 2016 Hehe, but that would be limiting - the mask depends on how many bits you want out of it. I found the original message, the source was the book "Graphic Gems". Yes, that might be handy. Repeating series of random numbers with a length of the power of 2 minus 1. Also you can reset seed after a certain count (to have any length of random numbers). Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 8, 2016 Author Share Posted December 8, 2016 To do list Splash screen.Music.Joystick input. 5 plane vector control. 32 sprite (top planes) display handling. 16 character (bottom plane) display handling. Adjustments. Colors etc. 3 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 10, 2016 Author Share Posted December 10, 2016 To do list Music.16 character (bottom plane) display handling.Adjustments. Colors etc. 3 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted December 14, 2016 Share Posted December 14, 2016 I tried plotting random points on a 256x192 screen using Tursi's and Lee's algorithms in Java. I plotted 1000 points, then cleared the screen and looped. Tursi's "Graphic Gems" algorithm is just displaying two tilted lines across the screen, which is not very random unless I programmed it wrong in Java (which is very likely). public int randTursi() { boolean carry = (randNo & 1) != 0; randNo = (randNo >> 1) & 0xFFFF; if (carry) { randNo ^= 0xB400; } return randNo; } Lee's is a lot better, but changing the shift from 5 to 7 makes it look truly random, like static on a TV screen, and very similar to Java's own algorithm. These are just screenshots of a single frame. It's better to judge the randomness from multiple frames, but it should be clear what I mean. Tursi Lee shift 5 Lee shift 7 Java EDIT: Note that I only used the lower 8 bits to plot the points. 2 Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted December 14, 2016 Share Posted December 14, 2016 Shouldn't this line: randNo = (randNo >> 1) & 0xFFFF; Be this: randNo = (randNo >> 1) & 0x7FFF; To correctly emulate a logical shift right instruction. Out of curiosity what did you use for the starting value of randNo? 1 Quote Link to comment Share on other sites More sharing options...
Tursi Posted December 15, 2016 Share Posted December 15, 2016 Since the routine was originally (supposedly) used to do a pseudo-random pixel dissolve, I suspected you did have an error in your conversion. The shift should be handled as unsigned, so the 0xffff mask is correct (although I don't understand why it was necessary? Right shift shouldn't be adding any higher bits...) I ported the routine to Blassic as a quick test... and I get much the same result as you - pretty much a line. I've never really tested the output of that function before, I find that pretty interesting. I may need to switch over to your tweaked version of Lee's myself... 2 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted December 15, 2016 Share Posted December 15, 2016 Java has both signed (>>) and unsigned (>>>) shift operators, but since I'm working on 32 bit ints and the upper 16 bits are zero it doesn't matter which one I use. The 0xffff mask is just a precaution to ensure I stay within 16 bits, but it's not necessary. 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 15, 2016 Author Share Posted December 15, 2016 (edited) Tursi's "Graphic Gems" algorithm is just displaying two tilted lines across the screen, which is not very random ... Very nice. I thought something wrong since I previously got lines of exponential growth instead of straight lines. But I do get the same result (lines but not exactly the same). This is pretty revealing. Major blow. Good catch. Not saying that this random routine is no good, but one has to be careful and watch out for unwanted patterns. And filling the screen with random dots is something we tend to do from time to time. .setPixel(rand(256), rand(192), My distribution was not alternating the use of the random numbers for x- and y-coordinates. I only used it for the x-coordinates. .setPixel(rand(256), i++, Exponential growth is evident, but not as significant as when I tried multiple seeds to get something that looked like random stars. Some seeds were better than others. Again, I can't believe the difference. Maybe I'm doing something wrong. Edited December 15, 2016 by sometimes99er 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 16, 2016 Author Share Posted December 16, 2016 I double-checked Tursi's implementation (outputting and filling 24K RAM from 8K ROM and dumping RAM (F10)) against my and Rasmus' implementation, and they're all returning the same numbers. So that's not explaining why I get more lines than Rasmus. I tried different variations, and ended up tilting the lines to get the same result. .setPixel(rand(256), rand(256)*256/341, Quote Link to comment Share on other sites More sharing options...
Tursi Posted December 17, 2016 Share Posted December 17, 2016 My lines were slightly different too, but the fact they were lines at all was enough for me. Interesting results though! 1 Quote Link to comment Share on other sites More sharing options...
Asmusr Posted December 17, 2016 Share Posted December 17, 2016 I used: plot(rand() & 0xff, ((rand() & 0xff) * 192) >> 8 ) 1 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 19, 2016 Author Share Posted December 19, 2016 I used: plot(rand() & 0xff, ((rand() & 0xff) * 192) >> 8 ) Excellent. Thanks. 192 / 256 = 0.75 ≈ 256 / 341 What I used is like {>0001, …, >ffff} % 192 → {0, …, 191) 2 Quote Link to comment Share on other sites More sharing options...
sometimes99er Posted December 22, 2016 Author Share Posted December 22, 2016 (edited) And here's the demo. You're looking out into space from your little spaceship. Turn the ship in any direction using your joystick controller. Stars move in 5 planes. 4 planes of stacked hardware sprites and 1 plane of software sprites. The planes has 6, 7, 9, 10 and 16 stars. Also ... Respecting the original TMS9918A limitation of only 4 sprites per horizontal raster line. No need for 32K RAM Memory Expansion. Fits the FlashROM99. Label in post #29. Edited December 24, 2016 by sometimes99er 7 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.