29-Apr-88 15:25:54-PDT,8851;000000000000 Return-Path: From: Subject: Animals Date: Fri, 29 Apr 88 18:26:35 EDT Here's a small diversion for those who are sick and tired of TeX files that produce useful output. It's an implementation of the classic pseudo-Artificial-Intelligence game ``Animals.'' For those who are unfamiliar with the game, the user thinks of an animal, and the program asks a whole bunch of yes/no questions about the animal, stepping down a binary tree until it gets to a guess: ``Is it a fish?'' If the guess is wrong, the program adds the user's animal and a question to tell them apart to the database. This program was written to prove that TeX can really be used as a regular programming language. Thanks for inspiration go to Mark Eichin and Jonathan Kamens, both members of the Studnet Information Processing Board at MIT, for telling me they'd believe it could be done when they saw it. There are two places in the program where filenames appear. These will have to be modified for your system, unless you have a directory called /mit/amgreene/texhax. These can be found quickly by searching for the flag *FLAG*. - Andrew Marc Greene (amgreene@athena.mit.edu) Student Information Processing Board, MIT ---------cut here for animals.tex----------------------------------- % Animals (in TeX, no less!!!) % % This is the program that uses a binary tree of questions to % guess the type of animal of which the user is thinking. % % Andrew Marc Greene, March-April 1988 % % Moral support (i.e., ``You can't do that! Show us!'') % provided by the Student Information Processing Board % of MIT. % % Instructions on running this program: % % tex animals % % Think of an animal. The program will try to guess your animal. % % You will be asked a whole bunch of yes/no questions. This is a % spartan implementation, so answer with a capital Y or N. When % the program finishes going through its tree, it will either have % guessed your animal or it will ask you to enter a question that % it can ask to differentiate between your animal and its guess. % It will then ask you which one is `yes.' % Here's where I declare all my variables, etc. % % ``curcode'' is the current index into the data file. % ``temp'' is a temporary holding variable. % ``lc'' is a loop counter % ``ifamg'' is a general-purpose flag. amg are my initials. % ``ifreploop'' controls loop repetitions. % ``ifmainlooprep'' controls repetitions of the main loop. % ``inp'' is the input file. % ``outp'' is the output file. % ``amgY'' and ``amgN'' are character constants. Why I did it this way I % don't remember. % \newcount\curcode\curcode=1\newcount\temp\temp=0\newcount\lc \newif\ifamg\newif\ifyn\newif\ifreploop\newif\ifmainlrep \newread\inp\newwrite\outp\def\foo{} \def\amgY{Y}\def\amgN{N} % The data file consists of records stored in the following format: % % Record Number % Question % If-Yes-Goto-Record Number % If-No-Goto-Record Number % % The following routine scans the data file until it reaches the % record requested in \curcode % \def\Scan{ {\loop \global\read\inp to \foo \ifnum\foo=\curcode\amgfalse\else\amgtrue\fi \ifamg\read\inp to \foo\read\inp to \foo\read\inp to \foo \repeat}} % The following routine displays the question and waits for a Y or N % answer % \def\Query{ {\read\inp to \foo \immediate\write16{} \message{\foo} \GetYN \ifyn \read\inp to \foo\global\curcode=\foo\read\inp to \foo \else \read\inp to \foo\read\inp to \foo\global\curcode=\foo \fi }} % The following routines deal with the user's input % \vread (verbatim read) ignores s and makes s normal % \GetYN gets input and repeats until it gets a Y or N response. % \def\vread#1{\catcode`\^^M=9\catcode`\ =12\global\read-1 to #1} \def\GetYN{ {\loop \vread{\bar} \def\baz{\bar} \reploopfalse %\message{\baz} \if\amgY\baz\global\yntrue\else \if\amgN\baz\global\ynfalse\else\replooptrue\fi\fi \ifreploop \immediate\write16{Hey, you! Answer Y or N, please.} \message{Please enter Y or N -->} \repeat }} % The following routine is called if the ``Goto-Record'' is -1, % meaning that the program didn't guess correctly and is clueless. % It gets the new animal and the differentiating question, and % modifies the data file. Actually, it makes a modified copy of % the file, then copies the temporary new one over the old outdated % one. % \def\NewAnimal{ \immediate\write16{Well, I'm stumped. What animal did you have in mind?} \vread{\usersanimal} \immediate\write16{OK. What question would let me tell the difference?} \vread{\userquery} \immediate\write16{Is the answer to that question Yes or No if I ask about} \message{\usersanimal?} \curcode=-1 \GetYN \Scan \read\inp to \lastcode\lc=\lastcode \closein\inp % % Open up the files. These names are system-dependent. *FLAG* % \openin\inp=/mit/amgreene/texhax/animal.dat \immediate\openout\outp=/tmp/animal-new.dat % % Read through the inp file, copying all records that don't need to % be changed, outputting modified versions of the changed ones (and % discarding the old), and appending the new records. % {\loop \read\inp to \foo \amgtrue \ifnum\foo=\temp\amgfalse\fi \ifnum\foo=-1=\amgfalse\fi \ifamg\immediate\write\outp{\foo} \read\inp to \foo\immediate\write\outp{\foo} \read\inp to \foo\immediate\write\outp{\foo} \read\inp to \foo\immediate\write\outp{\foo} \amgtrue \else\ifnum\foo=\temp \immediate\write\outp{\foo} \immediate\write\outp{\userquery} \immediate\write\outp{\number\lc} \global\advance\lc by 1 \immediate\write\outp{\number\lc} \read\inp to \animal\read\inp to \foo\read\inp to \foo \amgtrue \else \lc=\lastcode \ifyn\WriteUsers\WriteAnimal \else\WriteAnimal\WriteUsers \amgfalse\fi \fi\fi \ifamg \repeat} \immediate\write\outp{-1} \immediate\write\outp{\number\lc} \closeout\outp \closein\inp % % Now copy the temporary file over the original one % % These filenames are also system-dependent. *FLAG* % \openin\inp=/tmp/animal-new.dat \immediate\openout\outp=/mit/amgreene/texhax/animal.dat {\loop \read\inp to \foo \immediate\write\outp{\foo} \amgtrue \ifeof\inp\amgfalse\fi \ifamg\repeat} } % This routine is called by NewAnimal and writes the record for % the user's new animal % \def\WriteUsers{ \immediate\write\outp{\number\lc} \immediate\write\outp{Is it \usersanimal?} \immediate\write\outp{0} \immediate\write\outp{-1} \global\advance\lc by 1} % This one writes the modified old animal % \def\WriteAnimal{ \immediate\write\outp{\number\lc} \immediate\write\outp{\animal} \immediate\write\outp{0} \immediate\write\outp{-1} \global\advance\lc by 1} \openin\inp=/mit/amgreene/texhax/animal.dat % Now we get into the main routine. % It simply repeats the scan-query loop until it gets a 0 (right answer) % or a -1 (wrong answer, I'm stumped), and calls the appropriate routine. % \loop \temp=\curcode \Scan\Query \mainlreptrue \ifnum\curcode=0 \immediate\write16{Thank you for using Animals. I'm glad I got it right.} \mainlrepfalse \else \ifnum\curcode=-1\NewAnimal\mainlrepfalse\fi \fi \ifmainlrep \repeat % Ah, the joys of a job well-done. We can now exit to the system, knowing % that the world is a slightly better place for our efforts. % % The following line of code, probably the most profound in the entire % program, sums up this philosophy of life in four characters. The % Puritan work ethic is embodied in this amazingly meaning-laden % command designed by Donald Knuth. \end -----------------------cut here, too--------------------------------- The following is the data file: -----------------------cut once again for animal.dat----------------- 1 Does it fly? 2 3 2 Does it live on statues? 4 5 3 Does it have wings? 8 9 4 Is it grey? 6 7 5 Is it a condor? 0 -1 6 Is it a pigeon? 0 -1 7 Is it a sparrow? 0 -1 8 Does it look like it is wearing a tuxedo? 10 11 9 Is it a moose? 0 -1 10 Is it a penguin? 0 -1 11 Is it a turkey? 0 -1 -1 12 ----------------------this is the last cut----------------------------- -------