Junior DDM: Crossword Puzzle

To create a working program that allows users to solve crossword puzzles, I had to use several different techniques we’ve learned. First of all, the crossword puzzle’s data is stored in a file that needs to be read, so I used a Scanner in the FileUtils class to read each line and create the Clue objects that form the puzzle. Each Clue object has the row and column of the crossword’s 2D array to start the word in, the direction it goes in, the word number, the word itself, and an actual clue alluding to the word, all separated by a delimiter, in this case a colon. Additionally, when a Clue is initialized, the instance boolean solved is automatically set to false, which will come in handy later.

To actually set up the 2D array, I iterated through an array of all the clues and used the starting position, direction, and word length to find the farthest row and column needed. The 2D array was made up of squares, which contain a char. I’m not quite sure what the point of the Square class is, as using a char itself would work fine, but having it will make it easier to implement more features if necessary. Them using chars unfortunately meant that word numbers could not go past 9. 

Then, with the size of the 2D array figured out, a nested loop went through each square and set its character to a default one defined in a constant. I chose to keep everything organized by keeping any constants as public static variables in the FileUtils class so that they could be easily accessed from any class. Then, using the data from each Clue object, empty words could be added to the array, starting with the word number. An if statement was also used to make sure numbers were not overwritten by more blank spaces.

The most involved part was letting the user actually interact with the program. This could cause many problems as the user can input things that could cause the program to crash. For instance, when the user inputs the number of the word they want to fill in, they can type anything. If there’s no number and we try to set that to an int, it will cause an error. To fix this, I had to validate number inputs using try-catch statements. The statement will try to get a number from the user and will catch number format exceptions, telling the user that the input was invalid.

Initially, all of the Main class was just the main method, with a switch statement allowing users to choose what they want to do and each action detailed inside. For organization’s sake, I ended up refactoring the contents of the switch statement into separate methods, which wasn’t as simple as copying and pasting code, I usually had to make a small edit to accept a parameter or return something.

I wanted my program to be usable with not just the hard-coded default file. I added a menu option that allows the user to choose a file inside of the project to use as a puzzle. Of course, the user could write the name of a file wrong, or one that isn’t there at all. They could even input a file that is formatted wrong, which would cause the program to completely break. As such, more input validation was needed.

This piece of code in FileUtils handles just that. It’s the part that creates the array of clues in the first place, so it needs to iterate once to get the length of the array before it gets to do something more interesting. The code for reading each line and making them into clues is inside of a try statement to keep an attempt to use improperly formatted files from crashing the program. If an exception is thrown, it will print a message, still close the scanner, and return the empty array, though that’s useless for a crossword so it will loop until the user inputs a properly formatted file.

The two most significant methods in the Crossword class were the ones for writing and erasing answers. The one for writing answers takes the word number, direction, and a String for the word to write in it. If the word fits, it writes it and if it matches the word in the Clue object, the solved variable in the Clue is set to true.

For the erasing method, I used if statements to check perpendicular adjacent squares to make sure I didn’t erase a letter being used in another word. This was probably the most difficult part to get right, as you had to handle things slightly differently for horizontal and vertical words and the first letter of a word is reset to a number and squares should only be reset if the perpendicular adjacent squares are either: Out of bounds, the default character that is used to signify no word being there, the empty character representing a letter in an unanswered word, or a number, the first letter of an unanswered word. Plus, when I tried casting numbers to chars, it treated it as an ASCII value, so I had to add an ASCII value offset of 48, since 48 is 0 in ASCII. The erasing method broke so many times in so many different ways, such as me not setting up something properly for vertical erasing or forgetting to check for numbers, but I fixed them all. To reduce on duplicated code relating to erasing, I made this method:

 

This method is called for each character in a word to be erased and handles the checking of perpendicular adjacent squares to a character and will set the character if it is not part of another answered word. For the first character, the Clue’s number is passed in to the resetChar and the empty character is passed in for the rest. Additionally, the index of the current character within the word is passed in to the appropriate offset parameter depending on the direction so this method can get the correct current square.

As for anything that I’d like to implement but didn’t, there is the issue of crosswords with words not touching or using the same positions or numbers and directions that are technically valid and will be properly read by the program but won’t be good crosswords. Additionally, for numbers that only correspond to one direction, I would like for it to just automatically select that word instead of also asking for direction, but that would take some extra thought I didn’t have time for.

Other than that, I’m very happy with my program. It’s certainly not as smooth as a normal crossword, but it gets the job done and it was enjoyable to make.