home *** CD-ROM | disk | FTP | other *** search
/ Solo Programadores 22 / SOLO_22.iso / docs / lovelace / small.txt < prev    next >
Encoding:
Text File  |  1995-12-08  |  30.0 KB  |  1,022 lines

  1.  
  2. with Occupants;
  3. use  Occupants;
  4.  
  5. package Creatures is
  6.  type Creature is abstract new Occupant with private;
  7.  type Creature_Access   is access Creature'Class;
  8. private
  9.  type Creature is abstract new Occupant with null record;
  10. end Creatures;
  11.  
  12. with Ada.Strings.Unbounded;
  13. use  Ada.Strings.Unbounded;
  14.  
  15. package Directions is
  16.  
  17.  type Direction is (North, South, East, West, Up, Down);
  18.  
  19.  Reverse_Direction : constant array(Direction) of Direction :=
  20.                     (North => South, South => North,
  21.                      East =>West, West => East,
  22.                      Up => Down, Down => Up);
  23.  
  24.  function To_Direction(Text : Unbounded_String) return Direction;
  25.  -- Converts Text to Direction; raises Constraint_Error if it's not
  26.  -- a legal direction.
  27.  
  28.  function Is_Direction(Text : Unbounded_String) return Boolean;
  29.  -- Returns TRUE if Text is a direction, else false.
  30.  
  31. end Directions;
  32.  
  33.  
  34. with Occupants;
  35. use  Occupants;
  36.  
  37. package Items is
  38.  type Item     is new Occupant with private;
  39.  type Item_Access       is access Item'Class;
  40.  function May_I_Get(Direct_Object : access Item;
  41.                     Agent : access Occupant'Class) return Boolean;
  42.  
  43. private
  44.  type Item     is new Occupant with null record;
  45.  
  46. end Items;
  47.  
  48. with Creatures;
  49. use  Creatures;
  50.  
  51. package Monsters is
  52.  type Monster is new Creature with private;
  53.  type Monster_Access    is access Monster'Class;
  54. private
  55.  type Monster is new Creature with null record;
  56. end Monsters;
  57.  
  58. with Things, Directions;
  59. use  Things, Directions;
  60.  
  61. package Occupants is
  62.  
  63.  -- An "Occupant" is a Thing that can be inside a Room or another Occupant.
  64.  
  65.  type Occupant is abstract new Thing with private;
  66.  type Occupant_Access   is access all Occupant'Class;
  67.  
  68.  -- Dispatching subprograms:
  69.  
  70.  procedure Look(T : access Occupant);      -- Ask Occupant T to "look".
  71.  
  72.  procedure Get(Agent : access Occupant; Direct_Object : access Occupant'Class);
  73.            -- Ask Agent to get Direct_Object.  This assumes that Agent can
  74.            -- somehow access Direct_Object (i.e. is in the same room).
  75.            -- If the agent decides that it can get the object, it will
  76.            -- call May_I_Get to ask the object if that's okay.
  77.  
  78.  procedure Drop(Agent : access Occupant; Direct_Object : access Occupant'Class);
  79.            -- Ask Agent to drop Direct_Object.
  80.  
  81.  procedure Inventory(Agent : access Occupant);
  82.            -- Ask Agent to print a list of what Agent is carrying.
  83.  
  84.   procedure Go(Agent : access Occupant; Dir : in Direction);
  85.             -- Ask Agent to go the given Direction Dir (North, South, etc.)
  86.  
  87.  
  88.  -- Non-dispatching subprograms:
  89.  
  90.  procedure Put_View(T : access Occupant; Agent : access Thing'Class);
  91.  
  92.  function May_I_Get(Direct_Object : access Occupant;
  93.                     Agent : access Occupant'Class) return Boolean;
  94.            -- Ask Direct_Object if "Agent" can get this object.
  95.            -- Returns True if it's okay, else False.
  96.            -- If the object does something while being gotten (or an attempt
  97.            -- to do so) it does it in this call.
  98.  
  99.  function  May_I_Drop(Direct_Object : access Occupant;
  100.                       Agent         : access Occupant'Class) return Boolean;
  101.            -- Ask Direct_Object if "Agent" can drop this object;
  102.            -- returns True if it's okay.
  103.  
  104. private
  105.  
  106.  type Occupant is abstract new Thing with
  107.   record
  108.     null;  -- Nothing here for now.
  109.   end record;
  110.  
  111. end Occupants;
  112.  
  113.  
  114.   -- (C) 1995 David A. Wheeler.  Permission is granted to use this 
  115.   -- program for any purpose, commercial or otherwise, as long as
  116.   -- credit is given to David A. Wheeler.
  117.  
  118. with Ada.Strings.Unbounded;
  119. use  Ada.Strings.Unbounded;
  120.  
  121. package Parser is
  122.  procedure Execute(Command : in Unbounded_String; Quit : out Boolean);
  123.    -- Executes the given command.
  124.    -- Sets Quit to True if the user may run additional commands.
  125. end Parser;
  126.  
  127.  
  128. with Creatures;
  129. use  Creatures;
  130.  
  131. package Players is
  132.  type Player  is new Creature with private;
  133.  type Player_Access     is access Player'Class;
  134. private
  135.  type Player  is new Creature with null record;
  136. end Players;
  137.  
  138. with Things, Directions;
  139. use  Things, Directions;
  140.  
  141. package Rooms is
  142.  type Room     is new Thing with private;
  143.  type Room_Access       is access all Room'Class;
  144.  
  145.  procedure Put_View(T : access Room; Agent : access Thing'Class);
  146.  
  147.  procedure Connect(Source : access Room; Dir : in Direction; 
  148.                    Destination : access Thing'Class;
  149.                    Bidirectional : in Boolean := True);
  150.   -- Create a connection from Source to Destination in Direction Dir.
  151.   -- If it's bidirectional, create another connection the reverse way.
  152.  
  153.  procedure Disconnect(Source : access Room; Dir : in Direction; 
  154.                       Bidirectional : in Boolean := True);
  155.  -- Reverse of connect; disconnects an existing connection, if any.
  156.  
  157.  function What_Is(From : access Room; Dir : in Direction) return Thing_Access;
  158.  -- Returns what is at direction "Dir" from "From".
  159.  -- Returns null if nothing connected in that direction.
  160.  
  161. private
  162.  
  163.  type Destination_Array is array(Direction) of Thing_Access;
  164.  
  165.  type Room     is new Thing with
  166.   record
  167.     Destinations : Destination_Array;
  168.   end record;
  169.  
  170. end Rooms;
  171.  
  172.  
  173. with Ada.Strings.Unbounded, Ada.Finalization, Directions;
  174. use  Ada.Strings.Unbounded, Ada.Finalization, Directions;
  175.  
  176. package Things is
  177.  
  178.  -- "Thing" is the root class for all things in this small world.
  179.  -- Rooms, Players, Items, and Monsters are derived from Thing.
  180.  
  181.  -- This version (C) 1995 Ada Resource Association, Columbus, Ohio.
  182.  -- Permission is granted to use this program for any purpose,
  183.  -- commercial or not, as long as credit is given to David A. Wheeler
  184.  -- as the original developer.
  185.  
  186.  
  187.  type Thing is abstract new Limited_Controlled with private;
  188.  type Thing_Access is access all Thing'Class;
  189.  
  190.  type Article_Type is (A, An, The, Some, None);
  191.  
  192.  -- Public Dispatching operations.
  193.  
  194.  procedure Put_View(T : access Thing; Agent : access Thing'Class) is abstract;
  195.   -- Put what Agents sees inside T.
  196.  
  197.  function What_Is(From : access Thing; Dir : in Direction) return Thing_Access;
  198.  -- Returns what is at direction "Dir" from "From".
  199.  -- Returns null if nothing connected in that direction.
  200.  
  201.  -- Public non-Dispatching operations:
  202.  
  203.  procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
  204.                     Name : in Unbounded_String);
  205.  procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
  206.                     Name : in String);
  207.  function Name(T : access Thing'Class) return Unbounded_String;
  208.  pragma Inline(Name);
  209.  
  210.  function Short_Description(T : access Thing'Class) return Unbounded_String;
  211.  -- Returns Article + Name, i.e. "the box", "a car", "some horses".
  212.  
  213.  procedure Set_Description(T : access Thing'Class;
  214.                            Description : in Unbounded_String);
  215.  procedure Set_Description(T : access Thing'Class;
  216.                            Description : in String);
  217.  function Long_Description(T : access Thing'Class) return Unbounded_String;
  218.  
  219.  procedure Place(T : access Thing'Class; Into : Thing_Access);
  220.    -- Place T inside "Into" (removing it from wherever it was).
  221.    -- Attempting to place T into itself will print an error message
  222.    -- and fail.
  223.    -- The second parameter is Thing_Access, not Thing'Class, because
  224.    -- "null" is a valid value for "Into".
  225.  function Container(T : access Thing'Class) return Thing_Access;
  226.    -- Return access value to the container of T.
  227.  function Has_Contents(T : access Thing'Class) return Boolean;
  228.    -- Does T have anything in it?
  229.  
  230.  function Find(Agent : access Thing'Class;
  231.                Object_Name : in Unbounded_String) return Thing_Access;
  232.           -- Find the given Object_Name in the same container as the agent.
  233.           -- Prints and error message and returns null if not found.
  234.  
  235.  function Find_Inside(Agent       : access Thing'Class;
  236.                       Object_Name : in Unbounded_String)
  237.           return Thing_Access;
  238.           -- Find the given Object_Name inside the agent.
  239.           -- Prints and error message and returns null if not found.
  240.  
  241.  procedure Put_Contents(T : access Thing'Class;
  242.                         Ignore : access Thing'Class;
  243.                         Heading_With_Contents : in String;
  244.                         Heading_Without_Contents : in String := "");
  245.    -- Put a description of the contents of T.
  246.    -- Act as though "Ignore" isn't there.
  247.    -- If there is something, print Heading_With_Contents;
  248.    -- If there isn't something, print Heading_Without_Contents.
  249.  
  250.  procedure Sorry(Prohibited_Operation : String;
  251.                  Prohibited_Direct_Object : Unbounded_String);
  252.    -- Put "Sorry, you may not XXX the YYY".
  253.  
  254.  
  255. private
  256.  
  257.  type Thing is abstract new Limited_Controlled with
  258.   record
  259.    Name, Description : Unbounded_String;
  260.    Article           : Article_Type := A;
  261.    Container         : Thing_Access; -- what Thing contains me?
  262.    Next_Sibling      : Thing_Access; -- next Thing in my container.
  263.    First_Containee   : Thing_Access; -- first Thing inside me.
  264.   end record;
  265.  
  266. end Things;
  267.  
  268. with Text_IO, Ada.Strings.Unbounded;
  269. use  Text_IO, Ada.Strings.Unbounded;
  270.  
  271. package Ustrings is
  272.  
  273.   -- This package provides a simpler way to work with type
  274.   -- Unbounded_String, since this type will be used very often.
  275.   -- Most users will want to ALSO with "Ada.Strings.Unbounded".
  276.   -- Ideally this would be a child package of "Ada.Strings.Unbounded".
  277.   --
  278.  
  279.   -- This package provides the following simplifications:
  280.   --  + Shortens the type name from "Unbounded_String" to "Ustring".
  281.   --  + Creates shorter function names for To_Unbounded_String, i.e.
  282.   --    To_Ustring(U) and U(S).  "U" is not a very readable name, but
  283.   --    it's such a common operation that a short name seems appropriate
  284.   --    (this function is needed every time a String constant is used).
  285.   --    It also creates S(U) as the reverse of U(S).
  286.   --  + Adds other subprograms, currently just "Swap".
  287.   --  + Other packages can use this package to provide other simplifications.
  288.  
  289.   -- Developed by David A. Wheeler; released to the public domain.
  290.  
  291.   -- This version (C) 1995 Ada Resource Association, Columbus, Ohio.
  292.   -- Permission is granted to use this program for any purpose,
  293.   -- commercial or not, as long as credit is given to David A. Wheeler
  294.   -- as the original developer.
  295.  
  296.  
  297.   subtype Ustring is Unbounded_String;
  298.  
  299.   function To_Ustring(Source : String)  return Unbounded_String
  300.                                          renames To_Unbounded_String;
  301.   function U(Source : String)           return Unbounded_String
  302.                                          renames To_Unbounded_String;
  303.   function S(Source : Unbounded_String) return String
  304.                                          renames To_String;
  305.  
  306.   -- "Swap" is important for reuse in some other packages, so we'll define it.
  307.  
  308.   procedure Swap(Left, Right : in out Unbounded_String);
  309.  
  310.  
  311.   function Empty(S : Unbounded_String) return Boolean;
  312.    -- returns True if Length(S)=0.
  313.   pragma Inline(Empty);
  314.  
  315.  
  316.   -- I/O Routines.
  317.   procedure Get_Line(File : in File_Type; Item : out Unbounded_String);
  318.   procedure Get_Line(Item : out Unbounded_String);
  319.  
  320.   procedure Put(File : in File_Type; Item : in Unbounded_String);
  321.   procedure Put(Item : in Unbounded_String);
  322.  
  323.   procedure Put_Line(File : in File_Type; Item : in Unbounded_String);
  324.   procedure Put_Line(Item : in Unbounded_String);
  325.  
  326. end Ustrings;
  327.  
  328. with Occupants;
  329. use  Occupants;
  330.  
  331. package World is
  332.  
  333.  procedure Setup;
  334.   -- Setup the World; initialize the contents of the world.
  335.  
  336.  
  337.  function Me return Occupant_Access;
  338.            -- Return an access variable pointing to the current player.
  339.  
  340. end World;
  341.  
  342. with Ada.Characters.Handling;
  343. use  Ada.Characters.Handling;
  344.  
  345. package body Directions is
  346.  
  347.  Abbreviations : constant String := "nsewud";
  348.  
  349.  procedure To_Direction(Text : in Unbounded_String;
  350.                         Is_Direction : out Boolean;
  351.                         Dir  : out Direction) is
  352.   Lower_Text : String := To_Lower(To_String(Text));
  353.   -- Attempt to turn "Text" into a direction.
  354.   -- If successful, set "Is_Direction" True and "Dir" to the value.
  355.   -- If not successful, set "Is_Direction" False and "Dir" to arbitrary value.
  356.  begin
  357.    if Length(Text) = 1 then
  358.      -- Check if it's a one-letter abbreviation.
  359.      for D in Direction'Range loop
  360.        if Lower_Text(1) = Abbreviations(Direction'Pos(D) + 1) then
  361.          Is_Direction := True;
  362.          Dir := D;
  363.          return;
  364.        end if;
  365.      end loop;
  366.      Is_Direction := False;
  367.      Dir := North;
  368.      return;
  369.  
  370.    else
  371.      -- Not a one-letter abbreviation, try a full name.
  372.      for D in Direction'Range loop
  373.        if Lower_Text = To_Lower(Direction'Image(D)) then
  374.          Is_Direction := True;
  375.          Dir := D;
  376.          return;
  377.        end if;
  378.      end loop;
  379.      Is_Direction := False;
  380.      Dir := North;
  381.      return;
  382.    end if;
  383.  end To_Direction;
  384.  
  385.  function To_Direction(Text : in Unbounded_String) return Direction is
  386.    Is_Direction : Boolean;
  387.    Dir          : Direction;
  388.  begin
  389.    To_Direction(Text, Is_Direction, Dir);
  390.    if Is_Direction then
  391.       return Dir;
  392.    else
  393.       raise Constraint_Error;
  394.    end if;
  395.  end To_Direction;
  396.  
  397.  function Is_Direction(Text : in Unbounded_String) return Boolean is
  398.    Is_Direction : Boolean;
  399.    Dir          : Direction;
  400.  begin
  401.    To_Direction(Text, Is_Direction, Dir);
  402.    return Is_Direction;
  403.  end Is_Direction;
  404.  
  405. end Directions;
  406.  
  407.  
  408. package body Items is
  409.  
  410.  function May_I_Get(Direct_Object : access Item;
  411.                     Agent : access Occupant'Class) return Boolean is
  412.  begin
  413.   return True;
  414.  end May_I_Get;
  415.  
  416. end Items;
  417.  
  418. with Text_IO, Ada.Strings.Unbounded, Ustrings, Rooms;
  419. use  Text_IO, Ada.Strings.Unbounded, Ustrings, Rooms;
  420.  
  421. package body Occupants is
  422.  
  423.  
  424.  procedure Put_View(T : access Occupant; Agent : access Thing'Class) is
  425.  begin
  426.   Put("You are inside ");
  427.   Put_Line(Short_Description(T));
  428.   Put_Line(".");
  429.   Put_Contents(T, Agent, "You see:");
  430.  end Put_View;
  431.  
  432.  procedure Look(T : access Occupant) is
  433.  -- T is running a "look" command; tell T what he views.
  434.  begin
  435.   if Container(T) = null then
  436.     Put("You are inside nothing at all.");
  437.   else
  438.     Put_View(Container(T), T);
  439.   end if;
  440.  end Look;
  441.  
  442.  
  443.  procedure Get(Agent : access Occupant; Direct_Object : access Occupant'Class)
  444.  is
  445.  begin
  446.    if May_I_Get(Direct_Object, Agent) then
  447.      Place(T => Direct_Object, Into => Thing_Access(Agent));
  448.    end if;
  449.  end Get;
  450.  
  451.  function May_I_Get(Direct_Object : access Occupant;
  452.                     Agent : access Occupant'Class)
  453.           return Boolean is
  454.  begin
  455.    Sorry("get", Name(Direct_Object));  -- Tell the getter sorry, can't get it
  456.    return False;
  457.  end May_I_Get;
  458.  
  459.  procedure Drop(Agent : access Occupant;
  460.                 Direct_Object : access Occupant'Class) is
  461.  begin
  462.    if May_I_Drop(Direct_Object, Agent) then
  463.      Place(T => Direct_Object, Into => Container(Agent));
  464.    end if;
  465.  end Drop;
  466.  
  467.  function  May_I_Drop(Direct_Object : access Occupant;
  468.                       Agent : access Occupant'Class)
  469.            return Boolean is
  470.  begin
  471.    return True;
  472.  end May_I_Drop;
  473.  
  474.  
  475.  procedure Inventory(Agent : access Occupant) is
  476.  begin
  477.   Put_Contents(Agent, Agent,
  478.                "You're carrying:",
  479.                "You aren't carrying anything.");
  480.  end Inventory;
  481.  
  482.  procedure Go(Agent : access Occupant; Dir : in Direction) is
  483.  begin
  484.   if Container(Agent) = null then
  485.     Put_Line("Sorry, you're not in a room!");
  486.   else
  487.     declare
  488.       Destination : Thing_Access := What_Is(Container(Agent), Dir);
  489.     begin
  490.      if Destination = null then
  491.        Put_Line("Sorry, you can't go that way.");
  492.      else
  493.        Place(Agent, Destination);
  494.      end if;
  495.     end;
  496.   end if;
  497.  end Go;
  498.  
  499. end Occupants;
  500.  
  501.  
  502. with Text_IO, Ada.Strings.Maps.Constants, Ustrings, Things, Occupants, World;
  503. use  Text_IO, Ada.Strings.Maps.Constants, Ustrings, Things, Occupants, World;
  504. use  Ada.Strings, Ada.Strings.Maps;
  505.  
  506. with Directions;
  507. use  Directions;
  508.  
  509. package body Parser is
  510.  
  511.  Spaces : constant Character_Set := To_Set(' ');
  512.  
  513.  procedure Split(Source     : in  Unbounded_String;
  514.                  First_Word : out Unbounded_String;
  515.                  Rest       : out Unbounded_String) is
  516.   First : Positive; -- Index values of first word.
  517.   Last  : Natural;
  518.  -- Puts first word of Source into First_Word, the rest of the words in Rest
  519.  -- (without leading spaces); words are separated by one or more spaces;
  520.  -- if there are no spaces, Rest returns empty.
  521.  begin
  522.   Find_Token(Source, Spaces, Outside, First, Last);
  523.   First_Word := U(Slice(Source, First, Last));
  524.   Rest       := Trim(U(Slice(Source, Last + 2, Length(Source))), Left);
  525.  end Split;
  526.  
  527.  
  528.  
  529.  procedure Execute(Command : in Unbounded_String; Quit : out Boolean) is
  530.   Trimmed_Command : Unbounded_String := Trim(Command, Both);
  531.   Verb, Arguments, First_Argument, Rest_Of_Arguments : Unbounded_String;
  532.   Direct_Object : Occupant_Access;
  533.  begin
  534.   Quit := False; -- By default assume we won't quit.
  535.   if (Empty(Trimmed_Command)) then
  536.     return;      -- Ignore blank lines.
  537.   end if;
  538.  
  539.   -- Extract Verb and First_Argument and force them to lower case.
  540.   Split(Trimmed_Command, Verb, Arguments);
  541.   Translate(Verb, Lower_Case_Map);
  542.   Split(Arguments, First_Argument, Rest_Of_Arguments);
  543.   Translate(First_Argument, Lower_Case_Map);
  544.  
  545.  
  546.   -- Try to execute "Verb".
  547.  
  548.   if    Verb = "look" then
  549.     Look(Me);
  550.   elsif Verb = "get" then
  551.     Direct_Object := Occupant_Access(Find(Me, First_Argument));
  552.     if Direct_Object /= null then
  553.       Get(Me, Direct_Object);
  554.     end if;
  555.   elsif Verb = "drop" then
  556.     Direct_Object := Occupant_Access(Find_Inside(Me, First_Argument));
  557.     if Direct_Object /= null then
  558.       Drop(Me, Direct_Object);
  559.     end if;
  560.   elsif Verb = "inventory" or Verb = "inv" then
  561.     Inventory(Me);
  562.   elsif Verb = "quit" then
  563.     Quit := True;
  564.   elsif Verb = "go" and then Is_Direction(First_Argument) then
  565.     Go(Me, To_Direction(First_Argument));
  566.     Look(Me);
  567.   elsif Is_Direction(Verb) then  -- Is the verb a direction (north, etc)?
  568.     Go(Me, To_Direction(Verb));
  569.     Look(Me);
  570.   elsif Verb = "help" then
  571.     Put_Line("Please type in one or two word commands, beginning with a verb");
  572.     Put_Line("or direction. Directions are north, south, east, west, etc.");
  573.     Put_Line("Here are some sample commands:");
  574.     Put_Line("look, get box, drop box, inventory, go west, west, w, quit.");
  575.   else
  576.    Put_Line("Sorry, I don't recognize that verb. Try 'help'.");
  577.   end if;
  578.   
  579.  end Execute;
  580. end Parser;
  581.  
  582.  
  583.  
  584. with Text_IO, Ustrings;
  585. use  Text_IO, Ustrings;
  586.  
  587. package body Rooms is
  588.  
  589.  procedure Connect(Source : access Room; Dir : in Direction; 
  590.                    Destination : access Thing'Class;
  591.                    Bidirectional : in Boolean := True) is
  592.  begin
  593.    Source.Destinations(Dir) := Destination;
  594.    if Bidirectional then
  595.      Room_Access(Destination).Destinations(Reverse_Direction(Dir)) := Source;
  596.    end if;
  597.  end Connect;
  598.  
  599.  procedure Disconnect(Source : access Room; Dir : in Direction; 
  600.                       Bidirectional : in Boolean := True) is
  601.  begin
  602.    if Bidirectional then
  603.      -- if (Source.Destinations(Dir).all'Tag in Room'Class) then
  604.        Room_Access(Source.Destinations(Dir)).Destinations(Reverse_Direction(Dir)) := null;
  605.      -- end if;
  606.    end if;
  607.    Source.Destinations(Dir) := null;
  608.  end Disconnect;
  609.  
  610.  function What_Is(From : access Room; Dir : in Direction) return Thing_Access is
  611.  begin
  612.   return From.Destinations(Dir);
  613.  end What_Is;
  614.  
  615.  procedure Put_View(T : access Room; Agent : access Thing'Class) is
  616.  begin
  617.   Put("You are ");
  618.   Put(Long_Description(T));
  619.   Put_Line(".");
  620.   Put_Contents(T, Agent, "You see:");
  621.  end Put_View;
  622.  
  623. end Rooms;
  624.  
  625.  
  626.  
  627.  
  628.  
  629.   -- Main routine to start up "Small", a small text adventure game to
  630.   -- demonstrate Ada 95.
  631.  
  632.   -- This version (C) 1995 Ada Resource Association, Columbus, Ohio.
  633.   -- Permission is granted to use this program for any purpose,
  634.   -- commercial or not, as long as credit is given to David A. Wheeler
  635.   -- as the original developer.
  636.  
  637.   -- For documentation see the following URL:
  638.   --   http://lglwww.epfl.ch/Ada/Tutorials/Lovelace/small.htm
  639.  
  640. with Text_IO, Ada.Strings.Unbounded, Ustrings, World;
  641. use  Text_IO, Ada.Strings.Unbounded, Ustrings;
  642.  
  643. with Parser;
  644.  
  645. procedure Small is
  646.   Command : Unbounded_String; -- Contains user's current command.
  647.   Quit    : Boolean := False;
  648. begin
  649.  Put_Line("Welcome to a Small World!");
  650.  
  651.  World.Setup;
  652.  
  653.  while not Quit loop
  654.   New_Line;
  655.   Put_Line("Your Command?");
  656.   Get_Line(Command);
  657.   Parser.Execute(Command, Quit);
  658.  end loop;
  659.  
  660.  Put_Line("Bye!");
  661. end Small;
  662.  
  663. with Text_IO, Ustrings;
  664. use  Text_IO, Ustrings;
  665.  
  666.  
  667. package body Things is
  668.  
  669.  -- Define basic types for the world and their operations.
  670.  
  671.  
  672.  -- Supporting Subprograms:
  673.  
  674.  procedure Sorry(Prohibited_Operation : String;
  675.                  Prohibited_Direct_Object : Unbounded_String) is
  676.  begin
  677.   Put_Line("Sorry, you may not " & Prohibited_Operation & " the " &
  678.            S(Prohibited_Direct_Object));
  679.  end Sorry;
  680.  
  681.  
  682.  -- Routines to manipulate First_Containee, Next_Sibling, Container:
  683.  
  684.  function Previous_Sibling(Containee : access Thing'Class)
  685.           return Thing_Access is
  686.   -- Find the previous sibling of containee.  It's an error to call
  687.   -- this if Containee has no previous sibling.
  688.     Current : Thing_Access := Containee.Container.First_Containee;
  689.  begin
  690.     while Current.Next_Sibling /= Thing_Access(Containee) loop
  691.       Current := Current.Next_Sibling;
  692.     end loop;
  693.     return Current;
  694.  end Previous_Sibling;
  695.  
  696.  function Last_Containee(Container : access Thing'Class)
  697.           return Thing_Access is
  698.    -- Return an access value of the last contained Thing in container.
  699.    -- It's an error to call this routine if there are no containees.
  700.     Current : Thing_Access := Container.First_Containee;
  701.  begin
  702.     while Current.Next_Sibling /= null loop
  703.       Current := Current.Next_Sibling;
  704.     end loop;
  705.     return Current;
  706.  end Last_Containee;
  707.  
  708.  procedure Remove(Containee : access Thing'Class) is
  709.  -- Remove Containee from its current Container.
  710.   Previous_Thing : Thing_Access;
  711.  begin
  712.   if Containee.Container /= null then
  713.     if Containee.Container.First_Containee = Thing_Access(Containee) then
  714.        -- Containee is the first Thing in its container.
  715.        Containee.Container.First_Containee := Containee.Next_Sibling;
  716.     else
  717.        Previous_Thing := Previous_Sibling(Containee);
  718.        Previous_Thing.Next_Sibling := Containee.Next_Sibling;
  719.     end if;
  720.     Containee.Next_Sibling := null;
  721.     Containee.Container    := null;
  722.   end if;
  723.  end Remove;
  724.  
  725.  
  726.  procedure Place(T : access Thing'Class; Into : Thing_Access) is
  727.  -- Place "T" inside "Into".
  728.   Last : Thing_Access;
  729.  begin
  730.   if (Thing_Access(T) = Into) then
  731.     Put_Line("Sorry, that can't be done.");
  732.     return;
  733.   end if;
  734.   Remove(T); -- Remove Thing from where it is now.
  735.   if Into /= null then
  736.     if Into.First_Containee = null then
  737.       Into.First_Containee := Thing_Access(T);
  738.     else
  739.       Last := Last_Containee(Into);
  740.       Last.all.Next_Sibling := Thing_Access(T);
  741.     end if;
  742.   end if;
  743.   T.Container := Into;
  744.  end Place;
  745.  
  746.  procedure Put_Contents(T : access Thing'Class;
  747.                         Ignore : access Thing'Class;
  748.                         Heading_With_Contents : in String;
  749.                         Heading_Without_Contents : in String := "") is
  750.    -- Put a description of the contents of T.
  751.    -- If there is something, print Heading_With_Contents;
  752.    -- If there isn't something, print Heading_Without_Contents.
  753.    -- Ignore The_Player, since presumably the player already knows about
  754.    -- him/herself.
  755.    Current : Thing_Access := T.First_Containee;
  756.    Have_Put_Something : Boolean := False;
  757.  begin
  758.   while Current /= null loop
  759.     if Current /= Thing_Access(Ignore) then
  760.       -- This what we're to ignore, print it out.
  761.       if Have_Put_Something then
  762.         Put(", ");
  763.       else
  764.         -- We're about to print the first item; print the heading.
  765.         Put_Line(Heading_With_Contents);
  766.       end if;
  767.       Put(Short_Description(Current));
  768.       Have_Put_Something := True;
  769.     end if;
  770.     Current := Current.Next_Sibling;
  771.   end loop;
  772.   if Have_Put_Something then
  773.     Put_Line(".");
  774.   elsif Heading_With_Contents'Length > 0 then
  775.     Put_Line(Heading_Without_Contents);
  776.   end if;
  777.  end Put_Contents;
  778.  
  779.  
  780.  -- Dispatching Operations:
  781.  
  782.  function What_Is(From : access Thing; Dir : in Direction)
  783.           return Thing_Access is
  784.  begin
  785.    return null; -- As a default, you can't go ANY direction from "here".
  786.  end What_Is;
  787.  
  788.  
  789.  -- Non-dispatching public operations:
  790.  
  791.  procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
  792.                     Name : in Unbounded_String) is
  793.  begin
  794.    T.Article := Article;
  795.    T.Name    := Name;
  796.  end Set_Name;
  797.  
  798.  procedure Set_Name(T : access Thing'Class; Article : in Article_Type;
  799.                     Name : in String) is
  800.  begin
  801.    T.Article := Article;
  802.    T.Name    := To_Unbounded_String(Name);
  803.  end Set_Name;
  804.  
  805.  function Name(T : access Thing'Class) return Unbounded_String is
  806.  begin
  807.   return T.Name;
  808.  end Name;
  809.  
  810.  procedure Set_Description(T : access Thing'Class;
  811.                            Description : in Unbounded_String) is
  812.  begin
  813.   T.Description := Description;
  814.  end Set_Description;
  815.  
  816.  procedure Set_Description(T : access Thing'Class;
  817.                            Description : in String) is
  818.  begin
  819.   T.Description := To_Unbounded_String(Description);
  820.  end Set_Description;
  821.  
  822.  function Long_Description(T : access Thing'Class) return Unbounded_String is
  823.  begin
  824.    return T.Description;
  825.  end Long_Description;
  826.  
  827.  
  828.  -- Eventually we'll use an array for the article, but a minor GNAT 2.7.0 bug
  829.  -- will cause this to raise a Segmentation Fault when the program quits:
  830.  -- Article_Text : constant array(Article_Type) of Unbounded_String :=
  831.  --     (A => U("a "), An => U("an "), The => U("the "), Some => U("some "),
  832.  --      None => U(""));
  833.  
  834.  function Short_Description(T : access Thing'Class) return Unbounded_String is
  835.  begin
  836.   case T.Article is
  837.    when A    => return "a "    & T.Name;
  838.    when An   => return "an "   & T.Name;
  839.    when The  => return "the "  & T.Name;
  840.    when Some => return "some " & T.Name;
  841.    when None => return           T.Name;
  842.   end case;
  843.   -- Should become return Article_Text(T.Article) & T.Name;
  844.  end Short_Description;
  845.  
  846.  function Find(Agent : access Thing'Class;
  847.                Object_Name : in Unbounded_String) return Thing_Access is
  848.  begin
  849.    if Agent.Container = null then
  850.      Put_Line("You aren't in anything.");
  851.      return null;
  852.    else
  853.      return Find_Inside(Agent.Container, Object_Name);
  854.    end if;
  855.  end Find;
  856.  
  857.  function Find_Inside(Agent : access Thing'Class;
  858.                       Object_Name : in Unbounded_String)
  859.           return Thing_Access is
  860.    Current : Thing_Access := Agent.First_Containee;
  861.  begin
  862.    if Empty(Object_Name) then
  863.      Put_Line("Sorry, you need to name an object.");
  864.      return null;
  865.    end if;
  866.    while Current /= null loop
  867.      if Current.Name = Object_Name then
  868.        return Current;
  869.      end if;
  870.      Current := Current.Next_Sibling;
  871.    end loop;
  872.    Put("Sorry, I don't see a ");
  873.    Put_Line(Object_Name);
  874.    return null;
  875.  end Find_Inside;
  876.  
  877.  function Container(T : access Thing'Class) return Thing_Access is
  878.  begin
  879.    return T.Container;
  880.  end Container;
  881.  
  882.  function Has_Contents(T : access Thing'Class) return Boolean is
  883.  begin
  884.    if T.First_Containee = null then
  885.      return False;
  886.    else
  887.      return True;
  888.    end if;
  889.  end Has_Contents;
  890.  
  891. end Things;
  892. package body Ustrings is
  893.  
  894.   Input_Line_Buffer_Length : constant := 1024;
  895.     -- If an input line is longer, Get_Line will recurse to read in the line.
  896.  
  897.  
  898.   procedure Swap(Left, Right : in out Unbounded_String) is
  899.     -- Implement Swap.  This is the portable but slow approach.
  900.     Temporary : Unbounded_String;
  901.   begin
  902.     Temporary := Left;
  903.     Left := Right;
  904.     Right := Temporary;
  905.   end Swap;
  906.  
  907.   function Empty(S : Unbounded_String) return Boolean is
  908.    -- returns True if Length(S)=0.
  909.   begin
  910.    return (Length(S) = 0);
  911.   end Empty;
  912.  
  913.  
  914.   -- Implement Unbounded_String I/O by calling Text_IO String routines.
  915.  
  916.  
  917.   -- Get_Line gets a line of text, limited only by the maximum number of
  918.   -- characters in an Unbounded_String.  It reads characters into a buffer
  919.   -- and if that isn't enough, recurses to read the rest.
  920.  
  921.   procedure Get_Line (File : in File_Type; Item : out Unbounded_String) is
  922.  
  923.     function More_Input return Unbounded_String is
  924.        Input : String (1 .. Input_Line_Buffer_Length);
  925.        Last  : Natural;
  926.     begin
  927.        Get_Line (File, Input, Last);
  928.        if Last < Input'Last then
  929.           return   To_Unbounded_String (Input(1..Last));
  930.        else
  931.           return   To_Unbounded_String (Input(1..Last)) & More_Input;
  932.        end if;
  933.     end More_Input;
  934.  
  935.   begin
  936.       Item := More_Input;
  937.   end Get_Line;
  938.  
  939.  
  940.   procedure Get_Line(Item : out Unbounded_String) is
  941.   begin
  942.     Get_Line(Current_Input, Item);
  943.   end Get_Line;
  944.  
  945.   procedure Put(File : in File_Type; Item : in Unbounded_String) is
  946.   begin
  947.     Put(File, To_String(Item));
  948.   end Put;
  949.  
  950.   procedure Put(Item : in Unbounded_String) is
  951.   begin
  952.     Put(Current_Output, To_String(Item));
  953.   end Put;
  954.  
  955.   procedure Put_Line(File : in File_Type; Item : in Unbounded_String) is
  956.   begin
  957.     Put(File, Item);
  958.     New_Line(File);
  959.   end Put_Line;
  960.  
  961.   procedure Put_Line(Item : in Unbounded_String) is
  962.   begin
  963.     Put(Current_Output, Item);
  964.     New_Line;
  965.   end Put_Line;
  966.  
  967. end Ustrings;
  968.  
  969. with Text_IO, Ada.Strings.Unbounded, Ustrings;
  970. use  Text_IO, Ada.Strings.Unbounded, Ustrings;
  971.  
  972. with Things, Players, Items, Rooms, Directions;
  973. use  Things, Players, Items, Rooms, Directions;
  974.  
  975. package body World is
  976.  
  977.  The_Player : Player_Access;    -- This is the object representing the
  978.                                 -- current player.
  979.  
  980.  
  981.  procedure Setup is
  982.    Starting_Room : Room_Access := new Room;
  983.    Box           : Item_Access := new Item;
  984.    Knife         : Item_Access := new Item;
  985.    Living_Room   : Room_Access := new Room;
  986.  begin
  987.    Set_Name(Starting_Room, The, "Hallway");
  988.    Set_Description(Starting_Room, "in the hallway. There is a living room " &
  989.                    "to the west");
  990.  
  991.    Set_Name(Box, A, "box");
  992.    Set_Description(Box, "a red box");
  993.    Place(T => Box, Into => Thing_Access(Starting_Room));
  994.  
  995.    Set_Name(Knife, A, "knife");
  996.    Set_Description(Box, "a black knife");
  997.    Place(T => Knife, Into => Thing_Access(Starting_Room));
  998.  
  999.    Set_Name(Living_Room, The, "Living Room");
  1000.    Set_Description(Living_Room, "in the living room. " &
  1001.                                 "A hallway is to your east");
  1002.    Connect(Starting_Room, West, Living_Room);
  1003.  
  1004.    -- Setup player.
  1005.    The_Player := new Player; 
  1006.    Set_Name(The_Player, None, "Fred");
  1007.    Set_Description(The_Player, Name(The_Player));
  1008.    Place(T => Me,  Into => Thing_Access(Starting_Room));
  1009.    Look(Me);
  1010.  
  1011.  end Setup;
  1012.  
  1013.  
  1014.  function Me return Occupant_Access is
  1015.   -- Return access value to current player.
  1016.  begin
  1017.   return Occupant_Access(The_Player);
  1018.  end Me;
  1019.  
  1020. end World;
  1021.  
  1022.