Last week we created the SudokuPuzzle and SudokuBook objects to model and store any number of Sudoku puzzles. This week we will be using those existing objects and saving them to files. Saving object data to files can be as easy or as difficult as you need it to be, and for this assignment we will be trying out multiple ways of saving the puzzles that we’ve generated.
The coding skills you will practice in this assignment include:
writing data to sequential binary files
reading data from sequential binary files
writing data to random access files
reading data from random access files
The Outline
For this assignment you will be creating the SudokuSaver class. This is going to be a static class with methods for writing and reading SudokuBook and SudokuPuzzle data from files.
Create a static class called SudokuSaver and give it the following methods. More details are provided for each method further down.
public static void saveBinary(String filename, SudokuBook book) throws IOExceptionSaves an entire SudokuBook object into a sequential binary file.
public static SudokuBook readBinary(String filename) throws IOException, ClassNotFoundExceptionReads an entire SudokuBook object from an existing sequential binary file that was created using saveBinary().
public static void saveRABook(String filename, SudokuBook book) throws IOExceptionSaves an entire SudokuBook object into a random access file by writing out the values in each puzzle sequentially.
public static SudokuBook readRABook(String filename) throws IOExceptionReads an entire SudokuBook object from an existing random access file that was created using saveRABook().
public static void saveRAPuzzle(String filename, SudokuPuzzle puzzle, int position) throws IOExceptionSaves a single SudokuPuzzle object into a random access file at the specified position. Puzzles written in this way should remain consistent with the formatting of saveRABook so that the entire file can be read as a SudokuBook object as well.
public static SudokuPuzzle readRAPuzzle(String filename, int position) throws IOException
Reads a single SudokuPuzzle object from the specified position in a random access file that was created using saveRABook().
All of these methods must follow these rules:
All data in the objects must be saved to the files, which includes both the grid and original arrays in SudokuPuzzle and the puzzles array in SudokuBook.
The puzzle data must not be changed by the writing and reading processes. In other words, the puzzles must be the same coming out as they were going in.
All of the random access file methods must be compatible with each other if used properly.For example, if you use saveRABook to save a whole SudokuBook, then saveRAPuzzle to overwrite a single puzzle in that same file, then readRABook to read out the SudokuBook from that file, then the resulting book must be a modified version of the original SudokuBook object with one puzzle replaced. All of the other puzzles in the book should be unmodified.
You will be using the classes that you created last week (you will also want SudokuGenerator to create new puzzles you can use for testing). The SudokuSaver class must be in the same project as those files so that it is able to use them. Either add SudokuSaver to your project from last week, or make a new project with copies of last week’s files. If you were unable to get your SudokuPuzzle or SudokuBook classes working last week, then you may use these samples instead:
All of the methods in SudokuSaver will use throws clauses to allow the program to run despite the checked exceptions. Note that this means if anything goes wrong at any point in the file reading/writing process, the program will crash.
Method Details
saveBinaryIf you want to save SudokuBook and SudokuPuzzle objects to binary files, you must first implement the Serializable interface. Once you’ve done that this method becomes fairly straightforward: just write the whole SudokuBook object into a binary file using the provided file name.
readBinaryFor this method, just read out a single object from the specified binary file and return it. You must cast that object to type SudokuBook before you can return it.
saveRABookWriting SudokuBook objects to random access files is trickier than sequential binary files because you can’t use the writeObject method. Instead, you must write out all of the data from the puzzles in the book as integers. This means for each puzzle, you will be writing 162 integers: 81 from the grid array and 81 from the original array. This is repeated for each puzzle in the book.
There is one caveat: the puzzles in the SudokuBook can be null. You must check each puzzle to see if it is null before you write it into the file. If a puzzle is null, then you can write -1 into the file as an identifier that the puzzle in that location was null. Then you must use the seek() method to skip ahead to the next puzzle’s starting position so that the null space is big enough to fill a puzzle in the future. The saveRAPuzzle method needs to be able to write a full puzzle into the random access file at any position, so even empty spaces must be large enough to fit an entire puzzle. Each puzzle takes up exactly 648 bytes (162 integers * 4 bytes per integer).
Example: Your SudokuBook can hold 3 puzzles. The first and third puzzles are actual objects, but the second puzzle is null. To write this book into a random access file, you first write the integers from the first puzzle as normal starting from position 0 in the file. After you are done writing that puzzle’s values, you will end up at position 648 (right after the end of the first puzzle). Then for the second puzzle you detect that it is null. You write -1 into the file, then jump forward another 647 spaces, ending up at position 1296. Then you write the third puzzle’s values as normal, ending at position 1944. You are now done writing that book into the file.
readRABookThis method is the counterpart to the previous one, and it must read out and return a SudokuBook object that was previously written to a random access file. This means you must reverse the processes that you used to write the puzzle data into the file. Since you can’t use readObject, you will need to create all new SudokuBook and SudokuPuzzle objects and use the data from the file to fill in their values.
The first hurdle to overcome is finding the size of the book – you need to know how many spaces your new SudokuBook object needs before you read out any puzzle data. You can calculate this by using the length() method of the file. You can calculate how many puzzles are stored in the file because each puzzle takes up exactly 648 bytes. For example, a file of length 1944 contains 3 puzzles (1944 / 648 = 3).
The second hurdle is reconstructing your SudokuPuzzle objects. You can read out the integers into 2D arrays to reconstruct the grid and original arrays of the object, but there is no constructor that lets you create a new SudokuPuzzle object using both of those arrays. Create a new constructor in SudokuPuzzle with two 2D array parameters. This constructor sets both of the object’s 2D arrays to its parameters.
Lastly, make sure that whenever you start reading out the values of a puzzle in the file you are checking for -1. If you encounter a -1, then that puzzle is null and you don’t have to fill the corresponding position in the SudokuBook object. Don’t forget to use seek() to jump ahead to the next puzzle’s data afterward!
saveRAPuzzleThis method must save a single puzzle into a random access file at the specified parameter position. This method should be compatible with files created by the saveRABook method! For example, if you use saveRABook to write a whole book into a file, then you can use saveRAPuzzle to overwrite any of the books in that file without corrupting the rest of the book’s data.
The position parameter must correspond to the position value used by the SudokuBook object. A value of 2 corresponds to the third puzzle in the book/file (positions are indexed starting from 0). Remember that each puzzle takes up 648 bytes in the file!
For example, you use saveRABook to write a SudokuBook object with 3 puzzles into a new random access file. For this example we’ll call the puzzles A, B, and C respectively. Then you use saveRAPuzzle to overwrite the puzzle at position 1 in the file (B) with a new puzzle called D. After doing that, you use readRABook to read out the entire SudokuBook object stored in the file. The resulting object should be a book containing the puzzles A, D, and C in that order. Puzzles A and C are exactly as they were originally, and puzzle D is also exactly as it should be (not partially containing B’s values).
If you try to write a null puzzle into the file, then a -1 should be written at the first integer position of the puzzle. You don’t have to erase any existing data that might have been in the rest of the puzzle’s position.
readRAPuzzleThis method must read out and return a single SudokuPuzzle object that is stored in a random access file. The process is like a combination of readRABook and saveRAPuzzle. If you detect a -1 in the indicated position, then you must return a null object.
Saving to Text Files
This section is optional and you will not be penalized if it does not appear in your submission.
Once you have completed the required methods for this project, you can also try saving your SudokuBook objects to text files. Create two more methods in SudokuSolver called saveText and readText, which respectively write and read entire SudokuBook objects to/from text files. The behavior of these methods must follow these rules:
The number of puzzles stored in the file must be written at the top of the file.If you don’t do this then there is no way to determine how big to make the SudokuBook when you read it out.
Both the original and grid arrays must be saved into the file
The puzzles must be written to the file exactly as they would appear using the display() method. They must look like real Sudoku puzzles, not like long lists of numbers.This includes writing blank spaces into the puzzles instead of 0’s.
You may create new methods in SudokuPuzzle to facilitate this process.
If you open the resulting text file and modify the numbers (without ruining the formatting), then those changes must be reflected when you read the object back from the file.You don’t have to account for the user ruining the formatting – that is allowed to crash the program.
You must somehow detect and save any null puzzles that were in the book. The SudokuBook that is retrieved from the file must be EXACTLY as it was originally.
If you are able to correctly create both of these methods without major errors, you will be awarded an additional 10 points on this assignment. No partial credit will be given for incomplete/buggy code. You will not be penalized if these methods do not work properly, but please make sure the code for these methods doesn’t prevent the program from compiling or disrupt any other methods. You will lose points if your code does not compile for any reason.
Submission
Please test your methods thoroughly to avoid losing points for errors.
When you have completed this assignment, submit all of the Java class files used in your project. Your submission must include four files: SudokuPuzzle.java, SudokuBook.java, SudokuGenerator.java, and SudokuSaver.java.
Submit your files by clicking “Start Assignment” at the top of the page and then selecting all of your files for submission. This assignment will only accept the .java files individually and the screenshot file; it will NOT accept an Eclipse project file or a zip file. No points will be given to programs that do not compile.
CriteriaRatingsPts
This criterion is linked to a Learning OutcomesaveBinaryFor full points, the saveBinary method must correctly save an entire SudokuBook object to a sequential binary file.