CS 107 (Spring '09)
[Schedule] [Programs] [Notes & Reference] [Examples][Syllabus] [Lab & TA] [Tests] [Grades]

Program #4: Yote, part 2

Prof. Reed, CS 107, Fall '08
Due Friday 11/14 at 1:00 PM

Description

Continue to work on your graphical Yote game-playing program from last time, that will allow two human players to play the game. In this second part you will need to implement making moves, validating moves, and checking for a win or a draw. Rules of the game are the same as in the previous version. Note that to simplify things, no double-jumps will be allowed. If you prefer, you can start with my solution to part 1 of Yote.

On each move, the user will enter one of the following types of input:

      x to exit
      16
(for example) to place a piece on square 16
      17 16 (for example) to move the piece from position 17 to position 16

Only valid moves are allowed. If invalid input is given, an error message should be given and the user prompted to try again. Running the game should look like the following:

blank board

As play progresses, pieces might be (unskillfully) placed as follows:

Board with 4 pieces

Note how error messages are given for invalid moves, prompting the user to retry. Assume that black makes a jumping move:

Board with a jump

After a jump, an additional piece is taken off the board. so here another white piece will be removed.

Board after jump and piece removed

Play continues back and forth, filling up the board with pieces.

setup to move a piece already on the board

Let's say black now wants to take the white 'C' piece currently in position 10. On this move black can place a piece into the corner at position 5:

About to move piece

White could place a new piece at 15, or could alternatively move piece 'C' from position 10 to position 15, as shown below.

Made the move

Error checking must be done to ensure that only valid moves are made, as shown below for white's next move:

validating moves

Once all pieces have been placed, attempts to make that sort of move must result in an error message and a retry:

all pieces placed

The program should detect once a player loses, or there is a draw (when both players have 3 or less pieces)

end game


You need to know the following concepts in order to write this program:

Everything from the first three assignments, particularly how to use objects and classes; an understanding of the solution to program 3; how to chain method calls.

Notes:

  1. Here are some methods that you will find useful in different classes. I've given the general idea, however you will need to supply the appropriate parameters and return values. Note that these are useful when following my design approach. If you are taking a different approach, what you need will be different. All instance variables (a.k.a. fields) in each class must be defined as private, which means that you can only access them from another class by using a method call.

    1. Piece class:
      1. Implement get and set methods to retrieve and change the location index (1..20) of the playing piece. The location index should be -1 for pieces not on the board.
      2. moveTheCircleInDirection: Given a direction ('u' for up, 'd' for down, 'l' for left, 'r' for right), move the circle in that direction, making the appropriate call to the circle class moveSlowHorizontal or moveSlowVertical. This will get called when a piece gets moved to an adjacent square.
      3. moveTheCircleInDirectionMultiple: This method is given a direction (as above) and also is given the number of squares to move, which will be 2 for us if we are making a jump. Simply call the above method twice from this method to implement this.

    2. Board class:
      1. countPiecesForWhite: this should return the number of pieces on the board for white. This should go through the array of white pieces and retrieve the location index of each one, counting the number that have a location index that is not -1.
      2. getHowManyPiecesRemainToPlay: should return how many pieces a player (black or white) still has that could be placed on the board.
      3. placePiece: this should place a piece at a particular position on the board. This is done by changing the x,y locations for the circle (that is part of the piece), and by making the circle visible. More details on this are given in the javadoc from the previous Yote version.
        You need to have a variable that keeps track of the next piece to be used from the whitePieces or blackPieces arrays.
      4. removePieceAt: this should remove a piece from the board by making it invisible and changing its index location to -1. This is used when jumping a piece. The jumped piece gets removed.
      5. getPieceAt: return the piece at a particular index position on the board, or null if there is no piece there. This could be useful both from within the Board class as well as it could be used from within the PlayGame class.
      6. getColorOfPieceAt: get the color of a piece at the indicated index position. This could be used from within the PlayGame class when doing error checking for valid moves. For instance, you need to check the color of a piece being jumped, to verify that it is the opposite of the color of the piece doing the jumping.

    3. PlayGame class
      1. gameIsNotDone(): This should calculate how many pieces of each color there are. Note that this is the sum of the pieces on the board and the number of pieces remaining to be played for a color. Once the number of pieces of each color is known, these values can be compared to see if the game is over or not.
      2. parseInputAndSetPositions: You may want to keep track of the position your are moving from and the position you are moving to. This information should be pulled out of the user input. Your code to do this might look something like the following:
                String inputFirstPart;
                String inputSecondPart;
                int sourcePosition;         // will store the index of where we're moving from
                int destinationPosition;    // will store the index of where we're moving to
        
        
                // find the space, if there is one, in the user input
                int indexOfSpace = userInput.indexOf(' ');	// stores -1 if ' ' not found
                if( indexOfSpace < 0) {
                	// there should only be one number on the input line, so parse it to an integer
        	        destinationPosition = Integer.parseInt( userInput);
                }
                else {
                	// Since there is a space in the input, there should be more than one number.
                	// This means we are moving a piece, either to an adjacent position or to make a jump.
                	
                	// Break up input string into two parts
                	String inputFirstPart = userInput.substring(0,indexOfSpace);	
                	String inputSecondPart = userInput.substring(indexOfSpace, userInput.length());
        
                	// Trim off any excess leading or trailing space that might confuse the conversion to int
                	inputFirstPart =  inputFirstPart.trim();
                	inputSecondPart = inputSecondPart.trim();
        
                	// Now parse the two numbers from the two parts
                	sourcePosition = Integer.parseInt( inputFirstPart);
                	destinationPosition = Integer.parseInt( inputSecondPart);
        
                	//... other code ...
                }
                
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        I recommend that throughout this program you read user input as a whole line (keyboard.nextLine()), and then look at the different parts of it. It gets very confusing if you read a whole line sometimes but then later read only part of it (e.g. keyboard.nextInt()), leaving the carriage-return character still sitting on the input line. Your program ends up "skipping" your input the next time around your main loop.

      3. getMoveDirection: given the from and the to index positions, figure out what direction the move is. You could return the move direction as a character, using 'u' for up, 'd' for down, 'l' for left, and 'r' for right.
      4. piecesAreAdjacent: see if two index values are adjacent. They could be adjacent either horizontally or vertically.
      5. indexIsOnTheBoard: see if an index value is in the range 1..20
      6. piecesOnSameRow: see if two index values are on the same row. This is helpful for verifying jumps aren't going off the edge of the board
      7. piecesInSameColumn: see if two index values are in the same column. Similar to above.
      8. findPositionOfPieceBeingJumped: given two index values of where we are moving from and where we are moving to, find the index between those two.

  2. Chaining together method calls.
    It is convenient to chain together method calls. To illustrate this consider a section of code inside the PlayGame class that does some error checking to make sure that a player is moving a piece that is the correct color. In other words, if it is black's turn to move, the piece to be moved should have a color of "black", otherwise it is an error. Consider the definitions for the following variables:

         int sourcePosition: the index of the piece to be moved
         String playerToMove: a string representing whose move it is. This would be "black" if it is black's turn to move.
        
    The example code below calls the method theBoard.colorOfPieceAt( sourcePosition), and then uses the String result of that in a comparison to the playerToMove string. Let us examine how this connects to other parts of the project.

                // ensure sourcePosition is the same color as the playerToMove
    if( ! theBoard.colorOfPieceAt( sourcePosition).equals( playerToMove) ) {
    System.out.println("*** "+playerToMove+" you can only move your own piece. Please retry...");
    return false; // go back to prompt for another move
    }
    For reference remember the BlueJ class drawing for this project, that shows which classes use which other classes:
    BlueJ classes
    Note that the code itself is in the PlayGame class. In the PlayGame class we have declared  Board theBoard. By using theBoard we can then access the methods within the Board class, which in turn allow us to access the data defined within the Board class. The method colorOfPieceAt is one such method that is defined in the Board class. We pass to this method the parameter sourcePosition, which is the integer value of the index of the position we will be moving from, defined within the PlayGame class.
        The code for the method   colorOfPieceAt  in the Board class goes through the whitePieces and blackPieces arrays, comparing the value of parameter sourcePosition to the boardPosition of each piece. When a match is found, the color of that piece is returned as a string. 
        The string is then compared to the playerToMove (which is "white" or "black") using .equals( playerToMove), and the whole thing results in a boolean value of true or false.

         Similarly you should access whatever data you need that is defined in other classes by calling the appropriate methods in the next class in the "chain," which in turn can call another method, and so on, until you get what you want. If the methods don't exist to give you just what you want, you can wirte that method for that class. Another example of this is the code in PlayGame to actually move a piece. The code could be:
                    theBoard.getPieceAt( sourcePosition).moveTheCircle(theDirection);
    which for a sourcePosition value of 8 and a move direction value of 'r' would be:
                    theBoard.getPieceAt( 8).moveTheCircle( 'r');

  3. Turnin your program electronically using the "turnin" command from your CS account. The turnin command should be used as follows:
  4. turnin -c cs107 -p program4 Yote2
    where the directory containing your solution is called Yote2.  Within this directory the java class containing the main( ) method used to start the program must be defined in a file called PlayGame.java

[CS Dept] [UIC] [Prof. Reed]