<#68435#>;; <#46454#>Data Analysis and Definitions<#46454#>:<#68435#>
<#71776#>;; A <#68436#><#46455#>letter<#46455#><#68436#> is a symbol in: <#68437#><#46456#>'<#46456#><#46457#>a<#46457#> <#46458#>...<#46458#> <#46459#>'<#46459#><#46460#>z<#46460#><#68437#> plus <#68438#><#46461#>'<#46461#><#46462#>_<#46462#><#68438#><#71776#>
<#71777#>;; A <#68439#><#46463#>word<#46463#><#68439#> is a (listof letter).<#71777#>
<#71778#>;; A <#68440#><#46464#>body-part<#46464#><#68440#> is one of the following symbols:<#71778#>
<#46465#>(define<#46465#> <#46466#>PARTS<#46466#> <#46467#>'<#46467#><#46468#>(head<#46468#> <#46469#>body<#46469#> <#46470#>right-arm<#46470#> <#46471#>left-arm<#46471#> <#46472#>right-leg<#46472#> <#46473#>left-leg))<#46473#>
<#68441#>;; <#46474#>Constants<#46474#>:<#68441#>
<#46475#>;; some guessing words: <#46475#>
<#46476#>(d<#46476#><#46477#>efine<#46477#> <#46478#>WORDS<#46478#>
<#46479#>'<#46479#><#46480#>(<#46480#><#46481#>(h<#46481#> <#46482#>e<#46482#> <#46483#>l<#46483#> <#46484#>l<#46484#> <#46485#>o)<#46485#>
<#46486#>(w<#46486#> <#46487#>o<#46487#> <#46488#>r<#46488#> <#46489#>l<#46489#> <#46490#>d)<#46490#>
<#46491#>(i<#46491#> <#46492#>s)<#46492#>
<#46493#>(a)<#46493#>
<#46494#>(s<#46494#> <#46495#>t<#46495#> <#46496#>u<#46496#> <#46497#>p<#46497#> <#46498#>i<#46498#> <#46499#>d)<#46499#>
<#46500#>(p<#46500#> <#46501#>r<#46501#> <#46502#>o<#46502#> <#46503#>g<#46503#> <#46504#>r<#46504#> <#46505#>a<#46505#> <#46506#>m)<#46506#>
<#46507#>...<#46507#>
<#46508#>))<#46508#>
<#46509#>;; the number of words we can choose from <#46509#>
<#46510#>(define<#46510#> <#46511#>WORDS#<#46511#> <#46512#>(length<#46512#> <#46513#>WORDS))<#46513#>
<#46517#>Figure: Hangman Basics<#46517#>
Recall the hangman game from #sechangman#46519>. The goal of the game is to
test a person's active vocabulary. One player thinks of a word and draws
the noose of a gallows; the other player tries to guess the word, one
letter at a time. For every wrong guess, the first player adds another
part to the drawing (see figure~#fighangman#46520>): first the head, then
the body, the arms, and the legs. If, however, the player's guess reveals
new knowledge about the chosen word, the first player indicates where the
the letter occurs in the word. The game is over when the second player
guesses the complete word or when the first player has completed the stick
figure.
Figure~#fighangmanstate#46521> contains the data definitions for letters,
words, and body-parts. In particular, <#68442#><#46522#>PARTS<#46522#><#68442#> not only specifies the
body parts that are drawn, but also the order in which they are drawn. The
figure also defines an incomplete list of words so that the hangman program
can randomly pick a word for us to guess.
The random picking of words occurs at the beginning of the game, which
suggests a random initialization function, similar to that of the
color-guessing program in the preceding section. In contrast to the
latter, the hangman program must also remember the number of guesses that a
player made, because there is only a limited number of them. After
<#68443#><#46523#>'<#46523#><#46524#>left-leg<#46524#><#68443#> is drawn, the game is over. Counting down the number of
body parts also implies that as the program checks each guess, it must
inform the player not only what the guess revealed but also which body
part, if any, was lost.
Let us capture this thought in a data definition that specifies the legitimate
class of responses:
A <#68444#><#46526#>response<#46526#><#68444#> is either
- <#68445#><#46528#>``You<#46528#>\ <#46529#>won''<#46529#><#68445#>
- <#68446#><#46530#>(list<#46530#>\ <#46531#>``The<#46531#>\ <#46532#>End''<#46532#>\ <#46533#>word)<#46533#><#68446#>
- <#68447#><#46534#>(list<#46534#>\ <#46535#>``Good<#46535#>\ <#46536#>guess!''<#46536#>\ <#46537#>word)<#46537#><#68447#>
- <#68448#><#46538#>(list<#46538#>\ <#46539#>``Sorry''<#46539#>\ <#46540#>body-part<#46540#>\ <#46541#>word)<#46541#><#68448#>
Three of the responses are lists so that the program can provide
several pieces of information at once. Specifically, the first response
says that filling in the guess turns the status word into the chosen word
and that the player survived the game. The second response indicates the
opposite; the list of available body parts is exhausted and the game is
over because the player did not guess all the letters in the word. In the
third case, the player's guess was successful and the second item in the
list shows how much the player knows about the word. Finally, the fourth
response represents a bad guess, in which case the response is a list of
three items: a greeting, the lost body part, and a reminder of what the
player has found about the word.
We can now imagine the role of the two services in the <#46544#>hangman<#46544#>
program. The first, called <#68449#><#46545#>hangman<#46545#><#68449#>, picks a new word; the second,
called <#68450#><#46546#>hangman-guess<#46546#><#68450#>, consumes a letter and produces one of the four
possible responses. Here is a feasible dialog:
<#46551#>;SPMgt;<#46551#> <#46552#>(hangman)<#46552#>
<#46553#>;SPMgt;<#46553#> <#46554#>(hangman-guess<#46554#> <#46555#>'<#46555#><#46556#>a)<#46556#>
<#46557#>(list<#46557#> <#46558#>``Sorry''<#46558#> <#46559#>'<#46559#><#46560#>head<#46560#> <#46561#>(list<#46561#> <#46562#>'<#46562#><#46563#>_<#46563#> <#46564#>'<#46564#><#46565#>_<#46565#> <#46566#>'<#46566#><#46567#>_<#46567#> <#46568#>'<#46568#><#46569#>_<#46569#> <#46570#>'<#46570#><#46571#>_<#46571#> <#46572#>'<#46572#><#46573#>_<#46573#><#46574#>))<#46574#>
<#46575#>;SPMgt;<#46575#> <#46576#>(hangman-guess<#46576#> <#46577#>'<#46577#><#46578#>i)<#46578#>
<#46579#>(list<#46579#> <#46580#>``Good<#46580#> <#46581#>guess!''<#46581#> <#46582#>(list<#46582#> <#46583#>'<#46583#><#46584#>_<#46584#> <#46585#>'<#46585#><#46586#>_<#46586#> <#46587#>'<#46587#><#46588#>_<#46588#> <#46589#>'<#46589#><#46590#>_<#46590#> <#46591#>'<#46591#><#46592#>i<#46592#> <#46593#>'<#46593#><#46594#>_<#46594#><#46595#>))<#46595#>
<#46596#>;SPMgt;<#46596#> <#46597#>(hangman-guess<#46597#> <#46598#>'<#46598#><#46599#>s)<#46599#>
<#46600#>(list<#46600#> <#46601#>``Good<#46601#> <#46602#>guess!''<#46602#> <#46603#>(list<#46603#> <#46604#>'<#46604#><#46605#>s<#46605#> <#46606#>'<#46606#><#46607#>_<#46607#> <#46608#>'<#46608#><#46609#>_<#46609#> <#46610#>'<#46610#><#46611#>_<#46611#> <#46612#>'<#46612#><#46613#>i<#46613#> <#46614#>'<#46614#><#46615#>_<#46615#><#46616#>))<#46616#>
<#46617#>;SPMgt;<#46617#> <#46618#>(hangman-guess<#46618#> <#46619#>'<#46619#><#46620#>i)<#46620#>
<#46621#>(list<#46621#> <#46622#>``Sorry''<#46622#> <#46623#>'<#46623#><#46624#>body<#46624#> <#46625#>(list<#46625#> <#46626#>'<#46626#><#46627#>s<#46627#> <#46628#>'<#46628#><#46629#>_<#46629#> <#46630#>'<#46630#><#46631#>_<#46631#> <#46632#>'<#46632#><#46633#>_<#46633#> <#46634#>'<#46634#><#46635#>i<#46635#> <#46636#>'<#46636#><#46637#>_<#46637#><#46638#>))<#46638#>
<#46639#>...<#46639#>
<#46640#>;SPMgt;<#46640#> <#46641#>(hangman)<#46641#>
<#46642#>;SPMgt;<#46642#> <#46643#>(hangman-guess<#46643#> <#46644#>'<#46644#><#46645#>a)<#46645#>
<#46646#>``You<#46646#> <#46647#>won''<#46647#>
The dialog consists of two rounds of hangman. They show that the results of
<#68451#><#46651#>hangman-guess<#46651#><#68451#> depend on the prior use of
<#68452#><#46652#>hangman<#46652#><#68452#>. Furthermore, the first round illustrates how
<#68453#><#46653#>hangman-guess<#46653#><#68453#> applied to the same guess twice produces two different
answers. This again means that <#68454#><#46654#>hangman-guess<#46654#><#68454#> modifies and uses
memory, specifically, it counts down the body parts as the player makes
useless guesses.
In addition, the dialog shows that the player loses a body part whenever a
guess doesn't contribute any new knowledge. Consider the second guess:
<#68455#><#46655#>'<#46655#><#46656#>i<#46656#><#68455#>. It occurs in the penultimate position of the word and the
response of <#68456#><#46657#>hangman-guess<#46657#><#68456#> says so. When the player enters
<#68457#><#46658#>'<#46658#><#46659#>i<#46659#><#68457#> again as the fourth guess, <#68458#><#46660#>hangman-guess<#46660#><#68458#> detects no
progress because the positions of <#68459#><#46661#>'<#46661#><#46662#>i<#46662#><#68459#> have already been
revealed. In the informal description of the game, this aspect had been
left open. By putting together an example, we become aware of this
ambiguity and can make a decision.
<#71779#>;; <#68460#><#46667#>chosen-word<#46667#> <#46668#>:<#46668#> <#46669#>word<#46669#><#68460#><#71779#>
<#46670#>;; the word that the player is to guess<#46670#>
<#46671#>(define<#46671#> <#46672#>chosen-word<#46672#> <#46673#>(first<#46673#> <#46674#>WORDS))<#46674#>
<#71780#>;; <#68461#><#46675#>status-word<#46675#> <#46676#>:<#46676#> <#46677#>word<#46677#><#68461#><#71780#>
<#46678#>;; represents which letters the player has and hasn't guessed<#46678#>
<#46679#>(define<#46679#> <#46680#>status-word<#46680#> <#46681#>(first<#46681#> <#46682#>WORDS))<#46682#>
<#71781#>;; <#68462#><#46683#>body-parts-left<#46683#> <#46684#>:<#46684#> <#46685#>(listof<#46685#> <#46686#>body-part)<#46686#><#68462#><#71781#>
<#46687#>;; represents the list of body parts that are still ;SPMquot;available;SPMquot;<#46687#>
<#46688#>(define<#46688#> <#46689#>body-parts-left<#46689#> <#46690#>PARTS)<#46690#>
<#71782#>;; <#68463#><#46691#>hangman<#46691#> <#46692#>:<#46692#> <#46693#><#46693#><#46694#>-;SPMgt;<#46694#><#46695#><#46695#> <#46696#>void<#46696#><#68463#><#71782#>
<#71783#>;; effect: initialize <#68464#><#46697#>chosen-word<#46697#><#68464#>, <#68465#><#46698#>status-word<#46698#><#68465#>, and <#68466#><#46699#>body-parts-left<#46699#><#68466#><#71783#>
<#46700#>(d<#46700#><#46701#>efine<#46701#> <#46702#>(hangman)<#46702#>
<#46703#>(b<#46703#><#46704#>egin<#46704#>
<#46705#>(set!<#46705#> <#46706#>chosen-word<#46706#> <#46707#>(list-ref<#46707#> <#46708#>WORDS<#46708#> <#46709#>(random<#46709#> <#46710#>(length<#46710#> <#46711#>WORDS))))<#46711#>
<#46712#>(set!<#46712#> <#46713#>status-word<#46713#> <#46714#>...)<#46714#>
<#46715#>(set!<#46715#> <#46716#>body-parts-left<#46716#> <#46717#>PARTS)))<#46717#>
<#46721#>Figure: Hangman Basics (Part 2)<#46721#>
Thus far, our reasoning has revealed the need for two services and three
state variables:
-
- <#68467#><#46724#>chosen-word<#46724#><#68467#>, which is the word to be guessed;
-
- <#68468#><#46725#>status-word<#46725#><#68468#>, which records how much of the word has been
guessed;
-
- and <#68469#><#46726#>body-parts-left<#46726#><#68469#>, which remembers how many, and which,
imaginary body parts the player can still lose.
The first two variables always stand for <#68470#><#46728#>word<#46728#><#68470#>s, as their name
says. A natural value for the last one is a list of body parts; indeed, the
list should always be a suffix of <#68471#><#46729#>PARTS<#46729#><#68471#>.
Figure~#fighangmanstate2#46730> contains the definitions of the state variables
and their purpose statements. The first two, <#68472#><#46731#>chosen-word<#46731#><#68472#> and
<#68473#><#46732#>status-word<#46732#><#68473#>, are set to the first items if WORDS, so that they
represent some word. The third one is set to <#68474#><#46733#>PARTS<#46733#><#68474#> because this
list represents the entire collection of available body parts.
Next we must develop an initializer for the state variables. As in the
preceding section, a single initializer suffices. It is the
<#68475#><#46734#>hangman<#46734#><#68475#> function, and its purpose is to set up the program's
memory. Specifically, it picks a word for <#68476#><#46735#>chosen-word<#46735#><#68476#>, and it sets
<#68477#><#46736#>status-word<#46736#><#68477#> and <#68478#><#46737#>body-parts-left<#46737#><#68478#> to values that reflect
that the game has just begun. For the last one, this is easy because
<#68479#><#46738#>PARTS<#46738#><#68479#> is the appropriate list. The initial value for
<#68480#><#46739#>status-word<#46739#><#68480#> requires a short analysis. First, the value must be a
word. Second, it must consist of as many letters as
<#68481#><#46740#>chosen-word<#46740#><#68481#>. Finally, each of the letters is unknown, because the
player hasn't made any guesses yet. Thus, the matching action is a build a
word as long as <#68482#><#46741#>chosen-word<#46741#><#68482#> but to use <#68483#><#46742#>'<#46742#><#46743#>_<#46743#><#68483#> as the letters.
<#46746#>Exercise 37.2.1<#46746#>
Develop the function <#68484#><#46748#>make-status-word<#46748#><#68484#>, which consumes a word and
produces an equally long word consisting of just <#68485#><#46749#>'<#46749#><#46750#>_<#46750#><#68485#>. Use the
function to complete the definition of <#68486#><#46751#>hangman<#46751#><#68486#> in
figure~#fighangmanstate2#46752>.~ Solution<#68487#><#68487#>
<#46758#>Exercise 37.2.2<#46758#>
Use <#68488#><#46760#>build-list<#46760#><#68488#> to create the status word in a single
expression. Complete the definition of <#68489#><#46761#>hangman<#46761#><#68489#> in
figure~#fighangmanstate2#46762>.~ Solution<#68490#><#68490#>
Now we are ready to deal with the most difficult part: the design of
<#68491#><#46770#>hangman-guess<#46770#><#68491#>, a function that uses and modifies the memory. It
consumes a letter and produces an answer, specifically a <#68492#><#46771#>response<#46771#><#68492#>,
which depends on how the current value of <#68493#><#46772#>status-word<#46772#><#68493#>,
<#68494#><#46773#>chosen-word<#46773#><#68494#>, and <#68495#><#46774#>guess<#46774#><#68495#> compare. At the same time, the
function must affect the state variable <#68496#><#46775#>status-word<#46775#><#68496#> if the
player's guess added new knowledge. If not, the function must shorten
<#68497#><#46776#>body-parts-left<#46776#><#68497#>, the list of available body parts. The matching
contract, purpose and effect statements are as follows:
<#71784#>;; <#68498#><#46781#>hangman-guess<#46781#> <#46782#>:<#46782#> <#46783#>letter<#46783#> <#46784#><#46784#><#46785#>-;SPMgt;<#46785#><#46786#><#46786#> <#46787#>response<#46787#><#68498#><#71784#>
<#46788#>;; to determine whether the player has won, lost, or may continue to<#46788#>
<#46789#>;; play and, if no progress was made, which body part was lost<#46789#>
<#46790#>;; effect:<#46790#>
<#71785#>;; (1) if the guess represents progress, update <#68499#><#46791#>status-word<#46791#><#68499#><#71785#>
<#71786#>;; (2) if not, shorten the <#68500#><#46792#>body-parts-left<#46792#><#68500#> by one <#71786#>
We have already considered a sample dialog that illustrates the working of
<#68501#><#46796#>hangman-guess<#46796#><#68501#>. By dissecting this dialog, we can develop specific
examples for <#68502#><#46797#>hangman-guess<#46797#><#68502#>.
The sample dialog and the purpose/effect statements imply that
the result of <#68503#><#46798#>hangman-guess<#46798#><#68503#> depends on whether or not the guess
constitutes progress and, if not, whether or not the guess was the last
one. Let's use these distinctions for the development of examples:
-
If <#68504#><#46800#>status-word<#46800#><#68504#> is <#68505#><#46801#>(list<#46801#>\ <#46802#>'<#46802#><#46803#>b<#46803#>\ <#46804#>'<#46804#><#46805#>_<#46805#>\ <#46806#>'<#46806#><#46807#>_<#46807#>\ <#46808#>'<#46808#><#46809#>_<#46809#><#46810#>)<#46810#><#68505#> and
<#68506#><#46811#>chosen-word<#46811#><#68506#> is <#68507#><#46812#>(list<#46812#>\ <#46813#>'<#46813#><#46814#>b<#46814#>\ <#46815#>'<#46815#><#46816#>a<#46816#>\ <#46817#>'<#46817#><#46818#>l<#46818#>\ <#46819#>'<#46819#><#46820#>l)<#46820#><#68507#>, then evaluating
<#46825#>(hangman-guess<#46825#> <#46826#>'<#46826#><#46827#>l)<#46827#>
produces <#68508#><#46831#>(list<#46831#>\ <#46832#>``Good<#46832#>\ <#46833#>guess!''<#46833#>\ <#46834#>(list<#46834#>\ <#46835#>'<#46835#><#46836#>b<#46836#>\ <#46837#>'<#46837#><#46838#>_<#46838#>\ <#46839#>'<#46839#><#46840#>l<#46840#>\ <#46841#>'<#46841#><#46842#>l))<#46842#><#68508#> and
<#68509#><#46843#>status-word<#46843#><#68509#> becomes <#68510#><#46844#>(list<#46844#>\ <#46845#>'<#46845#><#46846#>b<#46846#>\ <#46847#>'<#46847#><#46848#>_<#46848#>\ <#46849#>'<#46849#><#46850#>l<#46850#>\ <#46851#>'<#46851#><#46852#>l)<#46852#><#68510#>.
-
If <#68511#><#46853#>status-word<#46853#><#68511#> is <#68512#><#46854#>(list<#46854#>\ <#46855#>'<#46855#><#46856#>b<#46856#>\ <#46857#>'<#46857#><#46858#>_<#46858#>\ <#46859#>'<#46859#><#46860#>l<#46860#>\ <#46861#>'<#46861#><#46862#>l)<#46862#><#68512#> and
<#68513#><#46863#>chosen-word<#46863#><#68513#> is <#68514#><#46864#>(list<#46864#>\ <#46865#>'<#46865#><#46866#>b<#46866#>\ <#46867#>'<#46867#><#46868#>a<#46868#>\ <#46869#>'<#46869#><#46870#>l<#46870#>\ <#46871#>'<#46871#><#46872#>l)<#46872#><#68514#>, then evaluating
<#46877#>(hangman-guess<#46877#> <#46878#>'<#46878#><#46879#>a)<#46879#>
produces <#68515#><#46883#>``You<#46883#>\ <#46884#>won''<#46884#><#68515#>. The evaluation has no effect in this case.
-
If <#68516#><#46885#>status-word<#46885#><#68516#> is <#68517#><#46886#>(list<#46886#>\ <#46887#>'<#46887#><#46888#>b<#46888#>\ <#46889#>'<#46889#><#46890#>_<#46890#>\ <#46891#>'<#46891#><#46892#>l<#46892#>\ <#46893#>'<#46893#><#46894#>l)<#46894#><#68517#>,
<#68518#><#46895#>chosen-word<#46895#><#68518#> is <#68519#><#46896#>(list<#46896#>\ <#46897#>'<#46897#><#46898#>b<#46898#>\ <#46899#>'<#46899#><#46900#>a<#46900#>\ <#46901#>'<#46901#><#46902#>l<#46902#>\ <#46903#>'<#46903#><#46904#>l)<#46904#><#68519#>, and
<#68520#><#46905#>body-parts-left<#46905#><#68520#> is <#68521#><#46906#>(list<#46906#>\ <#46907#>'<#46907#><#46908#>right-leg<#46908#>\ <#46909#>'<#46909#><#46910#>left-leg)<#46910#><#68521#>,
then evaluating
<#46915#>(hangman-guess<#46915#> <#46916#>'<#46916#><#46917#>l)<#46917#>
produces <#68522#><#46921#>(list<#46921#>\ <#46922#>``Sorry''<#46922#>\ <#46923#>'<#46923#><#46924#>right-leg<#46924#>\ <#46925#>(list<#46925#>\ <#46926#>'<#46926#><#46927#>b<#46927#>\ <#46928#>'<#46928#><#46929#>_<#46929#>\ <#46930#>'<#46930#><#46931#>l<#46931#>\ <#46932#>'<#46932#><#46933#>l))<#46933#><#68522#> and
<#68523#><#46934#>body-parts-left<#46934#><#68523#> becomes <#68524#><#46935#>(list<#46935#>\ <#46936#>'<#46936#><#46937#>left-leg)<#46937#><#68524#>.
-
Finally, if <#68525#><#46938#>status-word<#46938#><#68525#> is <#68526#><#46939#>(list<#46939#>\ <#46940#>'<#46940#><#46941#>b<#46941#>\ <#46942#>'<#46942#><#46943#>_<#46943#>\ <#46944#>'<#46944#><#46945#>l<#46945#>\ <#46946#>'<#46946#><#46947#>l)<#46947#><#68526#>,
<#68527#><#46948#>chosen-word<#46948#><#68527#> is <#68528#><#46949#>(list<#46949#>\ <#46950#>'<#46950#><#46951#>b<#46951#>\ <#46952#>'<#46952#><#46953#>a<#46953#>\ <#46954#>'<#46954#><#46955#>l<#46955#>\ <#46956#>'<#46956#><#46957#>l)<#46957#><#68528#>, and
<#68529#><#46958#>body-parts-left<#46958#><#68529#> is <#68530#><#46959#>(list<#46959#>\ <#46960#>'<#46960#><#46961#>left-leg)<#46961#><#68530#>,
then evaluating
<#46966#>(hangman-guess<#46966#> <#46967#>'<#46967#><#46968#>l)<#46968#>
produces <#68531#><#46972#>(list<#46972#>\ <#46973#>``The<#46973#>\ <#46974#>End''<#46974#>\ <#46975#>(list<#46975#>\ <#46976#>'<#46976#><#46977#>b<#46977#>\ <#46978#>'<#46978#><#46979#>a<#46979#>\ <#46980#>'<#46980#><#46981#>l<#46981#>\ <#46982#>'<#46982#><#46983#>l))<#46983#><#68531#> and
<#68532#><#46984#>body-parts-left<#46984#><#68532#> becomes <#68533#><#46985#>empty<#46985#><#68533#>.
The first two examples illustrate what happens when the player enters a
guess that reveals new information; the last two focus on those cases where
the guess contributes nothing.
The case split naturally suggests a basic template based on a distinction
among the possible situations:
<#46991#>(d<#46991#><#46992#>efine<#46992#> <#46993#>(hangman-guess<#46993#> <#46994#>guess)<#46994#>
<#46995#>(c<#46995#><#46996#>ond<#46996#>
<#46997#>[<#46997#><#46998#>.<#46998#><#46999#>..<#46999#> <#71787#>;; <#68534#><#47000#>guess<#47000#><#68534#> did reveal new information: <#71787#>
<#47001#>(c<#47001#><#47002#>ond<#47002#>
<#47003#>[<#47003#><#47004#>...<#47004#> <#47005#>;; guess completed the search for the word<#47005#>
<#47006#>...]<#47006#>
<#47007#>[<#47007#><#47008#>.<#47008#><#47009#>..<#47009#> <#68535#>;; guess did <#47010#>not<#47010#> complete the search for the word<#68535#>
<#47011#>(b<#47011#><#47012#>egin<#47012#>
<#47013#>(set!<#47013#> <#47014#>status-word<#47014#> <#47015#>...)<#47015#>
<#47016#>...)]<#47016#><#47017#>)]<#47017#>
<#47018#>[<#47018#><#47019#>...<#47019#> <#71788#>;; <#68536#><#47020#>guess<#47020#><#68536#> did <#47021#>not<#47021#> reveal any new information: <#71788#>
<#47022#>(b<#47022#><#47023#>egin<#47023#>
<#47024#>(set!<#47024#> <#47025#>body-parts-left<#47025#> <#47026#>...)<#47026#>
<#47027#>...<#47027#> <#47028#>)]<#47028#><#47029#>))<#47029#>
The location of the <#68537#><#47033#>set!<#47033#>-expression<#68537#>s in the template's nested
<#68538#><#47034#>cond<#47034#><#68538#>s specify exactly under which conditions effects happen.
First, the outermost conditional distinguishes whether or not
<#68539#><#47035#>guess<#47035#><#68539#> produces new knowledge about the hidden word; if it
doesn't, the function must modify <#68540#><#47036#>body-parts-left<#47036#><#68540#>. Second, if
<#68541#><#47037#>guess<#47037#><#68541#> reveals new knowledge, the function updates the
<#68542#><#47038#>status-word<#47038#><#68542#> variable unless the player has just finished the
entire word.
Because we haven't considered yet how to express these tests, we use
comments to indicate what the conditions are. Let us turn to this problem
first, so that we can start the function-definition step with a
full-fledged template. The first missing condition concerns the question
whether <#68543#><#47039#>guess<#47039#><#68543#> reveals new information. To this end, we must
compare <#68544#><#47040#>guess<#47040#><#68544#> with the letters in <#68545#><#47041#>chosen-word<#47041#><#68545#>. This
comparison should produce the new status word. Here is the specification
for the auxiliary function that conducts this computation:
<#71789#>;; <#68546#><#47046#>reveal-list<#47046#> <#47047#>:<#47047#> <#47048#>word<#47048#> <#47049#>word<#47049#> <#47050#>letter<#47050#> <#47051#><#47051#><#47052#>-;SPMgt;<#47052#><#47053#><#47053#> <#47054#>word<#47054#><#68546#><#71789#>
<#71790#>;; to compute the new status word from <#68547#><#47055#>chosen-word<#47055#><#68547#>,<#71790#>
<#71791#>;; <#68548#><#47056#>status-word<#47056#><#68548#>, and <#68549#><#47057#>guess<#47057#><#68549#><#71791#>
<#47058#>(define<#47058#> <#47059#>(reveal-list<#47059#> <#47060#>chosen-word<#47060#> <#47061#>status-word<#47061#> <#47062#>guess)<#47062#> <#47063#>...)<#47063#>
Fortunately, we have discussed this auxiliary function twice before (see
sections~#sechangman#47067> and exercise~#exhangmanlist#47068>) and know how to
define it; figure~#fighangmanstate3#47069> contains a suitable
definition. Using <#68550#><#47070#>reveal-list<#47070#><#68550#>, we can now formulate a condition that
determines whether <#68551#><#47071#>guess<#47071#><#68551#> reveals new knowledge:
<#47076#>(equal?<#47076#> <#47077#>status-word<#47077#> <#47078#>(reveal-list<#47078#> <#47079#>status-word<#47079#> <#47080#>chosen-word<#47080#> <#47081#>guess))<#47081#>
The condition uses <#68552#><#47085#>equal?<#47085#><#68552#> to compare the current value of
<#68553#><#47086#>status-word<#47086#><#68553#> with its new value, as computed by
<#68554#><#47087#>reveal-list<#47087#><#68554#>. If the two lists are equal, <#68555#><#47088#>guess<#47088#><#68555#> doesn't
produce new knowledge; otherwise it does.
The second missing condition concerns the question whether the given <#68556#><#47089#>guess<#47089#><#68556#>
completes the search for the word. If <#68557#><#47090#>guess<#47090#><#68557#> is equal to all missing
letters in <#68558#><#47091#>status-word<#47091#><#68558#>, then the player has found the complete
word. Here is the corresponding condition:
<#47096#>(equal?<#47096#> <#47097#>chosen-word<#47097#> <#47098#>(reveal-list<#47098#> <#47099#>status-word<#47099#> <#47100#>chosen-word<#47100#> <#47101#>guess))<#47101#>
That is, the game is over if <#68559#><#47105#>chosen-word<#47105#><#68559#> is equal to the result of
<#68560#><#47106#>reveal-list<#47106#><#68560#>.
Let's put everything together in a single template:
<#47111#>(d<#47111#><#47112#>efine<#47112#> <#47113#>(hangman-guess<#47113#> <#47114#>guess)<#47114#>
<#47115#>(l<#47115#><#47116#>ocal<#47116#> <#47117#>((define<#47117#> <#47118#>new-status<#47118#> <#47119#>(reveal-list<#47119#> <#47120#>status-word<#47120#> <#47121#>chosen-word<#47121#> <#47122#>guess)))<#47122#>
<#47123#>(c<#47123#><#47124#>ond<#47124#>
<#47125#>[<#47125#><#47126#>(equal?<#47126#> <#47127#>new-status<#47127#> <#47128#>status-word)<#47128#>
<#47129#>(b<#47129#><#47130#>egin<#47130#>
<#47131#>(set!<#47131#> <#47132#>body-parts-left<#47132#> <#47133#>...)<#47133#>
<#47134#>...<#47134#> <#47135#>)]<#47135#>
<#47136#>[<#47136#><#47137#>else<#47137#>
<#47138#>(c<#47138#><#47139#>ond<#47139#>
<#47140#>[<#47140#><#47141#>(equal?<#47141#> <#47142#>new-status<#47142#> <#47143#>chosen-word)<#47143#>
<#47144#>...]<#47144#>
<#47145#>[<#47145#><#47146#>else<#47146#>
<#47147#>(b<#47147#><#47148#>egin<#47148#>
<#47149#>(set!<#47149#> <#47150#>status-word<#47150#> <#47151#>...)<#47151#>
<#47152#>...)]<#47152#><#47153#>)]<#47153#><#47154#>)))<#47154#>
The template uses a <#68561#><#47158#>local<#47158#>-expression<#68561#> because the result of
<#68562#><#47159#>reveal-list<#47159#><#68562#> is used in two conditions. Also, in this template the two
outer <#68563#><#47160#>cond<#47160#><#68563#>-clauses are swapped, because it is more natural to write
<#47165#>(equal?<#47165#> <#47166#>new-status<#47166#> <#47167#>status-word)<#47167#>
than its negation. We can now turn to the function-design step.
The template is conditional. So we develop the effect and the answer for
each clause separately:
- Assume that <#68564#><#47172#>(equal?<#47172#>\ <#47173#>new-status<#47173#>\ <#47174#>status-word)<#47174#><#68564#> evaluates to
<#68565#><#47175#>true<#47175#><#68565#>, that is, the player made no progress. This implies that the
player loses an imaginary body part. To capture this effect, the
<#47176#>set!<#47176#>-expression\ must change the value of
<#68566#><#47177#>body-parts-left<#47177#><#68566#>. Specifically, it must set the state variable to the
rest of its current value:
<#47182#>(set!<#47182#> <#47183#>body-parts-left<#47183#> <#47184#>(rest<#47184#> <#47185#>body-parts-left))<#47185#>
The answer depends on the new value of <#68567#><#47189#>body-parts-left<#47189#><#68567#>. If it is
<#68568#><#47190#>empty<#47190#><#68568#>, the game is over; the appropriate response is
<#68569#><#47191#>(list<#47191#>\ <#47192#>``The<#47192#>\ <#47193#>End''<#47193#>\ <#47194#>chosen-word)<#47194#><#68569#> so that the player finds out what
the chosen word was. If <#68570#><#47195#>body-parts-left<#47195#><#68570#> is not empty, the
response is <#68571#><#47196#>(list<#47196#>\ <#47197#>``Sorry''<#47197#>\ <#47198#>?<#47198#><#47199#>?<#47199#><#47200#>?<#47200#>\ <#47201#>status-word)<#47201#><#68571#>. The response says
that <#68572#><#47202#>guess<#47202#><#68572#> is useless. Its last part is the current value of
<#68573#><#47203#>status-word<#47203#><#68573#> so that the player sees what he has discovered. The
<#68574#><#47204#>?<#47204#><#47205#>?<#47205#><#47206#>?<#47206#><#68574#> indicates a problem. To understand the problem, take a look
at what we have:
<#47211#>(b<#47211#><#47212#>egin<#47212#>
<#47213#>(set!<#47213#> <#47214#>body-parts-left<#47214#> <#47215#>(rest<#47215#> <#47216#>body-parts-left))<#47216#>
<#47217#>(c<#47217#><#47218#>ond<#47218#>
<#47219#>[<#47219#><#47220#>(empty?<#47220#> <#47221#>body-parts-left)<#47221#> <#47222#>(list<#47222#> <#47223#>``The<#47223#> <#47224#>End''<#47224#> <#47225#>chosen-word)]<#47225#>
<#47226#>[<#47226#><#47227#>else<#47227#> <#47228#>(list<#47228#> <#47229#>``Sorry''<#47229#> <#47230#>?<#47230#><#47231#>?<#47231#><#47232#>?<#47232#> <#47233#>status-word)]<#47233#><#47234#>))<#47234#>
In principle, the question marks should be the body part that the player
just lost to the gallows. But, because <#68575#><#47238#>set!<#47238#><#68575#> modifies
<#68576#><#47239#>body-parts-left<#47239#><#68576#>, we can no longer just say <#68577#><#47240#>(first<#47240#><#47241#> <#47241#>\ <#47242#>body-parts-left)<#47242#><#68577#>. As mentioned in section~#secseqtime#47243>, when
programming with <#68578#><#47244#>set!<#47244#><#68578#> timing matters. We can solve the problem
with a <#68579#><#47245#>local<#47245#>-expression<#68579#>s that names the first item on
<#68580#><#47246#>body-parts-left<#47246#><#68580#> before the state variable is modified.
-
The second case is much simpler than the first. We distinguish two subcases:
-
If <#68581#><#47248#>new-status<#47248#><#68581#> is equal to <#68582#><#47249#>chosen-word<#47249#><#68582#>, the player has
won. The response is <#68583#><#47250#>``You<#47250#>\ <#47251#>won''<#47251#><#68583#>; there is no effect.
-
If the two are not equal, the player made some progress and must be
told. Furthermore, the function must keep track of the progress; a
<#68584#><#47252#>(set!<#47252#>\ <#47253#>status-word<#47253#>\ <#47254#>new-status)<#47254#><#68584#> accomplishes this effect.
The response consists of an encouragement and the new status.
Figure~#fighangmanstate3#47257> contains the complete definition of
<#68585#><#47258#>hangman-guess<#47258#><#68585#>.
<#71792#>;; <#68586#><#47263#>hangman-guess<#47263#> <#47264#>:<#47264#> <#47265#>letter<#47265#> <#47266#><#47266#><#47267#>-;SPMgt;<#47267#><#47268#><#47268#> <#47269#>response<#47269#><#68586#><#71792#>
<#47270#>;; to determine whether the player has won, lost, or may continue to play<#47270#>
<#47271#>;; and, if so, which body part was lost, if no progress was made<#47271#>
<#71793#>;; effects: (1) if the guess represents progress, update <#68587#><#47272#>status-word<#47272#><#68587#><#71793#>
<#71794#>;; (2) if not, shorten the <#68588#><#47273#>body-parts-left<#47273#><#68588#> by one <#71794#>
<#47274#>(d<#47274#><#47275#>efine<#47275#> <#47276#>(hangman-guess<#47276#> <#47277#>guess)<#47277#>
<#47278#>(l<#47278#><#47279#>ocal<#47279#> <#47280#>((define<#47280#> <#47281#>new-status<#47281#> <#47282#>(reveal-list<#47282#> <#47283#>chosen-word<#47283#> <#47284#>status-word<#47284#> <#47285#>guess)))<#47285#>
<#47286#>(c<#47286#><#47287#>ond<#47287#>
<#47288#>[<#47288#><#47289#>(equal?<#47289#> <#47290#>new-status<#47290#> <#47291#>status-word)<#47291#>
<#47292#>(l<#47292#><#47293#>ocal<#47293#> <#47294#>((define<#47294#> <#47295#>next-part<#47295#> <#47296#>(first<#47296#> <#47297#>body-parts-left)))<#47297#>
<#47298#>(b<#47298#><#47299#>egin<#47299#>
<#47300#>(set!<#47300#> <#47301#>body-parts-left<#47301#> <#47302#>(rest<#47302#> <#47303#>body-parts-left))<#47303#>
<#47304#>(c<#47304#><#47305#>ond<#47305#>
<#47306#>[<#47306#><#47307#>(empty?<#47307#> <#47308#>body-parts-left)<#47308#> <#47309#>(list<#47309#> <#47310#>``The<#47310#> <#47311#>End''<#47311#> <#47312#>chosen-word)]<#47312#>
<#47313#>[<#47313#><#47314#>else<#47314#> <#47315#>(list<#47315#> <#47316#>``Sorry''<#47316#> <#47317#>next-part<#47317#> <#47318#>status-word)]<#47318#><#47319#>)))]<#47319#>
<#47320#>[<#47320#><#47321#>else<#47321#>
<#47322#>(c<#47322#><#47323#>ond<#47323#>
<#47324#>[<#47324#><#47325#>(equal?<#47325#> <#47326#>new-status<#47326#> <#47327#>chosen-word)<#47327#> <#47328#>``You<#47328#> <#47329#>won'']<#47329#>
<#47330#>[<#47330#><#47331#>else<#47331#>
<#47332#>(b<#47332#><#47333#>egin<#47333#>
<#47334#>(set!<#47334#> <#47335#>status-word<#47335#> <#47336#>new-status)<#47336#>
<#47337#>(list<#47337#> <#47338#>``Good<#47338#> <#47339#>guess!''<#47339#> <#47340#>status-word))]<#47340#><#47341#>)]<#47341#><#47342#>)))<#47342#>
<#71795#>;; <#68589#><#47343#>reveal-list<#47343#> <#47344#>:<#47344#> <#47345#>word<#47345#> <#47346#>word<#47346#> <#47347#>letter<#47347#> <#47348#><#47348#><#47349#>-;SPMgt;<#47349#><#47350#><#47350#> <#47351#>word<#47351#><#68589#><#71795#>
<#47352#>;; to compute the new status word<#47352#>
<#47353#>(d<#47353#><#47354#>efine<#47354#> <#47355#>(reveal-list<#47355#> <#47356#>chosen-word<#47356#> <#47357#>status-word<#47357#> <#47358#>guess)<#47358#>
<#47359#>(l<#47359#><#47360#>ocal<#47360#> <#47361#>((d<#47361#><#47362#>efine<#47362#> <#47363#>(reveal-one<#47363#> <#47364#>chosen-letter<#47364#> <#47365#>status-letter)<#47365#>
<#47366#>(c<#47366#><#47367#>ond<#47367#>
<#47368#>[<#47368#><#47369#>(symbol=?<#47369#> <#47370#>chosen-letter<#47370#> <#47371#>guess)<#47371#> <#47372#>guess]<#47372#>
<#47373#>[<#47373#><#47374#>else<#47374#> <#47375#>status-letter]<#47375#><#47376#>)))<#47376#>
<#47377#>(map<#47377#> <#47378#>reveal-one<#47378#> <#47379#>chosen-word<#47379#> <#47380#>status-word)))<#47380#>
<#47384#>Figure: Hangman Basics (Part 3)<#47384#>
<#47388#>Exercise 37.2.3<#47388#>
Draw a diagram that shows how <#68590#><#47390#>hangman<#47390#><#68590#> and <#68591#><#47391#>hangman-guess<#47391#><#68591#>
interact with the state variables.~ Solution<#68592#><#68592#>
<#47397#>Exercise 37.2.4<#47397#>
Formulate the four examples for <#68593#><#47399#>hangman-guess<#47399#><#68593#> as boolean-valued
expressions that produce <#68594#><#47400#>true<#47400#><#68594#> if <#68595#><#47401#>hangman-guess<#47401#><#68595#> is
correct. Develop an additional example for each case; turn these new
examples into additional tests.~ Solution<#68596#><#68596#>
<#47407#>Exercise 37.2.5<#47407#>
Develop a graphical user interface, similar to that of the teachpack
<#47409#>hangman.ss<#47409#>. Connect the functions in this section as
call-backs.~ Solution<#68597#><#68597#>
<#47415#>Exercise 37.2.6<#47415#>
Modify the program so that it keeps track of all the guesses. Then, if a
player enters the same guess twice for the same round of a hangman game,
the response of <#68598#><#47417#>hangman-guess<#47417#><#68598#> is <#68599#><#47418#>``You<#47418#>\ <#47419#>have<#47419#>\ <#47420#>used<#47420#>\ <#47421#>this<#47421#>\ <#47422#>guess<#47422#><#47423#> <#47423#><#47424#>before.``<#47424#><#68599#>~ Solution<#68600#><#68600#>
<#47430#>Exercise 37.2.7<#47430#>
Consider the following variant of <#68601#><#47432#>reveal-list!<#47432#><#68601#>:
<#71796#>;; <#68602#><#47437#>reveal-list!<#47437#> <#47438#>:<#47438#> <#47439#>letter<#47439#> <#47440#><#47440#><#47441#>-;SPMgt;<#47441#><#47442#><#47442#> <#47443#>void<#47443#><#68602#><#71796#>
<#71797#>;; effect: to modify <#68603#><#47444#>status-word<#47444#><#68603#> based on a comparison of <#68604#><#47445#>chosen-word<#47445#><#68604#>,<#71797#>
<#71798#>;; the <#68605#><#47446#>status-word<#47446#><#68605#>, and the player's <#68606#><#47447#>guess<#47447#><#68606#><#71798#>
<#47448#>(d<#47448#><#47449#>efine<#47449#> <#47450#>(reveal-list!<#47450#> <#47451#>chosen-word<#47451#> <#47452#>status-word<#47452#> <#47453#>guess)<#47453#>
<#47454#>(l<#47454#><#47455#>ocal<#47455#> <#47456#>((d<#47456#><#47457#>efine<#47457#> <#47458#>(reveal-one<#47458#> <#47459#>chosen-letter<#47459#> <#47460#>status-letter)<#47460#>
<#47461#>(c<#47461#><#47462#>ond<#47462#>
<#47463#>[<#47463#><#47464#>(symbol=?<#47464#> <#47465#>chosen-letter<#47465#> <#47466#>guess)<#47466#> <#47467#>guess]<#47467#>
<#47468#>[<#47468#><#47469#>else<#47469#> <#47470#>status-letter]<#47470#><#47471#>)))<#47471#>
<#47472#>(set!<#47472#> <#47473#>status-word<#47473#> <#47474#>(map<#47474#> <#47475#>reveal-one<#47475#> <#47476#>chosen-word<#47476#> <#47477#>status-word))))<#47477#>
It changes the state variable <#68607#><#47481#>status-word<#47481#><#68607#> to a value that is computed
from the old value of <#68608#><#47482#>status-word<#47482#><#68608#>, <#68609#><#47483#>chosen-word<#47483#><#68609#>, and the
guess.
Modify <#68610#><#47484#>hangman-guess<#47484#><#68610#> so that it works properly with the
<#68611#><#47485#>reveal-list!<#47485#><#68611#> function.~ Solution<#68612#><#68612#>