this computer, built of tinker-toy parts, plays tic …bh/pdf/ssch10.pdf147 (load...

24
This computer, built of Tinker-Toy parts, plays tic-tac-toe.

Upload: others

Post on 20-Jan-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

This computer, built of Tinker-Toy parts, plays tic-tac-toe.

Page 2: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

147

(load "ttt.scm")

10 Example: Tic-Tac-Toe

A Warning

Technical Terms in Tic-Tac-Toe

Now that you’ve learned about higher-order functions, we’re going to look at a largeexample that uses them extensively. Using the techniques you’ve learned so far, we’regoing to write a program that plays perfect tic-tac-toe.

You can load our program into Scheme by typing

(See Appendix A if this doesn’t work for you.)

Programs don’t always come out right the first time. One of our goals in this chapter is toshow 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 simplificationsto make our explanations easier. If you type in these early versions, they won’t work. Wewill show you how we corrected these “bugs” and also will present a complete, correctversion at the end of the chapter.

To indicate the unfinished versions of procedures, we’ll use comments like “firstversion” or “not really part of game.”

We’ll number the squares of the board this way:

Page 3: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

1 2 34 5 67 8 9

ox o

x x

o xox x

item

ttt

ttt

Thinking about the Program Structure

n

strategy

148 Part III Functions as Data

(ttt ’ o xox x ’o)

> (ttt ’ x ’o) ; Human goes first in square 51 ; Computer moves in square 1> (ttt ’o xx ’o) ; Human moves in square 46 ; Computer blocks in square 6> (ttt ’o xxxo ’o) ; Human moves in square 37 ; Computer blocks again> (ttt ’o xxxoox ’o)2

We’ll call a partially filled-in board a “position.”

To the computer, the same position will be represented by the word .The nine letters of the word correspond to squares one through nine of the board.(We’re thinking ahead to the possibility of using to extract the th square of a givenposition.)

Our top-level procedure, , will return the computer’s next move given the currentposition. It takes two arguments: the current position and whether the computer isplaying X or O. If the computer is O and the board looks like the one above, then we’dinvoke like this:

Here is a sample game:

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 theplayer where to move, and so on. For now, we’ll just write the procedure thatchooses the next move. As a paying customer, you wouldn’t be satisfied with this partialprogram, but from the programmer’s point of view, this is the more interesting part.

Page 4: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

you

data structures

Chapter 10 Example: Tic-Tac-Toe 149

cond

I-can-win?choose-winning-move

Opponent-can-win?Block-opponent-win

ttt

ttt i-can-win?

(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))))

Let’s plan the computer’s strategy in English before we start writing a computerprogram. How do 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 thatimmediate, you consider less urgent questions, such as how this move might affect whathappens two moves later.

So we’ll represent this set of rules by a giant expression:

We’re imagining many helper procedures. will look at the board andtell if the computer has an immediate winning move. If so, willfind that particular move. returns true if the human player hasan immediate winning move. will return a move that preventsthe computer’s opponent from winning, and so on.

We didn’t actually start by writing this definition of . The particular names ofhelper procedures are just guesses, because we haven’t yet planned the tic-tac-toe strategyin detail. But we did know that this would be the overall structure of our program. Thisbig picture doesn’t automatically tell us what to do next; different programmers mightfill in the details differently. But it’s a framework to keep in mind during the rest of thejob.

Our first practical step was to think about the in our program. Adata structure is a way of organizing several pieces of information into a big chunk. Forexample, a sentence is a data structure that combines several words in a sequence (thatis, in left-to-right order).

In the first, handwavy version of , the strategy procedures like arecalled with no arguments, but of course we knew they would need some informationabout the board position. We began by thinking about how to represent that informationwithin the program.

Page 5: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

x ox

o

x ox

x o

x 9 xx5x

x

The First Step: Triples

(1xo 4x6 o89 14o xx8 o69 1x9 oxo)

numbers

150 Part III Functions as Data

A person looking at a tic-tac-toe board looks at the rows, columns, and diagonals. Thequestion “do I have a winning move?” is equivalent to the question “are there threesquares 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, andtwo diagonals. Consider the combination containing the three squares 1, 5, and 9. Ifit contains both an and an then nobody can win with this combination and there’snothing to think about. But if it contains two s and a free square, we’re very interestedin the combination. What we want to know in particular is which square is free, since wewant to move in that square to win or block.

More generally, the only squares whose we care about are the ones we mightwant to move into, namely, the free ones. So the only interesting information about asquare is whether it has an or an , and if not, what its number is.

The information that 1, 5, 9 is a potential winning combination and the informationthat square 1 contains an , square 5 is empty, and square contains another can becombined into the single word . Looking at this word we can see immediately thatthere are two s in this “triple” and that the free square is square 5. So when we want toknow about a three-square combination, we will turn it into a triple of that form.

Here’s a sample board position:

and here is a sentence of all of its triples:

Take a minute to convince yourself that this sentence really does tell you everythingyou need to know about the corresponding board position. Once our strategy procedurefinds the triples for a board position, it’s never going to look at the original positionagain.

This technique of converting data from one form to another so that it can bemanipulated more easily is an important idea in computer science. There are really threerepresentations of the same thing. There’s this picture:

Page 6: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

13

73

x ox

o

Finding the Triples

(123 456 789 147 258 369 159 357)

xo x o (1xo 4x6 o89 14o xx8 o69 1x9oxo)

xo x o

Tttx

x

contents

inconvenient

Chapter 10 Example: Tic-Tac-Toe 151

as well as the word and the sentence. 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 personwho’s playing tic-tac-toe. Unfortunately, you can’t type that picture into a computer, sowe need a different format, the word , which contains the of thenine squares in the picture, but without the lines separating the squares and without thetwo-dimensional shape.

The third format, the sentence, is quite for human beings. You’d neverwant to think about a tic-tac-toe board that way yourself, because the sentence doesn’thave the visual simplicity that lets you take in a tic-tac-toe position at a glance. But thesentence of triples is the most convenient representation for our program. willhave to answer questions like “can win on the next move?” To do that, it will haveto consider an equivalent but more detailed question: “For each of the eight possiblewinning combinations, can complete that combination on the next move?” It doesn’treally matter whether a combination is a row or a column; what does matter is that each ofthe eight combinations be readily available for inspection by the program. The sentence-of-triples representation obscures part of the available information (which combinationis where) to emphasize another part (making the eight combinations explicit, instead ofimplicit in the nine boxes of the diagram).

The representation of fractions as “mixed numerals,” such as 2 , and as “improperfractions,” such as , is a non-programming example of this idea about multiple repre-sentations. A mixed numeral makes it easier for a person to tell how big the number is,but an improper fraction makes arithmetic easier.

We said that we would combine the current board position with the numbers of thesquares in the eight potential winning combinations in order to compute the things we’recalling triples. That was our first task in writing the program.

Our program will start with this sentence of all the winning combinations:

Page 7: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

152 Part III Functions as Data

xo x o

x oevery

substitute-triplefind-triples

substitute-triple 2582x8

every substitute-letter

Every

258 2x8 every (2 x 8)substitute-triple

Substitute-letter

item

(1xo 4x6 o89 14o xx8 o69 1x9 oxo)

(define (find-triples position) ;; first version(every substitute-triple ’(123 456 789 147 258 369 159 357)))

(define (substitute-triple combination) ;; first version(every substitute-letter combination))

(define (substitute-triple combination) ;; second version(accumulate word (every substitute-letter combination)))

(define (substitute-letter square) ;; first version(if (equal? ’ (item square position))

square(item square position)))

and a position word such as ; it will return a sentence of triples such as

All that’s necessary is to replace some of the numbers with s and s. This kind ofword-by-word translation in a sentence is a good job for .

We’ve made up a name for a procedure we haven’t writtenyet. This is perfectly OK, as long as we write it before we try to invoke .The function will take three digits, such as , and return atriple, such as :

This procedure uses to call on all three letters.

There’s a small problem, though. always returns a sentence, and we wantour triple to be a word. For example, we want to turn the potential winning combination

into the word , but would return the sentence . So here’s ournext version of :

knows that letter number 3 of the word that represents theboard corresponds to the contents of square 3 of the board. This means that it can justcall 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 thesquare.

Whoops! Do you see the problem?

Page 8: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

EveryUsing with Two-Argument Procedures

Chapter 10 Example: Tic-Tac-Toe 153

squaresubstitute-letter

substitute-lettersubstitute-triple substitute-letter

substitute-lettersubstitute-triple

substitute-letterevery

lambda

> (substitute-letter 5)ERROR: Variable POSITION is unbound.

(define (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

(define (substitute-triple combination) ;; second version again(accumulate word (every substitute-letter combination)))

(lambda (square) (substitute-letter square position))

Our procedure only takes one argument, , but it needs to know the position so itcan find out what’s in the given square. So here’s the real :

Now can do its job, since it has access to the position. Butwe’ll have to modify to invoke with twoarguments.

This is a little tricky. Let’s look again at the way we’re usinginside :

By giving another argument, we have made this formerly correctprocedure incorrect. The first argument to must be a function of one argument,not two. This is exactly the kind of situation in which can help us: We have afunction of two arguments, and we need a function of one argument that does the samething, but with one of the arguments fixed.

The procedure returned by

does exactly the right thing; it takes a square as its argument and returns the contents ofthe position at that square.

Page 9: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

154 Part III Functions as Data

substitute-triple

substitute-letter substitute-triple

find-triples

Substitute-triplelambda every

find-triples

(define (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

(define (find-triples position)(every (lambda (comb) (substitute-triple comb position))

’(123 456 789 147 258 369 159 357)))

> (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’s the final version of :

As you can see, Scheme prints some of these words with double-quote marks. The rule isthat a word that isn’t a number but begins with a digit must be double-quoted. But in thefinished program we’re not going to print such words at all; we’re just showing you theworking of a helper procedure. Similarly, in this chapter we’ll show direct invocations ofhelper procedures in which some of the arguments are strings, but a user of the overallprogram won’t have to use this notation.

We’ve fixed the problem by givingan extra argument, so we’re going to have to go through the same process with

. Here’s the right version:

It’s the same trick. is a procedure of two arguments. We useto transform it into a procedure of one argument for use with .

We’ve now finished , one of the most important procedures in thegame.

Page 10: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

Can the Computer Win on This Move?

Chapter 10 Example: Tic-Tac-Toe 155

Substitute-letterSubstitute-tripleFind-triples

i-can-win? #t

x6x oo7

appearances

xxo

(define (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))))

> (appearances ’o ’oo7)2

> (appearances ’x ’oo7)0

Here again are the jobs of all three procedures we’ve written so far:

finds the letter in a single square.finds all three letters corresponding to three squares.finds all the letters in all eight winning combinations.

We’ve done all this because we think that the rest of the program can use thetriples we’ve computed as data. So we’ll just compute the triples once for all the otherprocedures to use:

. . .

The obvious next step is to write , a procedure that should return if thecomputer can win on the current move—that is, if the computer already has two squaresof a triple whose third square is empty. The triples and are examples.

So we need a function that takes a word and a letter as arguments and counts howmany times that letter appears in the word. The primitive that we used inChapter 2 (and that you re-implemented in Exercise 9.10) will do the job:

The computer “owns” a triple if the computer’s letter appears twice and theopponent’s letter doesn’t appear at all. (The second condition is necessary to excludecases like .)

Page 11: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

156 Part III Functions as Data

opponent

lambda My-pair?

keep

Notice that we need a function that returns the opposite letter from ours.

Finally, the computer can win if it owns any of the triples:

By now you’re accustomed to this trick with . takes a triple and thecomputer’s letter as arguments, but we want a function of one argument for use with

.

(define (my-pair? triple me)(and (= (appearances me triple) 2)

(= (appearances (opponent me) triple) 0)))

(define (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

(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

Page 12: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

If So, in Which Square?

Chapter 10 Example: Tic-Tac-Toe 157

i-can-win? #t

keep

#f

#fcond

cond

cond i-can-win?

* A kludge is a programming trick that doesn’t follow the rules but works anyway somehow. Itdoesn’t rhyme with “sludge”; it’s more like “clue” followed by “j” as in “Jim.”

(define (choose-winning-move triples me) ;; not really part of game(keep number? (first (keep (lambda (triple) (my-pair? triple me))

triples))))

(define (ttt-choose triples me) ;; second version(cond ((i-can-win? triples me))

((opponent-can-win? triples me))))

(define (i-can-win? triples me)(choose-win(keep (lambda (triple) (my-pair? triple me))

triples)))

Suppose returns . We then have to find the particular square that willwin the game for us. This will involve a repetition of some of the same work we’ve alreadydone:

We again use to find the triples with two of the computer’s letter, but this time weextract the number from the first such winning triple.

We’d like to avoid this inefficiency. As it turns out, generations of Lisp programmershave been in just this bind in the past, and so they’ve invented a kludge* to get around it.

Remember we told you that everything other than counts as true? We’ll takeadvantage of that by having a single procedure that returns the number of a winningsquare if one is available, or otherwise. In Chapter 6 we called such a procedurea “semipredicate.” The kludgy part is that accepts a clause containing a singleexpression instead of the usual two expressions; if the expression has any true value, then

returns that value. So we can say

. . .

where each clause invokes a semipredicate. We then modify to havethe desired behavior:

Page 13: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

158 Part III Functions as Data

Second Verse, Same as the First

i-can-win?I-can-win?

#fi-can-win?

(define (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

(define (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

By this point, we’re starting to see the structure of the overall program. There willbe several procedures, similar to , that will try to choose the next move.

checks to see if the computer can win on this turn, another procedurewill check to see if the computer should block the opponent’s win next turn, and otherprocedures will check for other possibilities. Each of these procedures will be what we’vebeen calling “semipredicates.” That is to say, each will return the number of the squarewhere the computer should move next, or if it can’t decide. All that’s left is to figureout the rest of the computer’s strategy and write more procedures like .

Now it’s time to deal with the second possible strategy case: The computer can’t win onthis 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? Inthat case, we’ve already noticed the computer’s win, and by winning the game we avoidhaving to think about blocking the opponent.)

Once again, we have to go through the complicated business of finding triples thathave two of the opponent’s letter and none of the computer’s letter—but it’s alreadydone!

Is that amazing or what?

Page 14: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

x ox

o

x ox

x o

Now the Strategy Gets Complicated

two

two

Chapter 10 Example: Tic-Tac-Toe 159

x o x

x oo

xo

x47 3x7

i-can-fork?pivots

pivots(4 7) pivots i-can-fork?

(define (i-can-fork? triples me)(first-if-any (pivots triples me)))

Since our goal here is to teach programming, rather than tic-tac-toe strategy, we’re justgoing 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 tolook for a situation in which a move that we make now will give rise to winning triplesnext time. Here’s an example:

Neither nor can win on this move. But if the computer is playing , movingin 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:

From this position, can win by moving either in square 3 or in square 4. It’s ’s turn,but can block only one of these two possibilities. By contrast, if (in the earlier position)

moves in square 3 or square 6, that would create a single winning triple for next time,but could block it.

In other words, we want to find triples in which one square is taken by thecomputer and the other two are free, with one free square shared between the two triples.(In this example, we might find the two triples and ; that would lead us to movein square 7, the one that these triples have in common.) We’ll call a situation like this a“fork,” and we’ll call the common square the “pivot.” This isn’t standard terminology; wejust made up these terms to make it easier to talk about the strategy.

In order to write the strategy procedure we assume that we’ll needa procedure that returns a sentence of all pivots of forks currently availableto the computer. In this board, 4 and 7 are the pivots, so the procedurewould return the sentence . If we assume , then writing isstraightforward:

Page 15: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

Finding the Pivots

Pivots

keep x

Pivots

160 Part III Functions as Data

(define (first-if-any sent)(if (empty? sent)

#f(first sent)))

(xo3 4x6 78o x47 ox8 36o xxo 3x7)

(4x6 x47 3x7)

4x6x473x7

("" "" 3 44 "" 6 77 "" "")

(define (pivots triples me)(repeated-numbers (keep (lambda (triple) (my-single? triple me))

triples)))

(define (my-single? triple me)(and (= (appearances me triple) 1)

(= (appearances (opponent me) triple) 0)))

> (my-single? "4x6" ’x)#T

should return a sentence containing the pivot numbers. Here’s the plan. We’llstart with the triples:

We the ones that have an and two numbers:

We mash these into one huge word:

We sort the digits from this word into nine “buckets,” one for each digit:

We see that there are no ones or twos, one three, two fours, and so on. Now we can easilysee that four and seven are the pivot squares.

Let’s write the procedures to carry out that plan. has to find all the tripleswith one computer-owned square and two free squares, and then it has to extract thesquare numbers that appear in more than one triple.

Page 16: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

Chapter 10 Example: Tic-Tac-Toe 161

My-single? my-pair?

Repeated-numbers

accumulate

accumulate word

> (my-single? ’xo3 ’x)#F

> (keep (lambda (triple) (my-single? triple ’x))(find-triples ’xo x o))

("4X6" X47 "3X7")

(define (repeated-numbers sent)(every first

(keep (lambda (wd) (>= (count wd) 2))(sort-digits (accumulate word sent)))))

> (accumulate word ’("4x6" x47 "3x7"))"4X6X473X7"

(define (extract-digit desired-digit wd)(keep (lambda (wd-digit) (equal? wd-digit desired-digit)) wd))

> (extract-digit 7 "4x6x473x7")77

> (extract-digit 2 "4x6x473x7")""

is just like except that it looks for one appearance of the letterinstead of two.

takes a sentence of triples as its argument and has to return asentence of all the numbers that appear in more than one triple.

We’re going to read this procedure inside-out, starting with the and workingoutward.

Why is it okay to the sentence? Suppose that a number appearsin two triples. All we need to know is that number, not the particular triples throughwhich we found it. Therefore, instead of writing a program to look through several triplesseparately, we can just as well combine the triples into one long word, keep only the digitsof that word, and simply look for the ones that appear more than once.

We now have one long word, and we’re looking for repeated digits. Since this isa hard problem, let’s start with the subproblem of finding all the copies of a particulardigit.

Page 17: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

162 Part III Functions as Data

12

every

Sort-digits

repeated-numbers

* Brian thinks this is a kludge, but Matt thinks it’s brilliant and elegant.

Now we want a sentence where the first word is all the s, the second word is all thes, etc. We could do it like this:

. . .

but that wouldn’t be taking advantage of the power of computers to do that sort ofrepetitious work for us. Instead, we’ll use :

takes a word full of numbers and returns a sentence whose first wordis all the ones, second word is all the twos, etc.*

Let’s look at again:

(se (extract-digit 1 "4x6x473x7")(extract-digit 2 "4x6x473x7")(extract-digit 3 "4x6x473x7")

)

(define (sort-digits number-word)(every (lambda (digit) (extract-digit digit number-word))

’(1 2 3 4 5 6 7 8 9)))

> (sort-digits 123456789147258369159357)(111 22 333 44 5555 66 777 88 999)

> (sort-digits "4x6x473x7")("" "" 3 44 "" 6 77 "" "")

(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)

Page 18: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

x ox

o

Taking the Offensive

pivots i-can-fork?

ttt-choose

o

twois

into

Chapter 10 Example: Tic-Tac-Toe 163

(define (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))))

(define (opponent-can-fork? triples me) ;; not really part of game(i-can-fork? triples (opponent me)))

This concludes the explanation of . Remember that choosesthe first pivot, if any, as the computer’s move.

Here’s the final version of with all the clauses shown:

You already know about the first three possibilities.

Just as the second possibility was the “mirror image” of the first (blocking anopponent’s move of the same sort the computer just attempted), it would make sense forthe fourth possibility to be blocking the creation of a fork by the opponent. That wouldbe easy to do:

Unfortunately, although the programming works, the strategy doesn’t. Maybe theopponent has potential forks; we can block only one of them. (Why isn’t that aconcern when blocking the opponent’s wins? It a concern, but if we’ve allowed thesituation to reach the point where there are two ways the opponent can win on the nextmove, 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 thismove, then our opponent will be forced to block on the next move, instead of making afork. However, we want to make sure that we don’t accidentally force the opponentmaking a fork.

Let’s look at this board position again, but from ’s point of view:

Page 19: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

x oxo o

x ox

x o o

shouldn’t

all

164 Part III Functions as Data

X o369 789 o o

xo

x

keep

Best-move first-if-anybest-square

best-move

best-square

(define (i-can-advance? triples me)(best-move (keep (lambda (triple) (my-single? triple me)) triples)

triplesme))

(define (best-move my-triples all-triples me)(if (empty? my-triples)

#f(best-square (first my-triples) all-triples me)))

(define (best-square my-triple triples me)(best-square-helper (pivots triples (opponent me))

(keep number? my-triple)))

’s pivots are 4 and 7, as we discussed earlier; couldn’t take both those squares. Instead,look at the triples and , both of which are singles that belong to . So shouldmove in one of the squares 3, 6, 7, or 8, forcing to block instead of setting up the fork.But move in square 8, like this:

because that would force to block in square 7, setting up a fork!

The structure of the algorithm is much like that of the other strategy possibilities.We use to find the appropriate triples, take the first such triple if any, and thendecide which of the two empty squares in that triple to move into.

does the same job as , which we saw earlier, except that italso invokes on the first triple if there is one.

Since we’ve already chosen the relevant triples before we get to , youmay be wondering why it needs the triples as an additional argument. The answer isthat is going to look at the board position from the opponent’s point ofview to look for forks.

Page 20: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

keep

last

3o72o8

find-triples

both

last

Chapter 10 Example: Tic-Tac-Toe 165

xo

x

* Matt thinks this is a kludge, but Brian thinks it’s brilliant and elegant.

(define (best-square-helper opponent-pivots pair)(if (member? (first pair) opponent-pivots)

(first pair)(last pair)))

> (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

We the two numbers of the triple that we’ve already selected. We also selectthe opponent’s possible pivots from among all the triples. If one of our two possiblemoves is a potential pivot for the opponent, that’s the one we should move into, to blockthe fork. Otherwise, we arbitrarily pick the second ( ) free square.

What if of the candidate squares are pivots for the opponent? In that case, we’vepicked a bad triple; moving in either square will make us lose. As it turns out, this canoccur only in a situation like the following:

If we chose the triple , then either move will force the opponent to set up a fork, sothat we lose two moves later. Luckily, though, we can instead choose a triple like . Wecan 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 createforks for the opponent. But since we happen to know that this situation arises onlyon the diagonals, we can be lazier. We just list the diagonals in the procedure

. Since we take the first available triple, this ensures that we won’t take adiagonal if there are any other choices.*

Page 21: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

166 Part III Functions as Data

Leftovers

Complete Program Listing

If all else fails, we just pick a square. However, some squares are better than others. Thecenter square is part of four triples, the corner squares are each part of three, and theedge squares each a mere two.

So we pick the center if it’s free, then a corner, then an edge.

(define (best-free-square triples)(first-choice (accumulate word triples)

’(5 1 3 7 9 2 4 6 8)))

(define (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

;;; ttt.scm;;; Tic-Tac-Toe program

(define (ttt position me)(ttt-choose (find-triples position) me))

(define (find-triples position)(every (lambda (comb) (substitute-triple comb position))

’(123 456 789 147 258 369 159 357)))

(define (substitute-triple combination position)(accumulate word

(every (lambda (square)(substitute-letter square position))

combination) ))

Page 22: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

Chapter 10 Example: Tic-Tac-Toe 167

(define (substitute-letter square position)(if (equal? ’_ (item square position))

square(item square position) ))

(define (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)) ))

(define (i-can-win? triples me)(choose-win(keep (lambda (triple) (my-pair? triple me))

triples)))

(define (my-pair? triple me)(and (= (appearances me triple) 2)

(= (appearances (opponent me) triple) 0)))

(define (opponent letter)(if (equal? letter ’x) ’o ’x))

(define (choose-win winning-triples)(if (empty? winning-triples)

#f(keep number? (first winning-triples)) ))

(define (opponent-can-win? triples me)(i-can-win? triples (opponent me)) )

(define (i-can-fork? triples me)(first-if-any (pivots triples me)) )

(define (first-if-any sent)(if (empty? sent)

#f(first sent) ))

(define (pivots triples me)(repeated-numbers (keep (lambda (triple) (my-single? triple me))

triples)))

Page 23: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

168 Part III Functions as Data

(define (my-single? triple me)(and (= (appearances me triple) 1)

(= (appearances (opponent me) triple) 0)))

(define (repeated-numbers sent)(every first

(keep (lambda (wd) (>= (count wd) 2))(sort-digits (accumulate word sent)) )))

(define (sort-digits number-word)(every (lambda (digit) (extract-digit digit number-word))

’(1 2 3 4 5 6 7 8 9) ))

(define (extract-digit desired-digit wd)(keep (lambda (wd-digit) (equal? wd-digit desired-digit)) wd))

(define (i-can-advance? triples me)(best-move (keep (lambda (triple) (my-single? triple me)) triples)

triplesme))

(define (best-move my-triples all-triples me)(if (empty? my-triples)

#f(best-square (first my-triples) all-triples me) ))

(define (best-square my-triple triples me)(best-square-helper (pivots triples (opponent me))

(keep number? my-triple)))

(define (best-square-helper opponent-pivots pair)(if (member? (first pair) opponent-pivots)

(first pair)(last pair)))

(define (best-free-square triples)(first-choice (accumulate word triples)

’(5 1 3 7 9 2 4 6 8)))

(define (first-choice possibilities preferences)(first (keep (lambda (square) (member? square possibilities))

preferences)))

Page 24: This computer, built of Tinker-Toy parts, plays tic …bh/pdf/ssch10.pdf147 (load "ttt.scm") 10 Example: Tic-Tac-Toe A Warning Technical Terms in Tic-Tac-Toe Now that you’ve learned

o x oo x xx o

Exercises

10.1

10.2

10.3

10.4

10.5

four

Chapter 10 Example: Tic-Tac-Toe 169

tttttt

already-won? x o#t

tie-game? #t

9 tie-game?#t

find-triples

ttt

The procedure assumes that nobody has won the game yet. What happensif you invoke with a board position in which some player has already won? Try tofigure 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 that takes a board position and a letter ( or ) as itsarguments and returns if that player has already won.

The program also doesn’t notice when the game has ended in a tie, that is, when allnine squares are already filled. What happens now if you ask it to move in this situation?

Write a procedure that returns in this case.

A real human playing tic-tac-toe would look at a board like this:

and notice that it’s a tie, rather than moving in square . Modify fromExercise 10.2 to notice this situation and return .

(Can you improve the program’s ability to recognize ties even further? What aboutboards with two free squares?)

Here are some possible changes to the rules of tic-tac-toe:

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?

What if the diagonals didn’t win?

What if you could win by having squares in a corner, such as 1, 2, 4, and 5?

Answer the following questions for each of these modifications separately: What wouldhappen if we tried to implement the change merely by changing the quoted sentenceof potential winning combinations in ? Would the program successfullyfollow the rules as modified?

Modify to play chess.