about summary refs log tree commit diff stats
path: root/js/games/nluqo.github.io/~bh/ssch10/ttt
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2023-08-23 07:52:19 -0400
committerelioat <elioat@tilde.institute>2023-08-23 07:52:19 -0400
commit562a9a52d599d9a05f871404050968a5fd282640 (patch)
tree7d3305c1252c043bfe246ccc7deff0056aa6b5ab /js/games/nluqo.github.io/~bh/ssch10/ttt
parent5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff)
downloadtour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/ssch10/ttt')
-rw-r--r--js/games/nluqo.github.io/~bh/ssch10/ttt1127
1 files changed, 1127 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/ssch10/ttt b/js/games/nluqo.github.io/~bh/ssch10/ttt
new file mode 100644
index 0000000..85f731e
--- /dev/null
+++ b/js/games/nluqo.github.io/~bh/ssch10/ttt
@@ -0,0 +1,1127 @@
+\input bkmacs
+\pagetag{\tttttm}\photo{This computer, built of Tinker-Toy parts, plays
+tic-tac-toe.}{\pspicture{4in}{tinker}{tinker}{\TrimLeft{60pt}}}
+\chapter{Example: Tic-Tac-Toe}
+\chaptag{\ttt}
+\catcode`\_=12
+
+Now that you've learned about higher-order functions, we're going to look at
+a large example that uses them extensively.  Using the
+techniques you've learned so far, we're going to write a program that plays
+perfect tic-tac-toe.
+
+You can load our program into Scheme by typing
+
+{\prgex%
+(load "ttt.scm")
+}
+
+\noindent (See Appendix A if this doesn't work for you.)
+
+\subhd{A Warning}
+
+Programs don't always come out right the first time.  One of our goals in
+this chapter is to show you how a program is developed, so we're presenting
+early versions of procedures.  These include some mistakes that we made, and
+also some after-the-fact simplifications to make our explanations
+easier.  If you type in these early versions, they won't work.  We will show
+you how we corrected these ``bugs'' and also will present a complete, correct
+version at the end of the chapter.
+
+To indicate the unfinished versions of procedures, we'll use comments like
+``first version'' or ``not really part of game.''
+
+\subhd{Technical Terms in Tic-Tac-Toe}
+
+We'll number the squares of the board this way:
+
+{\ttboard123456789}
+
+We'll call a partially filled-in board a ``\idx{position}.''
+
+\ttboard__o_xox_x
+
+To the computer, the same position will be represented by the word {\ttx
+__o_xox_x}.  The nine letters of the word correspond to squares one through
+nine of the board.  (We're thinking ahead to the possibility of using {\tt
+item} to extract the $n$th square of a given position.)
+
+\subhd{Thinking about the Program Structure}
+
+Our top-level procedure, {\tt ttt}, will return the computer's next move 
+given the current position.  It takes two arguments:\ the current position
+and whether the computer is playing X or O.  If the computer is O and the
+board looks like the one above, then we'd invoke {\tt ttt} like this:
+
+{\prgex%
+(\ufun{ttt} '__o_xox_x 'o)
+}
+
+Here is a sample game:
+
+{\prgex%
+> (ttt '____x____ 'o)                   ; Human goes first in square 5
+1                                       ; Computer moves in square 1
+> (ttt 'o__xx____ 'o)                   ; Human moves in square 4
+6                                       ; Computer blocks in square 6
+> (ttt 'o_xxxo___ 'o)                   ; Human moves in square 3
+7                                       ; Computer blocks again
+> (ttt 'o_xxxoox_ 'o)
+2
+}
+
+This is not a complete game program!  Later, when we talk about input and
+output, you'll see how to write an interactive program that displays the
+board pictorially, asks the player where to move, and so on.  For now, we'll
+just write the {\it \idx{strategy}\/} procedure that chooses the next
+move.  As a paying customer, you wouldn't be satisfied with this
+partial program, but from the programmer's point of view, this is the more
+interesting part.
+
+Let's plan the computer's strategy in English before we start writing a
+computer program.  How do {\it you\/} play tic-tac-toe?  You have several
+strategy rules in your head, some of which are more urgent than others.  For
+example, if you can win on this move, then you just do it without thinking
+about anything else.  But if there isn't anything that immediate, you
+consider less urgent questions, such as how this move might affect what
+happens two moves later.
+
+So we'll represent this set of rules by a giant {\tt cond} expression:
+
+{\prgex%
+(define (ttt position me)                    ;; first version
+  (cond ((i-can-win?)
+         (choose-winning-move))
+        ((opponent-can-win?)
+         (block-opponent-win))
+        ((i-can-win-next-time?)
+         (prepare-win))
+        (else (whatever))))
+}
+
+We're imagining many helper procedures.  {\tt I-can-win?}\ will look at the
+board and tell if the computer has an immediate winning move. If so, {\tt
+choose-winning-move} will find that particular move.  {\tt
+Opponent-can-win?}\ returns true if the human player has an immediate
+winning move.  {\tt Block-opponent-win} will return a move that prevents the
+computer's opponent from winning, and so on.
+
+We didn't actually start by writing this definition of {\tt ttt}.  The
+particular names of helper procedures are just guesses, because we haven't
+yet planned the tic-tac-toe strategy in detail.  But we did know that this
+would be the overall structure of our program.  This big picture doesn't
+automatically tell us what to do next; different programmers might fill in
+the details differently.  But it's a framework to keep in mind during the
+rest of the job.
+
+Our first practical step was to think about the {\it
+\bkidx{data}{structure}s\/} in our program.  A data structure is a way of
+organizing several pieces of information into a big chunk.  For example, a
+sentence is a data structure that combines several words in a sequence (that
+is, in left-to-right order).
+
+In the first, handwavy version of {\tt ttt}, the strategy procedures like
+{\tt i-can-win?}\ are called with no arguments, but of course we knew they
+would need some information about the board position.  We began by thinking
+about how to represent that information within the program.
+
+\subhd{The First Step: Triples}
+
+A person looking at a tic-tac-toe board looks at the rows, columns, and
+diagonals.  The question ``do I have a winning move?'' is equivalent to the
+question ``are there three squares in a line such that two of them are mine
+and the last one is blank?''  In fact, nothing else matters about the game
+besides these potential winning combinations.
+
+There are eight potential winning combinations:\ three rows, three
+columns, and two diagonals.  Consider the combination containing the three
+squares 1, 5, and 9.  If it contains both an {\tt x} and an {\tt o} then
+nobody can win with this combination and there's nothing to think about.
+But if it contains two {\tt x}s and a free square, we're very interested in
+the combination.  What we want to know in particular is which square is
+free, since we want to move in that square to win or block.
+
+More generally, the only squares whose {\it numbers\/} we care about are the
+ones we might want to move into, namely, the free ones.  So the only
+interesting information about a square is whether it has an {\tt x} or an
+{\tt o}, and if not, what its number is.
+
+The information that 1, 5, 9 is a potential winning combination and the
+information that square 1 contains an {\tt x}, square 5 is empty, and square
+{\tt 9} contains another {\tt x} can be combined into the single word {\tt
+x5x}.  Looking at this word we can see immediately that there are two {\tt
+x}s in this ``\idx{triple}'' and that the free square is square 5.  So when we
+want to know about a three-square combination, we will turn it into a
+triple of that form.
+
+Here's a sample board position:
+
+\ttboard_xo_x_o__
+
+\noindent and here is a sentence of all of its triples:
+
+{\prgex%
+(1xo 4x6 o89 14o xx8 o69 1x9 oxo)
+}
+
+Take a minute to convince yourself that this sentence really does tell you
+everything you need to know about the corresponding board position.  Once
+our strategy procedure finds the triples for a board position, it's never
+going to look at the original position again.
+
+This technique of converting data from one form to another so that it can be
+manipulated more easily is an important idea in computer science.  There are
+really three \idx{representation}s of the same thing.  There's this picture:
+
+\ttboard_xo_x_o__
+
+\noindent as well as the word {\tt _xo_x_o__} and the sentence {\tt (1xo 4x6
+o89 14o xx8 o69 1x9 oxo)}.  All three of these formats have the same
+information but are convenient in different ways.  The pictorial form is
+convenient because it makes sense to the person who's playing tic-tac-toe.
+Unfortunately, you can't type that picture into a computer, so we need a
+different format, the word {\tt _xo_x_o__}, which contains the {\it
+contents\/} of the nine squares in the picture, but without the lines
+separating the squares and without the two-dimensional shape.
+
+The third format, the sentence, is quite {\it inconvenient\/} for human
+beings.  You'd never want to think about a tic-tac-toe board that way
+yourself, because the sentence doesn't have the visual simplicity that lets
+you take in a tic-tac-toe position at a glance.  But the sentence of triples
+is the most convenient representation for our program.  {\tt Ttt} will have
+to answer questions like ``can {\tt x} win on the next move?'' To do that, it
+will have to consider an equivalent but more detailed question:  ``For each
+of the eight possible winning combinations, can {\tt x} complete that
+combination on the next move?'' It doesn't really matter whether a
+combination is a row or a column; what does matter is that each of the eight
+combinations be readily available for inspection by the program.  The
+sentence-of-triples representation obscures part of the available
+information (which combination is where) to emphasize another part (making
+the eight combinations explicit, instead of implicit in the nine boxes of
+the diagram).
+
+The representation of fractions as ``mixed numerals,'' such as $2 {1 \over 3}$,
+and as ``improper fractions,'' such as $7 \over 3$, is a non-programming
+example of this idea about multiple representations.  A mixed numeral makes
+it easier for a person to tell how big the number is, but an improper
+fraction makes arithmetic easier.
+
+\subhd{Finding the Triples}
+
+We said that we would combine the current board position with the
+numbers of the squares in the eight potential winning combinations in order
+to compute the things we're calling triples.  That was our first task in
+writing the program.
+
+Our program will start with this sentence of all the winning combinations:
+
+{\prgex%
+(123 456 789 147 258 369 159 357)
+}
+
+\noindent and a position word such as {\tt _xo_x_o__}; it will return a
+sentence of triples such as
+
+{\prgex%
+(1xo 4x6 o89 14o xx8 o69 1x9 oxo)
+}
+
+All that's necessary is to replace some of the numbers with {\tt x}s and
+{\tt o}s.  This kind of word-by-word translation in a sentence is a good job
+for {\tt every}.
+
+{\prgex%
+(define (find-triples position)              ;; first version
+  (every substitute-triple '(123 456 789 147 258 369 159 357)))
+}
+
+We've made up a name {\tt substitute-triple} for a procedure we haven't
+written yet.  This is perfectly OK, as long as we write it before we try to
+invoke {\tt find-triples}.  The {\tt substitute-triple} function will take
+three digits, such as {\tt 258}, and return a triple, such as {\tt 2x8}:
+
+{\prgex%
+(define (substitute-triple combination)      ;; first version
+  (every substitute-letter combination))
+}
+
+\noindent This procedure uses {\tt every} to call {\tt substitute-letter} on
+all three letters.
+
+There's a small problem, though.  {\tt Every} always returns a sentence, and
+we want our triple to be a word.  For example, we want to turn the potential
+winning combination {\tt 258} into the word {\tt 2x8}, but {\tt every} would
+return the sentence {\tt (2~x~8)}.  So here's our next version of {\tt
+substitute-triple}:
+
+{\prgex%
+(define (substitute-triple combination)      ;; second version
+  (accumulate word (every substitute-letter combination)))
+}
+
+{\tt Substitute-letter} knows that letter number 3 of the word that
+represents the board corresponds to the contents of square 3 of the board.
+This means that it can just call {\tt item} with the given square number and
+the board to find out what's in that square.  If it's empty, we return the
+square number itself; otherwise we return the contents of the square.
+
+{\prgex%
+(define (substitute-letter square)           ;; first version
+  (if (equal? '_ (item square position))
+      square
+      (item square position)))
+}
+
+Whoops!  Do you see the problem?
+
+{\prgex%
+> (substitute-letter 5)
+ERROR: Variable POSITION is unbound.
+}
+
+\subhd{Using \ttpmb{Every} with Two-Argument Procedures}
+
+Our procedure only takes one argument, {\tt square}, but it needs to know
+the position so it can find out what's in the given square.  So here's the
+real {\tt substitute-letter}:
+
+{\prgex%
+(define (\ufun{substitute-letter} square position)
+  (if (equal? '_ (item square position))
+      square
+      (item square position)))
+
+> (substitute-letter 5 '_xo_x_o__)
+X
+
+> (substitute-letter 8 '_xo_x_o__)
+8
+}
+
+\noindent Now {\tt substitute-letter} can do its job, since it has access to
+the position.  But we'll have to modify {\tt substitute-triple} to invoke
+{\tt substitute-letter} with two arguments.
+
+This is a little tricky.  Let's look again at the way we're using {\tt
+substitute-letter} inside {\tt substitute-triple}:
+
+{\prgex%
+(define (substitute-triple combination)      ;; second version again
+  (accumulate word (every substitute-letter combination)))
+}
+
+\noindent By giving {\tt substitute-letter} another argument, we have made
+this formerly correct procedure incorrect.  The first argument to {\tt every}
+must be a function of one argument, not two.  This is exactly the kind of
+situation in which {\tt lambda} can help us:  We have a function of two
+arguments, and we need a function of one argument that does the same thing,
+but with one of the arguments fixed.
+
+The procedure returned by
+
+{\prgex%
+(lambda (square) (substitute-letter square position))
+}
+
+\noindent does exactly the right thing; it takes a square as its argument
+and returns the contents of the position at that square.
+
+Here's the final version of {\tt substitute-triple}:
+
+{\prgex%
+(define (\ufun{substitute-triple} combination position)
+  (accumulate word
+	      (every (lambda (square)
+		       (substitute-letter square position))
+		     combination)))
+
+> (substitute-triple 456 '_xo_x_o__)
+"4X6"
+
+> (substitute-triple 147 '_xo_x_o__)
+"14O"
+
+> (substitute-triple 357 '_xo_x_o__)
+OXO
+}
+
+\noindent As you can see, Scheme prints some of these words with
+double-quote marks.  The rule is that a word that isn't a number but begins
+with a digit must be double-quoted.  But in the finished program we're not
+going to print such words at all; we're just showing you the working of a
+helper procedure.  Similarly, in this chapter we'll show direct invocations
+of helper procedures in which some of the arguments are \idx{string}s, but a
+user of the overall program won't have to use this notation.
+
+We've fixed the {\tt substitute-letter} problem by giving {\tt
+substitute-triple} an extra argument, so we're going to have to go through
+the same process with {\tt find-triples}.  Here's the right version:
+
+{\prgex%
+(define (\ufun{find-triples} position)
+  (every (lambda (comb) (substitute-triple comb position))
+         '(123 456 789 147 258 369 159 357)))
+}
+
+\noindent It's the same trick.  {\tt Substitute-triple} is a procedure
+of two arguments.  We use {\tt lambda} to transform it into a procedure
+of one argument for use with {\tt every}.
+
+We've now finished {\tt find-triples}, one of the most important procedures in
+the game.
+
+{\prgex%
+> (find-triples '_xo_x_o__)
+("1XO" "4X6" O89 "14O" XX8 O69 "1X9" OXO)
+
+> (find-triples 'x_____oxo)
+(X23 456 OXO X4O "25X" "36O" X5O "35O")
+}
+
+Here again are the jobs of all three procedures we've written so far:
+
+\htstart
+<TABLE>
+<TR><TD><CODE>Substitute-letter&nbsp;&nbsp;</CODE>
+<TD>finds the letter in a single square.
+<TR><TD><CODE>Substitute-triple</CODE>
+<TD>finds all three leters corresponding to three squares.
+<TR><TD><CODE>Find-triples</CODE>
+<TD>finds all the letters in all eight winning combinations.
+</TABLE>
+\htend
+
+% \medskip
+% \def\tabRule{\noalign{\hrule}}
+% \def\two#1{\omit\span #1\hfil}
+% \noindent \vbox{\offinterlineskip
+% \baselineskip=12pt
+% \halign{\strut #\hfil & %function
+% \quad#\quad\hfil \cr%result
+% {\tt Substitute-letter}&finds the letter in a single square.\cr
+% {\tt Substitute-triple}&finds all three letters corresponding to
+% three squares.\cr
+% {\tt Find-triples}&finds all the letters in all eight winning
+% combinations.\cr
+% }}\hfil\medskip
+
+We've done all this because we think that the rest of the program can use
+the triples we've computed as data.  So we'll just compute the triples once
+for all the other procedures to use:
+
+{\prgex%
+(define (\ufun{ttt} position me)
+  (ttt-choose (find-triples position) me))
+
+(define (ttt-choose triples me)              ;; first version
+  (cond ((i-can-win? triples me)
+         (choose-winning-move triples me))
+        ((opponent-can-win? triples me)
+         (block-opponent-win triples me))
+        \ellipsis))
+}
+
+\subhd{Can the Computer Win on This Move?}
+
+The obvious next step is to write {\tt i-can-win?}, a procedure that should
+return {\tt \#t} if the computer can win on the current move---that is, if
+the computer already has two squares of a triple whose third square is empty.
+The triples {\tt x6x} and {\tt oo7} are examples.
+
+So we need a function that takes a word and a letter as arguments
+and counts how many times that letter appears in the word.  The
+{\tt appearances} primitive that we used in Chapter \functions\ (and
+that you re-implemented in Exercise \appear) will do the job:
+
+{\prgex%
+> (appearances 'o 'oo7)
+2
+
+> (appearances 'x 'oo7)
+0
+}
+
+The computer ``owns'' a triple if the computer's letter appears twice and
+the opponent's letter doesn't appear at all.  (The second condition is
+necessary to exclude cases like {\tt xxo}.)
+
+{\prgex%
+(define (\ufun{my-pair?} triple me)
+  (and (= (appearances me triple) 2)
+       (= (appearances (opponent me) triple) 0)))
+}
+
+Notice that we need a function {\tt opponent} that returns the opposite
+letter from ours.
+
+{\prgex%
+(define (\ufun{opponent} letter)
+  (if (equal? letter 'x) 'o 'x))
+
+> (opponent 'x)
+O
+
+> (opponent 'o)
+X
+
+> (my-pair? 'oo7 'o)
+#T
+
+> (my-pair? 'xo7 'o)
+#F
+
+> (my-pair? 'oox 'o)
+#F
+}
+
+Finally, the computer can win if it owns any of the triples:
+
+{\prgex%
+(define (i-can-win? triples me)                ;; first version
+  (not (empty?
+	(keep (lambda (triple) (my-pair? triple me))
+	      triples))))
+
+> (i-can-win? '("1xo" "4x6" o89 "14o" xx8 o69 "1x9" oxo) 'x)
+#T
+
+> (i-can-win? '("1xo" "4x6" o89 "14o" xx8 o69 "1x9" oxo) 'o)
+#F
+}
+
+\noindent By now you're accustomed to this trick with {\tt lambda}.  {\tt
+My-pair?}\ takes a triple and the computer's letter as arguments, but we
+want a function of one argument for use with {\tt keep}.
+
+\subhd{If So, in Which Square?}
+
+Suppose {\tt i-can-win?}\ returns {\tt \#t}.  We then have to find the
+particular square that will win the game for us.  This will involve a
+repetition of some of the same work we've already done:
+
+{\prgex%
+(define (choose-winning-move triples me)     ;; not really part of game
+  (keep number? (first (keep (lambda (triple) (my-pair? triple me))
+                             triples))))
+}
+
+\noindent We again use {\tt keep} to find the triples with two of the
+computer's letter, but this time we extract the number from the first such
+winning triple.
+
+We'd like to avoid this inefficiency.  As it turns out, generations of Lisp
+programmers have been in just this bind in the past, and so they've invented
+a \idx{kludge}\footnt{A kludge is a programming trick that doesn't follow
+the rules but works anyway somehow.  It doesn't rhyme with ``sludge''; it's
+more like ``clue'' followed by ``j'' as in ``Jim.''} to get around it.
+
+Remember we told you that everything other than {\tt \#f} counts as true?
+We'll take advantage of that by having a single procedure that returns the
+number of a winning square if one is available, or {\tt \#f} otherwise.  In
+Chapter \true\ we called such a procedure a ``semipredicate.'' The kludgy
+part is that \ttidx{cond} accepts a clause containing a single expression
+instead of the usual two expressions; if the expression has any true value,
+then {\tt cond} returns that value.  So we can say
+
+{\prgex%
+(define (ttt-choose triples me)              ;; second version
+  (cond ((i-can-win? triples me))
+        ((opponent-can-win? triples me))
+        \ellipsis))
+}
+
+\noindent where each {\tt cond} clause invokes a semipredicate.  We then
+modify {\tt i-can-win?}\ to have the desired behavior:
+
+{\prgex%
+(define (\ufun{i-can-win?} triples me)
+  (choose-win
+   (keep (lambda (triple) (my-pair? triple me))
+	 triples)))
+
+(define (\ufun{choose-win} winning-triples)
+  (if (empty? winning-triples)
+      #f
+      (keep number? (first winning-triples))))
+
+> (i-can-win? '("1xo" "4x6" o89 "14o" xx8 o69 "1x9" oxo) 'x)
+8
+
+> (i-can-win? '("1xo" "4x6" o89 "14o" xx8 o69 "1x9" oxo) 'o)
+#F
+}
+
+\vskip -5pt
+
+By this point, we're starting to see the structure of the overall program.
+There will be several procedures, similar to {\tt i-can-win?}, that will try
+to choose the next move.  {\tt I-can-win?} checks to see if the computer can
+win on this turn, another procedure will check to see if the computer should
+block the opponent's win next turn, and other procedures will check for
+other possibilities.  Each of these procedures will be what we've been
+calling ``semipredicates.'' That is to say, each will return the number of
+the square where the computer should move next, or {\tt \#f} if it can't
+decide.  All that's left is to figure out the rest of the computer's
+strategy and write more procedures like {\tt i-can-win?}.
+
+\backskipsubhd{Second Verse, Same as the First}{5}
+
+Now it's time to deal with the second possible strategy case:  The computer
+can't win on this move, but the opponent can win unless we block a triple
+right now.
+
+(What if the computer and the opponent both have immediate winning triples?
+In that case, we've already noticed the computer's win, and by winning the
+game we avoid having to think about blocking the opponent.)
+
+Once again, we have to go through the complicated business of finding
+triples that have two of the opponent's letter and none of the computer's
+letter---but it's already done!
+
+\vskip -2pt
+
+{\prgex%
+(define (\ufun{opponent-can-win?} triples me)
+  (i-can-win? triples (opponent me)))
+
+> (opponent-can-win? '("1xo" "4x6" o89 "14o" xx8 o69 "1x9" oxo) 'x)
+#F
+
+> (opponent-can-win? '("1xo" "4x6" o89 "14o" xx8 o69 "1x9" oxo) 'o)
+8
+}
+
+\vskip -2pt
+
+\penalty 10000
+
+\noindent Is that amazing or what?
+
+\goodbreak
+
+\subhd{Now the Strategy Gets Complicated}
+
+Since our goal here is to teach programming, rather than tic-tac-toe
+strategy, we're just going to explain the strategy we use and not give the
+history of how we developed it.
+
+The third step, after we check to see if either player can win on the next
+move, is to look for a situation in which a move that we make now will give
+rise to {\it two\/} winning triples next time.  Here's an example:
+
+\ttboard xo__x___o
+
+Neither {\tt x} nor {\tt o} can win on this move.  But if the computer is
+playing {\tt x}, moving in square 4 or square 7 will produce a situation
+with two winning triples.  For example, here's what happens if we move in
+square 7:
+
+\ttboard xo__x_x_o
+
+\noindent From this position, {\tt x} can win by moving either in square
+3 or in square 4.  It's {\tt o}'s turn, but {\tt o} can block only one of
+these two possibilities.  By contrast, if (in the earlier position) {\tt x}
+moves in square 3 or square 6, that would create a single winning triple for
+next time, but {\tt o} could block it.
+
+In other words, we want to find {\it two\/} triples in which one square is
+taken by the computer and the other two are free, with one free square
+shared between the two triples.  (In this example, we might find the two
+triples {\tt x47} and {\tt 3x7}; that would lead us to move in square 7, the
+one that these triples have in common.)  We'll call a situation like this a
+``\idx{fork},'' and we'll call the common square the ``\idx{pivot}.'' This
+isn't standard terminology; we just made up these terms to make it easier to
+talk about the strategy.
+
+In order to write the strategy procedure {\tt i-can-fork?}\ we assume that
+we'll need a procedure {\tt pivots} that returns a sentence of all pivots of
+forks currently available to the computer.  In this board, 4 and 7 are the
+pivots, so the {\tt pivots} procedure would return the sentence {\tt
+(4~7)}.  If we assume {\tt pivots}, then writing {\tt i-can-fork?}\ is
+straightforward:
+
+{\prgex%
+(define (\ufun{i-can-fork?} triples me)
+  (first-if-any (pivots triples me)))
+
+(define (\ufun{first-if-any} sent)
+  (if (empty? sent)
+      #f
+      (first sent)))
+}
+
+\subhd{Finding the Pivots}
+
+{\tt Pivots} should return a sentence containing the pivot numbers.  Here's
+the plan.  We'll start with the triples:
+
+{\prgex%
+(xo3 4x6 78o x47 ox8 36o xxo 3x7)
+}
+
+\noindent We {\tt keep} the ones that have an {\tt x} and two numbers:
+
+{\prgex%
+(4x6 x47 3x7)
+}
+
+\noindent We mash these into one huge word:
+
+{\prgex%
+4x6x473x7
+}
+
+\noindent We sort the digits from this word into nine ``buckets,'' one for
+each digit:
+
+{\prgex%
+("" "" 3 44 "" 6 77 "" "")
+}
+
+\noindent We see that there are no ones or twos, one three, two fours, and
+so on.  Now we can easily see that four and seven are the pivot squares.
+
+Let's write the procedures to carry out that plan.  {\tt Pivots} has to find
+all the triples with one computer-owned square and two free squares, and
+then it has to extract the square numbers that appear in more than one
+triple.
+
+{\prgex%
+(define (\ufun{pivots} triples me)
+  (repeated-numbers (keep (lambda (triple) (my-single? triple me))
+			  triples)))
+
+(define (\ufun{my-single?} triple me)
+  (and (= (appearances me triple) 1)
+       (= (appearances (opponent me) triple) 0)))
+
+> (my-single? "4x6" 'x)
+#T
+
+> (my-single? 'xo3 'x)
+#F
+
+> (keep (lambda (triple) (my-single? triple 'x))
+	(find-triples 'xo__x___o))
+("4X6" X47 "3X7")
+}
+
+\noindent {\tt My-single?}\ is just like {\tt my-pair?}\ except that it looks
+for one appearance of the letter instead of two.
+
+{\tt Repeated-numbers} takes a sentence of triples as its argument and has
+to return a sentence of all the numbers that appear in more than one
+triple.
+
+{\prgex%
+(define (\ufun{repeated-numbers} sent)
+  (every first
+         (keep (lambda (wd) (>= (count wd) 2))
+	       (sort-digits (accumulate word sent)))))
+}
+
+\noindent We're going to read this procedure inside-out, starting with the
+{\tt accumulate} and working outward.
+
+Why is it okay to {\tt accumulate word} the sentence?  Suppose that a number
+appears in two triples.  All we need to know is that number, not the
+particular triples through which we found it.  Therefore, instead of writing
+a program to look through several triples separately, we can just as well
+combine the triples into one long word, keep only the digits of that word,
+and simply look for the ones that appear more than once.
+
+{\prgex%
+> (accumulate word '("4x6" x47 "3x7"))
+"4X6X473X7"
+}
+
+We now have one long word, and we're looking for repeated digits.  Since
+this is a hard problem, let's start with the subproblem of finding all the
+copies of a particular digit.
+
+{\prgex%
+(define (\ufun{extract-digit} desired-digit wd)
+  (keep (lambda (wd-digit) (equal? wd-digit desired-digit)) wd))
+
+> (extract-digit 7 "4x6x473x7")
+77
+
+> (extract-digit 2 "4x6x473x7")
+""
+}
+
+Now we want a sentence where the first word is all the {\tt 1}s, the second
+word is all the {\tt 2}s, etc.  We could do it like this:
+
+{\prgex%
+(se (extract-digit 1 "4x6x473x7")
+    (extract-digit 2 "4x6x473x7")
+    (extract-digit 3 "4x6x473x7")
+    \ellipsis)
+}
+
+\noindent but that wouldn't be taking advantage of the power of computers to
+do that sort of repetitious work for us.  Instead, we'll use {\tt every}:
+
+{\prgex%
+(define (\ufun{sort-digits} number-word)
+  (every (lambda (digit) (extract-digit digit number-word))
+	 '(1 2 3 4 5 6 7 8 9)))
+}
+
+{\tt Sort-digits} takes a word full of numbers and returns a sentence whose
+first word is all the ones, second word is all the twos, etc.\footnt{Brian
+thinks this is a \idx{kludge}, but Matt thinks it's brilliant and elegant.}
+
+{\prgex%
+> (sort-digits 123456789147258369159357)
+(111 22 333 44 5555 66 777 88 999)
+
+> (sort-digits "4x6x473x7")
+("" "" 3 44 "" 6 77 "" "")
+}
+
+Let's look at {\tt repeated-numbers} again:
+
+{\prgex%
+(define (repeated-numbers sent)
+  (every first
+	 (keep (lambda (wd) (>= (count wd) 2))
+	       (sort-digits (accumulate word sent)))))
+
+> (repeated-numbers '("4x6" x47 "3x7"))
+(4 7)
+
+> (keep (lambda (wd) (>= (count wd) 2))
+	'("" "" 3 44 "" 6 77 "" ""))
+(44 77)
+
+> (every first '(44 77))
+(4 7)
+}
+
+This concludes the explanation of {\tt pivots}.  Remember that {\tt
+i-can-fork?}\ chooses the first pivot, if any, as the computer's move.
+
+\subhd{Taking the Offensive}
+
+Here's the final version of {\tt ttt-choose} with all the clauses shown:
+
+{\prgex%
+(define (\ufun{ttt-choose} triples me)
+  (cond ((i-can-win? triples me))
+        ((opponent-can-win? triples me))
+        ((i-can-fork? triples me))
+        ((i-can-advance? triples me))
+        (else (best-free-square triples))))
+}
+
+\noindent You already know about the first three possibilities.
+
+Just as the second possibility was the ``mirror image'' of the first
+(blocking an opponent's move of the same sort the computer just attempted), 
+it would make sense for the fourth possibility to be blocking the creation
+of a fork by the opponent.  That would be easy to do:
+
+{\prgex%
+(define (opponent-can-fork? triples me)      ;; not really part of game
+  (i-can-fork? triples (opponent me)))
+}
+
+Unfortunately, although the programming works, the strategy doesn't.  Maybe
+the opponent has {\it two\/} potential forks; we can block only one of
+them.  (Why isn't that a concern when blocking the opponent's wins?  It {\it
+is\/} a concern, but if we've allowed the situation to reach the point where
+there are two ways the opponent can win on the next move, it's too late to
+do anything about it.)
+
+Instead, our strategy is to go on the offensive.  If we can get two in a row
+on this move, then our opponent will be forced to block on the next move,
+instead of making a fork.  However, we want to make sure that we don't
+accidentally force the opponent {\it into\/} making a fork.
+
+Let's look at this board position again, but from {\tt o}'s point of view:
+
+\ttboard xo__x___o
+
+\noindent {\tt X}'s pivots are 4 and 7, as we discussed earlier; {\tt o}
+couldn't take both those squares.  Instead, look at the triples {\tt 369}
+and {\tt 789}, both of which are singles that belong to {\tt o}.  So {\tt o}
+should move in one of the squares 3, 6, 7, or 8, forcing {\tt x} to block
+instead of setting up the fork.  But {\tt o} {\it shouldn't\/} move in square
+8, like this:
+
+\ttboard xo__x__oo
+
+\noindent because that would force {\tt x} to block in square 7, setting up
+a fork!
+
+\ttboard xo__x_xoo
+
+The structure of the algorithm is much like that of the other strategy
+possibilities.  We use {\tt keep} to find the appropriate triples, take the
+first such triple if any, and then decide which of the two empty squares in
+that triple to move into.
+
+{\prgex%
+(define (\ufun{i-can-advance?} triples me)
+  (best-move (keep (lambda (triple) (my-single? triple me)) triples)
+             triples
+             me))
+
+(define (\ufun{best-move} my-triples all-triples me)
+  (if (empty? my-triples)
+      #f
+      (best-square (first my-triples) all-triples me)))
+}
+
+\noindent {\tt Best-move} does the same job as {\tt first-if-any}, which we
+saw earlier, except that it also invokes {\tt best-square} on the first
+triple if there is one.  
+
+Since we've already chosen the relevant triples before we get to {\tt
+best-move}, you may be wondering why it needs {\it all\/} the triples as an
+additional argument.  The answer is that {\tt best-square} is going to look
+at the board position from the opponent's point of view to look for forks.
+
+{\prgex%
+(define (\ufun{best-square} my-triple triples me)
+  (best-square-helper (pivots triples (opponent me))
+		      (keep number? my-triple)))
+
+(define (\ufun{best-square-helper} opponent-pivots pair)
+  (if (member? (first pair) opponent-pivots)
+      (first pair)
+      (last pair)))
+}
+
+We {\tt keep} the two numbers of the triple that we've already selected.  We
+also select the opponent's possible pivots from among all the triples.  If
+one of our two possible moves is a potential pivot for the opponent, that's
+the one we should move into, to block the fork.  Otherwise, we arbitrarily
+pick the second ({\tt last}) free square.
+
+{\prgex%
+> (best-square "78o" (find-triples 'xo__x___o) 'o)
+7
+
+> (best-square "36o" (find-triples 'xo__x___o) 'o)
+6
+
+> (best-move '("78o" "36o") (find-triples 'xo__x___o) 'o)
+7
+
+> (i-can-advance? (find-triples 'xo__x___o) 'o)
+7
+}
+
+What if {\it both\/} of the candidate squares are pivots for the opponent?
+In that case, we've picked a bad triple; moving in either square will make
+us lose.  As it turns out, this can occur only in a situation like the
+following:
+
+\ttboard x___o___x
+
+\noindent If we chose the triple {\tt 3o7}, then either move will force the
+opponent to set up a fork, so that we lose two moves later.  Luckily,
+though, we can instead choose a triple like {\tt 2o8}.  We can move in
+either of those squares and the game will end up a tie.
+
+In principle, we should analyze a candidate triple to see if both free
+squares create forks for the opponent.  But since we happen to know that
+this situation arises only on the diagonals, we can be lazier.  We just list
+the diagonals {\it last\/} in the procedure {\tt find-triples}.  Since we
+take the first available triple, this ensures that we won't take a diagonal
+if there are any other choices.\footnt{Matt thinks this is a
+\idx{kludge}, but Brian thinks it's brilliant and elegant.}
+
+\subhd{Leftovers}
+
+If all else fails, we just pick a square.  However, some squares are better
+than others.  The center square is part of four triples, the corner squares
+are each part of three, and the edge squares each a mere two.
+
+So we pick the center if it's free, then a corner, then an edge.
+
+{\prgex\prgexskipamount=10pt%
+(define (\ufun{best-free-square} triples)
+  (first-choice (accumulate word triples)
+		'(5 1 3 7 9 2 4 6 8)))
+
+(define (\ufun{first-choice} possibilities preferences)
+  (first (keep (lambda (square) (member? square possibilities))
+	       preferences)))
+
+> (first-choice 123456789147258369159357 '(5 1 3 7 9 2 4 6 8))
+5
+
+> (first-choice "1xo4x6o8914oxx8o691x9oxo" '(5 1 3 7 9 2 4 6 8))
+1
+
+> (best-free-square (find-triples '_________))
+5
+
+> (best-free-square (find-triples '____x____))
+1
+}
+
+\backskipsubhd{Complete Program Listing}{5}
+
+\medskip
+{\listingskipamount=10pt
+\listing{ttt.scm}}
+
+\esubhd{Exercises}
+
+{\exercise The {\tt ttt} procedure assumes that nobody has won the game
+yet.  What happens if you invoke {\tt ttt} with a board position in which
+some player has already won?  Try to figure it out by looking through the
+program before you run it.
+
+A complete tic-tac-toe program would need to stop when one of the two
+players wins.  Write a predicate {\tt \ufun{already-won?}}\ that takes a
+board position and a letter ({\tt x} or {\tt o}) as its arguments and returns
+{\tt \#t} if that player has already won.
+\extag{\tttwon}
+}
+
+\solution
+
+The {\tt ttt} procedure doesn't notice that someone has already
+won.  When someone wins, they make a triple {\tt xxx} or {\tt
+ooo}.  Since this triple doesn't have any free squares, the {\tt
+ttt} procedure isn't interested in it at all; the triple {\tt xxx}
+is treated the same as the triple {\tt oxx}.  If there are still
+free squares on the board, then the program picks a square as if
+the win hadn't happened.  But if the winning move filled the
+board, then the program crashes because of the same bug that
+affects tie games as described in Exercise 9.2.
+
+{\prgex%
+(define (already-won? position player)
+  (member? (word player player player)
+           (find-triples position)))
+}
+@
+
+{\exercise
+The program also doesn't notice when the game has ended in a tie, that is,
+when all nine squares are already filled.  What happens now if you ask it to
+move in this situation?
+
+Write a procedure {\tt \ufun{tie-game?}}\ that returns {\tt \#t} in this case.
+\extag{\ttttied}
+}
+
+\solution
+{\prgex%
+(define (tie-game? position)
+  (not (member? '_ position)))
+}
+
+As it stands in the book, the {\tt ttt} program generates an error if the
+board is full.  The functions {\tt i-can-win?}, {\tt i-can-fork?}, and {\tt
+i-can-advance?} all return {\tt #f} if the board is full.  {\tt
+Best-free-square} combines all the triples, none of which contain any
+numbers, into one long word, and passes this as the first argument to {\tt
+first-choice}.  None of the letters in {\tt possibilities} are numbers, so
+
+{\prgex%
+(keep (lambda (square) (member? square possibilities))
+      preferences)
+}
+
+\noindent is empty.  We generate an error in trying to take the {\tt first}
+of this empty sentence.
+@
+
+{\exercise
+A real human playing tic-tac-toe would look at a board like this:
+
+{\parindent=20pt
+
+\ttboard oxooxxxo_
+
+}
+
+\noindent and notice that it's a tie, rather than moving in square {\tt 9}.
+Modify {\tt tie-game?}\ from Exercise \ttttied\ to notice this situation and
+return {\tt \#t}.
+
+(Can you improve the program's ability to recognize ties even further?
+What about boards with two free squares?)
+}
+
+\solution
+{\prgex%
+(define (tie-game? position)
+  (empty? (keep winnable? (find-triples position))))
+
+(define (winnable? triple)
+  (or (not (member? 'x triple))
+      (not (member? 'o triple))))
+}
+
+\noindent (If you didn't think of looking at the triples, but instead
+tried to examine the position directly, the problem is a lot harder
+because it requires looking at several special cases, such as a board
+with no free squares, a board with one free square, and so on.)
+@
+
+{\exercise
+Here are some possible changes to the rules of tic-tac-toe:
+
+\smallskip
+{\parindent=1em\everypar={}
+\bb What if you could
+win a game by having three squares forming an L shape in a corner, such
+as squares 1, 2, and 4?
+
+\bb What if the diagonals didn't win?
+
+\bb What if you could win by having {\it four\/}
+squares in a corner, such as 1, 2, 4, and 5?
+
+}\smallskip
+
+Answer the following questions for each of these modifications separately:
+What would happen if we tried to implement the change merely by changing the
+quoted sentence of potential winning combinations in {\tt find-triples}?
+Would the program successfully follow the rules as modified?
+}
+
+\solution
+If the winning combinations were all still three squares, then none of the
+functions in the program would blow up.  There is a possible subtle strategy
+problem, though, because of the kludgy way in which {\tt best-move}
+relies on the ordering of triples in {\tt find-triples}.  It's important to make
+sure the best triples still come first.  This is the kind of bug that's very
+difficult to track down, because the problem has to do with the handling of
+pivots and  you'd never think that {\tt find-triples} was
+responsible for the pivot choosing algorithm.
+
+If the diagonals didn't win, then again nothing would break.  In
+fact, that would eliminate the program's reliance on keeping the
+triples in a particular order, because the case in which creating
+a fork can lose the game wouldn't exist.
+
+If a ``triple'' could have more than three squares in it, then lots of
+things would break.  For example, {\tt i-can-win?} depends on {\tt my-pair?},
+which has the number 2 built into it.
+@
+
+{\exercise
+Modify {\tt ttt} to play chess.
+}
+
+\solution
+{\prgex%
+(define (chess position me)
+  (if (= (random 1) 0)
+      '(i give up)
+      'checkmate!))
+}
+@
+
+\catcode`\_=8
+\bye