Close

Are We There Yet?

A project log for Lunar Lander for the PDP-1

My PDP-1 Replica (PiDP-1) from Obsolescence Guaranteed has arrived and I want to do something cool with it.

michael-gardiMichael Gardi 01/03/2026 at 15:460 Comments

Right now the LEM is like a ghost to the terrain, it just slips right through on its way down to the bottom of the screen. Obviously I will need to know when the LEM reaches the ground in order to update the score with a good landing or generate an explosion.  Collision detection is a big part of video games. 

Conceptually the technique here is pretty simple:

Again here is where the screen coordinates, with the origin 0,0 being at the center of the screen, caused me some grief. I was having issues with negative values. It didn't help that the PDP-1 uses 1's compliment for negatives whereas everything else I was familiar with used 2's compliment. So I struggled with my implementation.

Finally I took a step back and decided that for purposes of collision detection I could use only positive screen coordinates where the x and y axis ranged from 0 to 1023. Once I made that decision the coding took less than an hour to get working. 

The first thing I did was to add some code in the terrain generation Python script to emit a table with the y values expressed as 0-1023 integers.

print("y-coordinates")
count = 0
for y in smoothed_data:
    print(count,str(int(y)).zfill(3),'\t\t',oct(int(y))[2:].zfill(6))
    count = count+1

It turned out that this approach was faster too because I bypassed having to unpack and convert the Y coordinate from the terrain point. I still converted the values to octal so that they could be inserted into the Lunar Lander code.

/ Y-coordinates of the terrain points in 0-1023 screen coordinates. 
ypt, 000620
    000601
    000534
    000455
    000404
    000360
    000360
    000360
    000360
    000360
    000360
    000353
    000322
    000253
    000203
    000151
    000143
    000130
    000105
    000061
    000042
    000035
    000036
    000036
    000036
    000036
    000036
    000041
    000052
    000066
    000100
    000105
    000106
    000106
    000106
    000106
    000106
    000105
    000100
    000067
    000057
    000051
    000050
    000047
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000050
    000051
    000061
    000074
    000110
    000117
    000120
    000127
    000142
    000156
    000166
    000170
    000175
    000207
    000223
    000234
    000237
    000240
    000240
    000240
    000240
    000240
    000245
    000266
    000315
    000343
    000357
    000362
    000401
    000430
    000457
    000475
    000500
    000506
    000521
    000535
    000545
    000550
    000547
    000550
    000550
    000550
    000550
    0                   / End of data marker.

 Then I added the following code:

/ Check for LEM terrain intersection.
     lac gmx            / Convert the LEM game coordinates to 0-1023 
     sar 3s             /  screen coordinates by dividing by 8.
     dac scx
     lac gmy
     sar 3s
     dac scy
    
     lac scx            / Create a bounding box.
     add (27
     dac xmx
     lac scx
     sub (7
     dac xmn
     lac scy
     add (11
     dac ymx
     lac scy
     sub (22
     dac ymn

/ Iterate through the terrain points to see if any intersect with the LEM's bounding box.
     dzm xct            / Zero x coordinate counter.
     init chi,ypt       / Point to first word of terrain y 0-1023 data.
chi, lac .              / Get the next y coordinate.
     sza i              / Not zero?
     jmp vvl            /   No - Done checking intersections.
     dac yct            /  Yes - Save Y value for further checking.

     sub ymx            / Check top of bounding box.
     sma                / Negative?
     jmp nxp            /   No - y is above the bounding box.

     lac yct            /  Yes - Check bottom of bounding box.
     sub ymn    
     spa                / Positive?
     jmp nxp            /   No - y is below the bounding box.

     lac xct            /  Yes - Check left of bounding box.
     sub xmn        
     spa                / Positive?
     jmp nxp            /   No - y is left of the bounding box.    

     lac xct            /  Yes - check right of bounding box.
     sub xmx
     sma                / Negative?
     jmp nxp            /   No - point does not intersect.

     jmp a2             / If we get here the point is inside the LEM's bounding box.

nxp, lac xct            / Advance to next x coordinate.
     add (12            / Add 10 to the x coordinate.
     dac xct
     idx chi            / Index to the next y coordinate.
     jmp chi

These checks should be pretty fast since there will be a lot of "early exits".  Having just written that I now realize that the order of these checks could be optimized even further to encourage these early exits, probably starting with the bottom check first since there is no point in continuing if the bottom is above the terrain which will be the case for most of the decent. Added to my TODO list. 

In this video I have added four dots around the LEM that show where the bounding box is.

Right now when a collision is detected the game just resets. Time for something more interesting, explosions.

Discussions