File moon-lander-autopilot.patch of Package moon-lander

--- moon_lander.c
+++ moon_lander.c
@@ -14,6 +14,12 @@
 * 08/17/2001 - cleaned up some of the messy code, fixed last of the memory leaks,
 *  added ryan daniels' ship and thrusters
 *
+* 03/17/2002 - Dr. Robert Meier improved the autopilot to use minimum fuel
+*              trajectory from current position and velocity.
+*
+* 05/06/2005 - Dr. Robert Meier improved the autopilot to act as a
+*              flight director.
+*
 */
 
 
@@ -37,7 +43,6 @@
 #define XSIZE 640
 #define YSIZE 480
 #define TERRAIN_YSIZE (YSIZE / 2)
-#define FPS (1000 / 35)
 #define DATAPATH ""
 
 #define FRESHRUN   0
@@ -96,6 +101,7 @@
   int small_font;
   int demo_mode;
   int autopilot;
+  int flightdirector;
   int state;
   int ActualTime, LastTime;
   int landing_pad;
@@ -108,6 +114,10 @@
   Sprite thrustb;
   Sprite thrust_left;
   Sprite thrust_right;
+  Sprite crosshairs;
+  Sprite crosshairsb;
+  Sprite beacon;
+  Sprite beaconb;
   Sprite miniship;
   Sprite logo;
   Sprite gameover_screen;
@@ -118,6 +128,7 @@
   int opt_lp_warn;
   int opt_num_lives;
   int opt_fancy_terrain;
+  int opt_frame_period;
   AI ai;
 #ifndef NOSOUND
   Mix_Chunk *engine;
@@ -164,8 +175,8 @@
   /* timer - so that things run at an even speed regardless of cpu speed */
 
   game->ActualTime = SDL_GetTicks();
-  if (game->ActualTime < game->LastTime + FPS){
-    SDL_Delay(game->LastTime + FPS - game->ActualTime);
+  if (game->ActualTime < game->LastTime + 10*game->opt_frame_period){
+    SDL_Delay(game->LastTime + 10*game->opt_frame_period - game->ActualTime);
   }
   game->LastTime = game->ActualTime;
 } 
@@ -195,7 +206,7 @@
     }
   
     while (!done){
-      if ( files[count] = readdir(dir) ){
+      if ( (files[count] = readdir(dir)) ){
       
 	//printf("I see - %d %s\n", count, files[count]->d_name);
 	count++;
@@ -279,8 +290,9 @@
 
   if ( (file = fopen(filename, "w")) != NULL) {
     
-    fprintf(file,"%d %d %d %d %d",  game->opt_num_lives, game->opt_lp_bonus,game->opt_lp_warn,
-	    game->opt_prog_grav,game->opt_fancy_terrain);
+    fprintf(file,"%d %d %d %d %d %d",  game->opt_num_lives,
+	    game->opt_lp_bonus,game->opt_lp_warn,game->opt_prog_grav,
+	    game->opt_fancy_terrain,game->opt_frame_period);
     
   }
   else{
@@ -318,8 +330,9 @@
 
   if ( (file = fopen(filename, "r")) != NULL) {
     
-    fscanf(file,"%d %d %d %d %d",  &game->opt_num_lives, &game->opt_lp_bonus, &game->opt_lp_warn,
-	    &game->opt_prog_grav, &game->opt_fancy_terrain);
+    fscanf(file,"%d %d %d %d %d %d",  &game->opt_num_lives,
+	   &game->opt_lp_bonus, &game->opt_lp_warn, &game->opt_prog_grav,
+	   &game->opt_fancy_terrain, &game->opt_frame_period);
     
   }
   else{
@@ -345,11 +358,11 @@
   int done = 0;
   int *selected;
   int position = 0;
-  char options[5][100];
+  char options[6][100];
   char display_string[150];
   char selected_text[3];
   int count;
-  int value[5];
+  int value[6];
   Uint8 *key_table;
   SDL_Event event;
 
@@ -358,6 +371,7 @@
   sprintf(options[2],"%s", "Landing Pad Speed Warning");
   sprintf(options[3],"%s", "Variable Speed Landing Pads");
   sprintf(options[4],"%s", "Number Of Ships");
+  sprintf(options[5],"%s", "Frame Period (x10ms)");
 
 
   /* clear event buffer */
@@ -384,18 +398,38 @@
     if (key_table[SDLK_UP]){
       position--;
       if (position < 0){
-	position = 4;
+	position = 5;
       }
     }
 
     if (key_table[SDLK_DOWN]){
       position++;
-      if (position >4){
+      if (position >5){
 	position = 0;
       }
     }
 
-    if (key_table[SDLK_RETURN]){
+    if (key_table[SDLK_LEFT]){
+      (*selected)--;
+
+      if (position == 4){
+	if (*selected < 1 ){
+	  *selected = 5;
+        }
+      }
+      else if (position == 5) {
+        if (*selected < 1){
+	  *selected = 1;
+	}
+      }
+      else{
+	if (*selected < 0){
+	  *selected = 1;
+	}
+      }
+    }
+
+    if (key_table[SDLK_RETURN] || key_table[SDLK_RIGHT]){
       (*selected)++;
       
       if (position == 4){
@@ -403,6 +437,8 @@
 	  *selected = 1;
 	}
       }
+      else if (position == 5) {
+      }
       else{
 	if (*selected > 1){
 	  *selected = 0;
@@ -428,12 +464,16 @@
     else if (position == 4){
       selected = &(game->opt_num_lives);
     }
+    else if (position == 5){
+      selected = &(game->opt_frame_period);
+    }
     
     value[0] = game->opt_fancy_terrain;
     value[1] = game->opt_prog_grav;
     value[2] = game->opt_lp_warn;
     value[3] = game->opt_lp_bonus;
     value[4] = game->opt_num_lives;
+    value[5] = game->opt_frame_period;
 
 
     /* draw the options */
@@ -442,7 +482,7 @@
     DT_DrawText("Arrow Keys Select", game->screen, game->small_font, 260, 75);
     DT_DrawText("ENTER changes value", game->screen, game->small_font, 260, 90);
 
-    for (count = 0; count < 5; count ++) {
+    for (count = 0; count < 6; count ++) {
 
       if (position == count) {
 	sprintf(selected_text,"%s","**");
@@ -883,6 +923,7 @@
     draw_score(game, 0);
     DT_DrawText("Arrow keys control the ship", game->screen, game->big_font, 100, 100 );
     DT_DrawText("Q quit   P pause   ESC options", game->screen, game->big_font, 75, 125 );
+    DT_DrawText("A autopilot   F flight director", game->screen, game->big_font, 75, 150 );
     DT_DrawText("Press ENTER to play", game->screen, game->big_font, 175, 170 );
 
     DT_DrawText("Score for each round = landing pad score + remaining fuel.", game->screen, game->small_font, 150, 280 );
@@ -1256,6 +1297,44 @@
 
 /************************************************/
 
+void gamefd( Game *game)
+{
+  /* ----------------------------------------------------------------
+     Flight Directory - written by Dr. Robert Meier 06/05/2006
+
+     During powered descent from low-phi (nearly parallel to the
+       horizon) orbit, the lunar module analog computer presented the
+       pilot with two oscilloscope displays.
+     One presented the predicted rate of climb and crosstrack speed
+       at the time that downrange speed was zero.
+     The second predicted altitude and crosstrack error at the time
+       that downrange speed was zero.
+     The pilot's goal was to keep the blip on each oscilloscope
+       centered as the predictions were updated according to radar.
+
+     As a similar presentation, use a point to indicate the lowest
+       altitude and further side motion if thrust is applied continously.
+
+     ---------------------------------------------------------------- */
+  const float G = game->gravity;
+  const float A = 0.10;
+  const float B = 0.07;
+  Sprite *ship = &game->ship;
+  float xi = ship->x + 0.5 * ship->w;
+  float yi = ship->y + ship->h;
+  float hi = ship->x_vel;
+  float vi = ship->y_vel;
+  float sgn = hi > 0 ? 1 : -1;
+  int cx = xi - 4 + sgn * hi * hi / (2 * B);
+  int cy = yi - 4 + vi * vi / (2 * (A-G));
+  game->crosshairs.x = cx;
+  game->crosshairs.y = cy;
+  game->crosshairsb.x = game->crosshairs.x;
+  game->crosshairsb.y = game->crosshairs.y;
+}
+
+/************************************************/
+
 void gameai( int *left, 
 	     int *right, 
 	     int *down, 
@@ -1275,7 +1354,203 @@
      velocity. 
      4 => Freefall with minor course changes
 
+     Game AI -- improved by Dr. Robert Meier 03/17/2002
+
+     The lunar lander powered descent from low-phi orbit was a
+       nonlinear problem well beyond autopilots until the 1970s.
+     Landing with perfect attitude control on a flat surface is
+       easy.
+
+     The minimum fuel course is called bang-bang control.
+
+     No vertical thrust until continuous vertical thrust will zero
+       descent rate at the pad.
+     Accelerate horizontally the minimum necessary to reach the pad.
+     Coast horizontally until continuous deceleration is required.
+     Decelerate continuously to reach zero groundspeed over the pad.
+
+     To avoid burning landing gear off with backblast, you should actually
+       shutdown shutdown and freefall when altitude and vertical speed are
+       low enough.
+
      ------------------------------------------------------------- */
+#if 1
+  // Find the nearest pad.
+  game->ai.pad = 0;
+  game->ai.distance = 9999;
+  float x = game->ship.x + game->ship.w/2;
+  for( count = 0; count < game->current_level.num_landings; count++ ) {
+    if( abs( x-game->current_level.landing_x[count] ) < game->ai.distance ) {
+      game->ai.distance = abs( x - game->current_level.landing_x[count] );
+      game->ai.pad = count;
+    }
+  }
+
+  // Find the target hovering point. (Allow .05 margin for error.)
+  int max_x = game->current_level.landing_x[game->ai.pad]
+            + game->current_level.landing_w[game->ai.pad]/2;
+  float max_v = game->current_level.landing_speed[game->ai.pad]-.05;
+  int max_y = game->current_level.landing_y[game->ai.pad];
+
+  // Let the user know which pad is in use
+  game->beacon.x = max_x;
+  game->beacon.y = max_y;
+  game->beaconb.x = max_x;
+  game->beaconb.y = max_y;
+
+  // Determine the time to target.
+  //   y(-i)  - freefall altitude that would result in current descent rate
+  //              (assumed freefall)
+  //   y(-t)  - altitude now
+  //   v(-t)  - descent rate now
+  //              (actual freefall)
+  //   y(0)   - altitude at burn
+  //              (continuous burn)
+  //   y(f)   - pad altitude
+  //
+  // Thanks to Galileo, we know
+  //   g      - gravity
+  //   a      - acceleration (thrust - gravity)
+  //   y(-i)  = y(0) - g i i / 2
+  //   y(-t)  = y(-i) + g (i-t) (i-t) / 2
+  //   v(-t)  = g (i-t)
+  //   y(f)   = y(0) + a f f / 2
+  //   a [y(f) - y(0)] = g [y(0) - y(-i)]
+  //   a f = g i
+  // We can now rewrite
+  //   y(-i)  = y(-t) - v(-t) v(-t) / 2 g
+  //   y(0)   = [a y(f) + g y(-i)] / (a + g)
+  //   i      = sqrt(2 [y(0) - y(-i)] / g)
+  //   f      = sqrt(2 [y(f) - y(0)] / a)
+  //   t      = i - v(-t) / g
+
+  float g = game->gravity;
+  float a = .10 - g;
+  float yt = game->ship.y + game->ship.h;
+  float vt = game->ship.y_vel;
+  float yf = max_y;
+  float yi = yt - vt * vt / (2 * g);
+  float y0 = (a * yf + g * yi) / (a + g);
+  float i = sqrt(2 * (y0 - yi) / g);
+  float f = sqrt(2 * (yf - y0) / a);
+  float t = i - vt / g;
+
+  // Thrust upward if continuous thrust will be just enough.
+  //   (Allow one frame margin for error.)
+  //   (Dont thrust if we are climbing.)
+  //   (Dont cancel pilot command.)
+  if( !*down && 0 < vt ) {
+    if( 2 > t ) {
+      *down = 1;
+      t = 0;
+    }
+  }
+
+//  printf( "ai-y: pad: %3d ship: %3f %5.2f g: %4.2f d: %d\n",
+//	  max_y, yt, vt, g, *down);
+
+  // Bang-bang control has an eigenfunction and three states.
+  //
+  // The eigenfunction is the phase space (position vs speed) trajectory
+  //   during continuous burn.
+  //   w      - acceleration
+  //   x(-b)  - position now
+  //   u(-b)  - groundspeed now
+  //   C      - position of zero groundspeed relative to speed direction
+  //   T      - time to coast and decelerate
+  // The eigenfunction, the position axis, and the time to target,
+  //   divide the space into three pairs of regions.
+  //   C = x(-b) sgn(u(-b)) + u(-b) u(-b) / 2 abs(w)
+  //   T = abs(x(-b)) / abs(u(-b)) + abs(u(-b)) / 2 abs(w)
+  //
+  //                  x 
+  //  . accelerating  :  decelerating
+  //  .               : 
+  //  .           <<<<: 
+  //  .        <<<    : 
+  //    . . .<<       : 
+  //    .     . .     : 
+  //    .coast    .   : 
+  //      .         . : 
+  //      .    v    . : 
+  //        .  v      :       
+  //          .v.     : 
+  //              . . + . .         --- v 
+  //                  :     . . 
+  //                  :  coast  . 
+  //                  : .         . 
+  //        >         : .         . 
+  //         >>       :   .         . 
+  //           >>>    :     . .     . 
+  //              >>>>:         . . . 
+  //                  :               . 
+  //                  :               . 
+  //  decelerating    :  accelerating . 
+  //                  :               . 
+  //
+  // State I: decelerating
+  //   C > 0           - condition
+  //   w               = -abs(w) sgn(u(-b))
+  // State I transitions to state II if actual acceleration is greater than w.
+  //
+  // State II: coasting
+  //   C < 0 & t+f > T - condition
+  //   w               = 0
+  // State II transitions to state I as C increases.
+  //
+  // State III: accelerating
+  //   C < 0 & t+f < T - condition
+  //   w               = abs(w) sgn(u(-b))
+  // State III transition to state II if actual acceleration is greater than w.
+  //
+
+  float xb = x - max_x;
+  float w  = .065;
+  float ub = game->ship.x_vel;
+  float C  = xb * (0 < ub ? 1 : 0 > ub ? -1 : 0) + ub * ub / (2 * fabs(w));
+  float T  = fabs(xb) / (fabs(ub) + .001) + fabs(ub) / (2 * fabs(w));
+
+  // Thrust if continuous thrust will be just enough.
+  //   (Dont cancel pilot command.)
+  //   (Dont thrust if close enough)
+  //   (Shutdown if altitude is low enough)
+  if( !*left && !*right ) {
+    *left = 0;
+    *right = 0;
+    if( .1 < fabs(ub) || 1 < fabs(xb) ) {
+      if( 0 < C ) {		/* State I: decelerating */
+        if( 0 < ub ) {
+	  *left = 1;		/* confirmed */
+	}
+	else{
+	  *right = 1;		/* confirmed */
+	}
+      }
+      else{
+	if( t + f > T ) {	/* State II: coasting */
+	}
+  	else{			/* State III: accelerating */
+          if( 0 < ub ) {
+	    *right = 1;
+	  }
+	  else{
+	    *left = 1;
+	  }
+	}
+      }
+    }
+    else{
+      float alt = game->current_level.landing_y[game->ai.pad] - yt;
+      if( 2 * g * alt + vt * vt < max_v * max_v ) {
+	*down = 0;		/* shutdown */
+      }
+    }
+  }
+
+//  printf( "ai-x: pad: %3d ship: %3f %5.2f C: %5.2f t+f: %5.2f T: %5.2f l: %d r: %d\n",
+//           max_x, xb+max_x, ub, C, t+f, T, *left, *right);
+
+#else /* 1 */
   *left = 0;
   *right = 0;
 
@@ -1397,6 +1672,7 @@
 	
     //printf( "4: game->ship.x=%f state=%d target=%f x_vel=%f y_vel=%f vdiff=%f\n", game->ship.x, game->ai.state, game->ai.target, game->ship.x_vel, game->ship.y_vel, game->ai.vdiff );
   }
+#endif /* 1 */
 
   return;
 }
@@ -1423,6 +1699,7 @@
   int pause = 0;
   int pressed = 0;
   int appressed = 0;
+  int fdpressed = 0;
   char display_string[100]; 
 
   game->ai.state = 0;
@@ -1474,7 +1751,7 @@
       // toggle
       if( !appressed ) {
 	game->autopilot = (game->autopilot)?0:1;
-	appressed++;
+	appressed = 1;
 	if( game->autopilot == 1 ) {
 	  game->ai.state = 0;
 	}
@@ -1485,6 +1762,19 @@
 	appressed = 0;
       }
     }
+
+    if (key_table[SDLK_f]){	
+      // toggle
+      if( !fdpressed ) {
+	game->flightdirector = (game->flightdirector)?0:1;
+	fdpressed = 1;
+      }
+    }
+    else{ 
+      if( fdpressed ) {
+	fdpressed = 0;
+      }
+    }
 	    
     if (key_table[SDLK_q]){
       exit(0);
@@ -1522,10 +1812,19 @@
 
     if( !pause ) {
 
-      if( game->demo_mode || game->autopilot ) {
+      if( game->demo_mode ) {
 	game->gravity = 0.05;
 	gameai( &left, &right, &down, game );
       }
+      else{
+        if( game->autopilot ) {
+	  gameai( &left, &right, &down, game );
+	}
+      }
+
+      if( game->flightdirector ) {
+        gamefd( game );
+      }
 
       if( game->fuel > 0 ) {
 	if( right == 1 ) {
@@ -1632,6 +1931,40 @@
 	draw_sprite(game->screen, game->thrust_left);
       }    
 
+      /* display flight director */
+
+      if (game->flightdirector) {
+	if ( (odd_even%2) == 0) {
+	  draw_sprite(game->screen, game->crosshairs);
+	}
+	else{
+	  draw_sprite(game->screen, game->crosshairsb);
+	}
+	float hi = game->ship.x_vel;
+	float vi = game->ship.y_vel;
+	float scale = fabs(hi);
+        scale = fabs(vi) > scale ? fabs(vi) : scale;
+        scale = 2 > scale ? 2 : scale;
+        int xy, x, y;
+	int cx = game->crosshairs.x+4;
+	int cy = game->crosshairs.y+4;
+        for( xy = 1; xy < 15; xy++ ){
+          x = xy * (hi/scale) * 2, y = xy * (vi/scale) * 2;
+          DrawPixel(game->screen, 0, 255, 0, cx-x, cy-y);
+        }
+      }
+
+      /* display autopilot */
+
+      if (game->autopilot || game->demo_mode) {
+	if ( (odd_even%2) == 0) {
+	  draw_sprite(game->screen, game->beacon);
+	}
+	else{
+	  draw_sprite(game->screen, game->beaconb);
+	}
+      }
+
       /* display fuel  */
 
       draw_score(game, 1);
@@ -1801,6 +2134,18 @@
   sprintf(filename, "%simages/thrust2.png", DATAPATH);
   new_sprite(&(game.thrustb), filename, 0, 0, 1, 0);
 
+  sprintf(filename, "%simages/crosshairs1.bmp", DATAPATH);
+  new_sprite(&(game.crosshairs), filename, 0, 0, 1, 0);
+
+  sprintf(filename, "%simages/crosshairs2.bmp", DATAPATH);
+  new_sprite(&(game.crosshairsb), filename, 0, 0, 1, 0);
+
+  sprintf(filename, "%simages/beacon1.bmp", DATAPATH);
+  new_sprite(&(game.beacon), filename, 0, 0, 1, 0);
+
+  sprintf(filename, "%simages/beacon2.bmp", DATAPATH);
+  new_sprite(&(game.beaconb), filename, 0, 0, 1, 0);
+
   sprintf(filename, "%simages/thrust_left.bmp", DATAPATH);
   new_sprite(&(game.thrust_left), filename, 0, 0, 1, 0);
openSUSE Build Service is sponsored by