PROGRAM Artillery_Battle ; { Pascal/M version } { *** WRITTEN BY Jim Bearden Cancer Center, University of Hawaii 1236 Lauhala Street Honolulu, Hawaii 96813 *** DATE WRITTEN December, 1981 *** PROGRAM SUMMARY: This program simulates an artillery battle between two players. The initial setup (ground elevation on each side, locations of bunkers, height of the central "hill" or "valley", and wind direction and velocity) is generated at random for each game. Each player then enters the angle (in degrees above the horizontal) and velocity (in meters per second) of his shot. Flight of the projectile and its point of impact are displayed, as long as it remains on the screen. This program uses only ASCII characters and cursor positioning (no graphics capability required), so it should be usable on any CRT terminal with an addressable cursor (presently set up for a 24 x 80 screen size, but may be adapted to other sizes by changing constants as shown). } {$D- (Disable "Debug" option for increased speed) } CONST Grnd_sym = '-' ; { Character used to represent ground surface in profile } Burst_sym = '*' ; { Represents point of impact on ground } Air_sym = '+' ; { Represents projectile in flight } Max_X = 79 ; { Number of screen columns - 1 } Max_Y = 20 ; { Number of screen lines - 4 } Scale = 100 ; { Number of meters represented by each screen unit } Max_wind = 25 ; { Maximum allowed wind velocity, in meters/sec } Side_1 = 25 ; { Width of level ground section on player 1's side } Side_2 = 25 ; { Same, player 2's side } Center = 30 ; { Width of center ("hill" or "valley") section } { Sum of (Side_1 + Side_2 + Center) must equal width of screen } Acc_g = 9.8 ; { Acceleration of normal earth gravity, in meters/sec } Bell = 7 ; { ASCII equivalent of "bell" sound } VAR X_pos_1, X_pos_2, { Locations (on X-axis) of bunkers of players 1 and 2 } Level_1, Level_2, { Heights of level ground sections on each side } Center_ht : 0..Max_X ; Wind_vel : -Max_wind..Max_wind ; Ground : ARRAY [0..Max_X] OF Integer ; Direct_hit : ARRAY [1..2] OF Boolean ; Rnd_num : Real ; Answer : Char ; Quit : Boolean ; PROCEDURE Set_Up_Game ; VAR X, Y : 0..Max_X ; BEGIN { Set_Up_Game } { Set up initial conditions from random-number generator } Level_1 := Round (Max_Y * Random (0) / 2) ; Level_2 := Round (Max_Y * Random (0) / 2) ; Center_ht := Round (Max_Y * Random (0)) ; X_pos_1 := Round ((Side_1 - 3) * Random (0) + 1) ; X_pos_2 := Round ((Side_2 - 3) * Random (0) + Max_X - Side_2 + 1) ; Wind_vel := Round (2 * Max_wind * Random (0) - Max_wind) ; { Display initial layout and initialize "Ground" values } Conact (0) ; FOR X := 0 TO (Side_1 - 1) DO BEGIN Y := Level_1 ; GotoXY (X, (Max_Y - Y)) ; Write (Grnd_sym) ; Ground [X] := Y ; END ; FOR X := 0 TO (Center DIV 2 - 1) DO BEGIN Y := Round (Level_1 + (Center_ht - Level_1) * (X + 1) / (Center / 2)) ; GotoXY ((X + Side_1), (Max_Y - Y)) ; Write (Grnd_sym) ; Ground [X + Side_1] := Y ; END ; FOR X := 0 TO (Center DIV 2 - 1) DO BEGIN Y := Center_ht - Round ((Center_ht - Level_2) * (X + 1) / (Center / 2)) ; GotoXY ((X + Side_1 + Center DIV 2), (Max_Y - Y)) ; Write (Grnd_sym) ; Ground [X + Side_1 + Center DIV 2] := Y ; END ; FOR X := 0 TO (Side_2 - 1) DO BEGIN Y := Level_2 ; GotoXY ((X + Side_1 + Center), (Max_Y - Y)) ; Write (Grnd_sym) ; Ground [X + Side_1 + Center] := Y ; END ; { Show location of both players' bunkers } GotoXY (X_pos_1 - 1, (Max_Y - Ground [X_pos_1])) ; Write ('[1]') ; GotoXY (X_pos_2 - 1, (Max_Y - Ground [X_pos_2])) ; Write ('[2]') ; GotoXY (0, (Max_Y + 2)) ; Conact (1) ; Write ('Wind is to the ') ; IF Wind_vel <= 0 THEN Write ('LEFT ') ELSE Write ('RIGHT ') ; Write ('at ', Abs (Wind_vel), ' meters/sec; ') ; Write ('each bar (-) is 100 meters.') ; GotoXY (0, Max_Y + 3) ; Conact (1) ; Write ('When prompted, enter angle (degrees) and velocity (meters/sec)') ; Write (', or 0,0 to quit.') ; GotoXY (0, (Max_Y - Level_1 + 1)) ; Write ('Player #1: ') ; GotoXY ((Side_1 + Center), (Max_Y - Level_2 + 1)) ; Write ('Player #2: ') ; FOR X := 1 TO 2 DO Direct_hit [X] := False ; Quit := False ; END ; { Set_Up_Game } PROCEDURE Fire (Player : Integer) ; CONST Spaces = 11 ; Pi = 3.14159 ; Time_int = 100 ; VAR Angle, Init_vel, Last_X, Last_Y, Next_X, Next_Y, X_vel, Y_vel : Real ; N, Loc_X, Loc_Y : Integer ; Hit, On_screen : Boolean ; FUNCTION Wind_Fac (X_pos, Y_pos : Real) : Real ; CONST Wind_pct = 0.1 ; { "Coupling factor" between wind and projectile } VAR Shielded : Boolean ; BEGIN { Wind_Fac } IF Wind_vel > 0 THEN IF Center_ht > Level_1 THEN Shielded := (X_pos > (Side_1 + Center DIV 2 + 1)) AND (Y_pos < Center_ht) ELSE Shielded := (X_pos > Side_1) AND (Y_pos < Level_1) ELSE IF Center_ht > Level_2 THEN Shielded := (X_pos < (Side_1 + Center DIV 2)) AND (Y_pos < Center_ht) ELSE Shielded := (X_pos < (Side_1 + Center)) AND (Y_pos < Level_2) ; IF Shielded THEN Wind_Fac := 0 ELSE Wind_Fac := Wind_vel * Wind_pct ; END { Wind_Fac } ; BEGIN { Fire } IF Player = 1 THEN GotoXY (Spaces, (Max_Y - Level_1 + 1)) ELSE GotoXY ((Side_1 + Center + Spaces), (Max_Y - Level_2 + 1)) ; Read (Angle, Init_vel) ; { Routine for early termination of game by either player } IF (Angle <= 0) OR (Init_vel <= 0) THEN BEGIN Quit := True ; Exit (Fire) ; END ; { Set up zero-time co-ordinates and velocities } Angle := Angle * Pi / 180 ; { Convert degrees to radians } X_vel := Init_vel * Cos (Angle) ; IF Player = 2 THEN X_vel := -X_vel ; Y_vel := Init_vel * Sin (Angle) ; IF Player = 1 THEN BEGIN Last_X := X_pos_1 ; Last_Y := Ground [X_pos_1] ; END ELSE BEGIN Last_X := X_pos_2 ; Last_Y := Ground [X_pos_2] ; END ; Hit := False ; On_screen := False ; REPEAT { Compute velocities and positions after next second of travel } X_vel := X_vel + Wind_Fac (Last_X, Last_Y) ; Y_vel := Y_vel - Acc_g ; IF On_screen THEN { Erase last symbol printed during air travel } BEGIN GotoXY (Loc_X, (Max_Y - Loc_Y)) ; Write (' ') ; END ; Next_X := Last_X + (X_vel / Scale) ; Loc_X := Round (Next_X) ; Next_Y := Last_Y + (Y_vel / Scale) ; Loc_Y := Round (Next_Y) ; IF (Loc_Y < 0) AND NOT (Loc_X IN [0..Max_X]) THEN { Hit ground off screen } BEGIN Hit := True ; Write (Chr (Bell)) ; END ELSE IF Loc_X IN [0..Max_X] THEN IF Loc_Y <= Ground [Loc_X] THEN { Hit ground on screen } BEGIN Loc_X := Round (Last_X + (Next_X - Last_X) / (Next_Y - Last_Y)) ; Hit := True ; Write (Chr (Bell)) ; IF (Abs (Loc_X - X_pos_1)) <= 1 THEN Direct_hit [1] := True ELSE IF (Abs (Loc_X - X_pos_2)) <= 1 THEN Direct_hit [2] := True ELSE BEGIN GotoXY (Loc_X, (Max_Y - Ground [Loc_X])) ; Write (Burst_sym) ; END ; END ELSE { Still in flight above ground } BEGIN On_screen := Loc_Y IN [0..Max_Y] ; IF On_screen THEN BEGIN GotoXY (Loc_X, (Max_Y - Loc_Y)) ; Write (Air_sym) ; END ELSE GotoXY (Loc_X, 0) ; FOR N := 1 TO Time_int DO { Nothing } ; { Time delay for display } END ELSE BEGIN On_screen := False ; GotoXY (0, (Max_Y + 3)) ; END ; { Update co-ordinates for next calculation } Last_X := Next_X ; Last_Y := Next_Y ; UNTIL Hit ; END ; { Fire } PROCEDURE Show_Hit ; VAR N : Integer ; BEGIN { Show_Hit } IF Direct_hit [1] THEN GotoXY ((X_pos_1 - 1), (Max_Y - Level_1)) ELSE GotoXY ((X_pos_2 - 1), (Max_Y - Level_2)) ; FOR N := 1 TO 3 DO Write (Burst_sym) ; IF Direct_hit [1] THEN GotoXY (0, (Max_Y - Level_1 + 1)) ELSE GotoXY ((Side_1 + Center), (Max_Y - Level_2 + 1)) ; Conact (1) ; Writeln ('BLEEP!!! Wiped out!!!') ; FOR N := 1 TO 200 DO Write (Chr (Bell)) ; END ; { Show_Hit } BEGIN { Main program } Conact (0) ; Writeln ('Welcome to ARTILLERY BATTLE') ; Writeln ; { Routine to randomize pseudo-random-number generator } Write ('Press any key to start the game: ') ; WHILE Filebusy (Keyboard) DO Rnd_num := Random (0) ; Read (Keyboard, Answer) ; REPEAT Set_Up_Game ; REPEAT Fire (1) ; IF NOT (Direct_hit [1] OR Direct_hit [2] OR Quit) THEN Fire (2) ; UNTIL (Direct_hit [1] OR Direct_hit [2] OR Quit) ; IF NOT Quit THEN Show_hit ; GotoXY (0, (Max_Y + 2)) ; Conact (1) ; GotoXY (0, (Max_Y + 3)) ; Conact (1) ; Write ('Another game (Y/n) ? ') ; Read (Answer) ; UNTIL Answer IN ['N','n'] ; END . { Main program } .