Search code examples
recordadaunion-types

How do I modify the Ada record type to be a union type supporting two variants?


The program contains some type definitions that can be used in a program for playing noughts and crosses (also known as tic-tac-toe). gamePos2 describes a completed position where one player has won

I am trying to change the record type GamePos to be a union type supporting two variants: a position of a completed game and a position of a game in progress.

Feel free to ask any questions so I can elaborate on my problem

type GamePos is
        record
            board : TTTBoard;
            turn : Player;
            value : Float;
        end record;

    procedure Put_Pos(pos : GamePos) is
    begin
        Put_Board(pos.board);
        Put("Player to make the next move: ");
        Put(pos.turn);
        Put_Line("");
        Put("Estimated advantage of player X over player O is: ");
        Put(pos.value, 0, 0, 0);
        Put_Line("");
    end Put_Pos;

    gamePos1 : GamePos
        := (board =>
                ((X, X, O),
                 (O, B, X),
                 (O, B, B)),
            turn => Player_X,
            value => 0.0);

     gamePos2 : GamePos
         := (pos_variant => WON, -- the discriminant
             board =>
                 ((X, X, O),
                  (O, X, X),
                  (O, O, X)),
             winner => Player_X);

begin
    Put_Pos(gamePos1);
    Put_Line("");
     Put_Pos(gamePos2);
end TTT;

Solution

  • The example below shows how to create and use a variant record with a discriminator. More information can be found on learn.adacore.com.

    main.adb

    with Ada.Text_IO; use Ada.Text_IO;
    
    procedure TTT is
    
       type Player is (Player_X, Player_Y);
    
       type Cell_State is (X, O, B);
    
       type TTT_Board is array (Positive range 1 .. 3,
                                Positive range 1 .. 3) of Cell_State;
    
       --  Game_State is the enumeration used as the discriminant type.
       
       type Game_State is (Ongoing, Done);   
       
       --  Make sure you use a default initialization (here: "Ongoing"). If you
       --  do not provide a default values, then the discriminant will be immutable.
        
       type Game_Pos (State : Game_State := Ongoing) is record
          Board : TTT_Board;      
          case State is
             when Ongoing =>
                Turn      : Player;
                Advantage : Float;
             when Done =>
                Winner    : Player;
          end case;
          end record;
    
       ---------------
       -- Put_Board --
       ---------------
       
       procedure Put_Board (Board : TTT_Board) is
       begin
          for R in Board'Range (1) loop
             for C in Board'Range (2) loop
                Put (Board (R, C)'Image & " ");
             end loop;
             New_Line;
          end loop;
       end Put_Board;
       
       -------------
       -- Put_Pos --
       -------------
       
       procedure Put_Pos (Pos : Game_Pos) is
       begin
          Put_Board (Pos.Board);
          New_Line;
          
          case Pos.State is
             when Ongoing =>
                Put_Line ("Player to make the next move: " & Pos.Turn'Image);
                New_Line;
         
             when Done =>
                Put_Line ("Winner: " & Pos.Winner'Image);
                New_Line;
                
          end case;
          
       end Put_Pos;
    
       
       Game_Pos_1 : Game_Pos :=
         (State     => Ongoing,
          Board     => ((X, X, O),
                        (O, B, X),
                        (O, B, B)),
          Turn      => Player_X,
          Advantage => 0.0);
    
       Game_Pos_2 : Game_Pos :=
         (State  => Done,
          Board  => ((X, X, O),
                     (O, X, X),
                     (O, O, X)),
          Winner => Player_X);
    
    begin
       Put_Pos (Game_Pos_1);
       New_Line;
       Put_Pos (Game_Pos_2);
    end TTT;
    

    output

    $ ./ttt
    X X O 
    O B X 
    O B B 
    
    Player to make the next move: PLAYER_X
    
    
    X X O 
    O X X 
    O O X 
    
    Winner: PLAYER_X