13 planning - peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as i did in...

22
poker 13 Planning styles top-down; bottom-up; 233 Program file for this chapter: The picture on page 234 shows some of the architecture on the University of California campus at Berkeley. At the left of the picture is South Hall, one of the original campus buildings, with red brick, ivy, and many chimneys. The white brick clock tower that dominates the center of the picture is Sather Tower, popularly called the Campanile after the building in Venice, Italy, on which it is modeled. Just to its left is Evans Hall, the concrete fortress that houses the Mathematics Department. Andrews Hall, at the very front of the picture, is a small, one-floor building with an unusually shaped roof. Stephens Hall, mostly hidden by the trees behind Andrews, is a yellow-green zigzag. Page 235 shows a similar view of the Stanford University campus in Palo Alto, California. Compared to the Berkeley buildings, the ones you see here look very uniform. At the left in the first photo is a corner of the Quadrangle, the central building complex of the campus. The School of Education, down the path on the left, follows the same pattern of rough tan stone with a sloping orange roof. The Meyer Library, at the rear of the photo, follows the same color scheme, even though it’s obviously a more recent building. The second photo shows the new School of Law. In this building the architect has clearly worked to combine the same tan stone, orange roof theme with more modern details: the texture of the stone is more uniform and the arches are less ornate. Both of these campuses are the result of architectural planning, but they illustrate two different of planning. The Stanford campus was planned first came an overall concept and then the details to fill in that concept. The Berkeley campus was planned each new building was designed to fit its architect’s idea of the immediate, local situation. Some of the individual buildings are quite beautiful, but it’s those buildings, rather than the campus as a unit, that attract attention. (I’m oversimplifying, of course. In a strictly top-down approach, the entire campus would be laid out on paper before any building was built. Adding new buildings later,

Upload: others

Post on 17-Mar-2021

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

poker

13 Planning

styles top-down;

bottom-up;

233

Program file for this chapter:

The picture on page 234 shows some of the architecture on the University of Californiacampus at Berkeley. At the left of the picture is South Hall, one of the original campusbuildings, with red brick, ivy, and many chimneys. The white brick clock tower thatdominates the center of the picture is Sather Tower, popularly called the Campanileafter the building in Venice, Italy, on which it is modeled. Just to its left is Evans Hall,the concrete fortress that houses the Mathematics Department. Andrews Hall, at thevery front of the picture, is a small, one-floor building with an unusually shaped roof.Stephens Hall, mostly hidden by the trees behind Andrews, is a yellow-green zigzag.

Page 235 shows a similar view of the Stanford University campus in Palo Alto,California. Compared to the Berkeley buildings, the ones you see here look veryuniform. At the left in the first photo is a corner of the Quadrangle, the central buildingcomplex of the campus. The School of Education, down the path on the left, followsthe same pattern of rough tan stone with a sloping orange roof. The Meyer Library, atthe rear of the photo, follows the same color scheme, even though it’s obviously a morerecent building. The second photo shows the new School of Law. In this building thearchitect has clearly worked to combine the same tan stone, orange roof theme with moremodern details: the texture of the stone is more uniform and the arches are less ornate.

Both of these campuses are the result of architectural planning, but they illustratetwo different of planning. The Stanford campus was planned first camean overall concept and then the details to fill in that concept. The Berkeley campuswas planned each new building was designed to fit its architect’s idea of theimmediate, local situation. Some of the individual buildings are quite beautiful, but it’sthose buildings, rather than the campus as a unit, that attract attention.

(I’m oversimplifying, of course. In a strictly top-down approach, the entire campuswould be laid out on paper before any building was built. Adding new buildings later,

Page 2: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

234 Chapter 13 Planning

University of California, Berkeley

Page 3: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

Stanford University

Chapter 13 Planning 235

Page 4: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

ttt playgame

236 Chapter 13 Planning

Structured Programming

to tttdrawboardchoose.x.oplaygameend

to playgamemove "xif winp "x [stop]move "oif winp "o [stop]playgameend

even if they’re made to fit in with the old ones, means that the original plan was defective.Instead of patching it up, a top-down purist would have the architects begin all overagain, allowing for more buildings from the beginning of the design process. And infact the original Berkeley campus was much more uniform than the campus today, butvery rapid growth led to widespread changes in the original plan. Still, the difference inarchitectural planning styles is striking and suggestive.)

The same two planning strategies are possible in computer programming. Supposeyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start bysaying, “Let’s see if I can draw the board.” You’d write a procedure to draw the four linesthat make up the tic-tac-toe grid. Then you might write procedures to draw an X and anO in the right size for the boxes you’ve made. And so on. That would be a bottom-updesign. Alternatively, you might start by deciding on the major tasks that your programwill have to carry out. You might then write a top-level procedure like this:

In writing and , I’ve freely used subprocedures that I haven’t written yet.Later I could fill in the gaps, writing procedures that will do exactly what’s needed to filltheir places in the main procedure.

In recent years the majority of computer scientists have adopted a school of thoughtcalled structured programming. This phrase—the title of a 1972 book by O. J. Dahl,Edsger Dijkstra, and C. A. R. Hoare—describes an uncompromising top-down philosophyof programming. Structured programming is more than just the top-down idea, though;it also includes rules for each step in the program development process. For example,one potential problem with top-down programming is that it’s hard to test a procedure

Page 5: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

Critique of Structured Programming

stubs

object oriented

compiled

Critique of Structured Programming 237

you’ve written until its subprocedures are written also. (By contrast, a subprocedure canbe tested before its superprocedures are written.) Structured programming solves thisproblem by recommending the use of —preliminary versions of the subproceduresthat don’t really do the job but provide some result that allows the higher-level proceduresto be tested. For example, an operation that hasn’t been written yet might be replacedby a stub that always outputs zero, or the empty list, or some other simple, appropriatevalue.

More importantly, the structured programming approach tells us not to write anyprocedures at all until we’ve first written a detailed specification of the how the programshould behave, and then a detailed plan of how it will be organized to achieve that goal.

The programming language Pascal was designed by Niklaus Wirth in 1970 to promotea programming style and philosophy like that of structured programming. Pascal is meantto teach a top-down structured style by providing just the tools needed for that approachbut making it hard to program in other styles. The widespread use of Pascal in collegeprogramming courses reflects the popularity of the structured programming approach.

(As I am preparing the second edition of this book in 1995, Pascal is just beginningto lose ground as a teaching language; several competing schools of thought aboutprogramming have led to diverse language choices. The best known right now is thelanguage C++, which exemplifies an approach to program structure. Othersare Ada and Modula, two languages more or less in the Pascal tradition, and Scheme,which is, like Logo, a dialect of Lisp and represents the artificial intelligence tradition.)

One area of computer science in which the top-down approach has not been accepted soenthusiastically is artificial intelligence. AI researchers try to program computers to carryout ill-defined, complex tasks (playing chess is a prototypical example) for which thereis no single, obvious method. In that kind of research project you can’t start by writingdown on paper a complete specification of how the finished program will be organized.Instead you start with a more or less vague idea, you try programming it, and then youplay around with it to try to improve the results. That’s one reason why the majority ofAI programs are written in Lisp, a language that is interactive, so it encourages you to“program at the keyboard.” Pascal, on the other hand, was designed to be alanguage, in which you must write an entire program before you can carry out a singleinstruction.

Logo, a dialect of Lisp, was developed by artificial intelligence researchers. Theiridea was to see if they could use some of their experience with the problem of trying to

Page 6: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

238 Chapter 13 Planning

A Sample Project: Counting Poker Hands

* Home computers have become more powerful since I wrote that in 1984. I can now run Logoin one window and edit the book in another window on the same computer.

get computers to think in order to help human beings learn to think more effectively—atleast about certain kinds of problems. You shouldn’t be surprised, therefore, to learnthat Logoites tend not to be enthusiastic about structured programming.

It’s not that we’re against planning. On the contrary! Planning is one of themost fundamental problem-solving skills. But there are many kinds of planning. Thekind in which every part of your program’s behavior is written down before you beginprogramming isn’t very realistic in many contexts. Even in the large-scale business orgovernment projects that structured programmers like to talk about, it’s very commonthat the ultimate users of a program change their minds about how it should work, oncethey have some experience with using it. The wise programmer will anticipate thesechanging requirements in the original planning process. Still, one never anticipateseverything; a sensible person faced with an unexpected change in requirements will beflexible enough to modify the initial plan, not start all over again. And it’s even more truefor people like you, who are just learning to program, that the “goal” of a programmingproject is exploratory rather than predetermined.

Sometimes human lives depend on the correct operation of a computer program. Inone famous example, just about the time that the first edition of this book was published,one person died and others were injured because the program controlling a medicalX-ray machine gave patients massive overdoses of radiation. Certainly, any programmingtechniques that can help prevent such accidents are valuable. Still, the techniquesapplicable to life-or-death programming situations are not necessarily the best techniquesfor beginning learners, nor even for experienced researchers who are exploring a newarea rather than writing production programs.

To make all this more concrete, I’d like to show you an actual planning process for aprogramming project. I’m going to write a Logo program and tell you what I’m doing asI go along. I am sitting at a rather crowded desk; on my left is a microcomputer runningLogo, and on my right is the terminal with which I call up the large computer I use fortext editing. I’ll switch back and forth as I work.* Please understand that I’m not showing

Page 7: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

A Sample Project: Counting Poker Hands 239

pokerhand [3s qc 3d 3h qd]

pokerhand [4c 7h 5d 3c 6d]

pokerhand [2h 10d 5s 6s 10s]

?full house (threes and queens)?straight (seven high)?pair of tens?

[a 2 3 4 5 6 7 8 9 10 j q k]

[h s d c]

to pokerhand :cardsif royal.flushp :cards [print [royal flush] stop]if fourp :cards [print [four of a kind] stop]if straight.flushp :cards [print [straight flush] stop]...print [I suggest you fold.]end

you the Official Logo Programming Style. I’m showing you one way in which one Logoprogrammer approaches a particular project.

The project I have in mind is to announce the value of a poker hand. That is, theprogram should behave something like this:

In imagining this sample dialogue, I’m doing a kind of top-down planning. I’ve specified,for example, the form in which I intend to represent a hand (a list of five cards) and acard (a word combining a rank from

with a suit from

standing for hearts, spades, diamonds, and clubs). I suppose that means that I’ve decidedwe’re playing five-card draw poker rather than seven-card stud. But later I may want tothink again about that choice. I’ve also written down a few of the specific messages theprogram can print, although I’m much less certain about these. I may or may not actuallybother with the details in parentheses, for example.

Okay, how will the program work? I envision a series of tests for particular kinds ofpoker hands. So in my head there is a vague procedure template like this:

Page 8: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

An Initialization Procedure

once,

240 Chapter 13 Planning

if straightp

ranks suits pairs

Read.cards poker.initpairs threes fours straights

flushes

:ranks :suits

to poker.init :cards ;; first edition versionmake "ranks []make "suits []make "pairs []make "threes []make "fours []read.cards :cardsend

This isn’t something I’m ready to type into my computer. I’m still thinking about howthe details are likely to work out. One thing that comes to mind is that, as it stands, therewill be a great duplication of effort. The test for a royal flush is just like the test for astraight flush, plus a particular special condition (ace high). I shouldn’t really make thattest twice. For that matter the test for a straight flush is the test for a straight combinedwith the test for a flush. I shouldn’t have another instruction startingrepeating the same test.

I also shouldn’t read through the list representing the hand a million times, each timepulling out the rank without the suit or vice versa. It seems that I should begin by goingthrough the hand extracting various kinds of information into a bunch of variables.I’ll probably have and , along with things like , which will list theranks that appear twice in the hand. I’m not sure exactly what variables I’ll need, but Iam now impatient to start programming. What I’m going to do is write an initializationprocedure to set up all this information.

In revising this chapter for the second edition, I find that I have very different ideasabout how to write this initialization procedure. But I think that it’s worthwhile, since thisis a chapter about planning a program and not about the finished product, to preservemy original version and the reasoning that led me to write it. In the next section I’ll showanother approach.

, when I write it, will insert new members into the lists thatsets up as empty lists. Why , , and but not, for example,and ? Pairhood is a property of just part of a hand, whereas straighthood is aproperty of the entire hand. It doesn’t make sense to say that three of the five cards forma straight. But the lists and will help in determining whether the handis a straight or a flush, respectively. For instance, a flush is a hand in which there is only

Page 9: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

name

An Initialization Procedure 241

:suits:threes :pairs

local

pokerhand local

read.cards

read.card

make fput

butlast first

10s first 1butlast 10

insert"ranks :ranks

make Make

to read.cards :cardsforeach :cards "read.cardend

to read.card :cardmake "ranks fput butlast :card :ranksmake "suits fput last :card :suits...

to read.card :cardinsert butlast :card "ranksinsert last :card "suits...

one suit, so if turns out to be a list of length one, the hand is a flush. A fullhouse is a hand with one rank listed in and another listed in .

I seem to be violating my own rules here, with all these explicit assignments tovariables that are not made . But of course the whole point of an initializationprocedure is that the variables will be used later by some other procedure, not this oneor one of its subprocedures. In a large project, it’s typical for an initialization procedureto assign values to nonlocal variables. If I’m being careful, when I get around to writingthe top-level I’ll probably put instructions for these variables there.

I can write without thinking about it at all, and I hope you can too.It’s one of the standard templates: “Do something to each member of a list.”

It’s not obvious what goes inside , but I can imagine some of theinstructions. So I’ll start writing it anyway.

Okay, time to do some thinking. I can see that there are going to be a lot of those, instructions. I should have a subprocedure to handle it.

(By the way, do you see why I extract the rank of a card with rather than ?It wouldn’t matter, except for the tens where the rank is two digits. That is, the ten ofspades is represented by the word . The of that word is the single digit ; its

is , the number we really want.)

I know that the second input to has to be the name of the variable (like) and not the value of the variable (like ) because I’ve used techniques

like this before. That input is going to be used, among other things, as the first input toa invocation. needs the of the variable in order to be able to change its

Page 10: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

call by value call by name

alwaysbefore

Always make the mostrestrictive test first.

242 Chapter 13 Planning

:ranks "ranks

pairspairs

:ranks:ranks

:threes :fours:ranks

stop

if:threes stop if

:pairs :threes:threes

fours

threes fours

:threes:pairs

remove insert

if memberp butlast :card :ranks [insert butlast :card "pairs]

to read.card :cardinsert last :card "suitsif memberp butlast :card :threes [insert butlast :card "fours stop]if memberp butlast :card :pairs [insert butlast :card "threes stop]if memberp butlast :card :ranks [insert butlast :card "pairs stop]insert butlast :card "ranksend

value. Although this particular notation is specific to Logo, most programming languageshave some way to distinguish between ( ) and ( )or some similar mechanism to handle the special cases in which a subprocedure must beable to modify a superprocedure’s variable.

What about and so on? The idea is that if I’ve seen this particular rankbefore, I should insert it in :

But there’s a bug. If I put that instruction after the ones I’ve already written, the rankwill be found in because I’ve just put it there! Instead I have to putthis instruction the one that inserts into . In fact, the same problem willarise with the other lists. I have to start by testing and inserting into ,and work my way down to . This illustrates a general rule:

I learned that rule through hours of debugging earlier projects; now Irecognize the situation right away. Here’s the finished procedure:

The commands are just for efficiency. Suppose I’ve found a particular rank threetimes already in this poker hand, and the card I’m looking at now is the fourth of thesame rank. Then the first will succeed, since the rank was already a member of

. If the command were omitted, I’d go on to the next instruction,which would find the rank in and therefore insert it into . But that’sunnecessary; if I’ve found the rank in , there is no need to insert it there again!In other words, if I’m about to insert this card in the list of , there is no need tocheck to see if it’s in the lists of smaller runs of the same rank. (Of course, it’s sort offunny having and as lists, since there can’t be more than one of them ina five-card poker hand! But this structure makes the instructions pleasingly similar.)

I notice another potential bug. When I add a rank to, for example, , Idon’t remove it from . So my data base will claim that I have a pair as well asthree of a kind. I could write a procedure analogous to , but my guess isthat it won’t be necessary. If I follow the “most restrictive test first” principle later in the

Page 11: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

name

An Initialization Procedure 243

:pairs

butlast :card

insert

stop:suits

item thingthing

thing

make :listmake "list

listthrees

insert list foursmake insert

to read.card :cardlocal "rankmake "rank butlast :card...

to insert :item :listif memberp :item thing :list [stop]make :list fput :item thing :listend

insert butlast :card "fours

make "fours fput :item thing "fours

make "fours fput :item :fours

program, I’ll know I have three of a kind before I ever look at . If it turns out tobe a problem later, I’ll fix it then.

I’m slightly annoyed that this procedure computes so many times.Perhaps it should be

But in fact I haven’t bothered making that change.

Finally, here is the missing subprocedure :

The first instruction is there to ensure that nothing is added to the same list twice. Thecommands I mentioned earlier ought to ensure the same thing, except for the

list . But since I need the instruction for that case anyway, I’ll take a “belt andsuspenders” approach for all the lists.

The input that I’ve called here used to be called , because I was thinking,“Insert a thing into a list.” But I found that using the procedure next to thevariable looked too confusing to me, even though it wouldn’t have bothered theLogo interpreter.

I hope you noticed that the second instruction starts with rather than. This is the indirect assignment technique that I mentioned briefly in

Chapter 3. Remember that the variable contains the of another variable, suchas . It is that second variable whose value is changed. For example,

invokes with an input whose name is and whose value is . In thiscase, the instruction inside is equivalent to

or

Page 12: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

244 Chapter 13 Planning

Second Edition Second Thoughts

ranks

Remdup

pairs threes fours

to poker.init :cardsmake "ranks map "butlast :cardsmake "suits remdup map "last :cards...

to poker.init :cards ;; second edition versionmake "ranks map [ranknum butlast ?] :cardsmake "suits remdup map "last :cardsmake "rankarray {0 0 0 0 0 0 0 0 0 0 0 0 0}foreach :ranks [setitem ? :rankarray (item ? :rankarray)+1]end

to ranknum :rank ;; turn rank to numberif :rank = "a [output 1]if :rank = "j [output 11]if :rank = "q [output 12]if :rank = "k [output 13]output :rankend

I wrote the first edition using versions of Logo without higher order functions. Thesefunctions can be written in Logo, and in fact I did write them later in the book, but Iwasn’t using them in this chapter. But in retrospect, the style of creating a variable named

whose value is an empty list, and then adding the rank of each card by reassigninga new value to the variable, seems much harder to understand than this:

is an operation, primitive in Berkeley Logo, whose output is the same as its input,but with duplicate members removed.

As for , , and , I think they are most easily replaced by an arraythat keeps track of the number of times each rank appears in the hand.

Since I want to use the card’s rank as an index into an array, I have to use a number from1 to 13 to represent the ranks inside the program, even though the person using theprogram will still represent a rank in the more human-readable form of A for ace and soon.

Page 13: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

poker.init

Planning and Debugging

prove

Planning and Debugging 245

if not emptyp :fours ...

if memberp 4 :rankarray ...

{x o 3 x x 6 7 8 o}

[xo3 xx6 78o xx7 ox8 36o xxo 3x7]

Where the first version of the program would test for four of a kind with

this new version will say

Notice that my second thoughts are about low-level details of the program. I haven’tchanged my mind about the big idea, which is to have a procedure thatexamines the hand and converts the information into a format in which the rest of theprogram can use it more easily. This is the same idea I used in the tic-tac-toe program ofChapter 6, in which I converted a human-readable “position” such as

into an internal list of “triples”:

From now on, I won’t show two versions of every procedure. I’ll use the revised datarepresentation, even though the chapter tells the story of how I wrote the older versionof the program.

Ideally, according to structured programming, you should never have to do any debug-ging. You should start with a complete, clear program specification. Then you shoulduse the approved style to translate that specification into a program. Then you shouldbe able to mathematically that your program is correct! Debugging is a relic of thedark ages.

That’s not the Logo approach. I’ve already done some debugging in this project.Programming is sort of like real life: you don’t always get it right the first time. Structuredprogrammers don’t get it right the first time either; the difference is that Logoites aren’tembarrassed about it. We think of debugging as part of the process of solving problemsin general.

If you’re a student in a school, the odds are that you aren’t often encouraged toaccept debugging as valuable. When you hand in a paper or a quiz, the teacher doesn’t

Page 14: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

246 Chapter 13 Planning

Classifying Poker Hands

fourp royal.flushp

:cards

pokerhand

to fourpoutput memberp 4 :rankarrayend

to threepoutput memberp 3 :rankarrayend

to pairpoutput memberp 2 :rankarrayend

to full.housepoutput and threep pairpend

to pokerhand :cardspoker.init :cardsif fourp [print [four of a kind] stop]if full.housep [print [full house] stop]if threep [print [three of a kind] stop]if pairp [print ifelse paircount = 1 [one pair] [two pairs] stop]print [something else]end

point out errors and invite you to try again. Instead he marks your errors in red ink andtakes off points for them. You’re taught that your work has to be perfect the first time.One of the strong contributions that computer programming in general, and Logo inparticular, has made to education is to provide one context in which you are shown amore realistic approach to making and correcting mistakes.

The main thing remaining to be done in my project is the collection of predicates likeand to check for particular kinds of poker hands. I decided to

write some of the easy ones, namely the ones for multiples of the same rank.

These are all pretty obvious. Notice, though, that one thing has changed since my initialidea: these procedures don’t take as an input. They don’t examine the pokerhand directly; they examine the variables set up by the initialization procedure.

Now I want to start putting all these pieces together, so I’m going to write apreliminary version of .

Page 15: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

pokerhand [ah 2c 4d 2s 6h]

evaluates

Classifying Poker Hands 247

memberplocate :rankarray

locate

pokerhand

pokerhandone

ifelse ifelse

to paircountoutput count locate 2 1end

to locate :number :indexif :index > 13 [output []]if (item :index :rankarray) = :number ~

[output fput :index (locate :number :index+1)]output locate :number :index+1end

?I don’t know how to one in pokerhand[if pairp [print ifelse paircount = 1 [one pair] [two pairs] stop]]

[one pair]

if pairp [print ifelse paircount = 1 [[one pair]] [[two pairs]] stop]

If there’s a pair, I can’t simply use to find out how many pairs are in the hand.Instead, the procedure looks at each member of and outputs alist of all the ranks of which there are exactly two cards in the hand. For this purpose Icould have had output the number of pairs, which would be a little easier thancomputing the list of ranks of pairs. But I recall that I want to be able to say things like“pair of sevens,” and for that I’ll need the actual ranks.

Let’s try it:

Looks like a bug. (This really happened; I’m not just making it up to be able to talk aboutdebugging!) The first step in solving a problem like this is to read the error messagecarefully. This message tells me that when the error happened, the immediately activeprocedure was . So that’s where I should look for a mistake. (The exact formof the message will be different in different versions of Logo, but they’ll all give you thatpiece of information. In Berkeley Logo, the error message also includes the instructionline in which the error occurred.) I then edited and looked for the word

. I found it in the list

which is one of the inputs to an operation. Aha! The trouble is thatwhichever input is selected by its predicate input, so it’s trying to evaluate that

list as a Logo expression. What I meant was this:

Page 16: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

248 Chapter 13 Planning

[[one pair]] [one pair]print

straightpflushp

pokerhand royal.flushpstraightp

straightp

pokerhand [ah 2c 4d 2s 6h]

pokerhand [2h 5d 2s 2c 7d]

pokerhand [2h 5d 2s 2c 5h]

pokerhand [3h 4h 5h 6h 7h]

?one pair?three of a kind?full house?something else

to flushpoutput emptyp butfirst :suitsend

to straightpoutput nogap (reduce "min :ranks) 5end

to min :a :boutput ifelse :a < :b [:a] [:b]end

to nogap :smallest :howmanyif :howmany=0 [output "true]if not equalp (item :smallest :rankarray) 1 [output "false]output nogap :smallest+1 :howmany-1end

Now it should evaluate and come up with the value to useas the input to . Let’s try again:

So far so good, but of course there is more work to do. We need to write ,, and their combinations: straight flush and royal flush. I think I shouldn’t have

an instruction in testing as I originally planned; instead Ishould test for and, if that’s true, look for special cases within that.

It’s not so obvious how to write . Here’s my plan: First, find the lowest-rankcard in the hand. Then, in order to have a straight, the next four ranks must also bepresent in the hand.

This isn’t the only possible way to test for a straight; can you think of, and implement,another?

Page 17: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

Classifying Poker Hands 249

Nogap

:howmany

Straightp

pokerhand

straightp

straightp pairppokerhand

pokerhand

pokerhand [3h 6d 7h 5c 4d]

pokerhand [3h 6d 7h 5c 8d]

pokerhand [ah 2d 3c 4c 5h]

pokerhand [9d 10c jh qh kh]

pokerhand [js jh qs qh kd]

if straightp [print [straight] stop]

?straight?something else

?straight?straight?two pairs

if straightp [print ifelse flushp [[straight flush]] [[straight]] stop]

starts with the smallest rank in the hand and checks that there is exactly onecard in each of that and the next four ranks. It takes advantage of the fact that I’mrepresenting ranks internally as numbers; it can just add 1 to a rank to get the next onein sequence. If reaches zero, it means that we have indeed found all fiveconsecutive ranks in the hand. If one of the five desired ranks isn’t in the hand, or if thehand has more than one card in any of the ranks, then the hand isn’t a straight.

There is one problem with this approach. The ace can be used either high card(10-J-Q-K-A) or low card (A-2-3-4-5) in a straight. thinks that the ace canonly be the low card. We’ll fix that later.

Now let’s try some other cases. I’ve just added the line

to . It doesn’t much matter where I put that line, because there is no dangerof a straight also being found as a multiple of any one rank. This instruction will bechanged, eventually, because we want to test for straight flush and so on. But for now thiswill make it possible to debug .

I picked those examples pretty much at random. It’s a good idea, when testing aprocedure, to pick test cases “near the boundaries” of what the program is supposed toaccept. For example, what about an ace-low straight, or a king-high? What about a handin which “the next four ranks” don’t exist, because the lowest card is a Jack?

(Actually, that last example may never invoke at all, if the test forcomes first in .) Anyway, it looks okay. I could try more examples but I thinkI believe it. I now decide that the instruction I just put into should be

Page 18: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

250 Chapter 13 Planning

if flushp

nogap straightp

pokerhand

pokerhand [3h 6h ah kh 7h]

pokerhand [3h 6h ad kh 7h]

pokerhand [3h 6h 4h 5h 7h]

pokerhand [3h 6h 4h 5s 7h]

and that it should be followed by

(The instruction has to come second because of the principle of “mostrestrictive first.” If that test came first, a straight flush would be reported as just a flush.)Time for more tests:

Now it’s time to solve the problem of the ace-high straight. It turns out to be easy; ifthe hand has an ace, then I can use , the subprocedure of that checksfor consecutive ranks, to check for the four ranks from 10 to king.

That’s the end of the categories of poker hands, but to put it all together requires alittle editing of :

if flushp [print [flush] stop]

?flush?something else?straight flush?straight

to ace.highpif not equalp (item 1 :rankarray) 1 [output "false]output nogap 10 4end

to pokerhand :cardslocal [ranks suits rankarray]poker.init :cardsif fourp [print [four of a kind] stop]if full.housep [print [full house] stop]if threep [print [three of a kind] stop]if pairp [print ifelse paircount = 1 [[one pair]] [[two pairs]] stop]if ace.highp [print ifelse flushp [[royal flush]] [[straight]] stop]if straightp [print ifelse flushp [[straight flush]] [[straight]] stop]if flushp [print [flush] stop]print [nothing!]end

Page 19: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

Embellishments

Embellishments 251

pokerhand [ah 7s 3d 10c 7c]

pair of sixes one pair

sixes 6

pokerhand

one pair twopairs

nothing queen high

if pokerhand threep

to plural :rankoutput item :rank [aces twos threes fours fives sixes

sevens eights nines tens jacks queens kings]end

if pairp [print ifelse paircount = 1[sentence [pair of] plural first locate 2 1][[two pairs]]

stop]

?pair of sevens

I’ve now done more or less what I set out to do. It took 14 procedures. I hope you havea feeling for the process of switching back and forth between thinking about a particularsubproblem and thinking about the overall structure of the program.

I haven’t done every detail of what I first suggested. In particular, I don’t have theinformation about particular ranks in what I print. I think perhaps that’s more effortthan this project seems worth to me. (I’m not just being cute by saying “to me”; thepoint is that a real poker enthusiast might want to spend a lot of time on this programand make it as beautiful as possible.) But just to show how a completed program can bemodified, I’ll make it print things like instead of just .

First I have to be able to find words like “ ” starting with a rank indicator like .

The next step is to change one instruction in to use this new tool:

(If you were confused about the double square brackets around andbefore, seeing this new version in which one of the possibilities is the output from

a procedure, not a literal list, might help.)

If you’re motivated, you can modify the messages for other categories to include thespecific rank information. You might want to change “ ” to “ ,” forexample.

What if you wanted to use this program on a seven-card-stud hand? In other words,instead of a list of five cards, you’d be given a list of seven, from which you’d have to pickthe best five. The main thing I can think of is that you’d have to be more careful aboutthe order of the instructions in . I’ve said that you can test either

Page 20: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

play

252 Chapter 13 Planning

Putting the Project in a Context

straightp

pokerhand

[5 7 10]

first

poker

[3h 3s 3d 4d 5s 6h 7c]

[8s 9s 10s js qs kh ad]

to pokerdeal.cardsbiddraw.more.cardsbidpokerhandend

before or after because they can’t both be true. But that’s not the case for aseven-card hand:

If you try this challenge, make sure your program announces

as a straight flush, not as an ace-high straight.

I wrote this program because I was looking for an example for this book that would benot too long, not too short. That’s kind of an artificial reason for starting a project. Inreal life, if I wrote a program like this one, it would be part of a larger program that wouldactually poker.

In that context the problem would become very different. We wouldn’t want merelyto print the designation of a hand; we’d want to be able to compare several hands andannounce a winner. To do that, we’d have to attach something like a numerical rankingto the hand, which might become the output from . But it can’t be just asingle number; there are too many possible hands to have a list of all of them in rankorder. Instead, the ranking of a hand might be a list of numbers. might meanthat the hand is a full house (I’m guessing that that would rank about fifth in value), withthree sevens and two tens. To compare two lists of numbers, compare their s; ifthose are equal, go on to compare the next members.

The point is that I’m now back to something approaching top-down planning. As thescale of the project becomes a lot bigger, that kind of advance planning seems necessary.But this isn’t really top-down because comparing two hands is just one subproblem ofplaying poker. Really, according to the top-down view, I should start by designing thetop-level procedure . Perhaps a first attempt might look like this:

Page 21: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

pokerhand

Program Listing

Program Listing 253

But it would be premature to type this into a computer. We have to think about issueslike these: Is the computer a player or does it just deal and bank for the other players?How many people can play? What is a good strategy for bidding?

In the end it might turn out that the we’ve just written wouldn’t fitinto the larger project; it might have to be rewritten for that context. To a structuredprogrammer, the effort we’ve put in would then be wasted. But I think that even if everyprocedure had to be edited, I’d benefit from having taken the time to understand howto solve this subproblem.

to pokerhand :cardslocal [ranks suits rankarray]poker.init :cardsif fourp [print [four of a kind] stop]if full.housep [print [full house] stop]if threep [print [three of a kind] stop]if pairp [print ifelse paircount = 1 [[one pair]] [[two pairs]] stop]if ace.highp [print ifelse flushp [[royal flush]] [[straight]] stop]if straightp [print ifelse flushp [[straight flush]] [[straight]] stop]if flushp [print [flush] stop]print [nothing!]end

to poker.init :cardsmake "ranks map [ranknum butlast ?] :cardsmake "suits remdup map "last :cardsmake "rankarray {0 0 0 0 0 0 0 0 0 0 0 0 0}foreach :ranks [setitem ? :rankarray (item ? :rankarray)+1]end

to ranknum :rankif :rank = "a [output 1]if :rank = "j [output 11]if :rank = "q [output 12]if :rank = "k [output 13]output :rankend

to fourpoutput memberp 4 :rankarrayend

Page 22: 13 Planning - Peoplebh/pdf/v1ch13.pdfyou want to write a program to play tic-tac-toe, as I did in Chapter 6. You can start by saying, “Let’s see if I can draw the board.” You’d

254 Chapter 13 Planning

to threepoutput memberp 3 :rankarrayend

to pairpoutput memberp 2 :rankarrayend

to full.housepoutput and threep pairpend

to paircountoutput count locate 2 1end

to locate :number :indexif :index > 13 [output []]if (item :index :rankarray) = :number ~

[output fput :index (locate :number :index+1)]output locate :number :index+1end

to flushpoutput emptyp butfirst :suitsend

to straightpoutput nogap (reduce "min :ranks) 5end

to min :a :boutput ifelse :a < :b [:a] [:b]end

to nogap :smallest :howmanyif :howmany=0 [output "true]if not equalp (item :smallest :rankarray) 1 [output "false]output nogap :smallest+1 :howmany-1end

to ace.highpif not equalp (item 1 :rankarray) 1 [output "false]output nogap 10 4end