Recent

Author Topic: Rudimentary Lunar Lander  (Read 481 times)

TBMan

  • Full Member
  • ***
  • Posts: 113
Rudimentary Lunar Lander
« on: March 27, 2025, 09:23:18 pm »
I remember years ago there was this neat lunar lander game, nothing but black and white, with a lander that you had to land on a lunar surface that got larger as you got closer to it. Now I'm no math expert and I don't understand vectors, thrust, mass and velocity, but I can add and subtract so I devised this method of the effects of gravity and thrust.  I have no info on distance from the moon yet, or any lunar surface. I did not put in rotation of the LEM for the side thrusters, but I might as I recently did some things with rotation.  This runs on a Windows PC.

This is the first time I used page flipping for animation and it worked out fairly well.

Code: Pascal  [Select][+][-]
  1. program gravity6;
  2.  
  3. // trying a simplified lunar simulator
  4. // later versions I will attempt to draw the moon surface that enlarges as you get closer
  5.  
  6.  
  7. uses
  8.   windows,ptccrt,                 // keep this order of units
  9.   ptcgraph,sysutils;
  10.  
  11.   const
  12.    gravitycyclemax = 8000;
  13.    thrustCycleMax = 5000;
  14.  
  15.   Type
  16.    VideoPages = (Page0,Page1);
  17.  
  18.    ThrustSide = (Left,None,Right);
  19.  
  20.    trect = record
  21.       x1,y1,
  22.       x2,y2:integer;
  23.    end;
  24.  
  25. var
  26.   Page:VideoPages = Page0;
  27.  
  28.   ThrustFrom:Thrustside = None;
  29.  
  30.   LemRect,
  31.   mainrect,inforect:Trect;
  32.  
  33.   ThrustCycle,
  34.   gravitycycle:longint;
  35.  
  36.   gdriver,gmode:smallint;
  37.  
  38.   Fuel,
  39.   SideThruster,
  40.   SideThrust,
  41.   Inertia,
  42.   thrust,
  43.   xposition,
  44.   yposition,
  45.   gravity :single;
  46.  
  47.   xpos,ypos:integer;
  48.   OutOfBounds:boolean = false;
  49.  
  50.  
  51. Procedure SetTRect(Var T:Trect;X,Y,XX,YY:integer);
  52. begin
  53. With T do
  54.  begin
  55.      X1 := X;
  56.      Y1 := Y;
  57.      X2 := XX;
  58.      Y2 := YY;
  59.  end;
  60. end;
  61.  
  62. Function RectInRect(SmallRect,Bigrect:Trect):boolean;
  63. Begin
  64.  
  65. Result := (SmallRect.x1 >=BigRect.X1) and (SmallRect.Y1 >= BigRect.Y1)
  66.                   and (SmallRect.X2 <= bigrect.x2) and (SmallRect.Y2 <= BigRect.Y2);
  67. end;
  68.  
  69.  
  70. procedure Nextactivepage;
  71. begin
  72.   if page = page0 then page := page1 else page := page0;
  73.   SetactivePage(ord(Page));
  74. end;
  75.  
  76. Procedure NextVisualPage;
  77. begin
  78.  SetVisualpage(Ord(Page));
  79. end;
  80.  
  81.  
  82. Procedure DrawLEM;
  83. begin
  84. SetTrect(LemRect,Xpos-4,Ypos-8,xpos+4,ypos+5);
  85. if fuel < 400 then
  86.    setcolor(4)   // danger Will Robinson, danger!
  87. else
  88.    setcolor(15);
  89.  
  90. rectangle(xpos-4,ypos-4,xpos+4,ypos+2); // main body
  91.  
  92. rectangle(xpos-2,ypos-8,xpos+2,ypos-4); // top
  93.  
  94. line(xpos,ypos+2,xpos-3,ypos+5);       // left lander leg
  95.  
  96. line(xpos,ypos+2,xpos+3,ypos+5);       // right lander leg
  97.  
  98. setcolor(14);      // paint thrusters if firing
  99.  
  100. if thrust <> 0 then
  101.      line(xpos,ypos+2,xpos,ypos+6);
  102.  
  103. case thrustfrom of
  104.  Left:Line(xpos-4,ypos+2,xpos-6,ypos+4);
  105.  right: Line( xpos+4,ypos+2,xpos+6,ypos+4);
  106. end;
  107.  
  108. setcolor(15);
  109. end;
  110.  
  111. procedure updateinfo;
  112. var
  113. s:string;
  114. begin
  115.   with inforect do
  116.    begin
  117.      setviewport(x1+1,y1+1,x2-1,y2-1,false);  // info viewport
  118.      clearviewport;
  119.      str(Fuel:5:2,s);
  120.      outtextxy(4,4,'Fuel:'+s);
  121.    end;
  122.  
  123.   setviewport(0,0,getmaxx,getmaxy,true);      // full screen
  124.  
  125.   with mainrect do
  126.   begin
  127.     rectangle(x1,y1,x2,y2);
  128.     setviewport(x1+1,y1+1,x2-1,y2-1,true);   // moon viewport
  129.   end;
  130. end;
  131.  
  132. Procedure Init;
  133. begin
  134.   gravitycycle := 0;
  135.   xpos := 200;
  136.   yposition := 200;
  137.   gravity := 0.05;
  138.   Inertia := 0.001;
  139.   ypos := round(YPosition);
  140.   xposition := xpos;
  141.   thrust := 0;
  142.   thrustcycle := 0;
  143.   SideThrust := 0;
  144.   Fuel := 2000;
  145.   SideThruster:= 0;
  146. end;
  147.  
  148. begin
  149.  
  150.   gdriver := vesa;  // adjust initgraph for your system
  151.   Gmode := installusermode(640, 480, 256, 2, 8000, 6000);  // two pages
  152.   WindowTitle := 'LEM';
  153.   initgraph(gdriver, gmode, '');
  154.  
  155.   init;
  156.  
  157.   DrawLEM;     // draw the lander
  158.  
  159.   OutTextXY(20,220,'2 = thrust; 1 = left side thruster; 3 = right side thruster');
  160.   OutTextXY(20,240,'Press any key to begin');
  161.  
  162.   settRect(MainRect,0,0,getmaxx-120,getmaxy-20);
  163.  
  164. with Mainrect do
  165.  begin
  166.       rectangle(x1,y1,x2,y2);
  167.       SetTrect(InfoRect,x2+1,y1,getmaxx-1,y1+40);
  168.  end;
  169.  
  170. updateinfo;
  171. readkey;
  172.   repeat
  173.      if boolean(GetAsyncKeyState(VK_NUMPAD2)) then
  174.         begin
  175.             Thrust := -0.01;
  176.             thrustcycle := 0;
  177.             Thrustfrom := none;     // shut off side thrusters
  178.          end;
  179.      if boolean(GetAsyncKeyState(VK_NUMPAD1)) then
  180.       begin
  181.            thrustcycle := 0;
  182.            SideThrust :=  sidethrust+(thrust)+inertia;
  183.            Sidethruster := sidethrust;
  184.            Thrustfrom := Left;
  185.         end;
  186.     if boolean(GetAsyncKeyState(VK_NUMPAD3)) then
  187.      begin
  188.           thrustcycle := 0;
  189.           sidethrust := sidethrust-(thrust)-inertia;
  190.           sidethruster := sidethrust;
  191.           Thrustfrom := Right;
  192.       end;
  193.  
  194.     gravitycycle := gravitycycle+1;      // animation clock
  195.  
  196.     if gravitycycle = gravitycyclemax then
  197.      begin
  198.          ypos := round(YPosition);
  199.          xpos := round(Xposition);
  200.          nextvisualpage;
  201.          clearviewport;  // clear active page
  202.          gravitycycle := 0;
  203.          yposition := yposition+gravity;
  204.          xposition := xposition+sidethrust;
  205.          gravity := gravity+thrust+inertia;
  206.          ypos := round(YPosition);
  207.          xpos := round(Xposition);
  208.          // set new activepage
  209.          nextactivepage;
  210.          DrawLEM;
  211.          nextvisualpage;  // show the lander
  212.          Fuel := fuel+(thrust-(abs(sidethruster)))*100;
  213.          updateinfo;
  214.     end;
  215.  
  216.     thrustcycle := thrustcycle+1;    // thruster duration clock
  217.  
  218.     if ThrustCycle > ThrustCycleMax then
  219.        begin
  220.        thrust := 0;
  221.        Thrustcycle := 0;
  222.        SideThruster := 0;
  223.        Thrustfrom := None;
  224.        updateinfo;
  225.        end;
  226.  
  227.  If not RectInRect(LemRect,Mainrect) then OutofBounds := true;
  228.  
  229.   until (boolean(GetAsyncKeyState(VK_ESCAPE))) or (Fuel <=0) or OutOfBounds;
  230.   closegraph;
  231.  
  232. end.
  233.  
« Last Edit: March 27, 2025, 11:00:30 pm by TBMan »

Thaddy

  • Hero Member
  • *****
  • Posts: 16770
  • Ceterum censeo Trump esse delendam
Re: Rudimentary Lunar Lander
« Reply #1 on: March 28, 2025, 02:13:44 pm »
In 1978 I introdused random in this game..... Because I could predict . Got bored. (Burroughs terminal, 300 baud connection)

You can write moonlander in seven ....
« Last Edit: March 28, 2025, 02:15:47 pm by Thaddy »
Changing servers. thaddy.com may be temporary unreachable but restored when the domain name transfer is done.

TBMan

  • Full Member
  • ***
  • Posts: 113
Updated version
« Reply #2 on: March 28, 2025, 02:37:58 pm »
Updated version with LEM rotation and landing pad as a goal.

Code: Pascal  [Select][+][-]
  1.  
  2. program gravity7;
  3.  
  4.  
  5. // LEM rotates now
  6. // win scenario added
  7.  
  8. // later versions will attempt to draw the moon surface that enlarges as you get closer
  9.  
  10. // Keypad ...
  11.  
  12. // 5 - centers the LEM
  13. // 2 - fires main thrusters (best when upright)
  14. // 1 - right thrusters
  15. // 3 - left thrusters
  16. // Escape aborts the game
  17. // if crashed or landed q/Q quits exits.
  18.  
  19. uses
  20.   windows,ptccrt,                 // keep this order of units
  21.   ptcgraph,sysutils,math;
  22.  
  23.   const
  24.    gravitycyclemax = 8000;
  25.    thrustCycleMax = 5000;
  26.  
  27.    MaxPoints = 13;
  28.  
  29.   Type
  30.    VideoPages = (Page0,Page1);
  31.  
  32.    trect = record
  33.       x1,y1,
  34.       x2,y2:integer;
  35.    end;
  36.  
  37.  TSinglePoint = record
  38.     x, y: single;
  39.   end;
  40.  
  41. LemLanderType = record
  42.     points:array[0..MaxPoints] of tsinglepoint;
  43.     CenterX,
  44.     CenterY :single
  45. end;
  46.  
  47.  
  48. var
  49.  
  50.   TheLem:LemLanderType;
  51.  
  52.   Page:VideoPages = Page0;
  53.  
  54.  
  55.   LandingPadRect,
  56.   LemRect,
  57.   mainrect,inforect:Trect;
  58.  
  59.   ThrustCycle,
  60.   gravitycycle:longint;
  61.  
  62.   gdriver,gmode:smallint;
  63.  
  64.  
  65.   Speed,
  66.   Fuel,
  67.   SideThruster,
  68.   SideThrust,
  69.   Inertia,
  70.   thrust,
  71.   OldXposition,
  72.   OldYPosition,
  73.   xposition,
  74.   yposition,
  75.   gravity :single;
  76.  
  77.   OldDegrees,
  78.   xpos,ypos:integer;
  79.  
  80.   GoodLanding :boolean = false;
  81.   OutOfBounds:boolean = false;
  82.  
  83.   SpeedString:string;
  84.  
  85.   Key:Char;
  86.  
  87.  
  88. Procedure SetTRect(Var T:Trect;X,Y,XX,YY:integer);
  89. begin
  90. With T do
  91.  begin
  92.      X1 := X;
  93.      Y1 := Y;
  94.      X2 := XX;
  95.      Y2 := YY;
  96.  end;
  97. end;
  98.  
  99.  
  100. Function PositionInRect(PX,PY:Integer;Rect:Trect):boolean;
  101. Begin
  102.  
  103. PositionInRect := (Px >= Rect.X1) and (Px<=Rect.X2) and
  104.                   (Py >= Rect.Y1) and (PY<=Rect.Y2);
  105.  
  106. end;
  107.  
  108.  
  109.  
  110.  
  111. Function RectInRect(SmallRect,Bigrect:Trect):boolean;
  112. Begin
  113.  
  114. Result := (SmallRect.x1 >=BigRect.X1) and (SmallRect.Y1 >= BigRect.Y1)
  115.                   and (SmallRect.X2 <= bigrect.x2) and (SmallRect.Y2 <= BigRect.Y2);
  116. end;
  117.  
  118.  
  119. procedure rotatepoint(var P: Tsinglepoint; degrees, centerx, centerY: float);
  120.   var
  121.     newx, newy, angle: float;
  122.     cx, cy, y, x: single;
  123.   begin
  124.     angle := degTorad(degrees);
  125.     cx := centerx;
  126.     cy := centery;
  127.     x := p.x;
  128.     y := p.y;
  129.     x := x - cx;
  130.     Y := y - cy;
  131.     newx := (x * cos(angle)) - (y * sin(angle));
  132.     newy := (x * sin(angle)) + (y * cos(angle));
  133.     newx := newx + cx;
  134.     newy := newy + cy;
  135.     p.x := newx;
  136.     p.y := newy;
  137.   end;
  138.  
  139. procedure rotatelem(degrees:float);
  140. var
  141.   index:integer;
  142. begin
  143.   for index := 0 to MaxPoints do
  144.    rotatepoint(TheLem.points[index],degrees,TheLem.centerx,TheLem.centery);
  145. end;
  146.  
  147.  
  148. Procedure SetLEM2;
  149. begin
  150. with TheLem do
  151. begin
  152. centerx := xpos;
  153. centerY := ypos;
  154. Points[0].X := CenterX-8; Points[0].y := CenterY-8;
  155. Points[1].x := CenterX+8; Points[1].y := CenterY-8;  // top line main body
  156.  
  157. Points[2].x := Points[0].X; Points[2].Y := CenterY+4;
  158. Points[3].x := Points[1].x; Points[3].y := Points[2].y;
  159.  
  160. // top
  161.  
  162.      points[4].x := centerx-4;points[4].y := centerY-16;
  163.      points[5].x := centerx+4;points[5].y := centery-16;
  164.  
  165.      points[6].x := centerx-4; points[6].y := centery-8;
  166.      points[7].x := centerx+4; points[7].y := centerY-8;
  167.  
  168. // legs
  169.  
  170.     // left
  171.      points[8].x := centerx; points[8].y := centerY+4;
  172.      Points[9].x := centerx-6; Points[9].y := centerY+10;
  173.  
  174.     // right
  175.  
  176.      Points[10].x := CenterX; points[10].y := centerY+4;
  177.      Points[11].x := centerX+6;points[11].y := centery+10;
  178.  
  179.   // thruster setup
  180.     points[12].x := CenterX; Points[12].y := CenterY+4;
  181.     Points[13].x := CenterX; Points[13].y := CenterY+12;
  182. end;
  183. end;
  184.  
  185. Procedure AdjustLEM;
  186. var
  187.   index:integer;
  188.   Xc,Yc:Float;
  189. begin
  190. Xc := Xposition-OldXPosition;
  191. Yc := YPosition-OldYPosition;
  192. TheLem.centerX := TheLem.CenterX+XC;
  193. TheLem.CenterY := TheLem.CenterY+YC;
  194. for index := 0 to MaxPoints do
  195.       with TheLem do
  196.        begin
  197.            Points[index].X := Points[index].x+xc;
  198.            Points[Index].y := Points[index].y+yc;
  199.        end;
  200. end;
  201.  
  202. Procedure DrawLem2;
  203. begin
  204.  AdjustLEM;
  205.  SetTrect(LemRect,Xpos-8,ypos-16,xpos+8,ypos+10);
  206.  with TheLem do
  207.  begin
  208.  // main body
  209.    Line(round(points[0].x),Round(Points[0].y),round(Points[1].x),Round(Points[1].y));
  210.    Line(round(Points[0].x),round(Points[0].y),round(points[2].x),round(points[2].y));
  211.    line(round(points[2].x),round(points[2].y),round(points[3].x),round(points[3].y));
  212.    line(round(points[1].x),round(points[1].y),round(points[3].x),round(points[3].y));
  213.  
  214.    // top
  215.    line(round(points[4].x),round(points[4].y),round(points[6].x),round(points[6].y));
  216.    line (round(points[4].x),round(points[4].y),round(points[5].x),round(points[5].y));
  217.    line (round(points[5].x),round(points[5].y),round(points[7].x),round(points[7].y));
  218.  
  219.  
  220.    Line(round(points[8].x),round(points[8].y),round(points[9].x),round(points[9].y));
  221.    line(round(points[10].x),round(points[10].y),round(points[11].x),round(points[11].y));
  222.  
  223.    if thrust <> 0 then
  224.      line(round(points[12].x),round(points[12].y),round(points[13].x),round(points[13].y));
  225.  end;
  226. end;
  227.  
  228.  
  229. procedure Nextactivepage;
  230. begin
  231.   if page = page0 then page := page1 else page := page0;
  232.   SetactivePage(ord(Page));
  233. end;
  234.  
  235. Procedure NextVisualPage;
  236. begin
  237.  SetVisualpage(Ord(Page));
  238. end;
  239.  
  240. procedure updateinfo;
  241. var
  242. s:string;
  243. begin
  244. setcolor(15);
  245.   with inforect do
  246.    begin
  247.      setviewport(x1+1,y1+1,x2-1,y2-1,false);  // info viewport
  248.      clearviewport;
  249.      str(Fuel:5:2,s);
  250.      outtextxy(4,4,'Fuel:'+s);
  251.      Speed := abs(gravity)*100;
  252.      str(Speed:5:2,s);
  253.      OutTextXY(4,12,'Speed:'+s);
  254.    end;
  255.  
  256.   setviewport(0,0,getmaxx,getmaxy,true);      // full screen
  257.    setcolor(14);
  258.    with mainrect do
  259.     begin
  260.     OutTextXY(x2+4,290,'Esc = abort');
  261.     OutTextXY(x2+4,302,'2 thrusters');
  262.     OutTextXY(x2+4,314,'1 right thrust');
  263.     OutTextXY(x2+4,326,'3 left thrust');
  264.     OutTextXY(x2+4,338,'5 upright');
  265.     end;
  266. setcolor(15);
  267.  
  268.  
  269.   with mainrect do
  270.   begin
  271.     rectangle(x1,y1,x2,y2);
  272.     setviewport(x1+1,y1+1,x2-1,y2-1,true);   // main display
  273.     setTrect(LandingPadRect,400,y2-20,425,y2-1);
  274.   end;
  275.  
  276.   with landingpadrect do rectangle(x1,y1,x2,y2);  // draw landing pad
  277.  
  278.  
  279.  
  280. end;
  281.  
  282. Procedure Init;
  283. begin
  284.   gravitycycle := 0;
  285.   xpos := 300;
  286.   yposition := 200;
  287.   gravity := 0.05;
  288.   Inertia := 0.001;
  289.   ypos := round(YPosition);
  290.   xposition := xpos;
  291.   thrust := 0;
  292.   thrustcycle := 0;
  293.   SideThrust := 0;
  294.   Fuel := 2000;
  295.   SideThruster:= 0;
  296.   OldXposition := Xposition;
  297.   OldYPosition := YPosition;
  298.   OldDegrees := 0;
  299. end;
  300.  
  301. begin
  302.  
  303.   gdriver := vesa;  // adjust initgraph for your system
  304.   Gmode := installusermode(800, 600, 256, 2, 8000, 6000);  // two pages
  305.   WindowTitle := 'Play test';
  306.   initgraph(gdriver, gmode, '');
  307.  
  308.   init;
  309.   SetLem2;
  310.   DrawLEM2;     // draw the lander
  311.  
  312.   OutTextXY(20,220,'2 = thrust; 1 = left side thruster; 3 = right side thruster');
  313.   OuttextXY(20,230,'5 straightens LEM');
  314.   OutTextXY(20,240,'Press any key to begin');
  315.  
  316.   settRect(MainRect,0,0,getmaxx-120,getmaxy-20);
  317.  
  318. with Mainrect do
  319.  begin
  320.       rectangle(x1,y1,x2,y2);
  321.       SetTrect(InfoRect,x2+1,y1,getmaxx-1,y1+40);
  322.       setTrect(LandingPadRect,400,y2-20,425,y2-1);
  323.  end;
  324.  
  325. updateinfo;
  326.  
  327. readkey;
  328.   repeat
  329.     Speed := abs(gravity)*100;
  330.      if boolean(GetAsyncKeyState(VK_NUMPAD2)) then
  331.         begin
  332.             Thrust := -0.01;
  333.             thrustcycle := 0;
  334.          end;
  335.      if boolean(GetAsyncKeyState(VK_NUMPAD5)) then
  336.       begin
  337.           rotatelem(-OLDDEGREES);
  338.           olddegrees := 0;
  339.       end;
  340.      if boolean(GetAsyncKeyState(VK_NUMPAD1)) then
  341.       begin
  342.            Thrust := -0.01;
  343.            thrustcycle := 0;
  344.            SideThrust :=  sidethrust+inertia;
  345.            Sidethruster := sidethrust;
  346.            rotatelem(-OldDegrees);
  347.            rotatelem(45);
  348.            OldDegrees := 45;
  349.         end;
  350.     if boolean(GetAsyncKeyState(VK_NUMPAD3)) then
  351.      begin
  352.           thrustcycle := 0;
  353.           Thrust := -0.01;
  354.           sidethrust := sidethrust-inertia;
  355.           sidethruster := sidethrust;
  356.           rotatelem(-Olddegrees);
  357.           rotatelem(315);
  358.           OldDegrees := 315;
  359.       end;
  360.  
  361.     gravitycycle := gravitycycle+1;      // animation clock
  362.  
  363.     if gravitycycle = gravitycyclemax then
  364.      begin
  365.          OldXposition := Xposition;
  366.          OldYPosition := YPosition;
  367.          ypos := round(YPosition);
  368.          xpos := round(Xposition);
  369.          nextvisualpage;
  370.          clearviewport;  // clear active page
  371.          gravitycycle := 0;
  372.          yposition := yposition+gravity;
  373.          xposition := xposition+sidethrust;
  374.          gravity := gravity+thrust+inertia;
  375.          ypos := round(YPosition);
  376.          xpos := round(Xposition);
  377.          // set new activepage
  378.          nextactivepage;
  379.          DrawLEM2;
  380.          nextvisualpage;  // show the lander
  381.          Fuel := fuel+(thrust-(abs(sidethruster)))*100;
  382.          updateinfo;
  383.     end;
  384.  
  385.     thrustcycle := thrustcycle+1;    // thruster duration clock
  386.  
  387.     if ThrustCycle > ThrustCycleMax then
  388.        begin
  389.          updateinfo;
  390.          thrust := 0;
  391.          Thrustcycle := 0;
  392.          SideThruster := 0;
  393.        end;
  394.  
  395.  If not RectInRect(LemRect,Mainrect) then OutofBounds := true;
  396.  
  397.  if (olddegrees = 0) and (positioninrect(LemRect.x1,LemRect.y2,LandingPadRect) or Positioninrect(LemRect.x2,Lemrect.y2,landingpadrect))
  398.  and (speed <=15) then GoodLanding := true;
  399.  
  400.  if  (positioninrect(LemRect.x1,LemRect.y2,LandingPadRect) or Positioninrect(LemRect.x2,Lemrect.y2,landingpadrect)) and (speed> 15) then
  401.     OutofBounds := true;
  402.  
  403.    if fuel <=0 then OutOfBounds := true;
  404.  
  405. until (boolean(GetAsyncKeyState(VK_ESCAPE))) or (Fuel <=0) or OutOfBounds or Goodlanding;
  406.   Str(Speed:5:2,speedstring);
  407.   setviewport(0,0,getmaxx,getmaxy,true);
  408.   key := ' ';
  409.   cleardevice;
  410.  
  411.   if goodlanding then
  412.     begin
  413.       OutTextXY(200,300,'Congrats on the fine landing!');
  414.     end;
  415.  
  416.   if outofbounds then
  417.     begin
  418.       OutTextXY(200,300,'You crashed!');
  419.       OutTextXY(200,312,'Your speed:'+SpeedString);
  420.      end;
  421.   OutTextXY(200,324,'Press q/Q to exit.');
  422. repeat
  423.      if keypressed then Key := readkey;
  424. until (key ='Q') or (Key='q');
  425.   closegraph;
  426.  
  427. end.
  428.  
  429.  
  430.  

 

TinyPortal © 2005-2018