Section 3.7. Testing an Improved OneRowNim


[Page 129 (continued)]

3.7. Testing an Improved OneRowNim

Let us use the control structures we have discussed to improve the definition of the takeSticks() method of OneRowNim. We noted earlier that our current definition allows four or more sticks to be removed from nSticks even though the rules of One-Row Nim indicate that a player must take one, two, or three sticks on a turn. We can use if-else statements to make certain that no more than three sticks are removed.

What should happen if the method takeSticks() is called with an argument that does not represent a legal number of sticks to remove? In this case, it would probably make sense to remove no sticks at all and to keep the value of player the same, so that the player whose turn it is does not change. In addition, it would be nice if the method could signal that an illegal move has been attempted. This can be accomplished if we redefine takeSticks() to return a boolean value. The return value of true will represent the case that a valid number of sticks has been removed and the player to play next has been changed. A return of false will indicate that an illegal move has been attempted. Making these changes to the takeSticks() method will yield a method definition that looks like:


[Page 130]

public boolean takeSticks(int num) {   if (num < 1) {         return false;              // Error      } else if ( num > 3) {         return false;              // Error      } else        {            nSticks = nSticks - num;            player = 3 - player;            return true;        } // else } // takeSticks() 


The new definition of the takeSticks() method has a boolean return type. Note that the if-else multiway structure is used to handle the three cases of the parameter num being less than 1, more than 3, or a valid number.

Let us add one more method to the OneRowNim class. We will define a method called getWinner() that will return the number of the winning player if the game is over. Recall that the player who takes the last stick loses, so after that last play, the player whose turn it is to play next is the winner. However, we should be concerned about what value to return if the game is not over when the method is called. A common strategy is to have a method return a special value to indicate that it is in a state in which it cannot return the value requested. Returning a 0 value is a good way to indicate that the game is not over and a winner cannot be identified. With this information, the if-else statement can be used in the definition of getWinner().

public int getWinner() {   if (nSticks < 1)         return player;      else         return 0; } // getWinner() 


We now have the final version (for this chapter) of the OneRowNim class whose implementation is given in Figure 3.16. We have turned a very simple class into one that contains quite a few elements. Compared to our first version (in Chapter 1), the current version of OneRowNim presents an interface (to other objects) that is easy and convenient to use. The constructor methods with parameters provide an easy way to create a OneRowNim instance with any number of sticks. The use of private instance variables and a single, carefully designed mutator method, takeSticks(), prevents other objects from tampering with the state of a OneRowNim object's state. The other methods provide a flexible way to find out the state of a OneRowNim object. The complete implementation of this OneRowNim is shown in Figure 3.16.


[Page 131]

Figure 3.16. The OneRowNim class with improved methods.

public class OneRowNim {   private int nSticks = 7;     private int player = 1;     public OneRowNim()     {     } // OneRowNim() constructor     public OneRowNim(int sticks)     {   nSticks = sticks;     } // OneRowNim() constructor2     public OneRowNim(int sticks, int starter)     {   nSticks = sticks;         player = starter;     } // OneRowNim() constructor3     public boolean takeSticks(int num)     {   if (num < 1) return false;             // Error         else if ( num > 3) return false;       // Error         else                                   // this is a valid move         {   nSticks = nSticks - num;             player = 3 - player;             return true;         } // else     } // takeSticks()     public int getSticks()     {   return nSticks;     } // getSticks()     public int getPlayer()     {   return player;     } // getPlayer()     public boolean gameOver()     {   return (nSticks <= 0);     } // gameOver()     public int getWinner()     {   if (nSticks < 1) return getPlayer();         else return 0;  // game is not over     } // getWinner()     public void report()     {   System.out.println("Number of sticks left: " + getSticks());         System.out.println("Next turn by player " + getPlayer());     } // report() } // OneRowNim class 

We will now use a while statement to test the new methods of the class. A pseudocode description of how a game is played might look like:

Choose the initial number of sticks for the game while the game is not over {   Report the state of the game     Process the next move } Report the state of the game Report who the winner is 



[Page 132]

Translating this pseudocode into Java code in a main() method in a separate class gives us the class shown in Figure 3.17. We will use the Scanner class introduced in Chapter 2 to get moves from the keyboard for both players. Before each move, game.report() describes the state of the game before the user is prompted to input a move for one of the players. Readers interested in seeing the lengthy output to the console when the TestOneRowNim class is run are encouraged to actually run the program.

Figure 3.17. The TestOneRowNim class with a while loop.
(This item is displayed on page 133 in the print version)

import java.util.Scanner; public class TestOneRowNim {   public static void main(String argv[])   { Scanner sc = Scanner.create(System.in);     OneRowNim game = new OneRowNim(11);     while(game.gameOver() == false)     {   game.report();                            // Prompt the user         System.out.print("Input 1, 2, or 3: ");         int sticks = sc.nextInt();                // Get move         game.takeSticks(sticks);                  // Do move         System.out.println();     } // while     game.report();                                // The game is now over     System.out.print("Game won by player ");     System.out.println(game.getWinner());   } // main() } // TestOneRowNim class 

The return value of the takeSticks() method is ignored in this test program. We will make use of the return value in test programs in the next chapter when better user interfaces are developed for OneRowNim. Note, however, that taken together, the public methods for OneRowNim provide other objects with an interface that they can use to communicate with individual OneRowNim objects.

Effective Design: Interfaces

Well-designed objects provide a useful public interface and protect the object's private elements from other objects.


Intelligent Agents

Wouldn't it be nice if we had a computer program that could schedule appointments for us, remind us of meetings and commitments, find information for us on the World Wide Web, and manage our email messages for us? Wouldn't it be nice to have a computerized personal assistant?

Actually, such programs are called intelligent agents, a term indicating that they are capable of acting autonomously to carry out certain tasks. Intelligent agent technology is becoming an important research area in computer science. Most agent programs incorporate some kind of machine-learning capability, so that their performance improves over time.

As a typical agent activity, suppose I was able to tell my intelligent agent to buy me a copy of a certain book. Given a command like "buy me a copy of X," the agent would perform a search of online book sellers and come up with the best deal. Once it found the best buy, the agent would communicate with a computer-based agent representing the book seller. My agent would make the order and pay for it (assuming I gave it authority to do so), and the book seller's agent would process the order.

As far-fetched as the capability may now seem, this is the direction in which research in this area is headed. Researchers are developing agent languages and describing protocols that agents can use to exchange information in a reliable and trustworthy environment. Obviously, you wouldn't want your agent to give your money to a fraudulent book seller, so there are significant problems to solve in this area that go well beyond the problem of simply exchanging information between two agents.

The best way to learn more about this research area is to do a Web search using the search string "Intelligent Agent." There are numerous research groups and companies that provide online descriptions and demos of their products.



[Page 133]

To reiterate a point made at the outset, object-oriented programming is a process of constructing objects that will interact with each other. Object-oriented programs must ensure that the objects themselves are well designed in terms of their ability to carry out their designated functions. Good design in this sense requires careful selection of instance variables and careful design of methods to ensure that the object can carry out its assigned tasks. However, equal care must be taken to ensure that the interactions among objects are constrained in ways that make sense for the particular program. This aspect of designing objects comes into play in designing the methodsconstructor, accessor, and mutatorthat make up the object's interface.

Object-oriented design





Java, Java, Java(c) Object-Orienting Problem Solving
Java, Java, Java, Object-Oriented Problem Solving (3rd Edition)
ISBN: 0131474340
EAN: 2147483647
Year: 2005
Pages: 275

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net