-
Content Count
499 -
Joined
-
Last visited
Posts posted by vidak
-
-
-
Did some more work today, but didn't finish setting up the translation of the output of the RNG into graphics pointers.
I am using hex debug symbols in place of proper graphics, and I have to do some masking to show $00 through to $40, and it seems I have to do that in the kernel.
Honestly what I think I will do is probably just create all the graphics first, because none of this nonsense about calculating the debug graphics in the kernel will actually be in the final kernel, and it is a lot of work that will in the end be wasted.
I would complete the game faster if I worked on graphics now, and just put them in the game, AND THEN tested the randomness of the open world.
Anyway, find attached the current source code.
==
PS. Someone has forked my source on github, which is flattering.
-
Also
- Bank Switching: I may split the overworld kernel between two banks, if I run out of space for graphics in one bank. But that is a hairy task
-
-
Okay. Spent another 3 hours on the game today.
Remember the source code is here:https://github.com/bootlicker/guerrilla-game
Today I:
- Completed the logic for switching out the kernel, and for switching TO the kernel
- Fixed some bugs in converting the 16-bit LFSR output into four 6-bit numbers
- Restructured the code, and made lots of comments
I still have to:
- Translate the four 6-bit numbers into the pointers for four GRP1 objects
--
THEN I think a demo will be ready.
I am anticipating a LOT of bugs, and I predict the game will not compile the first time. This is the easy part: creating the final draft of the code. The awful part will be debugging everything.
-
Hmm.. well, I could be really careful and compute it, but I'm thinking the perceived randomness will be random enough. I'll try and get a working demo going, and if it is obviously not random, then I'll go back and try and fix it.
-
Someone on twitter reports:
(16 CHOOSE 6) * 2^(16-6)
-
Someone on reddit says the number of unique combinations doesn't change! Apparently it's just 2^16.
What you put in, is what you get out, apparently!
-
Okay this is my new reasoning:
these are the four sets of AND masks:
1111110000000000 0000000000111111 0000011111100000 0011100000011100
So that should be, from the bottom (16 CHOOSE 6) * (16 CHOOSE 6) * (16 CHOOSE 2) * (16 CHOOSE 2)
Which is...8008 * 8008 * 120 * 120
Which is... enormous. Far greater than 16^2. But that can't be right...Or is it 6! * 6! * 2! * 2!, which is still enormous...
-
I think the solution is
(16 CHOOSE 6) * (16 CHOOSE 6) * (16 CHOOSE 6) * (16 CHOOSE 6)
which is 8008 * 8008 * 8008which is enormous.
I think this is the correct solution.
EDIT: No, this is wrong, because the sets have similar objects within them.Some of the sets have unique objects, and some of them don't...
-
Or it could be 65536 CHOOSE 64, which is enormous.
-
Okay I have written the code for transforming the output of the 16 bit random number generator into (what I think are) unique indexes for the four zones to be drawn on the screen.
This is the code:
;================================= ; Forming the input of the Random Number Generator ;================================= ; Bit 5 of Map_Coords, the Up bit, is in the carry bit. ; The status register should look like this now: ; ; C76543210 ; UXXXX0RLD ;================================== ;Is the UP flag set in Map_Coords? ;================================== bcc CheckRNG_Down ; If UP flag not set, check DOWN flag ldx #0 jmp ShiftBackwards .CheckRNG_Down ror ; Put D in carry bit bcc CheckRNG_Left ; If DOWN flag not set, check LEFT flag ldx #0 jmp ShiftForwards .CheckRNG_Left ror ; Put L in carry bit bcc CheckRNG_Right ; If LEFT flag not set, check RIGHT flag ldx #255 jmp ShiftBackwards .CheckRNG_Right ror ; Put R in carry bit bcc No_RNG ; If RIGHT flag not set, then no flags set, then exit ldx #255 ;================================= ; Wait for Vertical Blank to End ;================================= ; What we're going to do is use up a blank frame of kernel time ; in order to have enough time to calculate a full row of screens: ; 256 positions on the RNG counter. lda #$00 ; 2 13 sta COLUBK ; 3 16 RNGStartWait: sta WSYNC ;--------------------------------- lda INTIM ; 4 4 bne RNGStartWait ; 2 6 sta VBLANK ; 3 9 - Accumulator D1=0 ;================================= ; Set timer for blank frame ; ; (192 * 76) / 64 = 228 ;================================= RNG_Timer: ldx #228 stx TIM64T ;================================= ; Random Number Generator Routine ;================================= ; SHIFT FORWARDS ShiftForwards: lda Rand8 ; 3 3 lsr ; 2 5 rol Rand16 ; 5 10 bcc noeor ; 2 12 eor #$D4 ; 2 14 - $D4 is the only number I know the inverse to .noeor ; sta Rand8 ; 3 17 eor Rand16 ; 3 20 inx clc bne ShiftForwards beq Pointer_Calc ; SHIFT BACKWARDS ShiftBackwards: lda Rand8 lsr rol Rand16 bcc noeorleft eor #$A9 ; $D4 is the only number I know the inverse to .noeorleft sta Rand8 eor Rand16 inx clc bne ShiftForwards beq Pointer_Calc ;================================================ ; Translation of the output of the random number ; generator into a useful index for selecting ; a random object in ROM. ; ; Here we: ; - Buffer the output of the RNG into RAM ; - Enter into a 4 cycle loop, which masks off ; different portions of the 16 bit random number ; so that a number between 0-63 is produced. ; ; This translates the random number output into ; an index that is 64 positions long, and allows ; us to randomly select 4 different objects from ; that 64 bit number. ; ; A second level of randomness could be introduced ; by generating an 8 bit random number, and masking ; THOSE bits off. But that would make the game ; impossible to seed the same way every time. ; ;================================================ Pointer_Calc: ldy #3 .Pointer_Calc_Loop ldx Rand8 stx Rand_Pointer_Calc8 ldx Rand16 stx Rand_Pointer_Calc16 cpy #3 beq Band_3_Calc cpy #2 beq Band_2_Calc cpy #1 beq Band_1_Calc cpy #0 beq Band_0_Calc .Band_3_Calc lda Rand_Pointer_Calc16 and #%11111100 lsr lsr sta Band_3_Index jmp Done_Calc .Band_0_Calc lda Rand_Pointer_Calc8 and #%00111111 sta Band_0_Index jmp Done_Calc .Band_2_Calc lda Rand_Pointer_Calc16 and #%00000111 asl asl asl sta Rand_Pointer_Calc16 lda Rand_Pointer_Calc8 and #%11100000 rol rol rol rol clc and Rand_Pointer_Calc16 sta Band_2_Index jmp Done_Calc .Band_1_Calc lda Rand_Pointer_Calc16 and #%0011100 asl asl sta Rand_Pointer_Calc16 lda Rand_Pointer_Calc8 and #%0011100 rol rol clc and Rand_Pointer_Calc16 sta Band_1_Index .Done_Calc dey bpl Pointer_Calc_LoopMy main concern is that masking off 6 bits from the random number generator output is actually reducing the number of random numbers from the generator.
I don't quite know the maths, but I am hoping that masking off 6 bits from the 16 bits is not actually making the output of the random number generator less random.
If it is reducing the randomness of the generator -- how else would one produce a random number between 0 and 2^6 four different times over 2^16 different screens?
My thinking was that 64 CHOOSE 4 combinations of objects is a larger number than 2^16, and that way there would always be a unique combination of 4 objects across the 2^16 screens.
The problem is, I don't quite know what mathematical function is being performed by masking off 6 bits from 16 bits.
Any ideas?
==
EDIT:
Is what is happening with my masking 16 CHOOSE 6 = 8008? Of which I am choosing 4 combinations?Is the randomness of that choice then 6! or 6 CHOOSE 4? or is it 64! ?
There's an answer to this, but I have never been good at combinatorial logic and statistics... -
Open World Atari 2600 Game Progress: Out of nowhere, I figured out how to simulate progressing from the beach, to the jungle, to the mountain in the game.
I am gonna use randomly generated "speckles" of playfield graphics on top of layered background colours.
What I will do is allow colours between certain vertical bands of screens up and down the map. I will just have a forward counting, non-rerversible polynomial counter for the random speckles. The playfield of course will be reflected.
-
I'd speculate it's because the circuit board was mounted to the back of the case. No need for extra wires to the front. That way you would just have to mount the sockets on the circuit board and nothing else.
-
1
-
-
You'll figure it out!
What I'd do is use a bit in memory to test if the controller has moved from up or down to centre.
So you'd have
IF (SavedX = 1) AND (CurrentX = 0) THEN ...
-
1
-
-
Okay!
Remember the git repository is here:
https://github.com/bootlicker/guerrilla-gameI have written the code for:
- Walking off the side of one screen and entering in on another
- Updating the map coordinates (I may make a mini-map)
- Created the structure for the code to swap out the kernel and draw the blank frame
- Updated the placeholder graphics to include 2 digits - the kernel will draw $00 to $40, as there are 64 unique objects in the overworld
I still have to:
- Create the logic to switch out the kernel and draw a blank frame on screen-change
- Link the random number generator to the Environment Graphics pointers
Then the scaffolding of the randomly generated overworld will be finished, really. A demo should be available after these things have been achieved.
-
1
-
Riiiigght - well in this game you don't pick up items, you don't have an inventory, and you only really have HP - but I don't like the idea of "loss" in games, so I think i will only count conquering a finite number of bosses.
Having items and upgradable weapons sounds fun, but I don't want to put them in this game.
I think the only other thing I can think of to also include would be randomly generated playfield graphics - so that some parts of a map look more like rocky mountains, some others look more like jungle, and others the beach.
But that would require adding to the kernel, and I am not focusing on that right now.
Thanks for the communication. It makes me feel more positive.
-
At this stage, all I want to do is increment and decrement the random number generator.
I have variables for controlling map movement and map coordinates, and I suppose I can add "teleportation".
I will have variables for controlling the number of soldiers in your company, but the whole point of the game is to walk around the randomly generated world at this point.
It's just an overworld, at this stage.
I'm not sure if that answers your question?
-
Hey, vidak! I am also back from quite a long break, haha.
I am glad to know that you are over the other side of this depression bout. I had to quit my first job over a year ago, and I was out of work for 10 months. It could have been a lot worse, but I would say I had a bit of depression, as well. Thankfully, I now have a better job that I am enjoying, and have some side projects going on as well, training for an upcoming marathon, learning to play guitar, and building my first AR-15, to name a few. Today I had the day off for Easter, and I am off tomorrow, as well. This will be my first time having two days off in a row, so I hope to spend some time on my game tomorrow.
I took a look at your latest .bin, and I see you have completed the player movement and animation. Looking good!
Thanks man!! The AR-15 sounds super cool!
I have loads of cycles left over in the kernel, so I will return at put more stuff in!
-
1
-
-
From looking at the Jump2 paper Bogax posted, it does look like one can come up with a simple mathematical function for a vector of fixed number of jumps forwards and backwards on a Linear Feedback Shift Register.
However Ockham's Razor would really favour the "single blank frame every screen transition" solution. You'd have 192x76 cycles to play with.
I am really intruged by the maths of the fixed jump number vector though.
I am gonna experiment with the math function just for fun, to see if it can fit into overscan. Why not have a bit of fun? It would make for insanely large RNG worlds if combined with the blank frame method.
This is very typical of me, I make things way more difficult than they have to be.
Another method that occured to me is to have maybe 2 8 bit counters, and swap between them when you move off the edge of one of them - like what happens in Fallout 4 - you only load certain areas into memory when you're actually in them.
-
Holy shit! Thanks Bogax!
Also thanks Thomas Jentzsch!
I'm not too bad with maths - so I may be able to wrap my head around this.
I was thinking about running 2 counters in each dimension, but the problem is that doesn't necessarily solve the problem - it would only give you one row and one column of the world.
For instance, you would get the same value on every Y coordinate of a /different/ X coordinate. So you'd move left and right, and the Y random number would stay the same.
You could combine the two counters though... I wonder if XOR with a proper tap would create a sufficiently random but reversible number...
-
OKAY.
First of all, in between finishing Phase Two of the game development and starting this new phase, I learned how to use git properly, so you can find the repository of the source code for this game here:
https://github.com/bootlicker/guerrilla-game
I was able to begin the work on the randomly generated overworld map today, instead of Tuesday. I think tomorrow will be busy, and I will be able to return to work on this on Tuesday.
16-bit Linear Feedback Shift Registers
This person's blog series contains a useful rundown of exactly how a polynomial counter (a Linear Feedback Shift Register) works to create a one-dimensional sequence of pseudo-random numbers.I opted for a 16-bit LFSR because I thought I had the RAM left over to do so. I was initially very excited to try and build a large world based on the two bytes of RAM, which would afford 65000+ screens of randomly generated content. I imagined I could generate a 256 screen x 256 screen square world.
I figured out that masking off 6 bits of the 16 bit number output of the LFSR would allow me to create 64 Choose 4 Combinations of unique screens across 65k+ screens. This would guarantee that virtually every screen on a 256x256 world would be a unique screen. A combination is the measure of the number of possible sets of some finite number of different objects that can be made from a smaller selection of that superset. A combination is different from a permutation because it doesn't care about the order of the smaller set of unique items. The fact that one of the superset's items appear at all makes a unique combination. If some other ordering of the same set of objects appears, combinatorial counting does not count that set again.
The main problem is, 256 horizontal screens is too large a number of screens to calculate in order one at a time.
This standard/famous code from batari takes at most 20 cycles to execute:
lda Rand8 ; 3 3 lsr ; 2 5 rol Rand16 ; 5 10 bcc noeor ; 2 12 eor #$D4 ; 2 14 .noeor ; sta Rand8 ; 3 17 eor Rand16 ; 3 20There are 37 x 76 cycles in Vertical Blank, and there are 30 x 76 cycles in Overscan, which means if I dedicated ALL the cycles in Overscan, which is currently empty for me right now, I would only be able to calculate at most 114 loops of the above.
So there is clearly a computational limit to calculating new rows of content in this pseduo-two-dimesional method.
I have done some brief research on two-dimensional LFSRs. This is the structure of the usual 2D LFSR:

The CN network on the left are registers of identical length with different XOR taps. These different registers are then selected to output into a small array of registers of identical length. The control unit then chooses which output register to mux with which input register. N is the bit length of the registers, and M is the number of parallel registers. One register at a time is looped back around into the CN network, and one register at a time is outputted from the CN network.
This confirms my suspicion - I think in order to reduce the amount of cycles required to compute some non-single iteration of the LFSR, a large amount of RAM would be needed.
As I am writing this, I'm wondering if it isn't possible somehow to "buffer" the surrounding screens of the current screen the player might be inhabiting. Calculating the left and right screen only requires one iteration of the feedback register. I wonder if there's a mathematical trick to reduce the amount of cycles required to go through in order to calculate a new Y-index of the overworld, instead of a new X-index. Perhaps a lookup table? A lookup table could perhaps be used to do multiple loops through the feedback register, just a row up and a row down. It might be possible to do some algebra and find out what XOR $D4 to XOR $D4 to XOR $D4 to an unknown number is...
Otherwise every movement up or down a Y-index is a full-row's worth of screens' calculations...
There has got to be a faster way... Feel free to jump in and give your two cents!
==
On the other hand, one could remove the STA and LDA functions and speed the routine up quite a bit... You know, just have EOR $D4
-
Wow, very cool!
I'm glad someone sorted it out!
-
Awesome!
Definitely might make designing a kernel faster!


"Sierra Maestra", an Early WIP
in Atari 2600 Programming
Posted
i think i'm understanding better how to colour the canopy