PROGRAM Bowling;
{Programmer: Gregory A. Perkins
CS 65
Due: 25 October 1996}
{This program produces graphical output of the scores for a game of bowling. The user
enters the number of pins knocked down with each successive roll of the ball and
the program determines the score which is displayed on a graphical scorecard.}
{------------------------------------------------------------------------------------}
{ Program shell for automatic bowling scorer.
Graphics routines by: John Zelle
Provided routines
DigitToChar: Converts a single digit into a character.
(Note: 10 is converted to 'X'). Example:
Ch := DigitToChar(MyDigit);
IntToStr: Converts an integer into a string suitable for use
with outTextXY. Example:
IntToStr(MyInt, 3, MyString);
outTextXY(10,100, 'The Value is: '+ MyString);
(Note: Width is the width of the resulting string. Blanks are
added on the left. If MyInt has more than Width digits, Mystring
will expand to the length required.)
DrawScorecard: Draw the bowling scorecard near the top of the screen. Ex:
DrawScoreCard;
FrameScore: Takes an Integer representing the Frame number and another
representing a score. The score is recorded as the score for
the given frame. Example:
FrameScore(ThisFrame, CurrentScore);
(Note: Frame must be a value from 1-10 inclusive, and score should be
between 0 and 300.)
FrameRoll: Fills in a roll "cell" in a given frame with the character provided.
Frames 1 through 9 have two cells, cell 1 is in the center of the top
of the frame (just to the left of the inset box), cell 2 is the inset
box (corresponding to the recording locations of the two rolls). Frame
10 has 3 cells across the top. Example:
FrameRoll(ThisFrame, 2, 'X');
Places an 'X' in the position for the second role in ThisFrame.
}
USES cs65;
CONST Width = 60; {The width (and height) of each square in scorecard}
TopMarg = 400; {Distance of card from top}
LeftMarg = 20; {Distance of card from left side}
TextHeight = 8; {Size of text characters in graphics mode}
TextWidth = 8;
{---------------------------------------------------------------------------}
FUNCTION DigitToChar(D : INTEGER) : Char;
{Converts a digit to a character (e.g. 3 becomes '3').
Numbers other than a single digit become an 'X' }
BEGIN
IF (D > 9) OR (D < 0) THEN
DigitToChar := 'X'
ELSE
DigitToChar := CHR(D+48);
END;
{---------------------------------------------------------------------------}
PROCEDURE IntToStr( X, Width:INTEGER; VAR Result:STRING);
{ Convert an Integer into a String suitable for outTextXY. If X has
fewer than Width digits, it is padded on the left with blanks. If
X has more than Width digits, Result will be whatever length is
required to store the digits}
VAR Length,Digit, i, Index : INTEGER;
tempStr : STRING;
BEGIN {IntToStr}
{First decode digits into tempStr}
Length := 0;
IF X = 0 THEN
BEGIN
Length := 1;
tempStr := "0";
END
ELSE
WHILE X > 0 DO
BEGIN
Length := Length + 1;
Digit := X MOD 10;
TempStr[Length] := DigitToChar(Digit);
X := X DIV 10;
END;
{Pad to appropriate width}
WHILE Length < Width DO
BEGIN
Length := Length + 1;
TempStr[Length] := ' ';
END;
{Copy into Reslt in reverse order}
Index := 1;
FOR i := Length DOWNTO 1 DO
BEGIN
Result[Index] := tempStr[i];
Index := Index + 1;
END;
{Set length of Result}
Result[0] := CHR(Length);
END; {IntToStr}
{---------------------------------------------------------------------------}
PROCEDURE DrawScorecard;
VAR Square : INTEGER;
Xul, Xur : INTEGER;
BEGIN
FOR Square := 0 TO 9 DO
BEGIN
setColor(3); {Use Cyan for main squares}
Xul := Width * Square + LeftMarg;
Xur := Xul+Width -1;
BAR(Xul, TopMarg, Xur, TopMarg+Width-1);
Xul := Xul + 2*Width DIV 3;
setColor(14); {Cells for rolls in yellow}
BAR(Xul, TopMarg, Xur, TopMarg+Width DIV 3-1);
IF Square = 9 THEN
BAR(Width*9+Width DIV 3+LeftMarg, TopMarg,
Xur-Width Div 3, TopMarg+Width DIV 3 -1);
END;
END;
{---------------------------------------------------------------------------}
PROCEDURE FrameScore(Frame, Score : INTEGER);
{ Set the score in given Frame to Score}
VAR X,Y : INTEGER;
ScoreText : STRING;
BEGIN
X := (Frame-1)*Width +LeftMarg +(Width-3*TextWidth)DIV 2;
Y := TopMarg +(Width+Width DIV 4+TextHeight) DIV 2;
IntToStr(Score,3,ScoreText);
OutTextXY(X,Y, ScoreText);
END;
{---------------------------------------------------------------------------}
PROCEDURE FrameRoll(Frame, Roll : INTEGER; Value : CHAR);
{ Put Value into the cell appropriate for the Roll in the Frame}
VAR X,Y : INTEGER;
ScoreText : STRING;
BEGIN
IF Frame = 10 THEN
Roll := Roll - 1;
X := (Frame-1)*Width+Roll*Width DIV 3+(Width-TextWidth) DIV 2;
Y := TopMarg + (Width DIV 3 + TextHeight) DIV 2;
OutTextXY(X,Y,Value);
END;
{---------------------------------------------------------------------------}
PROCEDURE DisTurkey;
{Displays message of congratulations to user for three strikes in a row}
VAR Count : INTEGER;
BEGIN
OUTTEXTXY(236, 350, '***CONGRATULATIONS***');
{Make red "turkey" blink five times}
FOR Count := 1 TO 5 DO
BEGIN
SetColor(4);
OUTTEXTXY(296, 365, 'TURKEY');
Delay(3000);
SetColor(15);
OUTTEXTXY(296, 365, 'TURKEY');
Delay(3000);
END;
{Erase all remarks}
SetColor(15);
OUTTEXTXY(236, 350, '***CONGRATULATIONS***');
OUTTEXTXY(296, 365, 'TURKEY');
SetColor(0);{Return printing color to normal}
END;
{---------------------------------------------------------------------------}
PROCEDURE DisStrike;
{Displays message of congratulations to user for a Strike}
VAR Count : INTEGER;
BEGIN
OUTTEXTXY(236, 350, '***CONGRATULATIONS***');
{Make red "strike" blink five times}
FOR Count := 1 TO 5 DO
BEGIN
SetColor(4);
OUTTEXTXY(296, 365, 'STRIKE');
Delay(3000);
SetColor(15);
OUTTEXTXY(296, 365, 'STRIKE');
Delay(3000);
END;
{Erase all remarks}
SetColor(15);
OUTTEXTXY(236, 350, '***CONGRATULATIONS***');
OUTTEXTXY(296, 365, 'STRIKE');
SetColor(0);{Return printing color to normal}
END;
{---------------------------------------------------------------------------}
{ Main Program Starts Here }
{Declarations section}
VAR Frame, Score, Roll1, Roll2 : INTEGER;
{Spare/Strike true in previous frame - Double true when strikes in previous two frames}
VAR Spare, Strike, Double : BOOLEAN;
VAR FrameStr, RollCorrect : STRING;{Used for printing frame numbers, correcting input numbers}
{constants used for positioning of frame numbers and rolls}
CONST FrameMarg = LeftMarg + 12;
CONST Roll1Marg = LeftMarg + 255;
CONST Roll2Marg = LeftMarg + 503;
BEGIN {Main Program}
{Variable initialization section}
Frame := 0; Score := 0; Roll1 := 0; Roll2 := 0;
Spare := FALSE; Strike := FALSE; Double := FALSE;
{Place computer in graphics mode and draw the scorecard and
the frame/roll column headings on the screen.}
SetGraph;
DrawScoreCard;
SetColor(0);{Set color to black for all printing}
OUTTEXTXY(175, 40, 'Welcome to the Automatic Bowling Scorer!!!');
OUTTEXTXY(155, 55, '(ENTER number of pins knocked down in each frame)');
OUTTEXTXY(LeftMarg, 80, 'Frame');
OUTTEXTXY(LeftMarg, 82, '_____');
OUTTEXTXY(LeftMarg + 248, 80, '1st Ball');
OUTTEXTXY(LeftMarg + 248, 82, '_______');
OUTTEXTXY(LeftMarg + 496, 80, '2nd Ball');
OUTTEXTXY(LeftMarg + 496, 82, '_______');
{Frame Labeling Loop}
FOR Frame := 0 TO 9 DO
BEGIN
IntToStr(Frame + 1, 2, FrameStr);{change frame number to string for output}
OUTTEXTXY(LeftMarg + Width * Frame + 2, TopMarg - 1, 'Frame ' + FrameStr);
END;
{Main Loop}
FOR Frame := 1 TO 10 DO
BEGIN{main loop}
IntToStr(Frame, 2, FrameStr);{change frame number to string for output}
OUTTEXTXY(FrameMarg, (Frame * 15) + 82, FrameStr);{print frame number}
OUTTEXTXY(Roll1Marg, (Frame * 15) + 82, '>> ');{position cursor for Roll1 input}
{Get value of Roll1 from the user}
ReadInt(Roll1);
{Test for valid rolls}
{************ NOT COVERED IN CLASS *************}
{****** INPUTTING INVALID NUMBERS WAS GETTING ON MY NERVES ******}
WHILE (Roll1 > 10) DO
BEGIN
IntToStr(Roll1, 2, FrameStr);{Change wrong input number to 'string'}
OUTTEXTXY(Roll1Marg, (Frame * 15) + 82, '>> ');{position cursor for correction}
{Erase wrong input number}
SetColor(15);
OUTTEXT(FrameStr);
SetColor(0);
{Tell user that he has screwed up}
SetColor(4);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
Pause;
SetColor(15);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
SetColor(0);
OUTTEXTXY(Roll1Marg, (Frame * 15) + 82, '>> ');{reposition cursor}
{Get new number from user}
ReadInt(Roll1);
END;
{Erase pointer showing next input location}
SetColor(15);
OUTTEXTXY(Roll1Marg, (Frame * 15) + 82, '>> ');
SetColor(0);
{Update frames (if any) pending one roll}
IF Double THEN
BEGIN
Score := Score + 20 + Roll1;
FrameScore(Frame - 2, Score);
END
ELSE IF Spare THEN
BEGIN
Score := Score + 10 + Roll1;
FrameScore(Frame - 1, Score);
END;
{Determine whether a second roll is required}
IF ((Roll1 = 10) AND (Frame = 10)) THEN
BEGIN
FrameRoll(Frame, 1, 'X');
{Test if Displaying Turkey or Strike Procedure is necessary}
IF Double THEN
BEGIN
DisTurkey;
END
ELSE
BEGIN
DisStrike;
END;
END
ELSE IF (Roll1 = 10) THEN
BEGIN
FrameRoll(Frame, 2, 'X');
{Test if Displaying Turkey or Strike Procedure is necessary}
IF Double THEN
BEGIN
DisTurkey;
END
ELSE
BEGIN
DisStrike;
END;
END
ELSE
BEGIN
FrameRoll(Frame, 1, DigitToChar(Roll1));
OUTTEXTXY(Roll2Marg, (Frame * 15) + 82, '>> ');{position cursor for Roll2 input}
{Get value of Roll2 from user}
ReadInt(Roll2);
{Test for valid rolls}
{************ NOT COVERED IN CLASS *************}
{****** INPUTTING INVALID NUMBERS WAS GETTING ON MY NERVES ******}
WHILE (Roll1 + Roll2 > 10) DO
BEGIN
IntToStr(Roll2, 1, FrameStr);{Change wrong input number to 'string'}
OUTTEXTXY(Roll2Marg, (Frame * 15) + 82, '>> ');{position cursor for correction}
{Erase wrong input number}
SetColor(15);
OUTTEXT(FrameStr);
SetColor(0);
{Tell user that he has screwed up}
SetColor(4);
OUTTEXTXY(183, 350, 'BALL 1 AND BALL 2 CANNOT TOTAL MORE THAN 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
Pause;
SetColor(15);
OUTTEXTXY(183, 350, 'BALL 1 AND BALL 2 CANNOT TOTAL MORE THAN 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
SetColor(0);
OUTTEXTXY(Roll2Marg, (Frame * 15) + 82, '>> ');{reposition cursor}
{Get new number from user}
ReadInt(Roll2);
END;
{Erase pointer showing next input location}
SetColor(15);
OUTTEXTXY(Roll2Marg, (Frame * 15) + 82, '>> ');
SetColor(0);
{Score any frames (if any) pending 2 rolls}
IF Strike THEN
BEGIN
Score := Score + 10 + Roll1 + Roll2;
FrameScore(Frame - 1, Score);
END;
{Score this frame (if possible)}
IF ((Roll1 < 10) AND (Roll1 + Roll2 = 10)) THEN
BEGIN
FrameRoll(Frame, 2, '/');
END
ELSE
BEGIN
FrameRoll(Frame, 2, DigitToChar(Roll2));
Score := Score + Roll1 + Roll2;
FrameScore(Frame, Score);
END;
END;{Second Roll }
{Compute new values of Double, Strike, and Spare}
Double := (Strike AND (Roll1 = 10));
Strike := (Roll1 = 10);
Spare := ((Roll1 < 10) AND (Roll1 + Roll2 = 10));
END;{of main loop}
{Clean up the tenth frame (if necessary)}
IF Strike THEN
BEGIN
OUTTEXTXY(LeftMarg, (Frame * 15) + 97, 'Extra Balls:');
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');{Position cursor for extra Roll1 input}
{Get value of extra Roll1 from user and record in correct cell}
ReadInt(Roll1);
{Test for valid rolls}
{************ NOT COVERED IN CLASS *************}
{****** INPUTTING INVALID NUMBERS WAS GETTING ON MY NERVES ******}
WHILE (Roll1 > 10) DO
BEGIN
IntToStr(Roll1, 2, FrameStr);{Change wrong input number to 'string'}
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');{position cursor for correction}
{Erase wrong input number}
SetColor(15);
OUTTEXT(FrameStr);
SetColor(0);
{Tell user that he has screwed up}
SetColor(4);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
Pause;
SetColor(15);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
SetColor(0);
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');{reposition cursor}
{Get new number from user}
ReadInt(Roll1);
END;
{Erase the pointer}
SetColor(15);
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');
SetColor(0);
FrameRoll(10, 2, DigitToChar(Roll1));
{Display Strike Procedure if appropriate}
IF ((Roll1 = 10) AND (NOT Double)) THEN
DisStrike;
IF Double THEN
BEGIN
Score := Score + 20 + Roll1;
FrameScore(9, Score);
{Test to see if displaying the Turkey Procedure is necessary}
IF (Roll1 = 10) THEN
DisTurkey;
END;
OUTTEXTXY(Roll2Marg, (Frame * 15) + 97, '>> ');{Position cursor for extra Roll2 input}
{Get value of extra Roll2 from user and record in correct cell}
ReadInt(Roll2);
{Test for valid rolls when strike rolled in second cell of tenth frame}
{************ NOT COVERED IN CLASS *************}
{****** INPUTTING INVALID NUMBERS WAS GETTING ON MY NERVES ******}
WHILE ((Roll1 = 10) AND (Roll2 > 10)) DO
BEGIN
IntToStr(Roll2, 2, FrameStr);{Change wrong input number to 'string'}
OUTTEXTXY(Roll2Marg, (Frame * 15) + 97, '>> ');{position cursor for correction}
{Erase wrong input number}
SetColor(15);
OUTTEXT(FrameStr);
SetColor(0);
{Tell user that he has screwed up}
SetColor(4);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
Pause;
SetColor(15);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
SetColor(0);
OUTTEXTXY(Roll2Marg, (Frame * 15) + 97, '>> ');{reposition cursor}
{Get new number from user}
ReadInt(Roll2);
END;
{Test for valid rolls for other than perfect game}
{************ NOT COVERED IN CLASS *************}
{****** INPUTTING INVALID NUMBERS WAS GETTING ON MY NERVES ******}
WHILE ((Roll1 < 10) AND (Roll1 + Roll2 > 10)) DO
BEGIN
IntToStr(Roll2, 1, FrameStr);{Change wrong input number to 'string'}
OUTTEXTXY(Roll2Marg, (Frame * 15) + 97, '>> ');{position cursor for correction}
{Erase wrong input number}
SetColor(15);
OUTTEXT(FrameStr);
SetColor(0);
{Tell user that he has screwed up}
SetColor(4);
OUTTEXTXY(183, 350, 'BALL 1 AND BALL 2 CANNOT TOTAL MORE THAN 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
Pause;
SetColor(15);
OUTTEXTXY(183, 350, 'BALL 1 AND BALL 2 CANNOT TOTAL MORE THAN 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
SetColor(0);
OUTTEXTXY(Roll2Marg, (Frame * 15) + 97, '>> ');{reposition cursor}
{Get new number from user}
ReadInt(Roll2);
END;
{Erase the pointer}
SetColor(15);
OUTTEXTXY(Roll2Marg, (Frame * 15) + 97, '>> ');
SetColor(0);
IF ((Roll1 < 10) AND (Roll1 + Roll2 = 10)) THEN
BEGIN
FrameRoll(10, 3, '/');
END
ELSE
BEGIN
FrameRoll(10, 3, DigitToChar(Roll2));
END;
{Test to see if displaying the Turkey or Strike Procedure is necessary}
IF ((Roll1 = 10) AND (Roll2 = 10)) THEN
BEGIN
DisTurkey;
END;
Score := Score + 10 + Roll1 + Roll2;
FrameScore(10, Score);
END
ELSE IF Spare THEN
BEGIN
OUTTEXTXY(LeftMarg, (Frame * 15) + 97, 'Extra Ball:');
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');{Position cursor for extra Roll1 input}
{Get value of extra Roll1 for user and record/score the frame}
ReadInt(Roll1);
{Test for valid rolls}
{************ NOT COVERED IN CLASS *************}
{****** INPUTTING INVALID NUMBERS WAS GETTING ON MY NERVES ******}
WHILE (Roll1 > 10) DO
BEGIN
IntToStr(Roll1, 2, FrameStr);{Change wrong input number to 'string'}
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');{position cursor for correction}
{Erase wrong input number}
SetColor(15);
OUTTEXT(FrameStr);
SetColor(0);
{Tell user that he has screwed up}
SetColor(4);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
Pause;
SetColor(15);
OUTTEXTXY(212, 350, 'NUMBER MUST BE BETWEEN 0 AND 10');
OUTTEXTXY(256, 365, 'Hit any key to continue...');
SetColor(0);
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');{reposition cursor}
{Get new number from user}
ReadInt(Roll1);
END;
{Erase the pointer}
SetColor(15);
OUTTEXTXY(Roll1Marg, (Frame * 15) + 97, '>> ');
SetColor(0);
FrameRoll(10, 3, DigitToChar(Roll1));
Score := Score + 10 + Roll1;
FrameScore(10, Score);
{Display 'STRIKE' remarks if necessary}
IF (Roll1 = 10) THEN
DisStrike;
END;
Pause;
END.