home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
simtel
/
sigm
/
vols000
/
vol069
/
animals.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1984-04-29
|
39KB
|
1,088 lines
PROGRAM animals; {Requires Pascal/Z 3.3 or later, CP/M 2.2 or later}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* This program is Copyright (C) 1981 by *}
{* Systems Engineering Associates *}
{* 124 West Blithedale Avenue *}
{* Mill Valley, California 94941 *}
{* (415) 982-7468 *}
{* This program may be copied and used by anyone wishing to do so, *}
{* provided that the following conditions are respected: *}
{* (1) Neither this program, nor any portion or adaptation of *}
{* may be sold without the specific written permission of *}
{* Systems Engineering Associates. *}
{* (2) The full text of this Copyright Notice must be *}
{* included in any presentation of the source program. *}
{* (3) The program code that prints the acknowledgement of *}
{* authorship must not be altered, disabled or bypassed. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
ack1 = 'This guessing and learning game program';
ack1a = ' was written by, and is copyrighted by,';
ack2 = ' Roy P. Allen';
ack3 = ' Systems Engineering Associates';
ack4 = ' 124 West Blithedale Avenue';
ack5 = ' Mill Valley, California 94941 USA';
ack6 = ' (415) 982-7468';
filepfx = 'BEASTS';
inviter = 'Would you like to play the animal guessing game';
start1 = 'You think of an animal, and I''ll try to guess what it is.';
start2 = 'When you''re ready to begin, press the <RETURN> key.';
askagain = 'Would you like to play another round';
maxlen = 240;
bufsize = 256;
maxx = 128; {No. entries per XFILE block }
{$L+}
TYPE
questx = 0..maxlen; {Index to a question text }
bufx = 1..bufsize; {Index to a QFILE buffer }
dirx = 1..maxx; {Index to an XFILE block }
recty = (quest,ctl);
qstring = string maxlen;
question = RECORD; {QUESTION logical record }
ident : integer; {Record number (1..MAXINT) }
typcode : recty; {Record type }
CASE recty OF
quest: (nextyes : integer; {Next Q if answer = yes }
nextno : integer; {Next Q if answer = no }
query : qstring); {Current question }
ctl : (lastq : integer; {Last recno in QFILE }
lastqbl : integer; {Last QFILE block used }
lastxbl : integer; {Last XFILE block used }
beastct : integer) {No. animals known }
END; {question record}
buffer = packed array[bufx] of char;
qrec = RECORD;
qentry : buffer
END; {qrec record}
queryfile= file of qrec;
xbuffr = array[dirx] OF integer;
xrec = RECORD;
xentry : xbuffr
END; {xrec record}
directory= file of xrec;
filestring = string 14;
$string0 = string 0;
$string255 = string 255;
charset = set of char;
{$L+}
VAR
db : text; {Debugging output file }
dbugging : boolean; {Is debugging active? }
moreokay : boolean; {Indicator - keep playing? }
runabort : boolean; {Indicator - fatal error has occurred }
zerochr : char; {One byte of binary zero }
vowels : charset; {Set of all vowels }
shiftup : integer; {Factor to shift from lower to upper case }
replytxt : qstring; {Text of a console reply }
maxquery : integer; {Maximum question number in file }
highblok : integer; {Relative block# of last QFILE block }
highxblk : integer; {Relative block# of last XFILE block }
maxanimals : integer; {No. animals file now knows }
currblok : integer; {Relative block# - current QFILE block }
currxblk : integer; {Relative block# - current XFILE block }
qimage : qrec; {Current qfile block image }
ximage : xrec; {Current xfile block image }
currec : question; {Current question file record }
i : integer;
qfile : queryfile; {Questions file }
xfile : directory; {Directory to Questions file }
FUNCTION length (x: $string255): integer; EXTERNAL;
FUNCTION index (x, y: $string255): integer; EXTERNAL;
PROCEDURE setlength (VAR x: $string0; y: integer); EXTERNAL;
{$L+}
FUNCTION cnvrt (VAR arr: buffer; pnt: bufx): integer; {$C-}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given buffer ARR, with PNT pointing to the leftmost of a pair of *}
{* entries in ARR, return the integer value of the two-byte pair *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
maxint = 32767;
VAR
i : integer;
BEGIN {cnvrt function}
IF ord(arr[pnt])>127
THEN BEGIN
i := (256*(ord(arr[pnt]) mod 128)) + ord(arr[pnt+1]);
cnvrt := i - maxint - 1
END
ELSE cnvrt := (256*ord(arr[pnt])) + ord(arr[pnt+1])
END; {cnvrt function}
{- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -}
PROCEDURE revert (VAR buff: buffer; ptr: bufx; x: integer);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given an integer X, store it as two bytes as location PTR in *}
{* buffer BUFF. This procedure complements function CNVRT. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
BEGIN {revert}
buff[ptr] := chr(x div 256);
buff[ptr+1] := chr(x mod 256)
END; {revert procedure} {$L+}
PROCEDURE error (errnumbr: integer);
CONST
set1 = 'I''ve just been told that error number ';
set2 = ' (whatever THAT means) has occurred.';
set3 = 'Ain''t that the pits?!!';
intro = 'FATAL PROGRAM OR FILE ERROR. DESCRIPTION:';
err1 = 'Invalid record number passed to GETRECORD procedure.';
err2 = 'Invalid block pointer found in .QQX file.';
err3 = 'Invalid block number passed to BLOKFETCH procedure.';
err4 = 'APPENDSEG1 procedure invoked for a too-full block.';
err5 = '.QQQ record not found where .QQX file says it should be.';
unknown = '(Undefined error code)';
VAR
message : string 75;
BEGIN {error procedure}
writeln;
writeln(set1, errnumbr:2, set2);
writeln(set3);
writeln;
writeln(intro);
IF errnumbr=1
THEN message := err1
ELSE IF errnumbr=2
THEN message := err2
ELSE IF errnumbr=3
THEN message := err3
ELSE IF errnumbr=4
THEN message := err4
ELSE IF errnumbr=5
THEN message := err5
ELSE message := unknown;
writeln(' ',message);
writeln;
runabort := true
END; {error procedure} {$L+}
FUNCTION getyes: boolean;
{$Icopyseaf.inc }
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Secure from the console a reply of yes (y) or no (n). *}
{* Return "true" if yes, "false" otherwise. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
suffix = '? (Y/N) ';
prompt = ' Please reply yes (Y) or no (N): ';
VAR
reply : string 10;
ans : char;
gotreply : boolean;
BEGIN {getyes function}
write(suffix);
gotreply := false;
while gotreply= false do
begin {while}
readln(reply);
gotreply := true;
ans := reply[1];
case ans of
'Y', 'y': getyes := true;
'N', 'n': getyes := false;
else:
begin {else}
gotreply := false;
write(prompt)
end {else}
end {case}
end {while}
END; {getyes function} {$L+}
PROCEDURE shiftxt (VAR arr: buffer;
org: bufx;
len: bufx;
VAR trg: qstring); {$C-}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Append a sequence of characters from ARR to TRG. Transcription *}
{* is of LEN consecutive bytes, beginning with byte ORG of ARR. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
i, j : integer;
BEGIN {shiftxt procedure}
i := 1;
j := org;
WHILE i<=len DO
BEGIN {while}
append(trg,arr[j]);
i := i + 1;
j := j + 1
END {while}
END; {shiftxt procedure} {$L+}
FUNCTION dirfetch (recno: integer): dirx;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given RECNO (logical record number of a desired QFILE record), *}
{* return the XIMAGE.XENTRY entry number for that record. *}
{* *}
{* Side effects: *}
{* highxblk - may be incremented +1 *}
{* currxblk - set to relative block# of current index block *}
{* ximage - will contain the current index block *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
xblkno : integer;
i : dirx;
BEGIN {dirfetch function}
xblkno := (recno div maxx) + 1;
IF xblkno=(highxblk+1)
THEN BEGIN
currxblk := highxblk + 1;
FOR i := 1 TO maxx DO
ximage.xentry[i] := 0;
write(xfile:currxblk,ximage);
highxblk := currxblk
END; {then}
IF xblkno>highxblk
THEN BEGIN
error(2);
xblkno := -1
END {then}
ELSE BEGIN
IF xblkno<>currxblk
THEN READ(xfile:xblkno,ximage);
currxblk := xblkno
END; {else}
dirfetch := (recno mod maxx) + 1
END; {dirfetch function} {$L+}
PROCEDURE blokfetch (blokno: integer;
VAR buff : qrec);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Fetch a specified relative QFILE block into a given buffer *}
{* *}
{* Side effects: *}
{* highblok - may be incremented +1 *}
{* currblok - set to block# of current qfile block *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
i : bufx;
BEGIN {blokfetch procedure}
IF blokno=(highblok+1)
THEN BEGIN
currblok := blokno;
FOR i := 1 TO bufsize DO
buff.qentry[i] := zerochr;
write(qfile:currblok,buff);
highblok := currblok
END; {then}
IF (blokno<1) OR (blokno>highblok)
THEN error(3)
ELSE BEGIN
IF blokno<>currblok
THEN READ(qfile:blokno,buff);
currblok := blokno
END {else}
END; {blokfetch procedure} {$L+}
FUNCTION findrec (recno: integer; buff : buffer): bufx;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Return a pointer to the starting byte of a requested record *}
{* number in a given buffer. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
i : integer;
found : boolean;
BEGIN {findrec function}
found := false;
i := 1;
WHILE ((i<(bufsize-3)) AND (buff[i]<>zerochr) AND (NOT found)) DO
BEGIN {while}
IF cnvrt(buff,i+2)=recno
THEN found := true
ELSE i := i + ord(buff[i])
END; {while}
IF NOT found
THEN error(5);
findrec := i
END; {findrec function} {$L+}
FUNCTION buildctl (VAR buff: qrec): question;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given BUFF, with control record image, return the equivalent *}
{* control record. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
equivalent : question;
BEGIN {buildctl function}
WITH buff, equivalent DO
BEGIN {with}
lastq := cnvrt(qentry,6);
lastqbl := cnvrt(qentry,8);
lastxbl := cnvrt(qentry,10);
beastct := cnvrt(qentry,12)
END; {with}
buildctl := equivalent
END; {buildctl function} {$L+}
FUNCTION getrecord (recno : integer): question;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Return from QFILE the RECNO record. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
ptr : bufx;
xptr : dirx;
questn : question;
{$L+}
FUNCTION buildquest (VAR buff: qrec; pnt: bufx): question;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Return the question-record that begins at position PNT of BUFF *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
blokno : integer;
equivalent : question;
BEGIN {buildquest function}
WITH equivalent, buff DO
BEGIN {with}
ident := cnvrt(qentry,pnt+2);
typcode := quest;
nextyes := cnvrt(qentry,pnt+5);
nextno := cnvrt(qentry,pnt+7);
setlength(query,0);
shiftxt(qentry,pnt+9,ord(qentry[pnt])-9,query);
IF ord(qentry[pnt+1])<>1
THEN BEGIN
blokno := currblok + 1;
blokfetch(blokno,buff);
IF NOT runabort
THEN pnt := findrec(recno,qentry);
IF NOT runabort
THEN shiftxt(qentry,pnt+4,ord(qentry[pnt])-4,query)
END {then}
END; {with}
buildquest := equivalent
END; {buildquest function} {$L+}
BEGIN {getrecord function}
IF ((recno<0) OR (recno>maxquery))
THEN BEGIN
writeln('INVALID RECORD NUMBER ',recno:1);
error(1)
END {then}
ELSE WITH qimage, questn DO
BEGIN {with}
xptr := dirfetch(recno);
IF NOT runabort
THEN blokfetch(ximage.xentry[xptr],qimage);
IF NOT runabort
THEN ptr := findrec(recno,qentry);
IF NOT runabort
THEN BEGIN
ident := recno;
IF qentry[ptr+4]=chr(ord(quest))
THEN typcode := quest
ELSE typcode := ctl;
CASE typcode OF
quest: questn := buildquest(qimage,ptr);
ctl : questn := buildctl(qimage)
END {case}
END {then}
END; {with and else}
IF NOT runabort
THEN getrecord := questn
END; {getrecord function} {$L+}
PROCEDURE reshift (VAR buff : buffer;
tbyte : bufx;
source : qstring;
sbyte : questx;
len : questx);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Copy to BUFF, starting at TBYTE, LEN consecutive characters of *}
{* SOURCE, starting at byte SBYTE. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
sptr : questx;
tptr : integer;
BEGIN {reshift procedure}
tptr := tbyte;
FOR sptr := sbyte TO (sbyte+len-1) DO
BEGIN {for}
buff[tptr] := source[sptr];
tptr := tptr + 1
END {for}
END; {reshift procedure} {$L+}
PROCEDURE appendseg1 (txt : qstring;
nyes, nno: integer;
VAR buff : qrec;
ptr : bufx);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* In BUFF at point PTR, build segment 1 of the logical record *}
{* expressed by TXT, NYES, NNO. *}
{* *}
{* Side effects: *}
{* maxquery - becomes the new record's record-ID. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
LABEL 1;
TYPE
switcher = 0..1;
VAR
avl : bufx;
need : integer;
shiftlen : integer;
seglength: integer;
lastind : switcher;
BEGIN {appendseg1 procedure}
need := length(txt) + 9;
avl := bufsize - ptr + 1;
IF avl<9
THEN BEGIN
error(4);
GOTO 1
END;
WITH buff DO
BEGIN {with}
IF avl<need
THEN seglength := avl
ELSE seglength := need;
IF seglength=need
THEN lastind := 1
ELSE lastind := 0;
qentry[ptr] := chr(seglength);
qentry[ptr+1] := chr(lastind);
revert(qentry,ptr+2,maxquery+1);
qentry[ptr+4] := chr(ord(quest));
revert(qentry,ptr+5,nyes);
revert(qentry,ptr+7,nno);
IF avl<need
THEN shiftlen := length(txt) - (need-avl)
ELSE shiftlen := length(txt);
reshift(qentry,ptr+9,txt,1,shiftlen)
END; {with}
1:
END; {appendseg1 procedure} {$L+}
PROCEDURE addrecord (txt : qstring;
nyes, nno: integer);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given the three data elements of a question record, append that *}
{* record to the question file. *}
{* *}
{* Side effects (updated as required): *}
{* xfile *}
{* highblok, highxblk, maxquery, maxanimals *}
{* qfile file control record *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
LABEL 1;
VAR
newaddr : integer;
xptr : dirx;
{$L+}
FUNCTION appendrec (txt : qstring;
nyes, nno: integer): integer;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given the three data elements of a question record, append the *}
{* record to QFILE, assigning it record number MAXQUERY+1. Return *}
{* block address. *}
{* *}
{* Side effects: *}
{* maxquery - used but not changed. *}
{* highblok - may be incremented +1. *}
{* currblok - equal to new highblok. *}
{* qimage - contains image of new highblok. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
LABEL 1;
VAR
objblok : integer;
i : bufx;
available: integer;
required : bufx;
{$L+}
BEGIN {appendrec function}
WITH qimage DO
BEGIN {with}
required := length(txt) + 9;
available:= 0;
objblok := highblok - 1;
WHILE available<9 DO
BEGIN {while}
objblok := objblok + 1;
blokfetch(objblok,qimage);
IF runabort
THEN GOTO 1;
i := 1;
WHILE ((i<bufsize) AND (qentry[i]<>zerochr)) DO
i := i + ord(qentry[i]);
available := bufsize - i + 1
END; {while}
appendseg1(txt,nyes,nno,qimage,i);
IF runabort
THEN GOTO 1;
appendrec := objblok;
write(qfile:objblok,qimage);
IF qentry[i+1]<>chr(1)
THEN BEGIN
objblok := objblok + 1;
blokfetch(objblok,qimage);
IF runabort
THEN GOTO 1;
qentry[1] := chr(required-available+4);
qentry[2] := chr(1);
revert(qentry,3,maxquery+1);
reshift(qentry,5,txt,available-8,required-available);
write(qfile:objblok,qimage)
END; {then}
1: END {with}
END; {appendrec function} {$L+}
BEGIN {addrecord procedure}
newaddr := appendrec(txt,nyes,nno);
IF runabort
THEN GOTO 1;
xptr := dirfetch(maxquery+1);
ximage.xentry[xptr] := newaddr;
write(xfile:highxblk,ximage);
IF ((nyes=0) AND (nno=0))
THEN maxanimals := maxanimals + 1;
maxquery := maxquery + 1;
blokfetch(1,qimage);
IF runabort
THEN GOTO 1;
revert(qimage.qentry, 6,maxquery);
revert(qimage.qentry, 8,highblok);
revert(qimage.qentry,10,highxblk);
revert(qimage.qentry,12,maxanimals);
write(qfile:1,qimage);
1:
END; {addrecord procedure} {$L+}
PROCEDURE initializefiles;
VAR
qfilename : string 15;
xfilename : string 15;
{$L+}
PROCEDURE newfile;
CONST
firstquestion = 'Does it live in the water';
yesguess = 'octopus';
noguess = 'moose';
VAR
i : dirx;
newq : queryfile;
newx : directory;
BEGIN {newfile procedure}
rewrite(qfilename,newq);
rewrite(xfilename,newx);
FOR i := 1 TO 4 DO
ximage.xentry[i] := 1; {First 4 records to block 1 }
FOR i := 5 TO maxx DO
ximage.xentry[i] := 0;
write(newx,ximage);
WITH qimage DO
BEGIN {with}
FOR i := 1 TO bufsize DO
qentry[i] := zerochr;
qentry[1] := chr(13); {Control record length is 13 }
qentry[2] := chr(1); {This is last & only segment }
qentry[5] := chr(ord(ctl)); {Identify as control rec }
qentry[7] := chr(3); {Highest question# is 3 }
qentry[9] := chr(1); {Last question block used is 1}
qentry[11] := chr(1); {Last index block used is 1 }
qentry[13] := chr(2); {File contains 2 animals }
END; {with}
i := 14;
maxquery := 0;
appendseg1(firstquestion,2,3,qimage,i);
i := i + 9 + length(firstquestion);
maxquery := 1;
appendseg1(yesguess,0,0,qimage,i);
i := i + 9 + length(yesguess);
maxquery := 2;
appendseg1(noguess,0,0,qimage,i);
write(newq,qimage)
END; {newfile procedure} {$L+}
FUNCTION testexist: boolean;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Test for existence of disk files QFILENAME and XFILENAME. *}
{* Return FALSE if either one is missing, TRUE if both there. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
testxfile : directory;
testqfile : queryfile;
missing : boolean;
BEGIN {testexist function}
reset(qfilename,testqfile);
reset(xfilename,testxfile);
missing := (eof(testqfile) OR eof(testxfile));
testexist := NOT missing
END; {testexist function} {$L+}
BEGIN {initializefiles procedure}
qfilename := filepfx;
append(qfilename,'.QQQ ');
xfilename := filepfx;
append(xfilename,'.QQX ');
IF NOT testexist
THEN newfile;
reset(qfilename,qfile);
reset(xfilename,xfile);
currblok := -1;
currxblk := -1;
highblok := 1;
highxblk := 1;
maxquery := 3;
maxanimals := 2;
read(xfile:1,ximage);
currxblk := 1;
read(qfile:1,qimage);
currblok := 1;
currec := buildctl(qimage);
maxquery := currec.lastq;
highblok := currec.lastqbl;
highxblk := currec.lastxbl;
maxanimals := currec.beastct
END; {initializefiles procedure} {$L+}
PROCEDURE guessing;
LABEL 1;
CONST
bell = 7; {ordinal of ASCII code for terminal bell }
boast = 'How about that - - - I WON!';
delay = 8000;
VAR
guesstime : boolean;
success : boolean;
nextquest : integer;
prevquest : integer;
querytxt : string maxlen+1;
holdguess : qstring;
i : integer;
{$L+}
FUNCTION voweler (noun: qstring): qstring;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given a noun, return a string with the correct choice of "a" or *}
{* "an" preceding the noun. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
holder : qstring;
BEGIN {voweler function}
IF noun[1] IN vowels
THEN holder := ' an '
ELSE holder := ' a ';
append(holder,noun);
voweler := holder
END; {voweler function}
{- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -}
PROCEDURE lowerize (VAR txt: qstring);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* In a given string, change all upper-case letters to lower-case, *}
{* unless it looks like the mix is intended. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
VAR
i : integer;
letter : char;
sloppy : boolean;
BEGIN {lowerize procedure}
sloppy := true;
FOR i := 1 TO 4 DO
IF i<=length(txt)
THEN IF txt[i] in ['a'..'z']
THEN sloppy := false;
IF sloppy
THEN FOR i := 1 TO length(txt) DO
BEGIN {for}
letter := txt[i];
IF ((letter>='A') AND (letter<='Z'))
THEN txt[i] := chr(ord(letter)-shiftup)
END {for}
END; {lowerize procedure} {$L+}
PROCEDURE askabout (qtext: qstring);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Publish a given question. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
maxline = 69;
VAR
i, j : questx;
holder : qstring;
BEGIN {askabout procedure}
IF length(qtext)<=maxline
THEN write(qtext)
ELSE BEGIN
i := maxline;
WHILE (i>(maxline-20)) AND (qtext[i]<>' ') DO
i := i - 1;
IF i>(maxline-20)
THEN BEGIN
setlength(holder,i-1);
FOR j := 1 to (i-1) DO
holder[j] := qtext[j];
writeln(holder);
holder := ' ';
FOR j := (i+1) TO length(qtext) DO
append(holder,qtext[j]);
write(holder)
END {else}
ELSE write(qtext)
END {else}
END; {askabout procedure} {$L+}
PROCEDURE learning (oldguess : qstring;
prevquest : integer);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given an old (wrong) guess (in the form "a fish" or "an egret", *}
{* and the record number of the question that led to that guess, *}
{* secure from the player the correct answer, and a yes-or-no *}
{* question that would have led to it. Insert the new question and *}
{* and animal into the question file linkage. *}
{* *}
{* Side effects: *}
{* maxanimals - updated *}
{* I/O variables as required (see subordinate procedures) *}
{* currec (used to build new record & view old guess) *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
humbler = 'Oh! I didn''t know about';
request1 = 'I''d like to learn more about animals.';
request2 = 'What''s a yes-or-no question to discriminate between';
clarify1 = 'Which answer to that question would mean';
clarify2 = ' - yes or no';
thanks = 'Thank you! Now I know ';
VAR
holdright : qstring;
rightbeast : qstring;
newbeast : boolean;
newquery : qstring;
qhold : qstring;
PROCEDURE depunctuate (VAR dtext: qstring);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Trim off any terminating punctuation marks. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
endset = '.!?';
BEGIN {depunctuate procedure}
WHILE index(endset,dtext[length(dtext)])<>0 DO
setlength(dtext,length(dtext)-1)
END; {depunctuate procedure} {$L+}
FUNCTION getbeast: qstring;
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Return the name of the animal the player had in mind. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
puzzled = 'Really? What sort of animal is it, then?';
VAR
altered : boolean;
oldlen : questx;
holder : qstring;
{$L+}
PROCEDURE markout (VAR btext: qstring; word: qstring);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Given a BTEXT, find any instances of WORD appearing as distinct *}
{* words. If there are any, eliminate from BTEXT all characters to *}
{* and including WORD. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
CONST
blank1 = ' ';
VAR
i, j : questx;
offset : questx;
padword : qstring;
padlen : questx;
BEGIN {markout procedure}
padword := word;
append(padword,blank1);
padlen := length(padword);
WHILE index(btext,padword)=1 DO
BEGIN {while}
setlength(btext,length(btext)-padlen);
FOR i := 1 TO length(btext) DO
btext[i] := btext[i+padlen];
WHILE btext[1]=blank1 DO
BEGIN {while}
setlength(btext,length(btext)-1);
FOR i := 1 TO length(btext) DO
btext[i] := btext[i+1]
END {while}
END; {while}
padword := blank1;
append(padword,word);
append(padword,blank1);
padlen := length(padword);
j := index(btext,padword);
WHILE j<>0 DO
BEGIN {while}
offset := j + padlen - 1;
setlength(btext,length(btext)-offset);
FOR i := 1 TO length(btext) DO
btext[i] := btext[offset+i];
WHILE btext[1]=blank1 DO
BEGIN {while}
setlength(btext,length(btext)-1);
FOR i := 1 TO length(btext) DO
btext[i] := btext[i+1]
END; {while}
j := index(btext,padword)
END {while}
END; {markout procedure} {$L+}
BEGIN {getbeast function}
writeln(puzzled);
readln(holder);
depunctuate(holder);
lowerize(holder);
oldlen := length(holder);
altered := (holder[1]='A');
IF altered
THEN holder[1] := 'a';
markout(holder,'a');
markout(holder,'an');
IF (altered AND (oldlen=length(holder)))
THEN holder[1] := 'A';
getbeast := holder
END; {getbeast function} {$L+}
PROCEDURE insertquestion (qstn : qstring;
ind : boolean;
ytxt : qstring;
rec : question;
prev : integer);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* Insert a new question and guess into the question file, with *}
{* all required linkages. QUESTN is the new question, YTXT is the *}
{* name of the new animal to be guessed. If IND is true, then YTST *}
{* is the guess for a YES answer, and the animal in REC for NO; *}
{* otherwise, it's the other way around. PREV is the question# *}
{* that led to this question; the new question is to be substituted *}
{* for REC in that question. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
LABEL 1;
VAR
newqstnum : integer;
newansnum : integer;
oldansnum : integer;
newyes : integer;
newno : integer;
{$L+}
PROCEDURE amendrec (recno, nyes, nno: integer);
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
{* For a given question record, update the NEXTYES and NEXTNO ptrs. *}
{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
LABEL 1;
VAR
blokno : integer;
xptr : dirx;
ptr : bufx;
BEGIN {amendrec procedure}
xptr := dirfetch(recno);
IF runabort
THEN GOTO 1;
blokno := ximage.xentry[xptr];
blokfetch(blokno,qimage);
IF runabort
THEN GOTO 1;
ptr := findrec(recno,qimage.qentry);
IF runabort
THEN GOTO 1;
revert(qimage.qentry,ptr+5,nyes);
revert(qimage.qentry,ptr+7,nno);
write(qfile:blokno,qimage);
1:
END; {amendrec procedure} {$L+}
BEGIN {insertquestion procedure}
newqstnum := maxquery + 1;
newansnum := maxquery + 2;
oldansnum := rec.ident;
IF ind
THEN BEGIN
newyes := newansnum;
newno := oldansnum
END {then}
ELSE BEGIN
newyes := oldansnum;
newno := newansnum
END; {else}
addrecord(qstn,newyes,newno);
IF runabort
THEN GOTO 1;
addrecord(ytxt,0,0);
IF runabort
THEN GOTO 1;
rec := getrecord(prev);
IF runabort
THEN GOTO 1;
IF rec.nextyes=oldansnum
THEN rec.nextyes := newqstnum
ELSE rec.nextno := newqstnum;
amendrec(prev,rec.nextyes,rec.nextno);
1:
END; {insertquestion procedure} {$L+}
BEGIN {learning procedure}
rightbeast := getbeast;
holdright := voweler(rightbeast);
writeln(humbler,holdright,'.');
writeln(request1);
qhold := request2;
append(qhold,holdright);
append(qhold,' and');
append(qhold,oldguess);
append(qhold,'?');
askabout(qhold);
writeln;
readln(newquery);
depunctuate(newquery);
lowerize(newquery);
IF ((newquery[1]>='a') AND (newquery[1]<='z'))
THEN newquery[1] := chr(ord(newquery[1])+shiftup);
qhold := clarify1;
append(qhold,holdright);
append(qhold,clarify2);
askabout(qhold);
IF getyes
THEN newbeast := true
ELSE newbeast := false;
insertquestion(newquery,newbeast,rightbeast,currec,prevquest);
IF NOT runabort
THEN writeln(thanks,maxanimals:1,' animals.')
END; {learning procedure} {$L+}
BEGIN {guessing procedure} {$C+}
guesstime := false;
nextquest := 1;
WITH currec DO
BEGIN {with}
WHILE NOT guesstime DO
BEGIN {while}
currec := getrecord(nextquest);
IF runabort
THEN GOTO 1;
guesstime := (nextyes=0) AND (nextno=0);
IF NOT guesstime
THEN BEGIN
prevquest := ident;
askabout(query);
IF getyes
THEN nextquest := nextyes
ELSE nextquest := nextno
END {then}
END; {while}
querytxt := 'Is it';
holdguess := voweler(query);
append(querytxt,holdguess);
askabout(querytxt);
IF getyes
THEN BEGIN
writeln;
writeln(chr(bell),boast);
FOR i := 1 TO delay DO;
END {then}
ELSE learning(holdguess,prevquest)
END; {with}
1:
END; {guessing procedure} {$L+}
BEGIN {mainline procedure of program}
runabort := false;
zerochr := chr(0);
vowels := ['A','E','I','O','U','a','e','i','o','u'];
shiftup := ord('A') - ord('a');
FOR i := 1 TO 14 DO
writeln;
writeln(ack1,ack1a);
writeln;
writeln(ack2);
writeln(ack3);
writeln(ack4);
writeln(ack5);
writeln(ack6);
writeln;
writeln;
{ rewrite('LST: ',db); }
{ dbugging := false; }
initializefiles;
write(inviter);
moreokay := getyes;
WHILE moreokay DO
BEGIN {while}
writeln(start1);
writeln(start2);
readln(replytxt);
guessing;
IF runabort
THEN moreokay := false
ELSE BEGIN
writeln;
write(askagain);
moreokay := getyes
END {else}
END; {while}
IF runabort
THEN writeln('TERMINATING DUE TO PROGRAM OR FILE ERROR')
ELSE writeln('Okay! Goodbye!')
END. {Animals program}