First two counters are never increased in dice rolling application

Viewed 92

I am writing a program that rolls two dice and uses an array of counters to show how many times each total is shown. My code compiles fine and does run but for some reason the first two counters are never increased, and seems to print in numeric ascending order - which to me sounds like a problem with my random number function. But I cannot quite see where I have gone wrong. It also never iterates the correct amount of times, about half in fact. Here is my code.

    with Ada.Text_IO; use Ada.Text_IO;
    with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
    with Ada.Numerics.Discrete_Random;
    
    -- procedure main - begins program execution
    procedure main is
    
        dice1, dice2, diceTotal : Integer;
    
        type Count_Array is array(1 .. 11) of Integer;
        intArray : Count_Array;
    
        -- function returnRand - produces a random number and returns it
        function returnRand return Integer is
    
            type magicNumber is new Integer range 2 .. 12;
            package Rand_Number is new Ada.Numerics.Discrete_Random(magicNumber);
            use Rand_Number;
            theNumber : magicNumber;
            g : Generator;
    
        begin
            Reset(g);
    
            theNumber := Random(g);
    
            return Integer(theNumber);
        end returnRand;
    
        -- procedure rollDice - rolls two dice 36000 times and tallys the totals
        procedure rollDice(dice1 : out Integer; dice2 : out Integer;
                     diceTotal : out Integer; intArray : in out Count_Array) is
        begin
            for I in 1 .. 36000 loop
                dice1 := returnRand;
                dice2 := returnRand;
                diceTotal := dice1 + dice2;
    
                if diceTotal = 2 then
                    intArray(1) := intArray(1) + 1;
                elsif diceTotal = 3 then
                    intArray(2) := intArray(2) + 1;
                elsif diceTotal = 4 then
                    intArray(3) := intArray(3) + 1;
                elsif diceTotal = 5 then
                    intArray(4) := intArray(4) + 1;
                elsif diceTotal = 6 then
                    intArray(5) := intArray(5) + 1;
                elsif diceTotal = 7 then
                    intArray(6) := intArray(6) + 1;
                elsif diceTotal = 8 then
                    intArray(7) := intArray(7) + 1;
                elsif diceTotal = 9 then
                    intArray(8) := intArray(8) + 1;
                elsif diceTotal = 10 then
                    intArray(9) := intArray(9) + 1;
                elsif diceTotal = 11 then
                    intArray(10) := intArray(10) + 1;
                elsif diceTotal = 12 then
                    intArray(11) := intArray(11) + 1;
                end if;
            end loop;
        end rollDice;
    
        -- procedure printResults - prints out the totals of each dice throw
        procedure printResults(intArray : in Count_Array) is
        begin
            Put_Line("Dice Total          Tally");
    
            for I in Count_Array'Range loop
               -- Set_Col(2);
               -- Put(integer'image(I));
               -- Set_Col(23);
                Put(integer'image(intArray(I)));
                New_Line;
            end loop;
        end printResults;
    
    begin
        New_Line;
    
        intArray := (0,0,0,0,0,0,0,0,0,0,0);
    
        rollDice(dice1, dice2, diceTotal, intArray);
        printResults(intArray);
    
       New_Line;
    end main;
1 Answers

You roll two D11s (range 2..12), not D6s, so when you add them together, you get a range of 4..24 instead of the expected 2..12.

If you used the type system to model the problem, this would have been detected:

    type Count_Array is array(2 .. 12) of Integer;

and then your if..elsif construct could be just:

    intArray(diceTotal) := intArray(diceTotal) + 1;

Change your magicNumber to a 1..6 range to roll a D6 instead.

Also, there's no need to reset the Generator every time you roll a die. Once is sufficient.

Edit:

You could also go much further with the type system, and do something like this:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Numerics.Discrete_Random;

procedure Main is
   Number_Of_Dice : constant := 2;

   type Die_Range is new Positive range 1 .. 6;
   subtype Sums_Range is Die_Range'Base 
      range Number_Of_Dice*Die_Range'First..Number_Of_Dice*Die_Range'Last;

   type Count_Array is array(Sums_Range) of Natural;

   package Random_Die is new Ada.Numerics.Discrete_Random(Die_Range);
   Die : Random_Die.Generator;

   function Roll(Die : Random_Die.Generator) return Die_Range 
      renames Random_Die.Random;

   procedure Roll_Dice(Sums : in out Count_Array) is
      Dice_Total : Sums_Range'Base; 
   begin
      for I in 1 .. 36_000 loop
         Dice_Total := Roll(Die);
         for I in 1..Number_Of_Dice-1 loop
            Dice_Total := Dice_Total + Roll(Die);
         end loop;
         Sums(Dice_Total) := Sums(Dice_Total) + 1;
      end loop;
   end Roll_Dice;

   procedure Print_Results(Sums : in Count_Array) is
   begin
      Put_Line("Dice Total          Tally");

      for I in Sums'Range loop
         Put(Sums(I)'Image);
         New_Line;
      end loop;
   end Print_Results;

   Sums : Count_Array;
begin
   Random_Die.Reset(Die);
   New_Line;

   Sums := (others => 0);

   Roll_Dice(Sums);
   Print_Results(Sums);
   New_Line;
end Main;

Notice how easy this code will be to maintain when the requirements change, for example the number of dice cast per roll, or the type of dice (D4, D6, D8, D12, etc)

Related