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.
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<String>
+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<String>$
}
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 |