Contributor: BENJAMIN ARNOLDY
{_____________________________________________________________________________
| Filename: CODE.PAS
| Title: Spite & Malice
| Written By: Benjamin Arnoldy and Raechel Kula
|_____________________________________________________________________________
| Contents:
| The procedures: Deal, WhoseTurn, PickupCards, Decision, GetMove,
| CheckMove, MoveCard
| Oject: Pile
|_____________________________________________________________________________
| Synopsis:
| This program allows the user to select either another person, or the
 computer as the opponent, then play the opponent in the card game
| Spite & Malice. The interface is textual.
|_____________________________________________________________________________
| Description:
| No references at this time.
|_____________________________________________________________________________
| Environment:
| TurboPASCAL for the PC.
|_____________________________________________________________________________
| Version History:
|
| Version 5.1 -- May 8, 1996
| Raechel Kula & Benjamin Arnoldy
| Improved interface and Decision.
|
| Version 5.0 -- May 7, 1996
| Raechel Kula & Benjamin Arnoldy
| Code is cleaned up and ready for presentation.
|
| Version 4.3 -- May 6, 1996
| Raechel Kula & Benjamin Arnoldy
| Additional testing, more tinkering with weights.
|
| Version 4.2 -- May 5, 1996
| Raechel Kula & Benjamin Arnoldy
| Added provisions in decision for jokers.
|
| Version 4.1 -- May 4, 1996
| Raechel Kula & Benjamin Arnoldy
| Testing and tinkering with weights to make
| the computer a better opponent.
|
| Version 4.0 -- May 3, 1996
| Raechel Kula & Benjamin Arnoldy
| An "operable" Decision procedure is
| in place.
|
| Version 3.1 -- May 2, 1996
| Raechel Kula & Benjamin Arnoldy
| Various Embellishments to make it an operable
| 2 player game (e.g. end of game stuff).
|
| Version 3.0 -- May 1, 1996
| Raechel Kula & Benjamin Arnoldy
| Ascii Graphical Interface is instituted.
|
| Version 2.9 -- April 30, 1996
| Raechel Kula & Benjamin Arnoldy
| Small display functions (CardString) coded.
|
| Version 2.2 -- April 28, 1996
| Raechel Kula & Benjamin Arnoldy
| CheckMove procedure ironed out.
|
| Version 2.1 -- April 26, 1996
| Raechel Kula & Benjamin Arnoldy
| Basic Main Program Procedures Modified to fit with new
| object structure.
|
| Version 2.0 -- April 25, 1996
| Raechel Kula & Benjamin Arnoldy
| Object Pile Coded.
|
| MidApril -- Meeting with Prof Squier & Subsequent Major Rethinking
|
| Version 1.1 -- Apr. 7, 1996
| Raechel Kula & Benjamin Arnoldy
| Pieces of Decision and CheckMove procedures are
| completed.
|
| Version 1.0 -- Mar. 29, 1996
| Raechel Kula & Benjamin Arnoldy
| WhoseTurn, PickupCards, MoveCard procedures are coded.
| The code successfully compiles.
|
| Version 0.2 -- Mar. 12, 1996
| Raechel Kula & Benjamin Arnoldy
| Deal and GetMove procedures are coded.
|
| Version 0.2 -- Mar. 5, 1996
| Raechel Kula & Benjamin Arnoldy
| GetValue and GetPlace procedures are coded.
|
| Version 0.1 -- Feb. 30, 1996
| Raechel Kula & Benjamin Arnoldy
| Main Program and Stubs
| Version 0.0
|____________________________________________________________________________}
program SpiteMalice;
uses CRT;
{=============================================================================
 CONSTANTS
=============================================================================}
const DRAWPILE_MAX = 108;
 HAND_MAX = 6;
 SCOREPILE_MAX = 14;
 DISCARDPILE_MAX = 108;
 ACEPILE_MAX = 13;
 TRASHPILE_MAX = 108;
 MAXSIZE = 108;
 NULL = -1;
{=============================================================================
 TYPES
=============================================================================}
type CardVal_t = integer;
 Pos_t = integer;
 CardArray_t = array [1..108] of CardVal_t;
 CardValTable_t = array [1..26] of CardVal_t;
 choiceTable_t = array [1..26, 1..19] of integer;
{=============================================================================
 OBJECT DECLARATION
=============================================================================}
type Pile = object
 {public}
 procedure Init;
 procedure RandomShuffle;
 procedure PutOnTop (CardtoPutOn: CardVal_t);
 function RemoveFromTop: CardVal_t;
 function SeeRandom (Pos: Pos_t): CardVal_t;
 function DeleteByValue (value : CardVal_t): CardVal_t;
 function IsPresent (CardtoFind: CardVal_t): boolean;
 function NumCards: integer;
 private
 data: CardArray_t;
 top: Pos_t; {top = slot with top card in it.}
end; {Object declaration}
{=============================================================================
 OBJECT DEPENEDENT TYPES
=============================================================================}
Type pilepointer_t = ^Pile;
 stack_t = array [1..26] of pilepointer_t;
{=============================================================================
 GLOBAL VARIABLES
=============================================================================}
var DrawPile: Pile;
 PlayerHand: Pile;
 ComputerHand: Pile;
 PlayerScorePile: Pile;
 ComputerScorePile: Pile;
 PlayerDiscardPile1: Pile;
 PlayerDiscardPile2: Pile;
 PlayerDiscardPile3: Pile;
 PlayerDiscardPile4: Pile;
 ComputerDiscardPile1: Pile;
 ComputerDiscardPile2: Pile;
 ComputerDiscardPile3: Pile;
 ComputerDiscardPile4: Pile;
 AcePile1: Pile;
 AcePile2: Pile;
 AcePile3: Pile;
 AcePile4: Pile;
 TrashPile: Pile;
 ComputerTurn: boolean;
 Game: boolean;
 Valid, Discard, DecisionDiscard: boolean;
 From, Tto: integer;
 PosTable : stack_t;
 TopCardTable: CardValTable_t;
 pos: integer;
 Winner: string;
 ChoiceRate: choiceTable_t;
 AnotherGame: boolean;
 TwoPlayer: boolean;
 MustMove: boolean;
{=============================================================================
 OBJECT PROCEDURES & FUNCTIONS
=============================================================================}
{____________________________________________________________________
| Init
| Initializes a pile's array (data) and pointer (top)
|___________________________________________________________________}
procedure Pile.Init;
var Count: integer;
begin
 top := MAXSIZE + 1;
 for Count := 1 to MAXSIZE do
 Pile.PutOnTop (NULL); {Stores NULL values in entire array.}
 top := MAXSIZE + 1;
end; {procedure Init}
{____________________________________________________________________
| RandomShuffle
| Shuffles the cards in a pile.
|___________________________________________________________________}
procedure Pile.RandomShuffle;
var ShuffleArray: Pile; {Temporary Storage Pile}
 Counter: Pos_t;
 RandSlot: integer;
 DeckSize: integer;
 TopofDeck: Pos_t;
begin
 DeckSize := DrawPile.NumCards;
 TopofDeck := (MAXSIZE - DeckSize) + 1;
 ShuffleArray.Init; {Initializing ShuffleArray}
 ShuffleArray.top := TopofDeck;
 for Counter := 1 to DeckSize do begin
 RandSlot := Random (DeckSize) + 1; {'+1' due to Random range.}
 While ShuffleArray.SeeRandom (RandSlot)  NULL do
 RandSlot := Random (DeckSize) + 1;
 ShuffleArray.top := TopofDeck + Randslot;
 {Set ShuffleArray's "top" pointer to slot beneath empty slot, so
 that PutOnTop will put the card in the empty slot.}
 ShuffleArray.PutOnTop (DrawPile.RemoveFromTop);
 ShuffleArray.top := TopofDeck;
 end; {for}
 ShuffleArray.top := TopofDeck;
 {Set ShuffleArray's "top" pointer to the top of the stack.}
 for Counter := 1 to DeckSize do
 DrawPile.PutOnTop (ShuffleArray.RemoveFromTop);
 {Transfered shuffled ShuffleArray to DrawPile.}
end; {Procedure RandomShuffle}
{____________________________________________________________________
| PutOnTop
| Places a card value on the top of the pile.
|
|___________________________________________________________________}
procedure Pile.PutOnTop (CardtoPutOn: CardVal_t);
begin
 top := top - 1; {Advance the top pointer to the empty slot above it.}
 If top < 0 then begin writeln ('ERROR. Array Overflow.'); HALT; {Program is stopped if program attempts to a put a card on top of what should be a full pile. This should never never happen given that the size of the pile arrays are the same size as the number of cards.} end; data [top] := CardtoPutOn; end; {procedure PutOnTop} {____________________________________________________________________ | RemoveFromTop | Removes the top card from a pile and return the value of | of the card. |___________________________________________________________________} function Pile.RemoveFromTop: CardVal_t; begin RemoveFromTop := data [top]; data [top] := NULL; top := top + 1; {Adjusts the top pointer so it points at the top card.} end; {Procedure RemoveFromTop} {____________________________________________________________________ | SeeRandom | Allows the program to view the card value in any given | position in a stack. |___________________________________________________________________} function Pile.SeeRandom (pos: Pos_t): CardVal_t; begin SeeRandom := data [top + pos - 1]; {The "- 1" in the equation defines position 1 as the top card.} if (top + pos - 1)> MAXSIZE then
 SeeRandom := NULL;
 {if the seek excedes the boundaries, a null value is returned.}
end; {Procedure SeeRandom}
{____________________________________________________________________
| DeleteByValue
| Searches through a pile for a designated value, and "pulls"
| the card out, returning the card's value. After the card is
| removed, the gap in the stack is filled in by readjusting the
| cards.
|___________________________________________________________________}
function Pile.DeleteByValue (value : CardVal_t): CardVal_t;
var count:integer; hold : CardVal_t;
begin
 count:=0;
 Repeat
 count :=count+1;
 Until (data[count] = value);
 hold := data[top];
 data[top] := value;
 data[count] := hold;
 hold := Pile.RemoveFromTop;
end; {Procedure DeleteByValue}
{____________________________________________________________________
| IsPresent
| Searches through a pile, looking to see if a designated card
| value is present.
|___________________________________________________________________}
function Pile.IsPresent (CardtoFind: CardVal_t): boolean;
var
 ValuePresent: boolean;
begin
 ValuePresent := FALSE;
 while ((ValuePresent = FALSE) OR (top> MAXSIZE)) do begin
 top := top + 1;
 If data [top] = CardtoFind then
 ValuePresent := TRUE;
 end; {While}
 If ValuePresent = FALSE then
 IsPresent := FALSE
 else
 IsPresent := TRUE;
end; {Function IsPresent}
{____________________________________________________________________
| NumCards
| Returns the number of cards in a pile.
|___________________________________________________________________}
function Pile.NumCards: integer;
begin
 NumCards := (MAXSIZE - top) + 1;
 {The "+ 1" in the equation takes into account that the position of top
 contains a card.}
end; {function NumCards}
{============================================================================
 GENERAL FUNCTIONS
============================================================================}
{____________________________________________________________________
| CardValue
| Converts card value (4..111) to orderinal value.
| (0 = Joker, 1,2,3,...10,11 = JACK,...)
|___________________________________________________________________}
function CardValue (Card: CardVal_t): integer;
begin
 if Card = NULL then
 CardValue := NULL
 else
 CardValue := Card DIV 8;
end; {function CardValue}
{____________________________________________________________________
| CardString
| Converts a card value to a string, for representation on the
| screen.
|___________________________________________________________________}
function CardString (Card: CardVal_t): string;
var
 Number: integer;
 Output: string;
begin
 Number := Card DIV 8;
 if Card = NULL then Output := '' else
 if Number = 0 then Output := 'JO' else
 if Number = 1 then Output := 'AC' else
 if Number = 2 then Output := '02' else
 if Number = 3 then Output := '03' else
 if Number = 4 then Output := '04' else
 if Number = 5 then Output := '05' else
 if Number = 6 then Output := '06' else
 if Number = 7 then Output := '07' else
 if Number = 8 then Output := '08' else
 if Number = 9 then Output := '09' else
 if Number = 10 then Output := '10' else
 if Number = 11 then Output := 'JA' else
 if Number = 12 then Output := 'QU' else
 if Number = 13 then Output := 'KI' else
 Output := 'ERROR';
 Number := Card MOD 4;
 if Card = NULL then Output := '' else
 if (Card DIV 8) = 0 then Output := Output + '!' else
 if Number = 0 then Output := Output + chr(3) else
 if Number = 1 then Output := Output + chr(4) else
 if Number = 2 then Output := Output + chr(5) else
 if Number = 3 then Output := Output + chr(6) else
 Output := 'ERROR';
 CardString := Output;
end; {function CardSuit}
{___________________________________________________________________
| AceTopCard
| Due to the possibility of a joker on an ace pile, this
| function returns the ordinal value of the card on the top of
| an ace pile -- if there's a joker it is converted to its
| ordinal value within the pile.
|___________________________________________________________________}
function AceTopCard (Number: integer): integer;
var position: integer;
begin
 position := 1;
 while (CardValue (PosTable [Number]^.SeeRandom (position)) = 0) do
 position := position + 1;
 AceTopCard := CardValue (PosTable [Number]^.SeeRandom (position)) +
 position - 1;
end; {function AceTopCard}
{============================================================================
 MAIN PROGRAM PROCEDURES
 (Grouped with corresponding sub-procedures)
============================================================================}
{___________________________________________________________________
| Initialize
| Does all the Non-Object initialization.
|__________________________________________________________________}
procedure Initialize;
var count:integer;
begin
 Randomize;
 DrawPile.Init;
 PlayerHand.Init;
 ComputerHand.Init;
 PlayerScorePile.Init;
 ComputerScorePile.Init;
 PlayerDiscardPile1.Init;
 PlayerDiscardPile2.Init;
 PlayerDiscardPile3.Init;
 PlayerDiscardPile4.Init;
 ComputerDiscardPile1.Init;
 ComputerDiscardPile2.Init;
 ComputerDiscardPile3.Init;
 ComputerDiscardPile4.Init;
 AcePile1.Init;
 AcePile2.Init;
 AcePile3.Init;
 AcePile4.Init;
 TrashPile.Init;
 Game := TRUE;
 {Set up Position Table}
 PosTable[1] := @PlayerHand;
 PosTable[2] := @PlayerHand;
 PosTable[3] := @PlayerHand;
 PosTable[4] := @PlayerHand;
 PosTable[5] := @PlayerHand;
 PosTable[6] := @PlayerHand;
 PosTable[7] := @PlayerScorePile;
 PosTable[8] := @PlayerDiscardPile1;
 PosTable[9] := @PlayerDiscardPile2;
 PosTable[10] := @PlayerDiscardPile3;
 PosTable[11] := @PlayerDiscardPile4;
 PosTable[12] := @AcePile1;
 PosTable[13] := @AcePile2;
 PosTable[14] := @AcePile3;
 PosTable[15] := @AcePile4;
 PosTable[16] := @ComputerDiscardPile1;
 PosTable[17] := @ComputerDiscardPile2;
 PosTable[18] := @ComputerDiscardPile3;
 PosTable[19] := @ComputerDiscardPile4;
 PosTable[20] := @ComputerHand;
 PosTable[21] := @ComputerHand;
 PosTable[22] := @ComputerHand;
 PosTable[23] := @ComputerHand;
 PosTable[24] := @ComputerHand;
 PosTable[25] := @ComputerHand;
 PosTable[26] := @ComputerScorePile;
end; {procedure Initialize}
{___________________________________________________________________
| InitTable
| Refreshes the values for the TopCardTable, which stores the
| values of the top card in all 26 positions.
|__________________________________________________________________}
procedure InitTable;
var count:integer;
begin
 for count := 1 to 6 Do
 TopCardTable[count] := PosTable[count]^.SeeRandom (count);
 for count := 7 to 19 Do
 TopCardTable[count] := PosTable[count]^.SeeRandom (1);
 for count := 20 to 25 Do
 TopCardTable[count] := PosTable[count]^.SeeRandom(count-19);
 TopCardTable[26] := PosTable[26]^.SeeRandom(1);
end; {procedure InitTable}
{___________________________________________________________________
| Deal
| Deals the cards at the beginning of each game and decides,
| based on the outcome of the deal, who will go first.
|__________________________________________________________________}
procedure Deal;
var Card: CardVal_t;
 Counter: integer;
 PlayerScoreTop: CardVal_t;
 ComputerScoreTop: CardVal_t;
begin
 for Card := (1 +3) to (MAXSIZE +3) do
 {Put 2 decks of cards in draw pile, +3 is necessary for the div and mod
 to operate correctly.}
 DrawPile.PutOnTop (Card);
 DrawPile.RandomShuffle; {Shuffle the draw pile.}
 for Counter := 1 to 5 do begin {Deal the hands}
 PlayerHand.PutOnTop (DrawPile.RemoveFromTop);
 ComputerHand.PutOnTop (DrawPile.RemoveFromTop);
 end; {for}
 for Counter := 1 to 14 do begin {Deal the score piles}
 PlayerScorePile.PutOnTop (DrawPile.RemoveFromTop);
 ComputerScorePile.PutOnTop (DrawPile.RemoveFromTop);
 end; {for}
 PlayerDiscardPile1.PutOnTop (DrawPile.RemoveFromTop);
 PlayerDiscardPile2.PutOnTop (DrawPile.RemoveFromTop);
 PlayerDiscardPile3.PutOnTop (DrawPile.RemoveFromTop);
 PlayerDiscardPile4.PutOnTop (DrawPile.RemoveFromTop);
 ComputerDiscardPile1.PutOnTop (DrawPile.RemoveFromTop);
 ComputerDiscardPile2.PutOnTop (DrawPile.RemoveFromTop);
 ComputerDiscardPile3.PutOnTop (DrawPile.RemoveFromTop);
 ComputerDiscardPile4.PutOnTop (DrawPile.RemoveFromTop);
 {Decide whose turn it is. ComputerTurn set to opposite, because
 it will be reversed in upcoming WhoseTurn procedure.}
 PlayerScoreTop := CardValue (PlayerScorePile.SeeRandom(1));
 ComputerScoreTop := CardValue (ComputerScorePile.SeeRandom(1));
 if PlayerScoreTop = 0 then
 ComputerTurn := FALSE
 else if ComputerScoreTop = 0 then
 ComputerTurn := TRUE
 else if PlayerScoreTop = ComputerScoreTop then
 ComputerTurn := FALSE
 else if PlayerScoreTop> ComputerScoreTop then
 ComputerTurn := TRUE
 else
 ComputerTurn := FALSE;
end; {Deal}
{___________________________________________________________________
| OutString
| One of the procedures involving the interface.
| This procedure receives x,y coordinates for a screen position
| and outputs a string starting at that position.
|__________________________________________________________________}
procedure OutString (x,y: integer; toPrint: string);
begin
 GotoXY (x,y);
 write (toPrint);
end; {procedure OutString}
{____________________________________________________________________
| ColorDim
| One of the procedures involving the interface.
| Sets colors for displaying things involving the player whose
| turn it is not (hence, they are dimmed.)
|___________________________________________________________________}
procedure ColorDim;
begin
 TextColor (LIGHTgray);
 TextBackground (BLACK);
end; {procedure ColorDim}
{___________________________________________________________________
| ColorCard
| One of the procedures involving the interface.
| Sets colors for displaying a card of the player whose turn it
| is.
|___________________________________________________________________}
procedure ColorCard;
begin
 TextColor (YELLOW);
 TextBackGround (BLUE);
end; {procedure ColorCard}
{____________________________________________________________________
| ColorFrame
| One of the procedures involving the interface.
| Sets colors for highlighting the section of the frame
| involving the player whose turn it is.
|___________________________________________________________________}
procedure ColorFrame;
begin
 TextColor (YELLOW);
 TextBackground (BLACK);
end; {procedure ColorFrame}
{____________________________________________________________________
| ColorNormalText
| One of the procedures involving the interface.
| Sets colors for normal text and is also the colors which the
| game returns to upon exiting.
|___________________________________________________________________}
procedure ColorNormalText;
begin
 TextColor (WHITE);
 TextBackground (BLACK);
end; {procedure ColorNormalText}
{___________________________________________________________________
| ColorPosition
| One of the procedures involving the interface.
| Sets colors for the display of position indicators.
|__________________________________________________________________}
procedure ColorPosition;
begin
 TextColor (WHITE);
 TextBackground (RED);
end; {procedure ColorPosition}
{___________________________________________________________________
| TitleScreen
| Displays a title screen and asks whether the user would like
| a one-player or a two-player game. Accompanying procedures are
| called by TitleScreen
|__________________________________________________________________}
procedure Heart;
begin
TextColor (red);
TextBackground (LightGray);
write (char(3));
end;
procedure Club;
begin
TextColor (black);
TextBackground (LightGray);
write (char(5));
end;
procedure Diamond;
begin
TextColor (red);
TextBackground (lightgray);
write (char(4));
end;
procedure Spade;
begin
TextColor (black);
TextBackground (lightgray);
write (char(6));
end;
procedure SuitsCol (x, y, count: integer);
var c :integer;
begin
c := 0;
while (count> 0) Do begin
 GotoXY (x, y+c*4);
 Heart;
 GotoXY (x, y+c*4+1);
 Club;
 GotoXY (x, y+c*4+2);
 Diamond;
 GotoXY (x, y+c*4+3);
 Spade;
 c := c + 1;
 count := count - 1;
 TextBackGround (black);
end; {while loop}
end; {SuitsCol}
procedure SuitsRow (x, y, count: integer);
var c :integer;
begin
c := 0;
while (count> 0) Do begin
 GotoXY (x + (4*c), y);
 Heart;
 Club;
 Diamond;
 Spade;
 c := c + 1;
 count := count - 1;
 TextBackground (black);
end; {while loop}
end; {SuitsRow}
procedure DrawTitleBox;
Begin
SuitsCol (25, 7, 2);
SuitsRow (25, 7, 8);
SuitsRow (25, 15, 8);
SuitsCol (57, 7, 2);
GotoXY (57, 15);
Heart;
end; {DrawTitleBox}
procedure Title;
 begin
 TextColor (white);
 TextBackground (black);
 OutString (28, 9, 'Welcome to Spite & Malice!');
 end;
procedure Info (var TwoPlayer : boolean);
var response : char;
begin
repeat
 OutString (33, 12, 'How many players?');
 OutString (37, 13, '(');
 TextColor (lightred);
 OutString (38, 13, '1 ');
 TextColor (white);
 OutString (40, 13, 'or ');
 TextColor (lightred);
 OutString (43, 13, '2');
 TextColor (white);
 OutString (44, 13, ')');
 GotoXY (40, 14);
 readln (response);
 until ((response = '1') OR (response = '2'));
 if response = '1' then
 TwoPlayer := FALSE
 else
 TwoPlayer := TRUE;
end;
procedure TitleScreen (var TwoPlayer:boolean);
var response: char;
Begin
TextBackground (black);
clrscr;
TextBackground (black);
DrawTitleBox;
Title;
Info (TwoPlayer);
TextBackground (black);
TextColor (white);
End; {procedure TitleScreen}
{___________________________________________________________________
| DrawFrame
| One of the procedures involving the interface.
| This procedure draws the ascii graphical skeleton of the
| screen. It also takes into account the turn in its choice of
| colors.
|__________________________________________________________________}
procedure DrawFrame (ComputerTurn: boolean);
var Row: integer;
 Column: integer;
begin
 {Clear screen with Black background.}
 TextBackGround (BLACK);
 TextColor (BLACK);
 For Row:= 1 to 25 do
 For Column := 1 to 80 do begin
 if NOT ((Row = 25) and (Column = 80)) then
 OutString (Column, Row, chr(219));
 end; {for column}
 if ComputerTurn = TRUE then
 ColorDim
 else
 ColorFrame;
 OutString (1,1,chr(201));
 OutString (1,24,chr(200));
 OutString (31,1,chr(203));
 OutString (31,24,chr(202));
 for Column := 2 to 30 do begin
 OutString (Column,1,chr(205));
 OutString (Column,24,chr(205));
 end; {for}
 For Row := 2 to 23 do begin
 OutString (1,Row,chr(186));
 OutString (31,Row,chr(186));
 end; {for}
 Outstring (1,18,chr(204));
 Outstring (31,18,chr(185));
 For Row := 2 to 30 do
 OutString (Row,18,chr(205));
 OutString (31,5,chr(204));
 OutString (31,13,chr(204));
 if ComputerTurn = TRUE then
 ColorFrame
 else
 ColorDim;
 For Column := 51 to 79 do begin
 OutString (Column,1,chr(205));
 OutString (Column,18,chr(205));
 OutString (Column,24,chr(205));
 end; {for}
 For Row := 2 to 23 do begin
 OutString (50,Row,chr(186));
 OutString (80,Row,chr(186));
 end; {for}
 OutString (50,1,chr(203));
 OutString (50,24,chr(202));
 OutString (50,5,chr(185));
 OutString (50,13,chr(185));
 OutString (50,18,chr(204));
 OutString (80,1,chr(187));
 OutString (80,24,chr(188));
 ColorFrame;
 For Column := 32 to 49 do begin
 OutString (Column,1,chr(205));
 OutString (Column,5,chr(205));
 OutString (Column,13,chr(205));
 OutString (Column,24,chr(205));
 end; {for}
 TextColor (BLUE);
 for Row := 2 to 4 do
 for Column := 32 to 49 do
 OutString (Column,Row,chr(219));
 TextColor (WHITE);
 TextBackground (BLUE);
 OutString (34,2,'Spite & Malice');
 OutString (34,3,'By Ben Arnoldy');
 OutString (34,4,'& Raechel Kula');
end; {procedure DrawFrame}
{___________________________________________________________________
| DrawDiscards
| One of the procedures involved with the interface.
| This procedure sets up the discard portions of the screen.
|__________________________________________________________________}
procedure DrawDiscards (ComputerTurn:boolean);
var Counter: Pos_t;
begin
 if ComputerTurn = TRUE then
 ColorDim
 else
 ColorNormalText;
 OutString (9,2,'Player Discard');
 if ComputerTurn = TRUE then
 ColorNormalText
 else
 ColorDim;
 if (TwoPlayer = FALSE) then
 OutString (58,2,'Computer Discard')
 else if (TwoPlayer = TRUE) then
 OutString (58,2,'Opponent Discard');
 ColorPosition;
 OutString (3,3,'H'+chr(26));
 OutString (10,3,'I'+chr(26));
 OutString (17,3,'J'+chr(26));
 OutString (24,3,'K'+chr(26));
 OutString (52,3,'P'+chr(26));
 OutString (59,3,'Q'+chr(26));
 OutString (66,3,'R'+chr(26));
 OutString (73,3,'S'+chr(26));
 for Counter := 1 to 14 do begin
 if ComputerTurn = TRUE then
 ColorDim
 else
 ColorCard;
 OutString(6,2+Counter,
 CardString (PlayerDiscardPile1.SeeRandom(Counter)));
 OutString(13,2+Counter,
 CardString (PlayerDiscardPile2.SeeRandom(Counter)));
 OutString(20, 2+Counter,
 CardString (PlayerDiscardPile3.SeeRandom(Counter)));
 OutString(27, 2+Counter,
 CardString (PlayerDiscardPile4.SeeRandom(Counter)));
 if ComputerTurn = FALSE then
 ColorDim
 else
 ColorCard;
 OutString(55, 2+Counter,
 CardString (ComputerDiscardPile1.SeeRandom(Counter)));
 OutString(62, 2+Counter,
 CardString (ComputerDiscardPile2.SeeRandom(Counter)));
 OutString(69, 2+Counter,
 CardString (ComputerDiscardPile3.SeeRandom(Counter)));
 OutString(76, 2+Counter,
 CardString (ComputerDiscardPile4.SeeRandom(Counter)));
 end; {for}
 {if there are too many cards in a discard pile to display...}
 TextColor (LIGHTred);
 TextBackground (BLACK);
 for Counter := 1 to 4 do begin
 if PosTable [7+Counter]^.NumCards> 14 then
 OutString ((-2 + (Counter*7)),17,'more');
 if PosTable [15+Counter]^.NumCards> 14 then
 OutString ((44 + (Counter*7)),17,'more');
 end; {for}
end; {procedure DrawDiscards}
{___________________________________________________________________
| DrawHands
| One of the procedures involved with the interface.
| This procedure displays the hands and scorepiles.
|__________________________________________________________________}
procedure DrawHands (ComputerTurn:boolean);
var CardFace: string;
begin
 if ComputerTurn = TRUE then
 ColorDim
 else
 ColorNormalText;
 GotoXY (2,19);
 write ('Player''s Hand:');
 if ComputerTurn = FALSE then
 ColorDim
 else
 ColorNormalText;
 if (TwoPlayer = FALSE) then begin
 GotoXY (51,19);
 write ('Computer''s Hand:');
 end
 else if (TwoPlayer = TRUE) then begin
 GotoXY (51,19);
 write ('Opponent''s Hand:');
 end;
 ColorPosition;
 OutString (3,21,'A'+chr(24));
 OutString (8,21,'B'+chr(24));
 OutString (13,21,'C'+chr(24));
 OutString (18,21,'D'+chr(24));
 OutString (23,21,'E'+chr(24));
 OutString (28,21,'F'+chr(24));
 OutString (52,21,'T'+chr(24));
 OutString (57,21,'U'+chr(24));
 OutString (62,21,'V'+chr(24));
 OutString (67,21,'W'+chr(24));
 OutString (72,21,'X'+chr(24));
 OutString (77,21,'Y'+chr(24));
 If ComputerTurn = TRUE then
 ColorDim
 else
 ColorCard;
 OutString(3,20,CardString (PlayerHand.SeeRandom(1)));
 OutString(8,20,CardString (PlayerHand.SeeRandom(2)));
 OutString(13,20,CardString (PlayerHand.SeeRandom(3)));
 OutString(18,20,CardString (PlayerHand.SeeRandom(4)));
 OutString(23,20,CardString (PlayerHand.SeeRandom(5)));
 OutString(28,20,CardString (PlayerHand.SeeRandom(6)));
 If ComputerTurn = FALSE then
 ColorDim
 else
 ColorCard;
 If TwoPlayer then begin
 OutString(52,20,CardString (ComputerHand.SeeRandom(1)));
 OutString(57,20,CardString (ComputerHand.SeeRandom(2)));
 OutString(62,20,CardString (ComputerHand.SeeRandom(3)));
 OutString(67,20,CardString (ComputerHand.SeeRandom(4)));
 OutString(72,20,CardString (ComputerHand.SeeRandom(5)));
 OutString(77,20,CardString (ComputerHand.SeeRandom(6)));
 end {if}
 else begin
 CardFace := chr(168) + chr(63);
 if ComputerHand.NumCards> 0 then
 OutString(52,20,CardFace);
 if ComputerHand.NumCards> 1 then
 OutString(57,20,CardFace);
 if ComputerHand.NumCards> 2 then
 OutString(62,20,CardFace);
 if ComputerHand.NumCards> 3 then
 OutString(67,20,CardFace);
 if ComputerHand.NumCards> 4 then
 OutString(72,20,CardFace);
 if ComputerHand.NumCards> 5 then
 OutString(77,20,CardFace);
 end; {if-else}
 if ComputerTurn = TRUE then
 ColorDim
 else
 ColorNormalText;
 GotoXY (2,23);
 write ('Score Pile: ', PlayerScorePile.NumCards,
 ' cards> ');
 ColorPosition;
 write('G'+chr(26));
 TextColor (BLACK);
 TextBackground (BLACK);
 write(' ');
 if ComputerTurn = TRUE then
 ColorDim
 else
 ColorCard;
 write (CardString (PlayerScorePile.SeeRandom(1)));
 if ComputerTurn = FALSE then
 ColorDim
 else
 ColorNormalText;
 GotoXY (51,23);
 write ('Score Pile: ', ComputerScorePile.NumCards,
 ' cards> ');
 ColorPosition;
 write('Z'+chr(26));
 TextColor (BLACK);
 TextBackground (BLACK);
 write(' ');
 if ComputerTurn = FALSE then
 ColorDim
 else
 ColorCard;
 write (CardString (ComputerScorePile.SeeRandom(1)));
end; {procedure DrawHands}
{___________________________________________________________________
| DrawAcePiles
| One of the procedures involved with the interface.
| This procedure draws the AcePile portion of the screen.
|__________________________________________________________________}
procedure DrawAcePiles;
var Counter: integer;
begin
 ColorNormalText;
 OutString (36,5,'Ace Piles:');
 ColorPosition;
 OutString (38,8,'L'+chr(26));
 OutString (38,9,'M'+chr(26));
 OutString (38,10,'N'+chr(26));
 OutString (38,11,'O'+chr(26));
 ColorCard;
 for Counter := 1 to 4 do begin
 OutString(41,7+Counter,CardString (TopCardTable [11+Counter] ));
 if CardValue( TopCardTable [11+Counter] )=0 then
 if AceTopCard (11+Counter) < 10 then OutString(45,7+Counter,chr(AceTopCard (11+Counter) + 48)) else if AceTopCard (11+Counter) = 10 then OutString(45,7+Counter,'10') else if AceTopCard (11+Counter) = 11 then OutString(45,7+Counter,'JA') else if AceTopCard (11+Counter) = 12 then OutString(45,7+Counter,'QU') else if AceTopCard (11+Counter) = 13 then OutString(45,7+Counter,'KI'); end; {for} end; {Display} {___________________________________________________________________ | DrawMessageBox | One of the procedures involved with the interface. | This procedure clears the message portion of the screen and | prints a message displaying the turn. |__________________________________________________________________} procedure DrawMessageBox (ComputerTurn: boolean); var Column: integer; Row: integer; begin TextColor (BLACK); TextBackground (BLACK); for Column := 32 to 49 do for Row := 14 to 23 do OutString (Column,Row,chr(219)); ColorNormalText; if ((ComputerTurn = TRUE) AND (TwoPlayer = FALSE)) then begin GotoXY (33,15); write ('Computer''s Turn'); end else if ((ComputerTurn = TRUE) AND (TwoPlayer = TRUE)) then begin GotoXY (33,15); write ('Opponent''s Turn'); end else begin GotoXY (34,15); write ('Player''s Turn'); end; end; {procedure DrawMessageBox} {___________________________________________________________________ | Display | This procedure directs the interface procedures for a complete | redrawing of the screen. |__________________________________________________________________} procedure Display; begin clrscr; DrawFrame (ComputerTurn); DrawDiscards (ComputerTurn); DrawHands (ComputerTurn); DrawAcePiles; DrawMessageBox (ComputerTurn); end; {Display} {___________________________________________________________________ | PickUpHand | Picks up the required number of cards from the draw pile and | places them in the hand of the person whose turn it is. | This procedure also checks to see if the draw pile has run out | of cards. If so the trash pile is placed in the draw pile and | the draw pile is subsequently reshuffled. |___________________________________________________________________} Procedure PickupHand (var Hand : pile); var numToGet, count, Counter : integer; begin If (Hand.NumCards> 3)Then
 numToGet := 1
 Else
 numToGet := (5 - Hand.NumCards);
 For count := 1 to numToGet Do begin
 If DrawPile.NumCards = 0 then begin {Draw pile out of card, replenish}
 For Counter := 1 to TrashPile.NumCards do
 DrawPile.PutOnTop (TrashPile.RemoveFromTop);
 DrawPile.RandomShuffle;
 end; {if}
 Hand.PutOnTop (DrawPile.RemoveFromTop);
 end; {for}
end; {procedure PickupHand}
{____________________________________________________________________
| PickUpCards
| Sends correct hand to the PickupHand procedure according to
| whose turn it is.
|___________________________________________________________________}
Procedure PickupCards;
begin
 If ComputerTurn Then
 PickupHand (ComputerHand)
 Else
 PickupHand (PlayerHand);
 InitTable; {Refresh the Top Card Table}
end; {PickupCards}
{____________________________________________________________________
| HouseKeeping
| Performs some checks after a card has been moved.
| These checks include: removing completed ace piles,
| checking for completed game, and checking for
| insufficient cards to discard.
|___________________________________________________________________}
procedure HouseKeeping;
var Counter: integer;
 Counter2: integer;
begin
 InitTable; {Keep current top card information updated.}
 {Clean up any full ace piles.}
 for Counter := 12 to 15 do
 if PosTable [Counter]^.NumCards = 13 then
 for Counter2 := 1 to 13 do
 TrashPile.PutOnTop (PosTable [Counter]^.RemoveFromTop);
 {Check for Game over.}
 if ComputerScorePile.NumCards = 0 then
 begin
 Game := FALSE;
 Discard := TRUE;
 Winner := 'Computer';
 end; {if}
 if PlayerScorePile.NumCards = 0 then
 begin
 Game := FALSE;
 Discard := TRUE;
 Winner := 'Player';
 end; {if}
 {Run out of cards before discard.}
 If ((Discard = FALSE) AND ComputerTurn AND
 (ComputerHand.NumCards = 0)) then
 PickUpCards;
 If ((Discard = FALSE) AND (NOT ComputerTurn) AND
 (PlayerHand.NumCards = 0)) then
 PickUpCards;
end; {procedure HouseKeeping}
{____________________________________________________________________
| MoveCard
| Moves a card from one pile to another as specified.
|___________________________________________________________________}
Procedure MoveCard (From, Tto : integer);
var frompile : pilepointer_t; value: CardVal_t;
 dummy: integer;
begin
 if ((From < 7) Or ((From> 19) AND (From < 26))) then begin frompile :=PosTable[From]; value := TopCardTable[From]; dummy := frompile^.DeleteByValue(value); PosTable[Tto]^.PutOnTop(value); end else PosTable[Tto]^.PutOnTop (PosTable[From]^.RemoveFromTop); HouseKeeping; {Calls the HouseKeeping procedure} end; {procedure MoveCard} {____________________________________________________________________ | WhoseTurn | This procedure changes the turns. |___________________________________________________________________} Procedure WhoseTurn (var ComputerTurn : boolean); begin If ComputerTurn Then ComputerTurn := False Else ComputerTurn := True; end; {WhoseTurn} {____________________________________________________________________ | CheckMove | Checks to see if the move proposed is a) valid, and | b) a discard. |___________________________________________________________________} Procedure CheckMove(var From, Tto: integer); var TopCard: integer; position: Pos_t; Counter: Pos_t; EmptyAcePile: boolean; begin InitTable; Valid := TRUE; Discard := FALSE; MustMove := FALSE; If TopCardTable [From] = NULL then Valid := FALSE; {Invalid if moving from empty space.} If (Valid AND ((Tto < 8) OR (Tto> 19))) then
 Valid := FALSE;{Invalid if proposed to move card to ScorePiles or Hands}
 If (Valid AND ComputerTurn AND ((Tto < 12) OR (From < 12))) then Valid := FALSE; {Invalid if computer proposed to or from player's side.} If (VALID AND (NOT ComputerTurn) AND ((Tto> 15) OR (From> 15))) then
 Valid := FALSE; {Invalid if player proposed to or from computer's side.}
 If (VALID AND ((From> 11) AND (From < 16))) then Valid := FALSE; {Invalid if to Acepile from Acepile.} if (VALID AND (((Tto> 7) AND (Tto < 12)) OR ((Tto> 15) AND (Tto < 20))) AND (((From < 12) AND (From> 6)) OR ((From = 26) OR
 ((From> 15) AND (From < 20))))) then Valid := FALSE; {Invalid if to discard from a discard or score pile.} {Ace on top of Discard Pile must be played first.} EmptyAcePile := FALSE; for Counter := 1 to 4 do if PosTable [Counter + 11]^.NumCards = 0 then EmptyAcePile := TRUE; if (EmptyAcePile AND Valid) then for Counter := 1 to 4 do begin if ((ComputerTurn) AND (CardValue (TopCardTable [Counter + 15]) = 1) AND (From  (Counter + 15))
 AND (NOT(CardValue(TopCardTable[From])=1))) then
 Valid := FALSE;
 if ((NOT ComputerTurn) AND (CardValue (TopCardTable[Counter+7]) = 1)
 AND (From  (Counter + 7))
 AND (NOT(CardValue(TopCardTable[From])=1))) then
 Valid := FALSE;
 end; {for}
 if (EmptyAcePile AND Valid) then
 for Counter := 1 to 4 do begin
 if ((ComputerTurn) AND (CardValue (TopCardTable [Counter + 15]) = 1)
 AND (From = (Counter + 15))
 OR (CardValue(TopCardTable[From])=1)) then begin
 Valid := True;
 MustMove := True;
 end; {if}
 end; {for} {forces computer to play ace when
 To/From scores below threshold}
 {Ace Piles Check}
 if (VALID AND ((Tto> 11) AND (Tto < 16))) then begin TopCard := AceTopCard (Tto); If ((TopCard = NULL) AND (CardValue (TopCardTable [From])  1)) then
 Valid := FALSE {If placing non-ace on empty ace pile.}
 else if TopCard = NULL then
 Valid := TRUE
 else if CardValue(TopCardTable[From]) = 0 then
 Valid := TRUE {In all cases but as ace, joker is valid.}
 else if ((TopCard + 1)  CardValue (TopCardTable[From])) then
 Valid := FALSE; {If it is not next card in series.}
 end; {if}
 {Discard Check}
 if (Valid AND ((ComputerTurn AND ((Tto < 20) AND (Tto> 15) AND (From> 19)
 AND (From < 26))) OR (NOT ComputerTurn AND ((Tto < 12) AND (Tto> 7)
 AND (From < 7) AND (From> 0))))) then
 if PosTable [Tto]^.NumCards> 0 then begin
 Discard := TRUE;
 if ComputerTurn then
 For Counter := 16 to 19 do
 if PosTable [Counter]^.NumCards = 0 then begin
 Valid := FALSE;
 Discard := FALSE;
 end; {if}
 if NOT ComputerTurn then
 For Counter := 8 to 11 do
 if PosTable [Counter]^.NumCards = 0 then begin
 Valid := FALSE;
 Discard := FALSE;
 end; {if}
 end; {if}
end;{CheckMove}
{____________________________________________________________________
| GetMove
| Requested a proposal for a move from the player.
|___________________________________________________________________}
Procedure GetMove (var From, Tto: integer);
var FromChar, ToChar: char;
begin
 Display;
 ColorNormalText;
 OutString (33,17,'Enter positions');
 ColorDim;
 OutString (35,18,'(@ to Quit)');
 ColorNormalText;
 OutString (33,19,'Move a card');
 OutString (33,20,'from: ');
 readln (FromChar);
 OutString (33,21,'to: ');
 readln (ToChar);
 From := ord(UpCase(FromChar)) - 64;
 Tto := ord(UpCase(ToChar)) - 64;
 {-64 to adjust for alphabet's position in ASCII table.}
 if ((From = 0) OR (Tto = 0)) then begin {quit}
 ColorNormalText;
 clrscr;
 HALT;
 end; {if}
 if ((From < 1) OR (From> 26) OR (Tto < 1) OR (From> 26)) then begin
 From := 1;
 Tto := 1;
 end; {if}
end; {GetMove}
{____________________________________________________________________
| ResultsofCheck
| Displays a message regarding the results of the check in
| CheckMove.
|___________________________________________________________________}
procedure ResultsofCheck;
begin
 DrawMessageBox (ComputerTurn); {Calls the DrawMessageBox procedure}
 ColorNormalText;
 OutString (33,17,'Proposed Move:');
 GotoXY (33,18);
 write ('From: ',chr(From + 64));
 GotoXY (33,19);
 write ('To: ',chr(Tto + 64));
 GotoXY (33,21);
 if NOT Valid then begin
 TextColor (WHITE+BLINK);
 write ('Is NOT Valid!!');
 end
 else begin
 TextColor (WHITE);
 write ('Is Valid.');
 end; {if else}
 TextColor (RED+BLINK);
 OutString (33,23,'Press ...');
 readln;
end; {ResultsofCheck}
{_____________________________________________________________________
| PlayAgainBox
| Displays Box and asks player if he/she wants to play again
|_____________________________________________________________________}
procedure PlayAgainBox;
Begin
 ColorNormalText;
 clrscr;
 DrawTitleBox;
 ColorNormalText;
 OutString (27, 11, 'Would you like to play again?');
 OutString (37, 12, '(');
 TextColor (LightRed);
 OutString (38, 12, 'Y ');
 TextColor (white);
 OutString (40, 12, 'or ');
 TextColor (lightRed);
 OutString (43, 12, 'N');
 TextColor (white);
 OutString (44, 12, ')');
End;
{____________________________________________________________________
| GameOverDisplay
| Notifies player that the game is over, displays who won, and
| asks the player if he/she would like to play again.
|___________________________________________________________________}
Procedure GameOverDisplay (Winner: string);
var Response: char;
 Valid: boolean;
begin
 ColorNormalText;
 clrscr;
 DrawTitleBox;
 ColorNormalText;
 OutString (36, 10, 'Game Over!!');
 OutString (32, 12, 'The ');
 OutString (36, 12, Winner);
 OutString (44, 12, ' wins!');
 readln;
 {Play Again?}
 Valid := FALSE;
 Repeat
 PlayAgainBox;
 readln (Response);
 if (Upcase (Response) = 'Y') then begin
 AnotherGame := TRUE;
 Valid := TRUE;
 end
 else
 if (Upcase (Response) = 'N') then begin
 AnotherGame := FALSE;
 Valid := TRUE;
 end
 else
 Valid := FALSE;
 Until Valid;
end; {function AnotherGame}
{___________________________________________________________________
| SetUp
| One of Decision's evaluative functions.
| This function adds a negative weight if a play will result in
| setting up the player to play from his/her score pile.
|__________________________________________________________________}
Function SetUp: integer;
const
 WEIGHT = -20;
 SWEIGHT =-10;
var
 position: integer;
 Points: integer;
 CardCanPlay: integer;
 ScoreCard: integer;
 CardPlayed: integer;
begin
 Points := 0;
 ScoreCard := CardValue (TopCardTable [7]);
 CardPlayed := AceTopCard (Tto) + 1;
 CardCanPlay := CardPlayed + 1;
 If CardCanPlay = ScoreCard then begin
 Points := WEIGHT;
 For position := 16 to 26 do begin
 if CardValue (TopCardTable [position]) = ScoreCard then
 Points := 0;
 if position = From then
 if CardValue (PosTable [position]^.SeeRandom(2)) = ScoreCard then
 Points := 0;
 end; {for}
 end; {if}
 If (Points = WEIGHT) AND (From = 26) then
 Points := SWEIGHT;
 SetUp := Points;
end; {function SetUp}
{___________________________________________________________________
| Block
| One of Decision's evaluative functions.
| This function adds a positive weight if the play results in
| preventing the player from playing from his score pile.
|__________________________________________________________________}
function Block: integer;
const
 WEIGHT = 25;
var
 Points: integer;
 ScoreCard: integer;
 CardPlayed: integer;
begin
 Points := 0;
 ScoreCard := CardValue (TopCardTable [7]);
 CardPlayed := AceTopCard (Tto) + 1;
 If CardPlayed = ScoreCard then
 Points := WEIGHT;
 Block := points;
end; {Block}
{___________________________________________________________________
| PlayMore
| One of Decision's evaluative functions.
| This function adds a positive weight if a play results in the
| computer being able to play more cards.
| It also adds a positive weight if a play allows the computer to
| move a card.
|___________________________________________________________________}
function PlayMore: integer;
const
 WEIGHT = 15; {If move allows the computer to move more cards.}
 WEIGHT2 = 10; {If Computer can move a card.}
var
 position: integer;
 Points: integer;
 CardCanPlay: integer;
 CardPlayed: integer;
begin
 Points := WEIGHT2; {Just for being able to play a card.}
 CardPlayed := AceTopCard (Tto) + 1;
 CardCanPlay := CardPlayed + 1;
 position := 16;
 While (Position < 27) do begin if CardValue (TopCardTable [position]) = CardCanPlay then Points := WEIGHT; if position = From then if CardValue(PosTable [position]^.SeeRandom (2)) = CardCanPlay then Points := WEIGHT; position := position + 1; end; {While} {Special case for Jokers} If CardValue (TopCardTable [From]) = 0 then Points := Points - WEIGHT; PlayMore := Points; end; {function PlayMore} {____________________________________________________________________ | MoreCards | One of Decision's evaluative functions | This function adds weight to a play that will result in the | computer being able to pick up more cards at the beginning of | its next turn. Additional weight is given to a play that will | result in the computer being able to pick up 5 more cards this | turn. |____________________________________________________________________} function MoreCards: integer; const WEIGHT = 10; WEIGHT2 = 20; var HolestoFill: integer; Counter: integer; Points: integer; begin Points := 0; {creates empty discard pile, ie a hole to fill} If ((From>15) AND (From <20) AND (PosTable [From]^.NumCards = 1) AND (NOT CardValue(TopCardTable [From]) = 0)) then Points := WEIGHT; {takes into account the holes} HolestoFill := 0; If ((From> 19) AND (From < 26 )) then begin Points := WEIGHT; For Counter := 16 to 19 do begin If PosTable [Counter]^.NumCards = 0 then HolestoFill := HolestoFill + 1; end; {for} If (ComputerHand.NumCards - HolestoFill) = 0 then Points := WEIGHT2; {special case for Jokers} If CardValue (TopCardTable [From]) = 0 then Points := Points - WEIGHT; end; {if} MoreCards := Points; end; {MoreCards} {_____________________________________________________________________ | HelpScore | One of Decision's evaluative functions | This function will add positive weight to a play that results | in the computer being able to play from its score pile. |____________________________________________________________________} function HelpScore: integer; const WEIGHT = 30; var ScoreCard: integer; CardPlayed: integer; CardCanPlay: integer; Points: integer; begin Points := 0; ScoreCard := CardValue (TopCardTable [26]); CardPlayed := AceTopCard (Tto) + 1; CardCanPlay := CardPlayed + 1; If CardCanPlay = ScoreCard then Points := WEIGHT; HelpScore := Points; end; {function HelpScore} {_____________________________________________________________________ | Score | One of Decision's evaluative functions. | This function adds positive weight to a score pile play. |____________________________________________________________________} function Score: integer; const WEIGHT = 60; WEIGHT2 = 10; var ScoreCard: integer; position: integer; Points: integer; Begin Points := 0; if From = 26 then begin ScoreCard := CardValue (TopCardTable [26]); if (((AceTopCard (Tto) + 1) = ScoreCard) OR (ScoreCard = 0)) then begin Points := WEIGHT; if ((ScoreCard + 1) = CardValue (TopCardTable [7])) then begin Points := WEIGHT2; position := 16; while (position < 26) do begin position := position + 1; if ((TopCardTable [position] = 0) OR (TopCardTable [position] = (ScoreCard +1))) then Points := WEIGHT; end; {While} end; {if} end; {if} end; {if} Score := Points; end; {function Score} {_____________________________________________________________________ | SameScore | One of DiscardDecision's evaluative functions | This function adds a negative weight to a discard | of a card that is the same value as the computer's score | pile. |____________________________________________________________________} function SameScore: integer; const WEIGHT = -5; JWEIGHT = -20; var Points: integer; begin Points := 0; If (CardValue(TopCardTable[From]) = CardValue (TopCardTable[26])) then Points := WEIGHT; {special case for Jokers} If CardValue (TopCardTable[From]) = 0 then Points := JWEIGHT; SameScore := Points; end; {function SameScore} {_____________________________________________________________________ | Order | One of DecisionDiscard's evaluative functions | This function uses weights to prioritize a discard to the closest | possible lower value in relation to the top cards of the discard | piles. |____________________________________________________________________} function Order: integer; const WEIGHT1 = 20; WEIGHT2 = 11; WEIGHT3 = 4; WEIGHT4 = -5; JWEIGHT = -20; var next: CardVal_t; Points: integer; begin next := CardValue (TopCardTable [Tto]) - 1; if (CardValue (TopCardTable [From]) = next) then Points := WEIGHT1; if ((CardValue (TopCardTable [From]) + 1) = next) then Points := WEIGHT2; if ((CardValue (TopCardTable[From]) + 1) < next) then Points := WEIGHT3; if (CardValue (TopCardTable [From])> next)
 then Points := WEIGHT4;
 {special case for Jokers}
 if CardValue (TopCardTable [From]) = 0 then
 Points := JWEIGHT;
 Order := Points;
end; {Order}
{_____________________________________________________________________
| HighCard
| One of DecisionDiscard's evaluative functions.
| This function weights the possible cards to fill in a space
| in the discard piles. It adds most weight to the highest
| valued card.
|____________________________________________________________________}
function HighCard: integer;
var count, Points: integer;
begin
 Points := 0;
 if ((PosTable [16]^.NumCards = 0) OR (PosTable [17]^.NumCards = 0) OR
 (PosTable [18]^.NumCards = 0) OR (PosTable [19]^.NumCards = 0)) then
 for count := 20 to 25 do
 if (CardValue(TopCardTable [From])>
 CardValue (TopCardTable [count])) then
 Points := Points + 1;
 HighCard := Points * 2;
end; {function HighCard}
{_____________________________________________________________________
| DiscardDecision
| This procedure is responsible for applying the various weights
| on to the decision surrounding the computer's discard.
|____________________________________________________________________}
Procedure DiscardDecision (var From, Tto: integer);
var max: integer;
 f, t: integer;
Begin
 For f := 20 to 25 Do
 For t := 16 to 19 Do begin
 From := f;
 Tto := t;
 CheckMove (From, Tto);
 If Not (Valid) Then
 ChoiceRate[f, t] := -10000
 Else
 ChoiceRate[f, t] := ((HighCard) + (Order) + (SameScore));
 end; {for}
 From := 20;
 Tto := 16;
 max := 0;
 For f := 20 to 25 Do
 For t := 16 to 19 Do begin
 If (ChoiceRate[f, t]> ChoiceRate[From, Tto]) Then begin
 max := ChoiceRate[f, t];
 From := f;
 Tto := t;
 end; {if}
 end; {for}
End; {DiscardDecision}
{_____________________________________________________________________
| Decision
| This procedure is responsible for applying the weights to the
| decision surrounding the computer's choice of moves.
|____________________________________________________________________}
Procedure Decision (var From, Tto: integer);
const Threshold = 10;
var Max: integer;
 f, t: integer;
Begin
 Display;
 For f := 1 to 26 do
 For t := 1 to 19 do
 ChoiceRate [f, t] := 0;
 For f := 16 to 26 Do
 For t := 12 to 15 Do begin
 From := f;
 Tto := t;
 CheckMove(From, Tto);
 If Not (Valid) Then
 ChoiceRate[f, t] := -10000
 Else
 ChoiceRate[f, t] := ((SetUp) + (Block) +
 (PlayMore) + (MoreCards) + (HelpScore) + (Score));
 end; {for}
 {Tests Threshold}
 From := 16;
 Tto := 12;
 max := 0;
 For f := 16 to 26 Do
 For t := 12 to 15 Do begin
 If (ChoiceRate[f, t]> ChoiceRate[From, Tto]) Then begin
 max := ChoiceRate[f, t];
 From := f;
 Tto := t;
 end; {if}
 end; {for}
 If (Max < Threshold) AND (NOT(MustMove)) Then
 DiscardDecision (From, Tto);
End; {Decision}
{============================================================================
 MAIN PROGRAM
============================================================================}
BEGIN {Main Program}
 Repeat
 TitleScreen (TwoPlayer);
 Initialize;
 Deal;
 While (Game) Do begin
 WhoseTurn (ComputerTurn);
 PickupCards;
 Repeat
 If ((ComputerTurn) AND (NOT TwoPlayer)) Then
 Decision (From, Tto)
 Else
 GetMove (From, Tto);
 CheckMove(From, Tto);
 ResultsofCheck;
 If Valid then
 MoveCard (From, Tto);
 Until (Discard);
 End; {While Loop}
 GameOverDisplay (Winner);
 Until (NOT AnotherGame);
END. {Main Program}


AltStyle によって変換されたページ (->オリジナル) /