patternjavaMinor
"Connect four" code to check for horizontals, verticals, and diagonals
Viewed 0 times
connectdiagonalsverticalsfourandforcodecheckhorizontals
Problem
My textbook (David Liang's Introduction to Java Programming) asks me to write a program which
The problem is a preliminary step to a "Connect Four" game that it asks us to implement later on. I've pasted my code at the very bottom for review, and I've included ample and thorough comments to clarify what I've done. I realize my code is a bit inefficient because it checks all the way up to the very bottom right element. Unfortunately, I was unable to find a workaround to this, as limiting the col and row variables will complicate things quite a bit. On paper, we could toss out the bottom right 3x3 block because there's nothing there for us to check, but there is no way to eliminate that block without eliminating the top-right 3x3 block.
Here are some example scenarios from my textbook. My program successfully returns true for each of these cases, but I would like to optimize my code, now that I've found a successful solution to the problem.
It's about 97 lines long, and I've used methods to make things a bit easier to understand.
```
import java.util.Scanner;
public class FourIdentical
{
// Class scanner, for simplicity
static Scanner scan = new Scanner(System.in);
/ CLIENT CODE /
public static void main( String [] args )
{
// Prompts the user to enter information
System.out.print("Number of rows: ");
int numRows = scan.nextInt();
System.out.print("Number of columns: ");
int numCols = scan.nextInt();
int [][] matrix = new int[numRows][numCols];
// Initializes the matrix's elements
System.out.println("Below, please enter each element of the array.");
initializeMatrix(matrix);
// Find identical fours and print t
- prompts a user to specify the number of rows and columns in a matrix
- prompts the user to enter each element of the matrix as it would appear in a table
- checks that matrix to see if there are any four elements in a row (horizontally, vertically, and diagonally) that are identical to one another
The problem is a preliminary step to a "Connect Four" game that it asks us to implement later on. I've pasted my code at the very bottom for review, and I've included ample and thorough comments to clarify what I've done. I realize my code is a bit inefficient because it checks all the way up to the very bottom right element. Unfortunately, I was unable to find a workaround to this, as limiting the col and row variables will complicate things quite a bit. On paper, we could toss out the bottom right 3x3 block because there's nothing there for us to check, but there is no way to eliminate that block without eliminating the top-right 3x3 block.
Here are some example scenarios from my textbook. My program successfully returns true for each of these cases, but I would like to optimize my code, now that I've found a successful solution to the problem.
It's about 97 lines long, and I've used methods to make things a bit easier to understand.
```
import java.util.Scanner;
public class FourIdentical
{
// Class scanner, for simplicity
static Scanner scan = new Scanner(System.in);
/ CLIENT CODE /
public static void main( String [] args )
{
// Prompts the user to enter information
System.out.print("Number of rows: ");
int numRows = scan.nextInt();
System.out.print("Number of columns: ");
int numCols = scan.nextInt();
int [][] matrix = new int[numRows][numCols];
// Initializes the matrix's elements
System.out.println("Below, please enter each element of the array.");
initializeMatrix(matrix);
// Find identical fours and print t
Solution
The primary question was about performance. But let me point out a few things that may be considered as "subjective", but that I consider as rather important:
Commenting
Comments are important. And I understand that you inserted comments to make things easier (maybe for you as well as for the reviewers). And I think that
These really can help to quickly find the relevant loop (as the loops look very similar otherwise).
(Note, however, that some people consider such comments as a "code smell". They argue that, when you think that you have to write a comment, then you should try to make the code clearer. In this particular case, they might recommend introducing additional methods like
Nevertheless: You can go too far. In practice, comments like these are basically just noise:
They don't really convey information or make clearer what is happening there.
Additionally, I strongly suggest to avoid
Formatting
No, this is not about the question of whether the
but in one case, you wrote
In most coding guidelines that I have read so far, the recommendation was to always use the
Similarly, you sometimes wrote
Your mileage may vary.
Testability
Although the task was to allow the user to enter the matrix, I cannot imagine how you tested this more than once or twice. Didn't it become boring to enter the same values again and again? The first thing that I did was replacing the contents of your
This allows you to quickly test different border cases. And this helped me to verify...
a bug!
As pointed out in the comments: The condition in the last if-statement is wrong. It should not be
causing an
Coming to the core: Performance
This, in fact, is difficult. It's nearly impossible to say how the performance could be improved here. One could argue about the last three
that could obviously be condensed into one. But it's nearly impossible that this will have a measurable performance impact, even if this is executed millions of times.
Similarly, one could argue that the expression
But it might be that the JIT does this sort of optimization internally.
So first, a disclaimer:
Without profiling and looking at the resulting assembly, this sort of optimization is just guesswork.
But if I had to make sure that the performance is as high as possible for this particular code snippet, then I would try something that you already mentioned in the question:
On paper, we could toss out the bottom right 3x3 block because there's nothing there for us to check
This can be done. One could write the checks as follows:
```
private static boolean checkRows(int[][] matrix)
{
for (int row = 0; row < matrix.length; row++)
{
for (int col = 0; col < matrix[row].length - 3; col++)
{
int element = matrix[row][col];
if (element == matrix[row][col + 1] &&
element == matrix[row][col + 2] &&
element == matrix[row][col + 3])
{
return true;
}
}
}
return false;
}
private static boolean checkColumns(int[][] matrix)
{
for (int row = 0; row < matrix.length - 3; row++)
{
for (int col =
Commenting
Comments are important. And I understand that you inserted comments to make things easier (maybe for you as well as for the reviewers). And I think that
// Inline comments can really help you to identify relevant parts of the code when quickly skimming over it. These may be short, like// Check row to the right
...
// Check column to the bottom
...
// Check diagonal to the bottom right
...
// Check diagonal to the bottom leftThese really can help to quickly find the relevant loop (as the loops look very similar otherwise).
(Note, however, that some people consider such comments as a "code smell". They argue that, when you think that you have to write a comment, then you should try to make the code clearer. In this particular case, they might recommend introducing additional methods like
checkRow, checkColumn, checkDiagonal etc. But I think that inline comments have their justification, as stated above.)Nevertheless: You can go too far. In practice, comments like these are basically just noise:
// This is the current element in our matrix
int element = matrix[row][col];They don't really convey information or make clearer what is happening there.
Additionally, I strongly suggest to avoid
/ block comments / in methods. These should be reserved for either implementation notes (outside of methods), or for JavaDoc method- and class comments. (This may be a bit subjective, though)Formatting
No, this is not about the question of whether the
{ should be in the same line or in the next line ;-) This is about consistency: In most cases, you wroteif(...)
return true;but in one case, you wrote
if(...)
{
return true;
}In most coding guidelines that I have read so far, the recommendation was to always use the
{ brackets } even for single-line blocks. Similarly, you sometimes wrote
matrix.length-4 and sometimes matrix.length - 4. Yes, I think that the spaces matter. Your mileage may vary.
Testability
Although the task was to allow the user to enter the matrix, I cannot imagine how you tested this more than once or twice. Didn't it become boring to enter the same values again and again? The first thing that I did was replacing the contents of your
main method with this:int matrix[][] = new int[][] {
{ 0, 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9,10,11 },
{12,13,14,15,16,17 },
{18,19,20,21,22,23 },
{24,25,26,27,28,29 },
};
// Find identical fours and print true if found, false otherwise
System.out.println( checkForIdenticalFour(matrix) );This allows you to quickly test different border cases. And this helped me to verify...
a bug!
As pointed out in the comments: The condition in the last if-statement is wrong. It should not be
col >= matrix[row].length-4, but col >= 3. Otherwise, it crashes with the following matrixint matrix[][] = new int[][] {
{ 0, 1, 2, 3, 4, 5 },
{ 6, 7, 1, 9,10,11 },
{12, 1,14,15,16,17 },
{ 1,19,20,21,22,23 },
{24,25,26,27,28,29 },
};causing an
ArrayIndexOutOfBoundsException. Coming to the core: Performance
This, in fact, is difficult. It's nearly impossible to say how the performance could be improved here. One could argue about the last three
if-blocks in the loop...if( row = 3 ) { ... }that could obviously be condensed into one. But it's nearly impossible that this will have a measurable performance impact, even if this is executed millions of times.
Similarly, one could argue that the expression
matrix[row] is repeated several times, and could be pulled out into a int currentRow[] = matrix[row]; to avoid repeated dereferencing. But it might be that the JIT does this sort of optimization internally.
So first, a disclaimer:
Without profiling and looking at the resulting assembly, this sort of optimization is just guesswork.
But if I had to make sure that the performance is as high as possible for this particular code snippet, then I would try something that you already mentioned in the question:
On paper, we could toss out the bottom right 3x3 block because there's nothing there for us to check
This can be done. One could write the checks as follows:
```
private static boolean checkRows(int[][] matrix)
{
for (int row = 0; row < matrix.length; row++)
{
for (int col = 0; col < matrix[row].length - 3; col++)
{
int element = matrix[row][col];
if (element == matrix[row][col + 1] &&
element == matrix[row][col + 2] &&
element == matrix[row][col + 3])
{
return true;
}
}
}
return false;
}
private static boolean checkColumns(int[][] matrix)
{
for (int row = 0; row < matrix.length - 3; row++)
{
for (int col =
Code Snippets
// Check row to the right
...
// Check column to the bottom
...
// Check diagonal to the bottom right
...
// Check diagonal to the bottom left// This is the current element in our matrix
int element = matrix[row][col];if(...)
return true;if(...)
{
return true;
}int matrix[][] = new int[][] {
{ 0, 1, 2, 3, 4, 5 },
{ 6, 7, 8, 9,10,11 },
{12,13,14,15,16,17 },
{18,19,20,21,22,23 },
{24,25,26,27,28,29 },
};
// Find identical fours and print true if found, false otherwise
System.out.println( checkForIdenticalFour(matrix) );Context
StackExchange Code Review Q#150518, answer score: 8
Revisions (0)
No revisions yet.