John B. Matthews, M.D.
Return home.
About war:
War is an Ada 95 program that repeatedly plays the children's card game of war. After playing a number of games, the program tallies up some statistics on wins and losses. It also prints out an ASCII plot of how long the games took.
The inspiration for many of the algorithms used in the program came from Dr. Michael B. Feldman's book Software Construction and Data Structures with Ada 95, Addison-Wesley, 1997, ISBN 0-201-88795-9.
Sample output:
Working...
Of 10000 games, 9896 ended before 1200 plays.
West won: 4971
East won: 4925
Average length: 317
Standard dev.: 221
Longest game: 1197
Shortest game: 33
W: K 4 T 9 A A 8 6 J J A 4 2 7 Q 6 Q Q Q 5 K 9 T J 8 3
E: 7 8 T 4 A 7 3 2 6 7 K K 9 4 3 5 8 9 J 3 T 5 5 2 6 2
Distribution of lengths:
0000-0100 |**********
0100-0200 |*****************************
0200-0300 |**********************
0300-0400 |***************
0400-0500 |**********
0500-0600 |*******
0600-0700 |****
0700-0800 |****
0800-0900 |**
0900-1000 |**
1000-1100 |*
1100-1200 |*
Source code:
-------------------------------------------------------
--
-- Simulate the card game of war.
--
-- John B. Matthews, M.D., Gem City Software, 5/14/1998
--
-- Distribution: per GPL
--
-------------------------------------------------------
with Ada.Text_IO;
with Ada.Strings.Fixed;
with Ada.Numerics.Elementary_Functions;
with Cards;
use type Cards.Value;
procedure War is
package TIO renames Ada.Text_IO;
Max_Games : constant Integer := 10_000;
Max_Count : constant Integer := 1_200;
Bin_Size : constant Integer := 100;
Plot_Size : constant Integer := 100;
Deck, Copy : Cards.Pack; -- the game deck and a copy
West, East : Cards.Pack; -- the players' hands
West_Count : Integer := 0; -- wins for West
East_Count : Integer := 0; -- wins for East
Longest : Integer := 0; -- longest game
Shortest : Integer := 9999; -- shortest game
Game_Count : Integer := 0; -- game count
Total : Integer := 0; -- total play count
Total2 : Float := 0.0; -- sum of squares
Sigma : Float;
type Histogram is array (0 .. Max_Count / Bin_Size - 1) of Integer;
Graph : Histogram := (others => 0);
procedure Deal (Deck : in Cards.Pack; West, East: in out Cards.Pack) is
Temp : Cards.Pack := Deck; -- deal from a copy
begin
Cards.Make_Empty(West);
Cards.Make_Empty(East);
while not Cards.Empty(Temp) loop
Cards.Move(Temp, West);
Cards.Move(Temp, East);
end loop;
end Deal;
procedure Play is
C1, C2 : Cards.Card;
Table : Cards.Pack;
begin
Cards.Draw(West, C1); -- draw from each hand
Cards.Draw(East, C2);
if C1.V = C2.V then -- war!
while C1.V = C2.V loop -- play until either wins
Cards.Place(Table, C1); -- lay 'em on the table
Cards.Place(Table, C2);
Cards.Draw(West, C1); -- draw two more
Cards.Draw(East, C2);
end loop;
end if;
if C1.V > C2.V then -- West wins
Cards.Place(West, C1); -- winning card first
Cards.Place(West, C2);
while not Cards.Empty(Table) loop
Cards.Move(Table, West); -- clear the table
end loop;
else -- East wins
Cards.Place(East, C2); -- winning card first
Cards.Place(East, C1);
while not Cards.Empty(Table) loop
Cards.Move(Table, East); -- clear the table
end loop;
end if;
end Play;
procedure Play_Game is
Counter : Integer := 0;
Bin : Integer;
begin
while Counter < Max_Count loop
Play;
Counter := Counter + 1;
end loop;
exception
when Cards.Pack_Empty => -- game over, collect stats
Total := Total + Counter;
Total2 := Total2 + Float(Counter * Counter);
Game_Count := Game_Count + 1;
Bin := Counter/Bin_Size;
Graph(Bin) := Graph(Bin) + 1;
if Cards.Empty(East) then
West_Count := West_Count + 1;
else
East_Count := East_Count + 1;
end if;
if Counter < Shortest then
Shortest := Counter;
Copy := Deck; -- save this one
end if;
if Counter > Longest then
Longest := Counter;
end if;
TIO.Put(ASCII.NUL);
end Play_Game;
function Label (J : Integer) return String is
use Ada.Strings; use Ada.Strings.Fixed;
Lower : String := Integer'Image(J * Bin_Size);
Upper : String := Integer'Image((J + 1) * Bin_Size);
begin
return Tail(Trim(Lower, Left), 4, '0') & "-" &
Tail(Trim(Upper, Left), 4, '0') & " |*";
end Label;
begin
TIO.Put_Line("Working...");
for I in 1 .. Max_Games loop
Cards.Shuffle(Deck);
Deal(Deck, West, East);
Play_Game;
end loop;
Sigma := Ada.Numerics.Elementary_Functions.Sqrt(
(Total2 - Float(Total) ** 2 / Float(Game_Count)) /
(Float(Game_Count) - 1.0) );
TIO.Put("Of" & Integer'Image(Max_Games) & " games,");
TIO.Put(Integer'Image(Game_Count) & " ended before");
TIO.Put(Integer'Image(Max_Count) & " plays.");
TIO.New_Line;
TIO.Put("West won: " & Integer'Image(West_Count));
TIO.New_Line;
TIO.Put("East won: " & Integer'Image(East_Count));
TIO.New_Line;
TIO.Put("Average length:" & Integer'Image(Total / Game_Count));
TIO.New_Line;
TIO.Put("Standard dev.: " & Integer'Image(Integer(Sigma)));
TIO.New_Line;
TIO.Put("Longest game: " & Integer'Image(Longest));
TIO.New_Line;
TIO.Put("Shortest game: " & Integer'Image(Shortest));
TIO.New_Line;
Deal(Copy, West, East); -- reconstruct the shortest
TIO.Put("W: "); Cards.Show(West); TIO.New_Line;
TIO.Put("E: "); Cards.Show(East); TIO.New_Line;
TIO.Put_Line("Distribution of lengths:");
for J in Graph'Range loop
TIO.Put(Label(J));
for K in 1 .. (Graph(J) * Plot_Size) / Game_Count loop
TIO.Put("*");
end loop;
TIO.New_Line;
end loop;
end War;
------------------------------------------------------
--
-- Simulate a pack of playing cards, suitable for war.
--
------------------------------------------------------
package Cards is
type Pack is private;
Pack_Empty : exception; -- attempt to Draw from an empty Pack
Pack_Full : exception; -- attempt to Place on a full Pack
type Value is (Deuce, Trey, Four, Five,
Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace);
type Suit is (Hearts, Clubs, Diamonds, Spades); -- not required
type Card is record
V : Value;
S : Suit;
end record;
procedure Make_Empty (P : in out Pack);
-- Make P empty
procedure Shuffle (P : in out Pack);
-- Shuffle a fresh pack
procedure Draw (P : in out Pack; C : out Card);
-- Draw one card C from the top of pack P
procedure Place (P : in out Pack; C : in Card);
-- Place the card C on the bottom of pack P
procedure Move (P1, P2 : in out Pack);
-- Draw from P1 and Place on P2;
function Empty (P : in Pack) return Boolean;
-- Returns true if P is empty
function Full (P : in Pack) return Boolean;
-- Returns true if P is full
procedure Show (P : in Pack);
private
subtype Card_Range is Positive range 1 .. 52;
type Card_Store is array (Card_Range) of Card;
-- a ring buffer holding up to 52 cards
type Pack is record
Size : Natural := 0;
Head : Natural := 1;
Tail : Natural := 0;
Card : Card_Store;
end record;
end Cards;
with Ada.Text_IO;
with Ada.Numerics.Discrete_Random;
package body Cards is
package TIO renames Ada.Text_IO;
package Any_Card is new Ada.Numerics.Discrete_Random(Card_Range);
G : Any_Card.Generator;
procedure Make_Empty (P : in out Pack) is
begin
P.Size := 0;
P.Head := 1;
P.Tail := 0;
end Make_Empty;
procedure Shuffle (P : in out Pack) is
N : Positive;
Temporary : Card;
begin
Make_Empty(P);
for S in Suit'Range loop
for V in Value'Range loop
Place (P, (V, S));
end loop;
end loop;
for J in Card_Range loop
N := Any_Card.Random(G);
Temporary := P.Card(N);
P.Card(N) := P.Card(J);
P.Card(J) := Temporary;
end loop;
end Shuffle;
procedure Draw (P : in out Pack; C : out Card) is
begin
if Empty(P) then
raise Pack_Empty;
else
C := P.Card(P.Head);
P.Size := P.Size - 1;
P.Head := P.Head + 1;
if P.Head > Card_Range'Last then
P.Head := Card_Range'First;
end if;
end if;
end Draw;
procedure Place (P : in out Pack; C : in Card) is
begin
if Full(P) then
raise Pack_Full;
else
P.Size := P.Size + 1;
P.Tail := P.Tail + 1;
if P.Tail > Card_Range'Last then
P.Tail := Card_Range'First;
end if;
P.Card(P.Tail) := C;
end if;
end Place;
procedure Move (P1, P2 : in out Pack) is
C : Card;
begin
Draw (P1, C);
Place(P2, C);
end Move;
function Empty (P : in Pack) return Boolean is
begin
return P.Size = 0;
end Empty;
function Full (P : in Pack) return Boolean is
begin
return P.Size = Card_Range'Last;
end Full;
procedure Show (P : in Pack) is
Temp : Pack := P;
C : Card;
begin
while not Empty(Temp) loop
Draw(Temp, C);
case C.V is
when Deuce => TIO.Put("2 ");
when Trey => TIO.Put("3 ");
when Four => TIO.Put("4 ");
when Five => TIO.Put("5 ");
when Six => TIO.Put("6 ");
when Seven => TIO.Put("7 ");
when Eight => TIO.Put("8 ");
when Nine => TIO.Put("9 ");
when Ten => TIO.Put("T ");
when Jack => TIO.Put("J ");
when Queen => TIO.Put("Q ");
when King => TIO.Put("K ");
when Ace => TIO.Put("A ");
end case;
end loop;
end Show;
begin
Any_Card.Reset(G);
end Cards;
Disclaimer:
Copyright 1998 by John B. Matthews. This program is distributed under the terms of the GNU General Public License (GPL), incorporated herein by reference. The author disclaims any liability arising out of use of this program, including (but not limited to) the time you waste playing with it. Happy programming!
Last updated 24-Oct-2009
Return home.