Lunarcy is a fun and simple lunar lander game written in Apple Logo and compatible with microM8’s microLogo, a vector-based OpenGL re-implementation. You control a lander with a joystick (or the keyboard’s arrow keys) and you can increase / decrease fuel to the main engine, and thrusters on the side of the craft as you try to land on the flat part of the moons surface. You need to avoid the hills or you will crash, but be careful – if you run out of fuel you will drop like a stone.
Once you’re inside Apple Logo, type LOAD “LUNARCY to load Lunacy from the disk image available at paleotronic.com/lunarcy.dsk or enter the listing by hand.
Type game to start. You can also find Lunarcy inside the microM8 Apple II emulator file catalog at /appleii/logo/lunarcy.lgo
Description of Logo procedures:
start.level is called to generate the terrain by calling generate.terrain, then calls draw.terrain to draw the terrain.
generate.terrain creates a terrain by generating random terrain heights across the screen, including a consecutive safe zone for landing. It builds a list of y values in the variable :tlevels, calling calc.terrain to calculate the level of the terrain in a given segment.
draw.terrain is called at the start of the level, and uses the data in the :tlevels variable to draw the moon surface. It does this by using setpos with the turtles pen in the down position which draws lines as it moves from point to point on the screen.
test.segment checks the landers horizontal and vertical position to determine if the lander is below the tip of the rocks. If so, the function returns 1, or 0 if the lander is safe. This function is called once per game.loop
game.loop is the main loop for the lander game. it draws the ship, waits for half a second (for the video to show), then checks the keyboard and the paddles for changes that will affect the crafts thrusters. It applies those changes by calling apply.forces, and updates the text display at the bottom of the screen with the remaining fuel, vertical velocity and horizontal velocity. It then calls test.segment, and if the craft has hit the mountains, it will call crash.terrain, otherwise it checks if the lander has reached the surface, and if so it calls eval.landing to test if the landing was safe. If none of those conditions were met, it calls itself (game.loop) again recursively to continue.
check.paddles checks the joystick/paddle controls. horizontal motion puts fuel to the side thrusters, vertical motion increases or decreases fuel to the main engine. check keys does the same, except rather than checking the stick, reads the state of the arrow keys. It calls one of thrust.up, thrust.down, thrust.left or thrust.right to alter the burn parameters.
apply.forces applies the current fuel settings, and adjusts the thrust levels and speed of the craft in the horizontal and vertical directions. It handles acceleration due to the moon’s gravity as well. To keep things fast, approximations are used.
eval.landing checks to make sure the craft is not too tilted, and that the horizontal and vertical speeds of the craft are below the safety threshold (1 m/s). If a safe landing is achieved, it reduces the width of the safe zone, and calls start.level to start a new level with a new terrain.
draw.ship moves the turtle (acting as our ship) to the correct position on the screen, and sets the “tilt” of the ship if it is moving horizontally.
abs returns the ‘absolute value’ of a number by changing a negative number into a positive one.
The four thrust procedures increase or decrease the fuel usage, or change the thrust balance between the two rockets as appropriate.
calc.terrain calculates the height of a segment in the terrain for generate.terrain.
crash.terrain outputs a message to let the player know they have crashed.
MAKE “angle :FUELATTITUDETHRUSTER * 5
PR [YOU CRASHED INTO THE ROCKY TERRAIN]
MAKE “fuelAttitudeThruster :FUELATTITUDETHRUSTER – :HDELTA
MAKE “fuelAttitudeThruster :FUELATTITUDETHRUSTER + :HDELTA
MAKE “fuelEngine :FUELENGINE + :VDELTA
IF not KEYP [STOP]
MAKE “CODE ASCII READCHAR
IF :CODE = 21 [THRUST.RIGHT STOP]
IF :CODE = 8 [THRUST.LEFT STOP]
IF :CODE = 10 [THRUST.DOWN STOP]
IF :CODE = 11 [THRUST.UP STOP]
IF :CODE = 27 [THROW “toplevel]
MAKE “fuelEngine :FUELENGINE – :VDELTA
IF :FUELENGINE < 0 [MAKE “fuelEngine 0]
TO draw.terrain :SEGMENTS
MAKE “idx 1
MAKE “y -64
MAKE “x -139
REPEAT :SEGMENTS [MAKE “y ITEM :IDX :TLEVELS MAKE “x :X + 279 / :SEGMENTS SETPOS LIST :X :Y MAKE “idx :IDX + 1]
MAKE “totalFuel 3000
MAKE “fuelEngine 0
MAKE “fuelAttitudeThruster 0
MAKE “fuelThrustRatio 0.09
MAKE “shipX 0
MAKE “shipY 94
MAKE “thrustY 0
MAKE “thrustX 0
MAKE “speedX 0
MAKE “speedY 0
MAKE “moonGAccel 1.62
MAKE “landMaxSafeV 1
MAKE “hDelta 1
MAKE “vDelta 10
MAKE “angle 0
MAKE “segments 40
PR [GENERATING THE MOON]
GENERATE.TERRAIN :SEGMENTS :SAFE
MAKE “aa ABS :ANGLE
MAKE “ay ABS :SPEEDY
MAKE “ax ABS :SPEEDX
IF :AY > :LANDMAXSAFEV [MAKE “shipY -64 LT 80 DRAW.SHIP PR [CRASHED INTO SURFACE – UNSAFE DESCENT] THROW “toplevel]
IF :AX > :LANDMAXSAFEV [MAKE “shipY -64 LT 45 DRAW.SHIP PR [CRASHED INTO SURFACE – UNSAFE DESCENT] THROW “toplevel]
IF :AA > 10 [MAKE “shipY -64 DRAW.SHIP PR [CRASHED INTO SURFACE – UNSAFE ANGLE] THROW “toplevel]
PR [HOORAY YOU LANDED SAFELY]
MAKE “safe :SAFE – 2
IF :SAFE < 4 [PR [NICE WORK – ACE LANDER] THROW “toplevel]
PR [LETS MAKE IT A BIT HARDER]
MAKE “ay ABS :SPEEDY
MAKE “ax ABS :SPEEDX
SETCURSOR [0 20]
PR SENTENCE “FUEL :TOTALFUEL
SETCURSOR [0 21]
PR SENTENCE “VY :AY
SETCURSOR [0 22]
PR SENTENCE “VX :AX
MAKE “crash TEST.SEGMENT :SEGMENTS
IF :CRASH = 1 [CRASH.TERRAIN STOP]
IF :SHIPY < -64 [EVAL.LANDING]
IF :SHIPY > 100 [PR [FLUNG INTO SPACE] STOP]
TO generate.terrain :SEGMENTS :SAFE
MAKE “av :SEGMENTS – :SAFE
MAKE “safestart RANDOM :AV
MAKE “idx 1
MAKE “tlevels 
REPEAT :SEGMENTS [MAKE “rval CALC.TERRAIN :IDX :SAFESTART :SAFE MAKE “tlevels LPUT :RVAL :TLEVELS MAKE “idx :IDX + 1]
MAKE “fe :FUELENGINE
MAKE “fa :FUELATTITUDETHRUSTER
IF :FE > :TOTALFUEL [MAKE “fe :TOTALFUEL MAKE “fa 0]
MAKE “thrustX :FUELTHRUSTRATIO * :FA
MAKE “thrustY :FUELTHRUSTRATIO * :FE
MAKE “totalFuel :TOTALFUEL – :FE – ABS :FA
MAKE “speedY :SPEEDY – :MOONGACCEL + :THRUSTY
MAKE “speedX :SPEEDX + :THRUSTX
MAKE “shipX :SHIPX + :SPEEDX
MAKE “shipY :SHIPY + :SPEEDY
MAKE “SAFE 20
MAKE “v 255 – PADDLE 1
MAKE “f :V / 255
IF :F < 0.167 [MAKE “f 0]
MAKE “fuelEngine 24 * :F
MAKE “h PADDLE 0
MAKE “a :H – 127
MAKE “fa :A / 127
MAKE “fuelAttitudeThruster :FA * 5
TO TEST.SEGMENT :SEGMENTS
MAKE “x XCOR
MAKE “y YCOR
MAKE “ssize 280 / :SEGMENTS
MAKE “rseg ROUND :SEG
IF :RSEG > :SEGMENTS [MAKE “rseg :SEGMENTS]
MAKE “fl ITEM :RSEG :TLEVELS
IF :Y > :FL [OUTPUT 0]
IF :FL = -64 [OUTPUT 0]
TO ABS :NUMBER
IF :NUMBER < 0 [OUTPUT 0 – :NUMBER]
TO CALC.TERRAIN :IDX :SAFESTART :SAFE
IF :IDX < :SAFESTART [OUTPUT -64 + RANDOM 64 STOP]
IF :IDX > :SAFESTART + :SAFE [OUTPUT -64 + RANDOM 64 STOP]