Assigned: 2019-10-16
Due Date: 2019-10-22 by 11:59PM U.S. Central Time
The greatest challenge to any thinker is stating the problem in a way that will allow a solution.
— attributed to Bertrand Russell
Now that you've gotten your feet wet with a bit of C++, your managers
want you to up the ante, to forge your own journey from
scratch.
Minisoft has just secured a deal with a company called Macrohard to produce a Sudoku-solver. Macrohard wants to ship it with their latest operating system, Doors 11™, so they need this solver to work with their proprietary text-based representation of Sudoku games. You'll have to follow their standards if you want to succeed!
WARNING: This assignment is time-consuming and introduces new C++ concepts that may be tough to grasp at first. Give yourself adequate time to complete this assignment!
Get your copy of the assignment repo here: https://classroom.github.com/a/Dh1bKHY0.
You will be implementing a program that loads in Sudoku puzzles, solves them, and then prints them out. If you're unfamiliar with Sudoku, you can read about it here.
In this assignment, you'll have your first exposure to operator
overloading
in C++, specifically overloading the >>
and <<
operators to
support reading in data and sending out data, respectively. You may
also find this
tutorial
to be helpful in figuring out what that looks like in the context of a
class
, though the example in the tutorial is not perfect.
You might already be familiar with this concept through std::cin
and
std::cout
:
std::istream int_data = std::cin; // Source of data is stdin int n; int_data >> n; std::cout << "Your number is: " << n << std::endl;
Note: reassigning int_data = std::cin
is a bit contrived for this
example. It's just to demonstrate that we're getting data from a
generic std::istream
, and the fact that the source of data is
specifically std::cin
doesn't matter in order for >>
to work.
We would like to be able to do the following:
std::istream a_single_unsolved_puzzle = /* Some source of data from somewhere */; YourPuzzleClass puzzle; a_single_unsolved_puzzle >> puzzle; // Initialize the puzzle std::cout << puzzle << std::endl; // Print the solved puzzle
Note: YourPuzzleClass
is a bad name for a class; it's just for
illustration purposes.
You will need to create a dedicated class for the purpose of storing a
single Sudoku game, and you will need to overload std::istream &operator>>
and std::ostream &operator<<
to support reading in a
single puzzle's worth of data and sending out a solved puzzle. If
you've done it correctly, you should even be able to support
sending/receiving data to/from arbitrary sources, like network
connections, although that's not a requirement for this assignment.
Macrohard uses a text-based file format for representing game(s) of
Sudoku. A valid .spf
file looks like this:
The first line must be the string #spf1.0
, which identifies the
file as an SPF file and the version of the file format being
used. Your program only needs to support SPF1.0, so if a user gives
you a file that doesn't have this exact line at the top of the
file, you should handle it appropriately. (Testing hint: consider
making "SPF" files that either don't have this line, or have a
different version than 1.0, so that you can test how your
application behaves)
Each following line will contain a Sudoku puzzle, which will be
exactly 81 characters long. There can be any number of Sudoku
puzzles in a single file. The values in a single puzzle will be
represented by the digits 1
through 9
, and blank tiles will be
represented by an underscore _
. The top row of the puzzle will be
the first 9 characters, the second row the next 9 characters,
etc. (Testing hint: consider making "SPF" files with blank lines,
too many/too few characters on a line, unexpected characters,
etc.)
An example of a single puzzle:
╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗ ║ 8 │ 5 │ ║ │ │ 2 ║ 4 │ │ ║ ╟───┼───┼───╫───┼───┼───╫───┼───┼───╢ ║ 7 │ 2 │ ║ │ │ ║ │ │ 9 ║ ╟───┼───┼───╫───┼───┼───╫───┼───┼───╢ ║ │ │ 4 ║ │ │ ║ │ │ ║ ╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣ ║ │ │ ║ 1 │ │ 7 ║ │ │ 2 ║ ╟───┼───┼───╫───┼───┼───╫───┼───┼───╢ ║ 3 │ │ 5 ║ │ │ ║ 9 │ │ ║ ╟───┼───┼───╫───┼───┼───╫───┼───┼───╢ ║ │ 4 │ ║ │ │ ║ │ │ ║ ╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣ ║ │ │ ║ │ 8 │ ║ │ 7 │ ║ ╟───┼───┼───╫───┼───┼───╫───┼───┼───╢ ║ │ 1 │ 7 ║ │ │ ║ │ │ ║ ╟───┼───┼───╫───┼───┼───╫───┼───┼───╢ ║ │ │ ║ │ 3 │ 6 ║ │ 4 │ ║ ╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝ 85___24__72______9__4_________1_7__23_5___9___4___________8__7__17__________36_4_
Here's a full example of a valid .spf
file containing two Sudoku puzzles:
#spf1.0
85___24__72______9__4_________1_7__23_5___9___4___________8__7__17__________36_4_
___8_5____3__6___7_9___38___4795_3______71_9____2__5__1____248___9____5______6___
In your program, you should allow the user to specify the location of
a local file (either by using something like std::cin
or
command-line
arguments,
your choice,) and then you should:
Once you've read in the data from the SPF file, you should use your Sudoku puzzle class to solve each puzzle in the file.
Design hint: the code that loads and processes an entire .spf
file
doesn't necessarily have to live in the same class that represents a
single Sudoku puzzle. Your Sudoku puzzle class only needs to be
capable of being initialized with data for a single Sudoku puzzle, not
the entire .spf
file!
You are not required to implement any specific Sudoku-solving algorithm. Feel free to look on the Internet for various strategies for solving Sudoku, or invent your own — just remember our course policies on citing outside code.
You are advised to not spend too much time on this part of the assignment. Don't worry about efficiency; pick an algorithm that is simple and easy to understand and implement. You might find this article on Wikipedia helpful.
If a given Sudoku puzzle is unsolvable, you should report that somehow (returning the string "Unsolvable", or returning the original unsolved puzzle would be sufficient).
You may assume that every Sudoku game in an SPF file will be a standard 9x9 board, but your code should still be extensible enough to support different board sizes with only minor changes. Hexadecimal Sudoku (16x16) is a thing, and maybe one day Macrohard will come out with an SPF2.0 that supports it.
Some standard library classes you might find helpful (but do not have to use!) in your code and tests:
.cpp
) to
represent a single Sudoku puzzle.std::istream &operator>>
in this puzzle class
to initialize an instance with data representing a single, unsolved
puzzle (not an entire .spf
file!)std::ostream &operator<<
in this puzzle class to
pretty-print the solved/unsolvable puzzle to a generic output
stream (a file, stdout, string, network connection, etc.)