Jump to content

vidak

Members
  • Content Count

    499
  • Joined

  • Last visited

Posts posted by vidak


  1. 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.

    Season 2- 07 APRIL 2018.zip


  2. 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.


  3. 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...


  4. I think the solution is

     

    (16 CHOOSE 6) * (16 CHOOSE 6) * (16 CHOOSE 6) * (16 CHOOSE 6)

    which is 8008 * 8008 * 8008

     

    which 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...


  5. 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_Loop
    

    My 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...


  6. 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.


  7. Okay!

     

    Remember the git repository is here:

    https://github.com/bootlicker/guerrilla-game

     

    I 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.

    • Like 1

  8. 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.


  9. 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?


  10. 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!

    • Like 1

  11. 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.


  12. 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...


  13. 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 20
    

    There 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:

     

    6-Figure4-1.png

     

    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

×
×
  • Create New...