-- player.adb -- simulate a CD player receiving input signals from a remote -- -- by Darren Provine, 10 March 2003 -- sample solution for Gloucester County High School Programming Contest with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure player is RCS_ID : constant String := "$Id: player.adb,v 1.3 2003/03/31 22:04:02 kilroy Exp kilroy $"; --------------- -- Constants -- limits from problem description --------------- Max_Disc_Tracks : constant Integer := 20; Max_Track_Time : constant Integer := 30 * 60; -- 30 mins, 60 secs ea. ---------------------- -- Integer Subtypes -- ranges based on limits ---------------------- subtype Track_Count_Type is Integer range 1..Max_Disc_Tracks; subtype Track_Length_Type is Integer range 1..Max_Track_Time; ---------------- -- Data Types -- ---------------- -- Possible activity states type Activity_Type is (Play, Pause, Stop, Sleep); -- package for easy formatted IO package Activity_IO is new Ada.Text_IO.Enumeration_IO(Activity_Type); use Activity_IO; -- Information about player type Player_Status_Type is record Activity : Activity_Type; Track : Track_Count_Type; Track_Time : Integer range 0..Max_Track_Time; Last_Signal_Time : Integer; -- stored as integer for easy math end record; -- Array for track lengths on CD type Track_Array is array (Track_Count_Type) of Track_Length_Type; -- information about disc type CD_Info_Type is record Tracks : Track_Count_Type; Time : Track_Array; end record; -- representation of time, per problem description subtype Time_String_Type is String(1..8); -- Possible signals; "Unused" takes the zero slot so the rest map -- to the numeric signals as given in the problem statement type Signal_Type is (Unused, On, Sleep, Play, Pause, Stop, Ahead, Back); -- package for easy formatted IO package Signal_IO is new Ada.Text_IO.Enumeration_IO(Signal_Type); use Signal_IO; -- structure of signal type Signal_Info_Type is record Content : Signal_Type; Time_String : Time_String_Type; Time_Seconds : Integer; -- saved in integer seconds for easy math end record; ------------------- -- Integer_Seconds -- convert HH:MM:SS to integer seconds ------------------- function Integer_Seconds (Time : Time_String_Type) return Integer is Hours : Integer; Minutes : Integer; Seconds : Integer; begin Hours := Integer'Value(Time(1..2)); Minutes := Integer'Value(Time(4..5)); Seconds := Integer'Value(Time(7..8)); return Hours * 3600 + Minutes * 60 + Seconds; end Integer_Seconds; ---------------- -- Initialize -- set up CD player to start state ---------------- procedure Initialize (Player : in out Player_Status_Type) is begin Player.Activity := Sleep; Player.Track := 1; Player.Track_Time := 0; Player.Last_Signal_Time := 0; end Initialize; ---------------- -- Print_Time -- print integer seconds as MM:SS ---------------- procedure Print_Time (Time : Integer) is Minutes, Seconds : Integer; begin Minutes := Time / 60; Seconds := Time - (Minutes * 60); Put(Minutes, Width=>2); Put(":"); if Seconds < 10 then Put("0"); end if; Put(Seconds, Width=>1); end Print_Time; ------------------ -- Read_CD_Data -- Read data about tracks on CD ------------------ procedure Read_CD_Data (Disc : in out CD_Info_Type) is begin Get(Disc.Tracks); for Track in 1..Disc.Tracks loop Get(Disc.Time(Track)); end loop; end Read_CD_Data; ------------------ -- Show_CD_Data -- Print data about tracks on CD ------------------ procedure Show_CD_Data (Disc : CD_Info_Type) is begin Put("CD has "); Put(Disc.Tracks, Width=>1); Put(" tracks:"); New_Line; for Track in 1..Disc.Tracks loop Put(Track, Width=>3); Put(" "); Print_Time(Disc.Time(Track)); New_Line; end loop; end Show_CD_Data; ----------------- -- Read_Signal -- Read time and type of signal ----------------- function Read_Signal return Signal_Info_Type is Signal : Signal_Info_Type; Signal_Number : Integer; begin Get(Signal.Time_String); Signal.Time_Seconds := Integer_Seconds(Signal.Time_String); Get(Signal_number); Signal.Content := Signal_Type'Val(Signal_Number); return Signal; end Read_Signal; ----------------- -- Show_Status -- show what the player is doing ----------------- procedure Show_Status(Player : in out Player_Status_Type) is begin Put(Player.Activity, Width=>6); Put("track "); Put(Player.Track, Width=>2); Put(" "); Print_Time(Player.Track_Time); end Show_Status; ------------------- -- Update_Status -- bring the player up to date ------------------- procedure Update_Status(Player : in out Player_Status_Type; Disc : Cd_Info_Type; Now : Integer) is Elapsed_Time : Integer; begin -- If the player is stopped or paused (or off), its state won't -- change between one signal and the next. The only thing we have -- to worry about is whether it was in play. if Player.Activity /= Play then return; end if; -- If the time since last signal is longer than the time remaining -- on the current track, we've gone to the next track. If we -- were on the last track, then we've hit the end of the disc. Elapsed_Time := Now - Player.Last_Signal_Time; while Elapsed_Time > 0 loop if Elapsed_Time + Player.Track_Time >= Disc.Time(Player.Track) then -- We finished a track (at least). if Player.Track = Disc.Tracks then -- We got to the end of disc. Player.Activity := Stop; Player.Track_Time := 0; Player.Track := 1; return; else -- Set to next track and then see if we finished it. Elapsed_Time := Elapsed_Time - ( Disc.Time(Player.Track) - Player.Track_Time ); Player.Track := Player.Track + 1; Player.Track_Time := 0; end if; else -- We didn't finish the track, but we're farther through it. Player.Track_Time := Player.Track_Time + Elapsed_Time; Elapsed_Time := 0; end if; end loop; -- If we stopped at end of disc, we already returned. If we're -- here, we're still in PLAY, so have to count this time as -- when we last updated the state. Player.Last_Signal_Time := Now; end Update_Status; ------------- -- Process -- Process a signal received by the player ------------- procedure Process( Player : in out Player_Status_Type; Disc : in CD_Info_Type; Signal : in Signal_Info_type) is begin case Signal.Content is when On => if Player.Activity = Sleep then Initialize(Player); Player.Activity := Stop; Player.Last_Signal_Time := Signal.Time_Seconds; end if; when Sleep => Initialize(Player); when Play => case Player.Activity is when Stop | Pause => Player.Activity := Play; Player.Last_Signal_Time := Signal.Time_Seconds; when others => null; end case; when Pause => case Player.Activity is when Play => Player.Activity := Pause; Player.Last_Signal_Time := Signal.Time_Seconds; when Pause => Player.Activity := Play; Player.Last_Signal_Time := Signal.Time_Seconds; when others => null; end case; when Stop => if Player.Activity /= Sleep then Initialize(Player); Player.Activity := Stop; end if; when Ahead => case Player.Activity is when Stop | Play => if Player.Track < Disc.Tracks then Player.Track := Player.Track + 1; Player.Track_time := 0; Player.Last_Signal_Time := Signal.Time_Seconds; end if; when others => null; end case; when Back => case Player.Activity is when Stop => if Player.Track > 1 then Player.Track := Player.Track - 1; end if; Player.Track_time := 0; Player.Last_Signal_Time := Signal.Time_Seconds; when Play => if Player.Track_Time < 1 and Player.Track > 1 then Player.Track := Player.Track - 1; else Player.Track_time := 0; end if; Player.Last_Signal_Time := Signal.Time_Seconds; when others => null; end case; when Unused => -- this shouldn't happen; 0 isn't legal input Put_Line("Signal Error: Zero is illegal"); raise Program_Error; end case; end Process; ------------------------------ -- Main Procedure Variables -- ------------------------------ Player : Player_Status_Type; -- the CD player Disc : CD_Info_Type; -- disc in player Num_Signals : Natural; -- how many to process Signal : Signal_Info_Type; -- each signal in turn begin Initialize(Player); Read_CD_Data(Disc); Show_CD_Data(Disc); Get(Num_Signals); Put("Processing "); Put(Num_Signals, Width=>1); Put(" signals:"); New_Line; for Line in 1..Num_Signals loop Signal := Read_Signal; -- time and signal Put(Signal.Time_String); Put(" - "); Put(Signal.Content, Width=>6); -- status when signal arrived Update_Status(Player, Disc, Signal.Time_Seconds); Put(" was: "); Show_Status(Player); -- status after signal processed Put("; now: "); Process(Player, Disc, Signal); Show_Status(Player); New_Line; end loop; end Player;