Chapter 6 A Case Study: The Eiht Queens Puzzle This chapter presents the ærst of several case studies èor paradims, in the oriinal sense of the wordè of prorams written in an object-oriented style. The prorams in this Chapter will be rather small so that we can present versions in several diæerent lanaues. Later case studies will be presented in only one lanuae. After ærst describin the problem, we will discuss how an object-oriented solution would diæer from another type of solution. The chapter then concludes with a solution written in each lanuae. 6.1 The Eiht-Queens Puzzle In the ame of chess, the queen can attack any piece that lies on the same row, on the same column, or alon a diaonal. The eiht-queens is a classic loic puzzle. The task is to place eiht queens on a chessboard in such a fashion that no queen can attack any other queen. A solution is shown in Fiure 6.1, but this solution is not unique. The eiht-queens puzzle is often used to illustrate problem-solvin or backtrackin techniques. How would an object-oriented solution to the eiht-queens puzzle diæer from a solution written in a conventional imperative prorammin lanuae? In a conventional solution, some sort of data structure would be used to maintain the positions of the pieces. A proram would then solve the puzzle by systematically manipulatin the values in these data structures, testin each new position to see whether it satisæed the property that no queen can attack any other. We can provide an amusin but nevertheless illustrative metaphor for the difference between a conventional and an object-oriented solution. A conventional proram is like a human bein sittin above the board, and movin the chess pieces, which have no animate life of their own. In an object-oriented solution, 125
126 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE Q Q Q Q Q Q Q Q Fiure 6.1: í One solution to the eiht-queens puzzle. on the other hand, we will empower the pieces to solve the problem themselves. That is, instead of a sinle monolithic entity controllin the outcome, we will distribute responsibility for ændin the solution amon many interactin aents. It is as if the chess pieces themselves are animate beins who interact with each other and take chare of ændin their own solution. Thus, the essence of our object-oriented solution will be to create objects that represent each of the queens, and to provide them with the abilities to discover the solution. With the computin-as-simulation view of Chapter 1, we are creatin a model universe, deænin behavior for the objects in this universe, then settin the universe in motion. When the activity of the universe stabilizes, the solution has been found. 6.1.1 Creatin Objects That Find Their Own Solution How miht we deæne the behavior of a queen object so that a roup of queens workin toether can ænd a solution on their own? The ærst observation is that, in any solution, no two queens can occupy the same column, and consequently no column can be empty. At the start we can therefore assin a speciæc column to each queen and reduce the problem to the simpler task of ændin an appropriate row. To ænd a solution it is clear that the queens will need to communicate with each other. Realizin this, we can make a second important observation that will reatly simplify our prorammin taskínamely, each queen needs to know only about the queens to her immediate left. Thus, the data values maintained for each queen will consist of three values: a column value, which is immutable; a row value, which is altered in pursuit of a solution; and the neihborin queen to the immediate left. Let us deæne an acceptable solution for column n to be a conæuration of
6.2. USING GENERATORS 127 columns 1 throuh n in which no queen can attack any other queen in those columns. Each queen will be chared with ændin acceptable solutions between herself and her neihbors on her left. We will ænd a solution to the entire puzzle by askin the riht most queen to ænd an acceptable solution. A CRC-card description of the class Queen, includin the data manaed by each instance èrecall that this information is described on the back side of the cardè, is shown in Fiure 6.2. 6.2 Usin Generators As with many similar problems, the solution to the eiht-queens puzzle involves two interactin steps: eneratin possible partial solutions and ælterin out solutions that fail to satisfy some later oal. This style of problem solvin is sometimes known as the enerate and test paradim. Let us consider the ælter step ærst, as it is easier. For the system to test a potential solution it is suæcient for a queen to take a coordinate èrow-columnè pair and produce a Boolean value that indicates whether that queen, or any queen to her left, can attack the iven location. A pseudo code alorithm that checks to see whether a queen can attack a speciæc position is iven below. The procedure canattack uses the fact that, for a diaonal motion, the diæerences in rows must be equal to the diæerences in columns. function queen.canattackètestrow, testcolumnè -é boolean èæ test for same row æè if row = testrow then return true èæ test diaonals æè columndifference := testcolumn - column if èrow + columndifference = testrowè or èrow - columndifference = testrowè then return true èæ we can't attack, see if neihbor can æè return neihbor.canattackètestrow, testcolumnè 6.2.1 Initialization We will divide the task of ændin a solution into parts. The method initialize establishes the initial conditions necessary for a queen object, which in this case simply means settin the data values. This is usually followed immediately by a call on ændsolution to discover a solution for the iven column. Because such a
128 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE Queen initialize í initialize row, then ænd ærst acceptable solution for self and neihbor advance í advance row and ænd next acceptable solution canattack í see whether a position can be attacked by self or neihbors Queen í data values row í current row number èchanesè column í column number èæxedè neihbor í neihbor to left èæxedè Fiure 6.2: ífront and back sides of the Queen CRC card.
6.2. USING GENERATORS 129 solution will often not be satisfactory to subsequent queens, the messae advance is used to advance to the next solution. A queen in column n is initialized by bein iven a column number, and the neihborin queen èthe queen in column n, 1è. At this level of analysis, we will leave unspeciæed the actions of the leftmost queen, who has no neihbor. We will explore various alternative actions in the example problems we subsequently present. We will assume the neihbor queens èif anyè have already been initialized, which includes their havin found a mutually satisfactory solution. The queen in the current column simply places herself in row 1. A pseudo-code description of the alorithm is shown below. function queen.initializeècol, neihè -é boolean èæ initialize our column and neihbor values æè column := col neihbor := neih èæ start in row 1 æè row := 1 return findsolution; 6.2.2 Findin a Solution To ænd a solution, a queen simply asks its neihbors if they can attack. If so, then the queen advances herself, if possible èreturnin failure if she cannotè. When the neihbors indicate they cannot attack, a solution has been found. function queen.findsolution -é boolean èæ test positions æè while neihbor.canattack èrow, columnè do if not self.advance then return false èæ found a solution æè return true As we noted in Chapter 5, the pseudo-variable self denotes the receiver for the current messae. In this case we want the queen who is bein asked to ænd a solution to pass the messae advance to herself.
130 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE 6.2.3 Advancin to the Next Position The procedure advance divides into two cases. If we are not at the, the queen simply advances the row value by 1. Otherwise, she has tried all positions and not found a solution, so nothin remains but to ask her neihbor for a new solution and start aain from row 1. function queen.advance -é boolean èæ try next row æè if row é 8 then bein row := row + 1 return self.findsolution èæ cannot o further æè èæ move neihbor to next solution æè if not neihbor.advance then return false èæ start aain in row 1 æè row := 1 return self.findsolution The one remainin task is to print out the solution. This is most easily accomplished by a simple method, print that is rippled down the neihbors. procedure print neihbor.print write row, column 6.3 The Eiht-Queens Puzzle in Several Lanuaes In this section we present solutions to the eiht-queens puzzle in several of the prorammin lanuaes we are considerin. Examine each variation, and compare how the basic features provided by the lanuae make subtle chanes to the ænal solution. In particular, examine the solutions written in Smalltalk and Objective-C, which use a special class for a sentinel value, and contrast this with the solutions iven in Object Pascal, C++, orjava, all of which use a null pointer for the leftmost queen and thus must constantly test the value of pointer variables.
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 131 6.3.1 The Eiht-Queens Puzzle in Object Pascal The class deænition for the eiht-queens puzzle in Apple Object Pascal is shown below. A subtle but important point is that this deænition is recursive; objects of type Queen maintain a data æeld that is itself of type Queen. This is suæcient to indicate that declaration and storae allocation are not necessarily linked; if they were, an inænite amount ofstorae would be required to hold any Queen value. We will contrast this with the situation in C ++ when we discuss that lanuae. type Queen = object èæ data æelds æè row : inteer; column : inteer; neihbor : Queen; èæ initialization æè procedure initialize ècol : inteer; nh : Queenè; èæ operations æè function canattack ètestrow, testcolumn : inteerè : boolean; function findsolution : boolean; function advance : boolean; procedure print; ; The class deænition for the Delphi lanuae diæers only slihtly, as shown below. The Borland lanuae allows the class declaration to be broken into public and private sections, and it includes a constructor function, which we will use in place of the initialize routine. TQueen = class ètobjectè public constructor Create èinitialcolumn : inteer; nbr : TQueenè; function findsolution : boolean; function advance : boolean; procedure print; private function canattack ètestrow, testcolumn : inteerè : boolean; row : inteer; column : inteer; neihbor : TQueen;
132 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE ; The pseudo-code presented in the earlier sections is reasonably close to the Pascal solution, with two major diæerences. The ærst is the lack of a return statement inpascal, and the second is the necessity to ærst test whether a queen has a neihbor before passin a messae to that neihbor. The functions ændsolution and advance, shown below, illustrate these diæerences. ènote that Delphi Pascal diæers from standard Pascal in permittin short-circuit interpretation of the and and or directives, in the fashion of C++. Thus, the code for the Delphi lanuae could in a sinle expression combine the test for neihbor bein non-null and the passin of a messae to the neihborè. function Queen.findSolution : boolean; var done : boolean; bein done := false; findsolution := true; èæ test positions æè if neihbor éé nil then while not done and neihbor.canattackèrow, columnè do if not self.advance then bein findsolution := false; done := true; ; ; function Queen.advance : boolean; bein advance := false; èæ try next row æè if row é 8 then bein row := row + 1; advance := self.findsolution; else bein èæ cannot o further æè èæ move neihbor to next solution æè if neihbor éé nil then if not neihbor.advance then advance := false else bein row := 1; advance := self.findsolution;
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 133 ; ; ; The main proram allocates space for each of the eiht queens and initializes the queens with their column number and neihbor value. Since durin initialization the ærst solution will be discovered, it is only necessary for the queens to print their solution. The code to do this in Apple Object Pascal is shown below. Here, neihbor and i are temporary variables used durin initialization and lastqueen is the most recently created queen. bein neihbor := nil; for i := 1 to 8 do bein èæ create and initialize new queen æè new èlastqueenè; lastqueen.initial èi, neihborè; if not lastqueen.findsolution then writelnèæno solutionæè; èæ newest queen is next queen neihbor æè neihbor := lastqueen; ; èæ print the solution æè lastqueen.print; ;. By providin explicit constructors that combine new object creation and initialization, the Delphi lanuae allows us to eliminate one of the temporary variables. The main proram for the Delphi lanuae is as follows: bein lastqueen := nil; for i := 1 to 8 do bein èè create and initialize new queen lastqueen := Queen.createèi, lastqueenè; lastqueen.findsolution; ; èè print the solution lastqueen.print; ;
134 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE 6.3.2 The Eiht-Queens Puzzle in C ++ The most important diæerence between the pseudo-code description of the alorithm presented earlier and the eiht-queens puzzle as actually coded in C++ is the explicit use of pointer values. The class description for the class Queen is shown below. Each instance maintains, as part of its data area, a pointer to another queen value. Note that, unlike the Object Pascal solution, in C ++ this value must be declared explicitly as a pointer rather than an object value. class Queen f public: èè constructor Queen èint, Queen æè; èè ænd and print solutions bool findsolutionèè; bool advanceèè; void printèè; private: èè data æelds int row; const int column; const Queen æ neihbor; ; èè internal method bool canattack èint, intè; As in the Delphi Pascal solution, we have subsumed the behavior of the method initialize in the constructor. We will describe this shortly. There are three data æelds. The inteer data æeld column has been marked as const. This identiæes the æeld as an immutable value, which cannot chane durin execution. The third data æeld is a pointer value, which either contains anull value èthat is, points at nothinè or points to another queen. Since initialization is performed by the constructor, the main proram can simply create the eiht queen objects, and then print their solution. The variable lastqueen will point to the most recent queen created. This value is initially a null pointeríit points to nothin. A loop then creates the eiht values, initializin each with a column value and the previous queen value. When the loop completes, the leftmost queen holds a null value for its neihbor æeld while every other queen points to its neihbor, and the value lastqueen points to the rihtmost queen.
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 135 void mainèè f Queen æ lastqueen = 0; for èint i = 1; i é= 8; i++è f lastqueen = new Queenèi, lastqueenè; if è! lastqueen-éfindsolutionèèè cout éé "no solutionën"; lastqueen-éprintèè; We will describe only those methods that illustrate important points. The complete solution can be examined in Appix A. The constructor method must use the initialization clauses on the headin to initialize the constant value column, as it is not permitted to use an assinment operator to initialize instance æelds that have been declared const. An initialization clause is also used to assin the value neihbor, althouh we have not declared this æeld as constant. Queen::Queenèint col, Queen æ nhè : columnècolè, neihborènhè f row = 1; Because the value of the neihbor variable can be either a queen or a null value, a test must be performed before any messaes are sent to the neihbor. This is illustrated in the method ændsolution. The use of short-circuit evaluation in the loical connectives and the ability to return from within a procedure simplify the code in comparison to the Object Pascal version, which is otherwise very similar. bool Queen::findSolutionèè f while èneihbor && neihbor-écanattackèrow, columnèè if è! advanceèèè return false; return true; The advance method must similarly test to make certain there is a neihbor before tryin to advance the neihbor to a new solution. When passin a messae to oneself, as in the recursive messae ændsolution, it is not necessary to specify a receiver. bool Queen::advanceèè
136 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE f if èrow é 8è f row++; return findsolutionèè; if èneihbor &&! neihbor-éadvanceèèè return false; row = 1; return findsolutionèè; 6.3.3 The Eiht-Queens Puzzle in Java The solution in Java is in many respects similar to the C++ solution. However, in Java the bodies of the methods are written directly in place, and public or private desinations are placed on the class deænitions themselves. The followin is the class description for the class Queen, with some of the methods omitted. class Queen f èè data æelds private int row; private int column; private Queen neihbor; èè constructor Queen èint c, Queen nè f èè initialize data æelds row = 1; column = c; neihbor = n; public boolean findsolutionèè f while èneihbor!= null && neihbor.canattackèrow, columnèè if è! advanceèèè return false; return true; public boolean advanceèè f...
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 137 private boolean canattackèint testrow, int testcolumnè f... public void paint ègraphics è f... Unlike in C ++, in Java the link to the next queen is simply declared as an object of type Queen and not as a pointer to a queen. Before a messae is sent to the neihbor instance variable, an explicit test is performed to see if the value is null. Since Java provides a rich set of raphics primitives, this solution will diæer from the others in actually drawin the ænal solution as a board. The method paint will draw an imae of the queen, then print the neihbor imaes. class Queen f... public void paint ègraphics è f èè x, y is upper left corner èè 10 and 40 ive sliht marins to sides int x = èrow - 1è æ 50 + 10; int y = ècolumn - 1è æ 50 + 40;.drawLineèx+5, y+45, x+45, y+45è;.drawlineèx+5, y+45, x+5, y+5è;.drawlineèx+45, y+45, x+45, y+5è;.drawlineèx+5, y+35, x+45, y+35è;.drawlineèx+5, y+5, x+15, y+20è;.drawlineèx+15, y+20, x+25, y+5è;.drawlineèx+25, y+5, x+35, y+20è;.drawlineèx+35, y+20, x+45, y+5è;.drawovalèx+20, y+20, 10, 10è; èè then draw neihbor if èneihbor!= nullè neihbor.paintèè; The raphics routines draw a small crown, which looks like this: JJææJJææ m
138 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE Java does not have lobal variables nor functions that are not member functions. As we will describe in more detail in Chapter 22, a proram is created by the deænin of a subclass of the system class JFrame, and then the overridin of certain methods. Notably, the constructor is used to provide initialization for the application, while the method paint is used to redraw the screen. Mouse events and window events are handled by creatin listener objects that will execute when their associated event occurs. We will describe listeners in much reater detail in later sections. We name the application class QueenSolver and deæne it as follows: public class QueenSolver exts JFrame f public static void mainèstrin ë ë arsè f QueenSolver world = new QueenSolverèè; world.showèè; private Queen lastqueen = null; public QueenSolverèè f settitleè"8 queens"è; setsizeè600, 500è; for èint i = 1; i é= 8; i++è f lastqueen = new Queenèi, lastqueenè; lastqueen.findsolutionèè; addmouselistenerènew MouseKeeperèèè; addwindowlistenerènew CloseQuitèèè; public void paintègraphics è f super.paintèè; èè draw board for èint i = 0; i é= 8; i++è f.drawlineè50 æ i + 10, 40, 50æi + 10, 440è;.drawLineè10, 50 æ i + 40, 410, 50æi + 40è;.drawStrinè"Click Mouse for Next Solution", 20, 470è; èè draw queens lastqueen.paintèè; private class CloseQuit exts WindowAdapter f public void windowclosin èwindowevent eè f System.exitè0è;
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 139 private class MouseKeeper exts MouseAdapter f public void mousepressed èmouseevent eè f lastqueen.advanceèè; repaintèè; Note that the application class must be declared as public, because it must be accessible to the main proram. 6.3.4 The Eiht-Queens Puzzle in Objective-C The interface description for our class Queen is as follows: @interface Queen : Object f èæ data æelds æè int row; int column; id neihbor; èæ methods æè - èvoidè initialize: èintè c neihbor: nh; - èintè advance; - èvoidè print; - èintè canattack: èintè testrow column: èintè testcolumn; - èintè findsolution; @ Each queen will maintain three data æelds: a row value, a column, and the neihbor queen. The last is declared with the data type id. This declaration indicates that the value bein held by the variable is an object type, althouh not necessarily a queen. In fact, we can use this typeless nature of variables in Objective-C to our advantae. We will employ a technique that is not possible, or at least not as easy, ina more stronly typed lanuae such asc ++ or Object Pascal. Recall that the leftmost queen does not have any neihbor. In the C++ solution, this was indicated by the null, or empty value, in the neihbor pointer variable in the leftmost queen. In the current solution, we will instead create a new type
140 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE of class, a sentinel value. The leftmost queen will point to this sentinel value, thereby ensurin that every queen has a valid neihbor. Sentinel values are frequently used as markers and are found in alorithms that manipulate linked lists, such as our linked list of queen values. The diæerence between an object-oriented sentinel and a more conventional value is that an object-oriented sentinel value can be activeíit can have behavioríwhich means it can respond to requests. What behaviors should our sentinel value exhibit? Recall that the neihbor links in our alorithm were used for two purposes. The ærst was to ensure that a iven position could not be attacked; our sentinel value should always respond neatively to such requests, since it cannot attack any position. The second use of the neihbor links was in a recursive call to print the solution. In this case our sentinel value should simply return, since it does not have any information concernin the solution. Puttin these toether yields the followin implementation for our sentinel queen. @implementation SentinelQueen : Object - èintè advance f èæ do nothin æè return 0; - èintè findsolution f èæ do nothin æè return 1; - èvoidè print f èæ do nothin æè - èintè canattack: èintè testrow column: èintè testcolumn; f @ èæ cannot attack æè return 0; In the full solution there is an implementation section for SentinelQueen, but no interface section. This omission is leal, althouh the compiler will provide a warnin since it is somewhat unusual.
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 141 The use of the sentinel allows the methods in class Queen to simply pass messaes to their neihbor without ærst determinin whether or not she is the leftmost queen. The method for canattack, for example, illustrates this use: - èintè canattack: èintè testrow column: èintè testcolumn f int columndifference; èæ can attack same row æè if èrow == testrowè return 1; columndifference = testcolumn - column; if èèrow + columndifference == testrowè jj èrow - columndifference == testrowèè return 1; return ë neihbor canattack:testrow column: testcolumn ë; Within a method, a messae sent to the receiver is denoted by a messae sent to the pseudo-variable self. - èvoidè initialize: èintè c neihbor: nh f èæ set the constant æelds æè column = c; neihbor = nh; row = 1; - èintè findsolution f èæ loop until we ænd a solution æè while èëneihbor canattack: row and: column ëè if è! ëself advanceëè return 0; èæ return false æè return 1; èæ return true æè Other methods are similar, and are not described here. 6.3.5 The Eiht-Queens Puzzle in Smalltalk The solution to the eiht-queens puzzle in Smalltalk is in most respects very similar to the solution iven in Objective-C. Like Objective-C, Smalltalk handles
142 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE the fact that the leftmost queen does not have a neihbor by deænin a special sentinel class. The sole purpose of this class is to provide a taret for the messaes sent by the leftmost queen. The sentinel value is the sole instance of the class SentinelQueen, a subclass of class Object, which implements the followin three methods: advance " sentinels do not attack " " false canattack: row column: column " sentinels cannot attack " " false result " return empty list as result " " List new One diæerence between the Objective-C and Smalltalk versions is that the Smalltalk code returns the result as a list of values rather than printin it on the output. The techniques for printin output are rather tricky in Smalltalk and vary from implementation to implementation. By returnin a list we can isolate these diæerences in the callin method. The class Queen is a subclass of class Object. Instances of class Queen maintain three instance variables: a row value, a column value, and a neihbor. Initialization is performed by the method setcolumn:neihbor: setcolumn: anumber neihbor: aqueen " initialize the data æelds " column := anumber. neihbor := aqueen. row := 1. The canattack method diæers from the Objective-C counterpart only in syntax: canattack: testrow column: testcolumn j columndifference j columndifference := testcolumn - column. èèèrow = testrowè or: ë row + columndifference = testrowëè or: ë row - columndifference = testrowëè iftrue: ë " true ë. " neihbor canattack: testrow column: testcolumn
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 143 Rather than testin for the neation of a condition, Smalltalk provides an explicit iffalse statement, which is used in the method advance: advance " ærst try next row " èrow é 8è iftrue: ë row := row + 1. " self findsolution ë. " cannot o further, move neihbor " èneihbor advanceè iffalse: ë " false ë. " bein aain in row 1 " row := 1. " self findsolution The while loop in Smalltalk must use a block as the condition test, as in the followin: ændsolution ë neihbor canattack: row column: column ë whiletrue: ë self advance iffalse: ë " false ë ë. " true A recursive method is used to obtain the list of answer positions. Recall that an empty list is created by the sentinel value in response to the messae result. result " neihbor result; addlast: row A solution can be found by invocation of the followin method, which is not part of class Queen but is instead attached to some other class, such as Object. solvepuzzle j lastqueen j lastqueen := SentinelQueen new. 1 to: 8 do: ë:i j lastqueen := èqueen newè setcolumn: i neihbor: lastqueen. lastqueen findsolution ë. " lastqueen result 6.3.6 The Eiht-Queens Puzzle in Ruby Ruby is a recent scriptin lanuae, similar in spirit to Python or Perl. There are only functions in Ruby, every method returns a value, which is simply the value of the last statement in the body of the method. A feel for the syntax for
144 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE Ruby can be found by the deænition of the sentinel queen, which can be written as follows: class NullQueen def canattackèrow, columnè false def first? true def next? false def etstate Array.new The class Queen handles all but the last case. In Ruby instance variables must bein with an at-sin. Thus the initialization method is written as follows: class Queen def initialcolumnècolumn, neihbourè @column = column @neihbour = neihbour nil... Conditional statements are written in a curious form where the expression is iven ærst, followed by the if keyword. This is illustrated by the method canattack: def canattackèrow, columnè return true if row == @row cd = ècolumn - @columnè.abs
6.3. THE EIGHT-QUEENS PUZZLE IN SEVERAL LANGUAGES 145 rd = èrow - @rowè.abs return true if cd == rd @neihbour.canattackèrow, columnè The remainder of the Ruby solution can be found in the appix. Chapter Summary In this ærst case study we have examined a classic puzzle, how to place eiht queens on a chessboard in such a way that no queen can attack any of the others. While the problem is moderately intriuin, our interest is not so much in the problem itself, but in the way the solution to the problem has been structured. We have addressed the problem by makin the queens into indepent aents, who then work amon themselves to discover a solution. Further Readin A solution to the eiht-queens puzzle constructed without the use of a sentinel value was described in my earlier book on Smalltalk ëbudd 1987ë. The eiht queens puzzle is found in many computin texts. See ëgriswold 1983, Budd 1987, Berztiss 1990ë, for some representative examples. For further information on the eneral technique termed enerate and test, see ëhanson 1981ë, or ëberztiss 1990ë. The solution in Ruby was written by Mike Stok. Further information on Ruby can be found in ëthomas 2001ë. Self Study Questions 1. What is the eiht queens puzzle? 2. In what way is the object-oriented solution presented here diæerent from a conventional solution? 3. What is the enerate and test approach to ændin a solution in a space of various alternative possibilities? 4. What is a sentinel? èthe term is introduced in the solution presented in Objective-Cè.
146 CHAPTER 6. A CASE STUDY: THE EIGHT QUEENS PUZZLE Exercises 1. Modify any one of the prorams to produce all possible solutions rather than just one. How many possible solutions are there for the eiht-queens puzzle? How many of these are rotations of other solutions? How miht you ælter out rotations? 2. Can you explain why the sentinel class in the Objective-C and Smalltalk versions of the eiht-queens puzzle do not need to provide an implementation for the method ændsolution, despite the fact that this messae is passed to the neihbor value in the method advance? 3. Suppose we eneralize the eiht-queens problem to the N-queens problem, where the task is to place N queens on an N by N chessboard. How must the prorams be chaned? It is clear that there are values for N for which no solution exists èconsider N=2 or N=3, for exampleè. What happens when your proram is executed for these values? How miht you produce more meaninful output? 4. Usin whatever raphics facilities your system has, alter one of the prorams to display dynamically on a chessboard the positions of each queen as the proram advances. What portions of the proram need to know about the display?