Lunar lander

Old school style. I wrote this in 2011, before there was font support, thus the 7 segment display code.

All three dots need to be green to not crash when contacting the platform.

The version of Codea I ran it on today looks to be 2.3.1 (47)

function setup() -- Lander
    delayCalib=200000  -- For the iPad 2 (hacky way to tune play speed)
    -- delayCalib=3400000  -- For the iPad Air
    touches={}
    thrust=0
    dx=0
    dy=-15
    dxx=0
    dyy=0
    rot=0
    fuel=250
    displayMode(FULLSCREEN_NO_BUTTONS)
    ground = {}
    ground[0] = math.random(60)+112
    flatsx={}
    flatsy={}

   -- create the three landing zones
    i=0
    while i <= WIDTH do
        i=i+1      
        ground[i] = ground[i-1] + math.random(11) - 6
        
        if ground[i] < 112 then
            ground[i] = 112
        end
    
        if i==100 then 
            flatsx[0] = i
            flatsy[0] = ground[i]
            while i <= 120 do
                i=i+1
                ground[i] = flatsy[0]
            end
        end 
        if i == WIDTH/2 then
            flatsx[1] = i
            flatsy[1] = ground[i]
            while i<=WIDTH/2+30 do
                i=i+1
                ground[i] = flatsy[1]
            end 
        end 
        if i == WIDTH-100 then
            flatsx[2] = i
            flatsy[2] = ground[i]
            while i<=WIDTH-100+20 do
                i=i+1
                ground[i] = flatsy[2]
            end
        end    
    end

-- lander has two possible start positions
    landerx=(math.random(2)-1)*260+WIDTH/2-130
    landery=HEIGHT
    
    ended=0
    crashed=0
    gravity=.6
    segScale=12
    piece1rot=0
    piece1x=0
    piece1y=0
    piece2rot=0
    piece2x=0
    piece2y=0
    piece3rot=0
    piece3x=0
    piece3y=0
    piece4rot=0
    piece4x=0
    piece4y=0
end

function touched(touch)
    if touch.state == ENDED then
        touches[touch.id] = nil
    else
        touches[touch.id] = touch
    end
end

-- 7 segment display, origin of display is lower left

function segA()
line(0, segScale*2, segScale, segScale*2)        
end

function segB()
line(segScale, segScale, segScale, segScale*2)
end

function segC()
line(segScale, 0, segScale, segScale)    
end

function segD()
line(0, 0, segScale, 0)    
end

function segE()
line(0,0,0,segScale )    
end

function segF()
line(0,segScale,0,segScale*2)  
end

function segG()
line(0,segScale,segScale,segScale)    
end

function drawDigit(digit)
if digit == '0' then
    segA()  
    segB() 
    segC() 
    segD()
    segE()
    segF()
elseif digit ==  '1' then
    segB()
    segC()
elseif digit == '2' then
    segA()
    segB()
    segG()
    segE()
    segD()
elseif digit == '3' then
    segA()
    segB()
    segG()
    segC()
    segD()
elseif digit == '4' then
    segF()
    segB()
    segG()
    segC()
elseif digit == '5' then
    segA()
    segF()
    segG()
    segC()
    segD()
elseif digit == '6' then
    segA()
    segF()
    segG()
    segE()
    segC()
    segD()
elseif digit == '7' then
    segA()
    segB()
    segC()
elseif digit == '8' then
    segA()  
    segB() 
    segC() 
    segD()
    segE()
    segF()
    segG()  
elseif digit == '9' then
    segA()  
    segB() 
    segC() 
    segF()
    segG() 
elseif digit == ':' then
    line(segScale/2, segScale/2, segScale/2, segScale/2-1)    
    line(segScale/2, segScale+segScale/2, segScale/2, segScale-1+segScale/2)    
elseif digit == ' ' then
-- blank    
elseif digit == '-' then
    segG()
else
    print("unknown digit"..digit)
end
end

function drawNumber(number,x,y)
    pushMatrix()
    translate(x,y)
    len= string.len(number)
    for i = 1, len do 
        drawDigit(string.sub(number, i,i))
        translate(segScale+segScale/5+1, 0)
    end
    popMatrix()
    return (len+1)*segScale
end


function draw()
    background(0, 0, 0, 255)
    noSmooth()
    stroke(255,255,255)
    fill(255,255,255)
    
    pushStyle()
    strokeWidth(2)
    stroke(186, 186, 186, 255)  
    fill(54, 54, 54, 255)  
    rect(0,0,75,75)              
    rect(100,0,75,75)

    stroke(214, 194, 58, 255)
    fill(45, 45, 26, 255)        
    rect(WIDTH-75,0,75,75)
    popStyle()    
    
    rect(0,100,WIDTH,1)
    
    thrust = 0
    
    for k,touch in pairs(touches) do
        x=touch.x
        y=touch.y  
        
        if ended == 0 then 
            if x < 75 and y < 75 then
                rot = rot + 3
            end
    
            if x > 100 and x < 175 and y < 75 then
                rot=rot-3
            end
        
            if x > WIDTH-75 and y < 75 then
                thrust = 1
            end
        end
    end
    
    if ended == 0 then
        landerx = landerx + dx/30
        landery = landery + dy/50
        dy = dy - gravity
    end
    
    dyy = math.cos(math.rad(rot))
    dxx = math.sin(math.rad(rot))
    
    if thrust == 1 and ended == 0 and fuel > 0 then
  --      sound("explode", 0)      sounds not multi threaded :(
        dy = dy + 2*dyy      
        dx = dx - 2*dxx
        if fuel >= 1 then
            fuel=fuel-1
        end
    end
    
    -- draw the lander      
    strokeWidth(1)
    stroke(255, 255, 255, 255)  
    
    pushMatrix()  
    translate(landerx,landery)  
      
    if crashed == 0 then 
        rotate(rot) 
        line(3,0,-3,0)
        line(-3,0,-6,3)    
        line(-6,3,-6,8)
        line(-6,8,-3,11)
        line(-3,11,3,11)
        line(3,11,6,8)
        line(6,8,6,3)
        line(6,3,3,0)
    
        line(-4,1,-8,-7)
        line(4,1,8,-7) 
    
        if thrust == 1 and ended == 0 and fuel > 0 then
            pushStyle()
            c = color(255, 134, 0, 255)
            fill(c)
            stroke(255,134,0,255)
            rect(-1, -13, 2, 8)
            popStyle()
        end          
    else -- lander exploding
        piece1rot = piece1rot - 6
        piece1x = piece1x -.2
        piece1y = piece1y -.4
        piece2rot = piece2rot + 2
        piece2x = piece2x - .4
        piece2y = piece2y + .3
        piece3rot = piece3rot - 4
        piece3x = piece3x + .7
        piece3y = piece3y + .6
        piece4rot = piece4rot + 2
        piece4x = piece4x -.2
        piece4y = piece4y + .1
        
        translate(piece1x, piece1y)
        pushMatrix()      
        rotate(piece1rot)
        line(3,0,-3,0)
        line(-3,0,-6,3)    
        popMatrix()      
        
        translate(piece2x, piece2y)
        pushMatrix()      
        rotate(piece2rot)
        line(-6,8,-3,11)
        line(-3,11,3,11)
        line(3,11,6,8)  
        popMatrix()  
    
        translate(piece3x, piece3y)
        pushMatrix()          
        rotate(piece3rot)
        line(3,11,6,8)
        line(6,8,6,3)
        line(6,3,3,0)
        popMatrix()
        
        translate(piece4x, piece4y)
        pushMatrix()
        rotate(piece4rot)
        line(-4,1,-8,-7)
        popMatrix()          
    end  
    popMatrix()
    
    for i = 0, delayCalib do
    end

    spacing = 80
    pos1 = WIDTH/2-100
    pos2 = pos1+spacing
    pos3 = pos2+spacing
    pos4 = pos3+spacing      

    drawNumber(-math.floor(dy), pos1, 5)  
    drawNumber(math.floor(dx), pos2, 5)
    drawNumber(math.abs(rot), pos3,5)  
    drawNumber(fuel, pos4, 5)
    green = color(20, 181, 63, 255) 
    red = color(207, 22, 14, 255)

    if math.abs(dy) > 12 then
        fill(red)
    else
        fill(green)
    end
    ellipse(pos1+segScale, 60, 15)
    if math.abs(dx) > 5 then
        fill(red)
    else
        fill(green)
    end
    ellipse(pos2+segScale, 60, 15)
    if math.abs(rot) > 7 then
        fill(red)
    else
        fill(green)
    end
    ellipse(pos3+segScale, 60, 15) 
    if fuel < 150 then      
        if fuel<50 then
            fill(red)
        else
            fill(237, 230, 59, 255) 
        end
        if math.floor(ElapsedTime%2) == 0 then --flash
            ellipse(pos4+segScale, 60, 15)
        end
    end    
    
    stroke(255,255,255)
      
    for i = 0,WIDTH do
        line(i,ground[i],i,ground[i]-1)
    end
              
-- check to see if landed or crashed
    for i=0,2 do  
        if i==1 then 
            platformWidth = 30
        else
            platformWidth = 20        
        end
        line(flatsx[i], flatsy[i], flatsx[i]+platformWidth, flatsy[i])
            
        if ended == 0 then 
            if landerx > flatsx[i]+3 and landerx < flatsx[i] +platformWidth-3 and landery <= flatsy[i] + 8 then
                if math.abs(dy) <= 12 and math.abs(dx) <= 5 and math.abs(rot) <= 7 then
                    print("Landed!!!")        
                elseif math.abs(dy) > 12 or math.abs(dx) > 7 then
                    print("Crashed!!! Too fast")
                    crashed = 1
                elseif math.abs(rot) > 5 then
                    print("Crashed!!! Not vertical")
                    crashed = 1
                end
                ended = 1
            end
        end
    end 
    
    if landery <= ground[math.floor(landerx)]+8 and ended == 0 then        
        print("Crashed!!! Missed platform")
        ended = 1
        crashed = 1
    end
     
end

@Ophitoxaemia Nice program. I haven’t been able to land yet, I keep crashing. I corrected an error in your code. Not sure why it was there. You had

ground[i] = flatsy[2]850000

I commented out the 850000 in the code above.

Thanks! And I removed the number entirely, must have gotten pasted in somehow when I copied the code.

The center pad is the easiest (and largest). The initial lander location is set so there is always a closer small pad and the far one (hardest).

@Ophitoxaemia I increased my fuel amount and was able to land 1 time so far. The controls are very touchy and you have to do it just right. Are you going to rewrite the code to use the updated functions.

I was able to land on all three types of platforms just now. Only the farthest one should fuel be a problem (I had 42 fuel left). The trick is to wait as long as possible to start the burn (but not too long!)

If your iPad is faster than an iPad 2, you might want to adjust delayCalib to slow the game down.

I think I’ll just leave this one and write a new game, if I do.

@Ophitoxaemia I’m on an iPad Air, so things run a lot faster than an iPad 2. Maybe I should try it on my iPad 1.

I had a slightly different version for my iPad 1.

But you don’t need to do that.

Change this line in the setup() function on your iPad Air:

delayCalib=200000 – hacky way to tune play speed

such that it takes 20 seconds for the lander to fall from the top and crash, not touching any controls.

I had to change the 200,000 to 3,400,000 for the 20 sec free fall. That made it easier to land.

BTW, for the younger readers, the inspiration for this was a game I remember from the Arcades of Old: https://en.wikipedia.org/wiki/Lunar_Lander_(1979_video_game)

You can use delta time updating to standardise the velocity of game objects across the various devices it’ll be played on