Last Updated: 2016-03-30 Wed 22:34

CS 211 Project 4: Modern Gems

CODE DISTRIBUTION

CHANGELOG

Wed Mar 30 22:34:12 EDT 2016
Fixed broken link to LookAhead1PlayerTests.java
Wed Mar 30 00:13:33 EDT 2016
Tests for the honors section classes have been added.
Mon Mar 28 11:19:39 EDT 2016
Tests for the remaining standard classes have been added.

Corrected a minor typo which referred to a Globals class which should have been ModernGems.

Thu Mar 24 00:23:41 EDT 2016
Tests for the Board class are now available.

Table of Contents

1 Modern Gems

This project will develop a reasonably large application, a novel game called Modern Gems. It is a two-dimensional tile-matching game in which players are presented with a board of tiles of various kinds of gems. The player selects one such gem which is eliminated from the board; nearby gems of the same kind which are connected to the selected gem are also eliminated. The player's score increases by the square of the number of gems eliminated. Removed gems leave holes causing remaining gems to fall. Gems first fall down into holes, then any empty columns are eliminated causing columns to the right of the gap to shift left.

A primary part of your efforts when building the project will be to create a Board class which uses a 2D array to place Gems in a grid and adjusts positions as Gems are removed. Make sure to gain familiarity with 2D arrays if you have not done so already.

This project will introduce the use of interfaces to allow interchangeable parts of the game to be adjusted. There are two interfaces for two parts of the game that can change.

Removal Policy
The game can be played according to several sets of rules, referred to as policies which pertain to which gems are connected to one another and should be removed on selecting a particular gem. You will be responsible for implementing at least two such policies, both of which implement the same RemovalPolicy interface. They are described in subsequent sections.
Interactive vs. Automated Play
Games are fun to play interactively but take on a new dimension when one attempts to write an artificial "intelligence" to make decisions instead. Code to play interactively is provided but you will be responsible for implementing a simple artificial intelligence (AI) which can play the game automatically. Interactive and AI players adhere to the interface Player.

1.1 Project Files

There are a variety of files provided for this project and part of the learning that is to occur is to examine provided code to gain experience reading work done by others. You will also need to create several files and modify those provided

File State Notes
Gem.java Provided Gems live on a Board, display as numbers; note special makeEmpty() method
Game.java Provided State of a game, provided
QuitGameException.java Provided Simple class that indicates a game should be quit, thrown by InteractivePlayer
ModernGems.java Modify Interactive main() to play the game, uncomment some lines as you finish required classes
     
Board.java Modify Board where Gems live, a few methods are provided bust implement all others
     
Player.java Interface Interface establishing capabilities of a player
InteractivePlayer.java Provided Allows humans to play the a game of Modern Gems
GreedyPlayer.java Create Simple AI which makes the highest scoring move at each step
LookAhead1Player.java Honors Honors problem: make best move based on looking ahead 1 move
Coord.java Provided Simple utility class representing row/col position; may be used while determining best moves
     
RemovalPolicy.java Interface Interface establishing methods to remove gems
SingleRemovalPolicy.java Provided Dumb rules in which only a single gem is removed per move
AdjacentRemovalPolicy.java Create Gems one position up, down, left, and right are removed if they are the same kind as center gem
WholeRowColRemovalPolicy.java Create Gems adjacent in the same row and column of the same kind are removed
ChainedRemovalPolicy.java Honors Honors problem, all gems of the same kind connected to selected gem are removed; requires recursive search
     
junit-cs211.jar Testing JUnit library for command line testing
ID.txt Create Create in setup to identify yourself

2 Gems on Boards

The Gem class is provided and does not require modification. You will want to examine the public methods contained in it to get a sense of how to use a Gems.

A Board.java file is provided which has only toString() present. You will need to implement all other methods listed in the Board class structure.

The Board class represents a 2D grid of Gem objects. It serves to manage Gem movement and removal in games. This is mainly facilitated by flagging gems for removal. Instances of the Gem class have methods to set, clear, and check whether they are flagged as below. Gems can also compare their kinds with the g.sameKind(h) method.

Welcome to DrJava.
> Gem g = new Gem(2); // gem with kind 2
> g.toString()        // not implemented
Gem@1ef62466
> g.kindString()
"2"
> g.flagged()         // is the gem flagged
false
> g.setFlag()         // flag the gem for removal
> g.flagged()
true
> g.kindString()
"2"
> g.clearFlag()       // unflag the gem
> g.flagged()
false
> Gem h = new Gem(2); // gem with kind 2
> Gem k = new Gem(4); // gem with kind 4
> g.sameKind(k)       // kind 2 not same as kind 4
false
> g.sameKind(k)       // kind 2 is same kind as 2
true
> g.flagged()         // g is flagged h is not
true                  // but they have the same kind
> h.flagged()
false

A board tracks gem positions and can access gems using the gemAt(row,col) method. It is used to mark gems and ultimately remove them. The toString() method of boards prints a nice representation of the gem layout with rows and columns numbered. Flagged gems have an * next to them. After marking gems, a board can remove them using the doRemovals() method. A demonstration is below.

Welcome to DrJava.  
// Load a board using a string; requires implementation 
// of the fromSaveString() method
> Board b = Board.fromSaveString(ModernGems.boardString("tiny"));
// Show the board
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 

> b.gemAt(1,0).setFlag()
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1* 1  1 
 2|  1  2  2 

> b.gemAt(2,2).setFlag()
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1* 1  1 
 2|  1  2  2*

> b.gemAt(2,1).setFlag()
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1* 1  1 
 2|  1  2* 2*

> b.gemAt(2,1).clearFlag()  // Don't remove previous gem
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1* 1  1 
 2|  1  2  2*

> b.gemAt(2,0).setFlag()
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1* 1  1 
 2|  1* 2  2*

> b.doRemovals()  // Remove all flagged gems
> b
     0  1  2
   ---------
 0|     3    
 1|     1  2 
 2|  1  2  1 

// Gems shift down when lower gems have been removed

> b.gemAt(2,0).setFlag()
> b
     0  1  2
   ---------
 0|     3    
 1|     1  2 
 2|  1* 2  1 

> b.doRemovals();
> b
     0  1  2
   ---------
 0|  3       
 1|  1  2    
 2|  2  1    

// Gems shift left when a whole column is eliminated.

Importantly, all parts of the board should always have proper gems in them. A call to gemAt(row,col) should always return a gem, but they may be gems of a special kind, empty gems which are used as fillers (as opposed to null values). One can check positions on a board for valid gems which are non-empty using a call to validGemAt(row,col) which returns true when the position is in bounds and a non-empty gem resides at that position. The below code demonstrates these methods as well as how to create empty gems.

> b
     0  1  2
   ---------
 0|     3    
 1|     1  2 
 2|  1  2  1 

> b.gemAt(0,0)  // Tile 0,0 has a Gem but it is empty
Gem@6ab63d99
> b.gemAt(0,0).kindString()
""
> b.gemAt(0,0).isFilled()
false
> b.validGemAt(0,0)
false

> b.gemAt(0,1).isFilled() // Tile 0,1 has a filled gem
true
> b.gemAt(0,1).kindString()
"3"
> b.validGemAt(0,1)
true

// validGemAt(r,c) works for out of bounds access too
> b.validGemAt(0,3) 
false
> b.validGemAt(0,-1)
false
> b.validGemAt(-1,0)
false

// Empty gems can be created using Gem.makeEmpty()
> Gem empty = Gem.makeEmpty();  
> empty.isFilled()
false
> empty.kindString()
""

2.1 Implementation Notes

You will need to implement most of the functionality of the Board class aside from the provided toString() method. The most obvious feature is a field which is a 2D array of gems. There are no public methods to add or alter gems on the board, only a gemAt(r,c) accessor and the ability to flag gems and call doRemovals() to get rid of them.

It is likely that you will need to spend significant time on doRemovals() as getting the code right here takes some care. Divide the process into three phases as suggested in the comment string below:

  1. Replace all flagged Gems with empty Gems. Remember, there are no null values in the Board, only empty gems produced with Gem.makeEmpty().
  2. Shift Gems down in their columns so that all empty Gems are at the top of columns.
  3. If any columns are empty, shift remaining columns left to fill in the gaps.

It is strongly suggested that you write a few private helper methods to assist you with this process.

There are two ways to create Boards. There is a constructor which takes a 2D array of ints for ease of construction with negative ints representing empty gems.

The other way to create a board initially is to parse one from a String. The toString() method prints a pretty version of the board to look at but is a bit tedious to parse. Boards have a saveString() which presents their their contents in an easier fashion for a Scanner to read. This method can be used to save a board during a game. It is also then possible to load a board using the static method Board.fromSaveString(s) which should use a Scanner to parse the given string and return a board based on its contents. The main game in ModernGems.java relies on fromSaveString() and it is probably the easiest way to quickly make boards of your own.

Once you have created a board, it should also be possible to make copies of it with the board.clone() method which returns a distinct copy of the original. All internal data, including the arrays and gems must be copied to the clone so that the boards act distinctly such as the following example.

Welcome to DrJava. 
> Board x = Board.fromSaveString(ModernGems.boardString("tiny"));
> x
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 

> x.gemAt(1,1).setFlag();
> x
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1* 1 
 2|  1  2  2 

> Board y = x.clone();
> y
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1* 1 
 2|  1  2  2 

> y.doRemovals();
> y
     0  1  2
   ---------
 0|  1     2 
 1|  1  3  1 
 2|  1  2  2 

> x
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1* 1 
 2|  1  2  2

Try to finish the Board class quickly as it is difficult to complete the other classes without a working Board.

2.2 (20%) Board Manual Inspection Criteria   grading

  • doRemovals() is broken into clear logical steps. Documentation is provided which describes the process. The code is reasonably short and self-contained or uses well-documented helper methods to make the main main body of the method concise. Excessive looping is avoided: careful thought has been applied to recognize an effective loop ordering that minimizes the number of iterations required to get gems to fall down and slide left.
  • fromSaveString() is broken into several logical steps and clearly commented as to what phase (counting vs reading) is being executed.
  • The clone() method clearly makes a deep and distinct copy, allocating a fresh 2D array of gems and cloning the original gems into this array.
  • Fields of the class are documented with how they are used.

2.3 Class Structure of Board Class

public class Board {

  // Convenience constructor to build from a 2D array of ints. If
  // negative int appears, treat it as an empty gem. Otherwise, the
  // numbers represent the kinds of the gems to be created.
  public Board(int g[][]);

   // Make a deep copy of this Board. Requires that the 2D array of
  // gems be copied entirely to prevent shallow references to the same
  // arrays.  One of the required constructors is useful for this method.
  public Board clone();

  // Access the number of rows and columns. Assumes all rows have equal length.
  public int getRows();

  public int getCols();

  // True if the given position is in bounds and contains a gem which
  // can be removed. False if the row/col is out of bounds or the
  // board is empty at the specified position.
  public boolean validGemAt(int r, int c);

  // Return true if at least one gem exists on the board. False if all
  // board positions are empty.
  public boolean hasValidGem();

  // Retrieve gem at given location. Do not do bounds checking; out of
  // bounds positions should automatically raise an
  // IndexOutOfBoundsException.
  public Gem gemAt(int i, int j);

  // Clear all flags of gems on the board by invoking their
  // clearFlag() method.
  public void clearFlags();

  // Any gem flagged for removal will be removed.  Blocks that should
  // "fall" will do so and columns that should shift left do so.
  // Example: The board below has 4 Gems marked with X's which are
  // flagged.
  // 
  //     0  1  2  3  4
  //   ---------------
  // 0|  4     4      
  // 1|  9  1  6     2
  // 2|  6  5  1     9
  // 3|  2  5  5  x  x
  // 4|  5  9  x  x  x
  //
  // Calling doRemovals() should first remove them from the board
  // creating gaps.
  //
  //     0  1  2  3  4
  //   ---------------
  // 0|  4     4      
  // 1|  9  1  6     2
  // 2|  6  5  1     9
  // 3|  2  5  5      
  // 4|  5  9         
  //
  // Gems should then fall downward.
  //
  //     0  1  2  3  4
  //   ---------------
  // 0|  4           
  // 1|  9  1  4     
  // 2|  6  5  6      
  // 3|  2  5  1     2
  // 4|  5  9  5     9  
  //
  // Then any empty colums should be eliminated by shifting left.
  // 
  //     0  1  2  3  4
  //   ---------------
  // 0|  4           
  // 1|  9  1  4     
  // 2|  6  5  6   
  // 3|  2  5  1  2
  // 4|  5  9  5  9  
  //
  // You may wish to write some private helper methods to help break
  // this task down into manageable chunks.
  public void doRemovals();

  // Convert to a simple saveable string. This string should have each
  // gem space separated for easy reading with Scanner. Empty
  // locations on the board should be denoted by a period (.) as in
  // the following sample:
  //
  // . . 4 . . 
  // . 10 6 8 2 
  // . 5 1 5 9 
  // 2 5 5 8 4 
  // 5 9 4 9 5 
  // 
  // Each line ends with a newline (\n) character.
  public String saveString();

  // Create a board from the given string.  The format accepted should
  // be identical to what is produced by saveString(). Parsing this
  // string will require two passes through the string, first to count
  // the size, then to read the gems and spaces.
  public static Board fromSaveString(String s);

  // Implementation Provided. Fancy display string version of the
  // board; assumes gem kinds fit in 2 chars. Flagged gems have an
  // asterisk put to the right of them.
  public String toString();

}

3 Full Games of Modern Gems

The provided class Game.java encapsulates all the functionality and state associated with an individual game. Game.java does not require modification but you should examine is at you will need to use public methods within it for other parts of the project.

Once the Board class is up and running, you should be able to play a game of Modern Gems using the provided ModernGems.java file. ModernGems.main() will prompt for user input and about new games, loading from files, and other niceties that are already built for you. It creates instances of Game and allows them to played, saved, or quit midstream.

Initially running the main() method provides a set of choices about which of the pre-built boards to play on along with single choices of playing according to the SingleRemovalPolicy interactively with the InteractivePlayer class. As you complete further parts of this project, you will should uncomment lines near the top ModernGems.java so that classes you finish such as GreedyPlayer and AdjacentRemovalPolicy appear as options for play.

A First a Demonstration:

> javac ModernGems.java
> java ModernGems
----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy 
Policy choice:
>>> SingleRemovalPolicy
Select the player from the following:
  InteractivePlayer 
Player choice:
>>> InteractivePlayer

Let's play!!

Single gems are removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 1 1

-- Move   1 --
Current score: 1
     0  1  2
   ---------
 0|  1     2 
 1|  1  3  1 
 2|  1  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 1

-- Move   2 --
Current score: 2
     0  1  2
   ---------
 0|  1     2 
 1|  1     1 
 2|  1  3  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 1

-- Move   3 --
Current score: 3
     0  1  2
   ---------
 0|  1  2    
 1|  1  1    
 2|  1  2    

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 1 1

-- Move   4 --
Current score: 4
     0  1  2
   ---------
 0|  1       
 1|  1  2    
 2|  1  2    

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0

-- Move   5 --
Current score: 5
     0  1  2
   ---------
 0|          
 1|  1  2    
 2|  1  2    

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0

-- Move   6 --
Current score: 6
     0  1  2
   ---------
 0|          
 1|     2    
 2|  1  2    

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0

-- Move   7 --
Current score: 7
     0  1  2
   ---------
 0|          
 1|  2       
 2|  2       

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0

-- Move   8 --
Current score: 8
     0  1  2
   ---------
 0|          
 1|          
 2|  2       

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0
Final Score: 9

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> quit
Thanks for playing!

4 Making the Game More Interesting

Initially, the only way to play Modern Gems is interactively via InteractivePlayer according to the SingleRemovalPolicy which boringly allows only single gems to be removed at once scoring one point per removal.

The remainder of the project involves implementing several ways to play. On completion of all parts (including the honors section problems), the following options for play will be available.

Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 

Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player

The last classes in each line (ChainedRemovalPolicy and LookAhead1Player) are for the honors section only.

The following sections discuss these two feature sets, the Removal Policy and the Players.

5 The RemovalPolicy interface and SingleRemovalPolicy

Several sets of rules can be applied to Modern Gems play which are more engaging than the SingleRemovalPolicy. Each of these rule sets must implement the RemovalPolicy interface which is below.

// Interface specifying methods required for any gem removal policy in
// Modern Gems. All methods must be implemented.
public interface RemovalPolicy{

  // Flag all gems that would be removed if location (r,c) were
  // selected by this policy.
  public void flagConnectedGems(int row, int col, Board b);

  // Provides the score for the suggested (r,c) move. The gems
  // involved in the move may be flagged in the process or may remain
  // unflagged.
  public int scoreMove(int row, int col, Board b);

  // a human-friendly description of this policy's rules.
  public String description();

  // return the name of the class
  public String toString();

  // return the name of the class
  public String saveString();

}

The primary methods of interest are

  • flagConnectedGems(row,col,board) which will flag gems that should be removed according to the policy starting with the gem specified at row,col on the given board.
  • scoreMove(row,col,board) which calculates and returns how many points would be earned by removing the gems centered at the given position.

It is instructive to examine the provided SingleRemovalPolicy to see some of the techniques that will be used in all of the Removal Policies however it is a simpler class than the rest will be.

6 AdjacentRemovalPolicy

The Adjacent Removal Policy states that gem G is connected to the gems directly above, below, to the right, and to the left of it so long as those gems exist and are of the same kind (same number). Connected gems are flagged and removed. Roughly, the player specifies the center of a "plus" shape to remove with portions cut off for out of bounds access or mismatched gem types. Importantly, points are scored as the square of the number of gems removed. Since the player can remove up to 5 gems in a single move with the AdjacentRemovalPolicy, good moves can net up to 5*5 = 25 points per move.

Below is a short demo game demonstrating this policy. The play is not optimal but is representative of the game mechanics.

java ModernGems
----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> AdjacentRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> InteractivePlayer

Let's play!!

Adjacent gems of the same kind will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 1 1

-- Move   1 --
Current score: 9
     0  1  2
   ---------
 0|          
 1|  1  3  2 
 2|  1  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 2

-- Move   2 --
Current score: 18
     0  1  2
   ---------
 0|          
 1|  1       
 2|  1  3    

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0

-- Move   3 --
Current score: 22
     0  1  2
   ---------
 0|          
 1|          
 2|  3       

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0
Final Score: 23

6.1 Implementing AdjacentRemovalPolicy

Create the file AdjacentRemovalPolicy.java and fill in the required methods of the RemovalPolicy interface. You may wish to start from the SingleRemovalPolicy and make adjustments. Make sure that you calculate the score as the square of the number of gems that would be removed.

Both flagConnectedGems(r,c,b) and scoreMove(r,c,b) should assume that there may already be some flagged gems on the board, and should clear them out. Use a method of Board to do this easily.

The Board method validGemAt(r,c) is useful for checking neighbors of the center gem at (r,c).

Once you have finished the AdjacentRemovalPolicy, uncomment the associated line in ModernGems.java to enable the policy to be used.

  // Supported RemovalPolicy classes 
  public static RemovalPolicy [] policies = {
    new SingleRemovalPolicy(),

    // Uncomment below as you finish classes
    new AdjacentRemovalPolicy(),
    // new WholeRowColRemovalPolicy(),

    // Honors Class
    // new ChainedRemovalPolicy() 
  };

You can test your code in DrJava's interactive loop using the following style of code.

> Board b = Board.fromSaveString(ModernGems.boardString("tiny"));
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 

> RemovalPolicy p = new AdjacentRemovalPolicy();
> p.flagConnectedGems(0,0,b)
> b
     0  1  2
   ---------
 0|  1* 3  2 
 1|  1* 1  1 
 2|  1  2  2 

> p.flagConnectedGems(0,1,b)
> b
     0  1  2
   ---------
 0|  1  3* 2 
 1|  1  1  1 
 2|  1  2  2 

> p.flagConnectedGems(1,0,b)
> b
     0  1  2
   ---------
 0|  1* 3  2 
 1|  1* 1* 1 
 2|  1* 2  2 

// Scoring moves leave gems flagged
> p.scoreMove(0,0,b)
4
> b
     0  1  2
   ---------
 0|  1* 3  2 
 1|  1* 1  1 
 2|  1  2  2 

> p.scoreMove(1,0,b)
16
> b
     0  1  2
   ---------
 0|  1* 3  2 
 1|  1* 1* 1 
 2|  1* 2  2 

> p.scoreMove(2,2,b)
4
> b
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2* 2*

6.2 (5%) AdjacentRemovalPolicy Manual Inspection Criteria   grading

  • While performing scoreMove(r,c,b) and flagConnectedGems(r,c,b), appropriate methods of Board are used to access gems and determine if valid gems exist at various positions.
  • Appropriate methods of Gem are employed to determine if gems are the same kind and flag them.
  • Any existing flags on the Board are cleared before calculating a score or flagging new gems.

7 WholeRowColRemovalPolicy

An alternative to the the adjacent gem policy is the Whole Row-Column Removal Policy which states all gems continuously connected by the same kind in a row or column are connected. The scoring is calculated as the square of the number of gems removed so that long rows and columns of same-kind gems net large scores.

Here is a brief example.

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> WholeRowColRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> InteractivePlayer

Let's play!!

Adjacent gems in whole row/column will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 1 0

-- Move   1 --
Current score: 25
     0  1  2
   ---------
 0|          
 1|  3  2    
 2|  2  2    

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 1

-- Move   2 --
Current score: 34
     0  1  2
   ---------
 0|          
 1|          
 2|  3       

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 0
Final Score: 35

In Move 0, there is a continous chain of gem kind 1 along the row 1 and in column 0 so that the selected center (1,0) removes all 5 gems for a score of 25 points.

7.1 Implementing the WholeRowColRemovalPolicy

Create the file WholeRowColRemovalPolicy.java and fill in the required methods of the RemovalPolicy interface.

Several loops will be required for flagging gems and scoring to move up, down, left, and right from the central gem. As with AdjacentRemovalPolicy, the board method validGemAt(r,c) is useful in these loops. Don't forget to check that the kind of the gems match the center gem as well.

When you are finished with the WholeRowColRemovalPolicy, uncomment the associated line in ModernGems.java to enable the policy to be used.

  // Supported RemovalPolicy classes 
  public static RemovalPolicy [] policies = {
    new SingleRemovalPolicy(),

    // Uncomment below as you finish classes
    new AdjacentRemovalPolicy(),
    new WholeRowColRemovalPolicy(),

    // Honors Class
    // new ChainedRemovalPolicy() 
  };

7.2 (5%) WholeRowColRemovalPolicy Manual Inspection Criteria   grading

  • scoreMove(r,c,b) and flagConnectedGems(r,c,b) share some implementation such as a private helper method or one calling the other. The loops required to search whole rows and columns from the center gem are clearly documented and follow a logical structure.
  • While performing scoreMove(r,c,b) and flagConnectedGems(r,c,b), appropriate methods of Board are used to access gems and determine if valid gems exist at various positions.
  • Appropriate methods of Gem are employed to determine if gems are the same kind and flag them.
  • Any existing flags on the Board are cleared before calculating a score or flagging new gems.

8 Honors Section: ChainedRemovalPolicy

The central idea of this policy is that any chain of gems are all marked for removal. This means that any gem of the same kind that is up/down/left/right of the selected gem is connected as is any gem that is up/down/left/right of that gem and so forth. This is perhaps the most interesting rule set to play under as it allows one to strategically remove gems to set up long chains. This concept is best illustrated with a picture on the "curve" board.

Welcome to DrJava. 
> Board b = Board.fromSaveString(ModernGems.boardString("curve"));
> b
     0  1  2
   ---------
 0|  1  1  2 
 1|  2  1  1 
 2|  2  2  1 

> RemovalPolicy p = new ChainedRemovalPolicy();

// Flag all 1's connected to upper left gem
> p.flagConnectedGems(0,0,b);
> b
     0  1  2
   ---------
 0|  1* 1* 2 
 1|  2  1* 1*
 2|  2  2  1*

> b.clearFlags()
> b
     0  1  2
   ---------
 0|  1  1  2 
 1|  2  1  1 
 2|  2  2  1 

// Flag all gems connected to lower right 1
> p.flagConnectedGems(2,2,b);
> b
     0  1  2
   ---------
 0|  1* 1* 2 
 1|  2  1* 1*
 2|  2  2  1*

> b.clearFlags();
> b
     0  1  2
   ---------
 0|  1  1  2 
 1|  2  1  1 
 2|  2  2  1 

// Flag all 2's connected to middle bottom gem
> p.flagConnectedGems(2,1,b);
> b
     0  1  2
   ---------
 0|  1  1  2 
 1|  2* 1  1 
 2|  2* 2* 1 

> Board b = Board.fromSaveString(ModernGems.boardString("cross"));
> b
     0  1  2  3  4
   ---------------
 0|  1  1  2  1  1 
 1|  1  1  2  1  1 
 2|  2  2  2  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 

> p.flagConnectedGems(1,1,b);
> b
     0  1  2  3  4
   ---------------
 0|  1* 1* 2  1  1 
 1|  1* 1* 2  1  1 
 2|  2  2  2  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 

> b.clearFlags();
> p.flagConnectedGems(2,3,b);
> b
     0  1  2  3  4
   ---------------
 0|  1  1  2* 1  1 
 1|  1  1  2* 1  1 
 2|  2* 2* 2* 2* 2*
 3|  1  1  2* 1  1 
 4|  1  1  2* 1  1

8.1 Implementing ChainedRemovalPolicy

Computing the connected gems according to the Chained Gem Policy is visually straight-forward but more difficult algorithmically. Visually, one starts at the selected gem and looks above, below, left, and right. For any valid gem of the same kind that is present, repeat the process of looking above, below, left, and right. This goes on until an invalid gem is found at which point the chain stops.

The process is readily described by a recursive function and life is much simpler if one writes a private recurisve method to help this process. The method ChainedRemovalPolicy.flagConnectedGems(..) will simply call the auxiliary recursive method.

Use the fact that you can flag gems to keep track of which gems have been visited so as not search the same location more than is necessary. Also terminate recursive calls when a gem of a different kind is reached or an invalid gem is found (empty or out of bounds).

8.2 (5%) ChainedRemovalPolicy Manual Inspection Criteria   grading

  • Clear recursion is employed to perform the search for connected gems
  • A combination of gem flags, gem kind, and validGemAt() are used to terminate the recursion on relevant cases (already visited gem, different kind gem, empty gem or out of bounds).
  • The recursion traverses in the 4 cardinal directions: up down left and right of a central gem.
  • A helper method is used to facilitate the recursion.
  • Code is shared between scoreMove(r,c,b) and flagConnectedGems(r,c,b) via a helper method to avoid duplication.

8.3 Games played with ChainedRemovalPolicy

Here are two example session using two different boards (curve and skinny) to further demonstrate the nature of the ChainedRemovalPolicy.

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> curve
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> ChainedRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> InteractivePlayer

Let's play!!

All gems connected by any adjacency chain will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  1  2 
 1|  2  1  1 
 2|  2  2  1 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 2 2

-- Move   1 --
Current score: 25
     0  1  2
   ---------
 0|          
 1|  2       
 2|  2  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 1 0
Final Score: 41

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> skinny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> ChainedRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> InteractivePlayer

Let's play!!

All gems connected by any adjacency chain will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  3  3  2 
 1|  3  2  3 
 2|  1  2  1 
 3|  2  2  2 
 4|  2  1  3 
 5|  1  1  2 
 6|  2  1  1 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 5 0

-- Move   1 --
Current score: 25
     0  1  2
   ---------
 0|          
 1|  3     2 
 2|  3     3 
 3|  1  3  1 
 4|  2  2  2 
 5|  2  2  3 
 6|  2  2  2 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 6 0

-- Move   2 --
Current score: 89
     0  1  2
   ---------
 0|          
 1|          
 2|          
 3|        2 
 4|  3     3 
 5|  3     1 
 6|  1  3  3 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 6 0

-- Move   3 --
Current score: 90
     0  1  2
   ---------
 0|          
 1|          
 2|          
 3|        2 
 4|        3 
 5|  3     1 
 6|  3  3  3 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 5 2

-- Move   4 --
Current score: 91
     0  1  2
   ---------
 0|          
 1|          
 2|          
 3|          
 4|        2 
 5|  3     3 
 6|  3  3  3 

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 6 1

-- Move   5 --
Current score: 116
     0  1  2
   ---------
 0|          
 1|          
 2|          
 3|          
 4|          
 5|          
 6|  2       

Choices:
move row col        : remove gem at row/col position
save filename.mg    : save game and continue playing
quit                : quit current game
>>> move 6 0
Final Score: 117

9 The Player Interface and InteractivePlayer

The Player interface shows the capabilities any player, human or computer, must provide to play a full game of Modern Gems.

// Interface to which all player classes must adhere
public interface Player{

  // Perform a single move in the given game. May throw a
  // QuitGameException to indicate that no move is desired.
  public void executeMove(Game game);

  // Return the name of class name of the Player
  public String toString();

  // Return the class name of the Player
  public String saveString();

}

The executeMove(game) method is the only one of great consequence. In it, the Player is handed a Game which provides the board via game.getBoard() as well as the RemovalPolicy, score, and move number via other accessor methods. The player is responsible for determining a single move to make and adjusting the game state. The easiest way to do this is via the provided Game method removeGemAdjustScore(row,col) which scores the move and removes the gems centered at the given position.

The logic of determining which move to make is dictated by whatever code can be incorporated into the executeMove(game) method of the player. In the InteractivePlayer this is done by querying a human for input. However, the GreedyPlayer and LookAhead1Player will make their own decisions by examining the board to find a decent scoring move.

10 GreedyPlayer Implementation

A simple approach to playing Modern Gems is to remove the gem at each turn which would score the most points. In computing, this is known as a greedy approach as the best-looking decision at a given moment is made without looking ahead. Greedy approaches are often easy to implement but less often provide optimal results as is the case here: even for simple boards a GreedyPlayer will make short-sited moves which result in a lower score than would be gained via a little foresight.

Create a GreedyPlayer.java and fill in the required methods of the Player class. The most significant aspect of Players are their executeMove(game) method which determines which gem to remove then alters the game accordingly, likely with a call to game.removeGemAdjustScore(row,col).

For the GreedyPlayer, us a set of loops to consider each gem on the board. Use accessor methods of the game to get the board, iterate through each of its positions, and use the RemovalPolicy provided by the game to score each move. Select the gem which produces the highest score and remove it.

If multiple gem positions would result in the same maximum score, favor gems that are higher first, then gems that are farther left (lower numbered rows, then lower numbered columns) which are the gems that would be seen "first" in the natural set of iterations used to tr avers the board.

Keep in mind that purpose of a Player is to make a single move, not play an entire game. A single call to executeMove(game) should make one move only. Printing information to the screen while your GreedyPlayer decides which gem to remove can be helpful to debug its behavior in the event of a malfunction.

You may find it useful to use the Coord class which is a pair of row/col ints which can make it a bit more convenient to track promising gem positions.

Once you have finished the AdjacentRemovalPolicy, uncomment the associated line in ModernGems.java to enable the policy to be used.

  // Supported Player classes
  public static Player [] players = {
    new InteractivePlayer(stdin),
    // Uncomment below as you finish classes
    new GreedyPlayer(),

    // Honors Class
    // new LookAhead1Player(),
  };

10.1 (5%) GreedyPlayer Manual Inspection Criteria   grading

  • executeMove() employs a clear set of loops search the board for all possible moves.
  • Appropriate methods are used to to extract information from the parameter Game such as the Board and RemovalPolicy.
  • Appropriate methods of Board are used to access gems and determine if valid gems exist at various positions.

10.2 Sample Games by GreedyPlayer

Below is a sample of how the GreedyPlayer makes moves on several different boards according to the different rules.

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> AdjacentRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> GreedyPlayer

Let's play!!

Adjacent gems of the same kind will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 


-- Move   1 --
Current score: 16
     0  1  2
   ---------
 0|     2    
 1|  3  1    
 2|  2  2    


-- Move   2 --
Current score: 20
     0  1  2
   ---------
 0|          
 1|     2    
 2|  3  1    


-- Move   3 --
Current score: 21
     0  1  2
   ---------
 0|          
 1|          
 2|  3  1    


-- Move   4 --
Current score: 22
     0  1  2
   ---------
 0|          
 1|          
 2|  1       

Final Score: 23

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> WholeRowColRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> GreedyPlayer

Let's play!!

Adjacent gems in whole row/column will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 


-- Move   1 --
Current score: 25
     0  1  2
   ---------
 0|          
 1|  3  2    
 2|  2  2    


-- Move   2 --
Current score: 34
     0  1  2
   ---------
 0|          
 1|          
 2|  3       

Final Score: 35

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> almostCross
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> AdjacentRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> GreedyPlayer

Let's play!!

Adjacent gems of the same kind will be removed

-- Move   0 --
Current score: 0
     0  1  2  3  4
   ---------------
 0|  1  1  2  1  1 
 1|  1  1  2  1  1 
 2|  2  2  1  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 


-- Move   1 --
Current score: 9
     0  1  2  3  4
   ---------------
 0|        2  1  1 
 1|     1  2  1  1 
 2|  2  2  1  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 


-- Move   2 --
Current score: 18
     0  1  2  3  4
   ---------------
 0|        2       
 1|     1  2     1 
 2|  2  2  1  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 


-- Move   3 --
Current score: 27
     0  1  2  3  4
   ---------------
 0|        2       
 1|        2     1 
 2|     1  1  2  2 
 3|     2  2  1  1 
 4|  2  1  2  1  1 


-- Move   4 --
Current score: 36
     0  1  2  3  4
   ---------------
 0|                
 1|              1 
 2|        2  2  2 
 3|     1  2  1  1 
 4|  2  1  1  1  1 


-- Move   5 --
Current score: 52
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|              1 
 3|     1  2     2 
 4|  2  1  2  2  1 


-- Move   6 --
Current score: 61
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|        1       
 3|     1  2       
 4|  2  1  1       


-- Move   7 --
Current score: 70
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|                
 3|     1          
 4|  2  2          


-- Move   8 --
Current score: 74
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|                
 3|                
 4|  1             

Final Score: 75

11 Honors Section: LookAhead1Player

As noted above, the GreedyPlayer does not play optimally even on simple boards. An improvement on this comes in the form of a player that looks ahead, in this case by 1 move. The general strategy is to score two moves at a time.

  • Consider a gem at (r1,c1) and calculate its score s1
  • Then consider the state of the board select another move (r2,c2) and calculate its score s2
  • The goodness of the pair of moves (r1,c1) and (r2,c2) is simply s1+s2.
  • Whichever pair of moves results in the highest combined score is selected for actual execution.

The need to look ahead a move presents a problem: once gems are removed from a board, they cannot be "put back". Instead, consider the use of the board.clone() method which creates an identical but distinct copy of a board which can be manipulated independently of the original. A sensible strategy is to establish a series of loops.

  • An outer set of loops iterates over all possible (r1,c1) moves. A clone is made of the original board and then the move (r1,c1) is made.
  • An inner set of loops iterates over all possible (r2,c2) positions similarly, cloning and scoring moves s2.
  • Whichever move pair ultimately yields the highest score is selected to actually execute on the original board.

Looking ahead is more costly in terms of code to write and execution time (many more loop iterations than the greedy player) but can yield higher scores.

11.1 (5%) Honors: LookAhead1Player Manual Inspection Criteria   grading

  • Steps are clearly taken to calculate the highest scoring pair of moves possible.
  • Clear documentation is present to assist understanding the approach.
  • Code is reasonably concise. Employ helper methods to encapsulate common tasks if a method body gets too long.
  • Appropriate methods of Board and Gem are employed to query their structure.

11.2 Sample Games by LookAhead1Player

Below are examples of how the LookAhead1Player executes on some boards. Note that in several cases, the LookAhead1Player exceeds the score achieved by the GreedyPlayer.

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> AdjacentRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> LookAhead1Player

Let's play!!

Adjacent gems of the same kind will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 


-- Move   1 --
Current score: 16
     0  1  2
   ---------
 0|     2    
 1|  3  1    
 2|  2  2    


-- Move   2 --
Current score: 17
     0  1  2
   ---------
 0|          
 1|  3  2    
 2|  2  2    


-- Move   3 --
Current score: 18
     0  1  2
   ---------
 0|          
 1|     2    
 2|  2  2    

Final Score: 27

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> tiny
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> WholeRowColRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> LookAhead1Player

Let's play!!

Adjacent gems in whole row/column will be removed

-- Move   0 --
Current score: 0
     0  1  2
   ---------
 0|  1  3  2 
 1|  1  1  1 
 2|  1  2  2 


-- Move   1 --
Current score: 25
     0  1  2
   ---------
 0|          
 1|  3  2    
 2|  2  2    


-- Move   2 --
Current score: 26
     0  1  2
   ---------
 0|          
 1|     2    
 2|  2  2    

Final Score: 35

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit
>>> new
Select the board from the following:
  tiny cross almostCross curve normal skinny large 
Board choice:
>>> almostCross
Select the removal policy from the following:
  SingleRemovalPolicy AdjacentRemovalPolicy WholeRowColRemovalPolicy ChainedRemovalPolicy 
Policy choice:
>>> AdjacentRemovalPolicy
Select the player from the following:
  InteractivePlayer GreedyPlayer LookAhead1Player 
Player choice:
>>> LookAhead1Player

Let's play!!

Adjacent gems of the same kind will be removed

-- Move   0 --
Current score: 0
     0  1  2  3  4
   ---------------
 0|  1  1  2  1  1 
 1|  1  1  2  1  1 
 2|  2  2  1  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 


-- Move   1 --
Current score: 4
     0  1  2  3  4
   ---------------
 0|        2  1  1 
 1|  1  1  2  1  1 
 2|  1  1  1  2  2 
 3|  1  1  2  1  1 
 4|  1  1  2  1  1 


-- Move   2 --
Current score: 29
     0  1  2  3  4
   ---------------
 0|           1  1 
 1|        2  1  1 
 2|  1     2  2  2 
 3|  1     2  1  1 
 4|  1  1  2  1  1 


-- Move   3 --
Current score: 45
     0  1  2  3  4
   ---------------
 0|              1 
 1|           1  1 
 2|  1        1  2 
 3|  1        1  1 
 4|  1  1  2  1  1 


-- Move   4 --
Current score: 54
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|  1        1  2 
 3|  1        1  1 
 4|  1  1  2  1  1 


-- Move   5 --
Current score: 63
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|        1  2    
 3|        1  1    
 4|  1  2  1  1    


-- Move   6 --
Current score: 64
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|        1       
 3|        1  1    
 4|  1  2  1  1    


-- Move   7 --
Current score: 80
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|                
 3|                
 4|  1  2  1       


-- Move   8 --
Current score: 81
     0  1  2  3  4
   ---------------
 0|                
 1|                
 2|                
 3|                
 4|  1  1          

Final Score: 85

----------------------
Welcome to Modern Gems
----------------------
Select from:
     new: Start a new game
    load: Load an existing game
    quit: Quit

12 (50%) Automated Tests   grading

  • We are providing a battery of unit tests in the various files, all of which may be run via P3Tests.java which will be used by graders to evaluate your code. You may want to begin studying how these tests work and experiment with your own. (See the specification for project one for instructions on running JUnit tests.)
  • Tests may be expanded as the HW deadline approaches.
  • It is your responsibility to get and use the freshest set of tests available.
  • Tests will be provided in source form so that you will know what tests are doing and where you are failing.
  • Code that does not compile and run tests according to the specified command line invocation may lose all automated testing credit. Graders will usually try to fix small compilation errors such as bad directory structures or improper use of packages. Such corrections typically result in a loss of 5-10% credit on automated testing. However, if more than a small amount of error to fix problems seems required, no credit will be given.

12.1 (5%) Honors Section: Automated Tests   grading

Passing all honors section tests is worth 5% of of the honors section grading. The manual inspection of ChainedRemovalPolicy and GreedyPlayer are each worth 5% for a total of the extra 15% for honors section students.

13 (50%) Manual Inspection Criteria

The following features will be evaluated during manual inspection of your code and analysis documents. It is worth a total of 50% of the overall grade for this project.

Several sections above are marked grading and pertain to manual inspection as do the following criteria.

13.1 (10%) Coding Style and Readability   grading

There are a few more points for coding style and readability this time. You are creating many more files from scratch this time, and we want to see you creating well organized, readable code. Comment meaningfully and adequately. We won't deduct points for too many comments and will give feedback if comments are too abundant and not meaningful.

Class Documentation (2%)
Each class has an initial comment indicating its intended purpose, how it works, and how it relates to or uses other classes in the project.
Field Documentation (2%)
Each field of a class is documented to indicate what piece of data is tracked and how it will be used. Both public and private fields are documented.
Method documentation (3%)
Each method has a short description indicating its intended purpose and how it gets its job done.
Code Cleanliness (3%)
Indent and {bracket} code uniformly throughout the program to improve readability.

13.2 (5%) Correct Project Setup   grading

Correctly setting up the directory structure for each project greatly eases the task of grading lots of projects. Graders get cranky when strange directory structures or missing files are around and you do not want cranky graders. The following will be checked for setup on this project.

  • The Setup instructions were followed closely
  • The Project Directory is named according to the specification
  • There is an Identity Text File present with the required information in it
  • Code can be compiled and tests run from the command line

14 Project Submission

As in previous project

  • Use a project folder named as ckauffm2-p4 with your own NetID substituted.
  • Include and ID.txt file with your information in it.
  • Zip the project folder and submit it to blackboard by the deadline.

Author: Mark Snyder, Chris Kauffman (msnyde14@gmu.edu, kauffman@cs.gmu.edu)
Date: 2016-03-30 Wed 22:34