Skip to content

Homework 5: Scrabble, Part Deux

Great job creating the first parts of Scrabble! The Letter and Hand classes you created last time are an essential part of any Scrabble game, especially Shortcut Scrabble.

Scrabble Board

Now, it's time to create the game board – where else are you supposed to put your letter tiles? Each space on the board has a point multiplier, so if a letter was placed on an x3 multiplier space, it would be worth three times the points when determining the score. You will be implementing a Board class that represents these spaces and letters.

Since we're learning about exceptions and copy constructors, we should also create a new version of Hand that includes these features. Let's call it ✨EnhancedHand✨!

For this homework, your goal is to implement the Board class and re-write parts of Hand to create a new EnhancedHand class.

Soft Submission Limit

You are limited to 10 Gradescope submissions for HW 5, after which, any additional submissions will incur a 2 pt penalty each.

You are not required to submit tests, but we strongly encourage you to write unit tests to verify your code locally and use Checkstyle in VS Code to fix any style errors, so you don't waste submissions.

Important

You will need to create a hw5 folder in your CS159/src/hws folder. All classes must be defined in the hws.hw5 package.

Please review the Grading Criteria at the end of this page before you start programming.

Submission

You will submit only two files: EnhancedHand.java, Board.java

Do not submit Letter.java, PlayScrabble.java, words.txt, WordsUtility.jar, or any other file.

You are not required to submit unit tests for this assignment.

Learning Objectives

After completing this homework, you should be able to:

  • Implement copy constructors that perform a deep copy.
  • Throw exceptions from methods.
  • Use try-catch statements to handle exceptions.
  • Write code to iterate over ArrayLists.

This assignment must be completed individually. Your work must comply with the JMU Honor Code. Authorized help is limited to general discussion on Piazza, the lab assistants assigned to CS 159, and the instructor. Copying code from someone else, including the use of generative AI, is prohibited and will be grounds for a reduced or failing grade in the course.

Getting Started

We've provided some files that you will need to complete this homework. First, follow the steps below to make sure you have downloaded all the files and put them in the correct location.

Scrabble.jar and words.txt

To help you with word validation, we've provided a .jar file containing a WordsUtility class and a text file containing a list of valid words.

  • Download Scrabble.jar and place it in your CS159/lib folder.

  • Download words.txt and place it in your CS159/hws/hw5 folder.

To use WordsUtility, call the readWords static method and pass in a file path. This method reads the file and returns an ArrayList<String> containing all valid words. The readWords method will throw a FileNotFoundException if the path is invalid, so make sure you handle this properly with a try-catch statement!

An example of calling readWords is below:

String wordsPath = "path/to/some/file.txt";
ArrayList<String> words = WordsUtility.readWords(wordsPath);

EnhancedHand.java

Download EnhancedHand.java and put it in your CS159/hws/hw5 folder. This is mostly a solution to HW 4's Hand class.

You must download this file, even if you had a correct solution to HW 4! There are differences between this file and HW 4.

Look through the code for EnhancedHand and make sure you understand our implementation. You will be modifying this code for HW 5.

Importing Letter

Since you will need to use Letter in both the Board and EnhancedHand classes, you must first import it.

Assuming that you have your code from HW 4 in the correct hw4 folder, you should be able to put this line before the class declaration in both Board.java and EnhancedHand.java:

import hws.hw4.Letter;

If you do not have a completed Letter.java from HW 4, please contact your instructor.

PlayScrabble.java

Download PlayScrabble.java and put it in your CS159/hws/hw5 folder.

Here's the big one: if you've correctly implemented both EnhancedHand and Board, you can run PlayScrabble.java and it will give you an interactive version of Scrabble to play! This is your goal: to implement the other classes so that you can actually play the game!

UML Diagram

Note that Letter and WordsUtility are included in this diagram for completeness. You do not need to implement either class.

Methods that you need to create/modify in EnhancedHand are in bold.

classDiagram
  class EnhancedHand {
    +MAX_SIZE: int = 8$
    -hand: Letter[]
    +EnhancedHand()
    +EnhancedHand(size: int)
    !+EnhancedHand(otherHand: EnhancedHand)
    +getSize() int
    +getLetter(index: int) Letter
    !+insert(letter: Letter, index: int)
    !+remove(index: int) Letter
    +indexOf(letter: char) int
    +toString() String
  }

  class Letter {
    -letter: char
    -points: int
    +Letter(letter: char, points: int)
    +setLetter(letter: char)
    +setPoints(points: int)
    +getLetter() char
    +getPoints() int
    +equals(other: Object) boolean
    +toString() String

  }

  class Board {
    -entries: Letter[]
    -pointMult: int[]
    -words: ArrayList&lt;String&gt;
    +Board(multiplier: int[], wordsPath: String)
    +Board(board: Letter[], multiplier: int[], wordsPath: String)
    +setEntries(entries: Letter[])
    +getEntries() Letter[]
    +getLetter(index: int) Letter
    +getLetterScore(index: int) int
    +getPointMult(index: int) int
    +play(letter: Letter, index: int) boolean
    +readBoardWord() String
    +wordValidation(word: String) boolean
    +getBoardScore() int
    +toString() String
  }

  class WordsUtility {
    +readWords(filename: String) ArrayList&lt;String&gt;$
  }

EnhancedHand -- Letter
Board -- Letter
Board -- WordsUtility

style Letter stroke:#f00,line:#f00,fill:#aaaaaa22
style WordsUtility stroke:#f00,line:#f00,fill:#aaaaaa22

EnhancedHand.java

Use the starter file we provided in Getting Started.

Now, create/modify only the following methods according to the descriptions below:

EnhancedHand(otherHand: EnhancedHand)

The copy constructor should initialize this EnhancedHand to be a copy of other.

However, you must implement a deep copy of the other object. That is, you cannot just say this.hand = other.hand. This copies a reference to the other array, instead of making a new array. You must create a new array of the same size and initialize it with the same letters as the other hand.

In addition, you must ensure that you create a deep copy of each individual letter, instead of just copying the references. This means you will need to invoke the Letter constructor to create a new instance of each letter when copying.

insert(letter: Letter, index: int)

This method should function the same as its previous implementation, except instead of returning a boolean value, it should throw an IllegalArgumentException if this.hand is null, index is out of range, or there is already a letter at index.

remove(index: int) Letter

This method should function the same as its previous implementation, except instead of returning null, it should throw an IllegalArgumentException if this.hand is null or index is out of range.

Board.java

Implement the methods based on the UML diagram and the method descriptions below:

Board(multiplier: int[], wordsPath: String)

The constructor should create an empty entries board that is the same length of the multiplier parameter and use the multiplier parameter to initialize the pointMult attribute. The pointMult attribute should be a new array that is a copy of the multiplier array (don't just copy the reference).

The constructor should also call the WordsUtility.readWords(...) method to initialize the words attribute. Use the wordsPath parameter as the argument to the readWords() method. Do not hardcode a string literal for the path.

If readWords() throws a FileNotFoundException, then initialize words to an empty ArrayList.

Board(board: Letter[], multiplier: int[], wordsPath: String)

The constructor should create a new entries board that is a copy of the board parameter (don't just copy the reference).

The pointMult and words attributes should be initialized the same way as the previous constructor.

Protip: Constructor Chaining

A lot of the functionality of this constructor is the same as the other one. You can shorten the amount of duplicated code by calling the other constructor from this one. You call this(...) like a method (passing in any arguments you wish), and the call must be the first statement in the constructor.

If you're interested, read more here: Java - Constructor Chaining

setEntries(entries: Letter[])

This method should set each entry in this.entries to be the same as entries. If the length of the entries parameter is not the same as the length of this.entries, this method should do nothing.

Use a for loop to set each entry. Do not just copy the reference to entries.

getEntries() : Letter[]

This method should return a reference to the entries attribute.

getLetter(index: int) : Letter and getPointMult(index: int) : int

These methods should get the value indicated by index. If index is out of bounds, an IllegalArgumentException should be thrown.

getLetterScore(index: int) : int

This method must return the point value of the letter at index multiplied by the multiplier at the same index. If index is out of bounds, or there is no letter at index, the method must return 0.

play(letter: Letter, index: int) : boolean

This method should put letter into entries at index. The method should return true if index is in bounds and there is no letter at index, otherwise it should return false.

readBoardWord() : String

This method should return a String consisting of the current word that the board represents. For instance, if entries contained letters like

[null, null, s:1, p:3, o:1, o:1, n:1, null]

then readBoardWord() should return "spoon". Note how the null entries before the first letter and after the last letter are ignored.

However, a Board may be partially filled with letters. The returned String should represent all gaps (null values) between letters with a dash (-). For instance, if entries contained

[null, null, d:2, null, g:2, s:1, null, null]

then readBoardWord() should return "d-gs". Again, the null values at the beginning and end are ignored.

If entries does not contain any letters, this method should return an empty String: "".

wordValidation(word: String) : boolean

This method should check if word exists in the words ArrayList of valid words. If so, the method should return true, and false otherwise.

You must use a for loop to iterate over the ArrayList. You may not use the .contains() or .indexOf() ArrayList methods.

getBoardScore() : int

This method will compute the total score of the word currently played on the board. However, if the current word is not valid, as determined by wordValidation(), this method should return 0.

Otherwise, it should compute the total score using both each letter's point value in entries multiplied by the corresponding point multiplier in pointMult.

For example, if we had the following entries and point multipliers,

  entries: [p:1, o:1, n:1, g:2, o:1]
pointMult: [  2,   1,   1,   2,   3]

this method would return 11 (1*2 + 1*1 + 1*1 + 2*2 + 1*3 = 11).

toString() : String

This method should return a String representing the board. The String should represent the Letters (represented by its character and point value, separated with a single colon) in entries, each followed by a comma and a single space. If an entry is null, it should be represented with a single dash (-) instead.

For example, a board with length 4 and a single Letter at index 1 (with the character of 'a' and the point value of 10) would be represented by:

-, a:10, -, -,

(Note that the String ends with a comma and a space.)

Grading Criteria

Your code will first be graded by Gradescope and then by the professor. The grade you receive from Gradescope is the maximum grade that you can receive on the assignment.

After the due date, the professor may manually review your code. At that time, points may be deducted for inelegant code, inappropriate variable names, bad comments, etc.

Your code must compile with the official tests and pass a Checkstyle audit for you to receive any points.

Gradescope will provide you with hints but might not completely identify the defects in your submission. You are expected to test your own code before submitting.

There is a soft limit of 10 submissions, after which, any additional submissions will incur a 2 pt penalty each. Points will be allocated as follows:

Criterion Points Details
Compile 0 pts Success Required
CompileOfficialTests 0 pts Success Required
Style 0 pts Success Required
OfficialTests 100 pts Partial Credit Possible