3-In-A-Row Solver
Start: 04/30/2020
Due: 05/20/2020
Collaboration: individual
Important Links: style guidelines, maze.zip, nQueens.kt, input_outputs.zip, gradesheet
Lab idea from Dave Reed, Creighton University.
In the 3-In-A-Row puzzle you are given an n by n board for some even integer n. The board is partially filled—some cells are occupied by blue squares, and some by white squares. The rest of the board is empty (shown as gray).
Figure 1. Input board
You have to fill the board with blue and white squares in such a way that the following two conditions are satisfied.
- No row or column has 3-In-A-Row (or 3-In-A-Column) squares of the same color, and
- Each row and column has equal number of blue and white squares.
The given puzzle has this solution:
Figure 2. Solution
A human trying to solve this puzzle will have to carefully reason her way through it using logical analysis. See an example of such analysis at 3-In-A-Row help. Nevertheless, we can write a computer program that takes an initial board configuration and searches for an answer. However, due to the exponential growth rate in the size of the search space, a naive brute-force search will take prohibitively long. We need to search intelligently.
Backtracking
Backtracking is an established search method for solving many intractable computational problems, including a problem like the 3-In-A-Row puzzle. The algorithm starts with an empty current partial solution. It keeps attempting to complete the current partial solution to a full solution by incrementally adding to the current partial solution one new item at each step. It will systematically try all possibilities of expanding the current partial solution, so long as it cannot prove that the current partial solution is doomed, i.e., not capable of being expanded to a complete solution. Immediately after trial adding a new item to the current partial solution, it checks whether this results in a full solution or not. If it does, the algorithm halts and declares success. Otherwise, it checks whether the last addition has resulted in a doomed partial solution or not. If the new partial solution is not doomed, it continues the search using the new partial solution as the current partial solution. If the new partial solution is doomed, it retracts the last addition and continues with the next possible way to expand this current partial solution. If all possible ways to expand the current partial solution has been exhausted without finding a solution, it retracts its last addition, and continues the search. The search stops when we have checked all possible ways to expand the current partial solution without finding a solution, and the current partial solution is empty. In such a case, it declares failure.
This basic algorithm can be modified to search for all solutions instead of just one solution.
Getting Started
First, make sure you understand the rules and objectives of the puzzle. Second, study the lecture notes on backtracking. Third, design your backtracking search algorithm for the puzzle. Decide on the data structures to be used, the ordering of the items to be added to the current partial solution, how to check for success condition, how to check whether a partial solution is doomed or not, etc. Fourth, write pseudocode for the search. Last, you can now implement your pseudocode as a Kotlin program. Don’t forget to test as soon as you have written even one function.
Required Structure
Instead of being given a skeleton Kotlin file to start from, you are going to write the code more or less from scratch. However, in order to enforce some uniformity (which will make the grading easier), we request that you do certain things:
You must write at least one file called
TiarSolverText.kt
. This file should contain the methods that together perform the backtracking search. The necessary data structures can be kept within this one file, or you may create a separate class forBoard
. You can make theBoard
class disjoint from or include it inTiarSolverText.kt
. It’s all up to you.Your
main
function is top-level and should be inTiarSolverText.kt
. Your program should read in the data for the initial board from a file whose name is given on the command line. It writes a solution (if there is one) to the standard output; it writes a failure message if there is no solution.The format of the input is like this. The first line contains an even positive integer
n
. (This means the dimension of the board isn
byn
.) Then follown
lines, each line containing exactlyn
characters. A position has the characterW
if there is a white square there. It has the characterB
if there is a blue square there. The character-
denotes a blank square. For example, the board in Fig 1 is encoded like this6 --W-B- ------ BB--W- -B---- -----W -BW---
The format of the output is like this. It consists of exactly
n
lines, each line of which contains exactlyn
characters. Each character is eitherW
orB
, denoting a white or blue square in that position, respectively. For example, the board in Fig 2 is encoded like thisBWWBBW WWBWBB BBWBWW WBBWWB BWBWBW WBWBWB
Your final program should be run like this. Let’s say you write only one Kotlin file
TiarSolverText.kt
. It defines all the functions necessary for doing the backtracking search, and also contains the top-levelmain
function. Suppose further that your data file is named3inarow.in
and contains the input as described in the example above. You should be able to compile and run your program by typing$ kotlinc TiarSolverText.kt $ kotlin TiarSolverTextKt 3inarow.in
Your program should output the 6 output lines described above.
Suppose we also have a file
impossible.in
with this content6 B-BB-- -W-W-W ------ B-B-BB ---B-- -W----
Clearly this input file does not have a solution. So running
$ kotlin TiarSolverTextKt impossible.in
should give a failure message like
There is no solution.
Extra Credit
You must have completed the program for the regular part described above before attempting this extra credit part. Your attempted extension to the basic program will not be graded unless you also submit a correct program for the regular part. You can make either one or both of the following extensions.
The first entension is to make your program output graphically. Instead of just simple text, make your program display both the input board along side the solution board. This will make it easy for the user to check the correctness of the program’s solution. Here is the result of running my program on the 03-26-easy-6x6.in input file.
Figure 3. Input and solution boards for
03-26-easy-6x6.in
The second extension is to animate the backtracking search like this
Figure 4. Input board and search animation for
03-26-easy-6x6.in
except that, in contrast to the animated gif, your animation should not loop. Your program should animate how the backtrack search proceeds until it finds a solution or determines that there is none, and then stop.
The third extension is to combine all three versions of the program (text, GUI, and animation) into one program. The user chooses which version to execute by providing the appropriate flag on the command line.
The fourth extension is to write a text-only version of the program that prints all solutions.
Gradesheet
We will use this gradesheet when grading your lab.
Submission
As usual, you should submit via Moodle. Simply submit TiarSolverText.kt
if you attempt the basic backtracking algorithm only. You should create a new project for each extra credit you attempt. However, when submitting the extra credit work, simply submit the kotlin source code.