This question already has answers here:
Connect 4 check for a win algorithm
(6 answers)
Closed 6 years ago.
I have programming assignment where a 2D board game needs to made. The game I am trying to make is a connect 4 game. The problem I have is that I can't seem to get the win conditions to work. Does anybody have any recommendations. I am still relatively new in programming so I am sorry if it is a simple fix. Here is my Code:
import java.io.*;
import java.net.*;
class C4GameSession implements C4Constants {
private Socket player1;
private Socket player2;
// Create and initialize cells
private char[][] cell = new char[6][7];
private DataInputStream fromPlayer1;
private DataOutputStream toPlayer1;
private DataInputStream fromPlayer2;
private DataOutputStream toPlayer2;
// Continue to play
private boolean continueToPlay = true;
/** Construct a thread */
public C4GameSession(Socket player1, Socket player2) {
this.player1 = player1;
this.player2 = player2;
// Initialize cells with a blank character
for (int i = 0; i < 42; i++)
for (int j = 0; j < 42; j++)
cell[i][j] = ' ';
}
public void runGame() {
try {
// Create data input and output streams
DataInputStream fromPlayer1 = new DataInputStream(player1.getInputStream());
DataOutputStream toPlayer1 = new DataOutputStream(player1.getOutputStream());
DataInputStream fromPlayer2 = new DataInputStream(player2.getInputStream());
DataOutputStream toPlayer2 = new DataOutputStream(player2.getOutputStream());
// Write anything to notify player 1 to start
// This is just to let player 1 know to start
// in other words, don't let the client start until the server is ready
toPlayer1.writeInt(CONTINUE);
// Continuously serve the players and determine and report
// the game status to the players
while (true) {
// Receive a move from player 1
int row = fromPlayer1.readInt();
int column = fromPlayer1.readInt();
cell[row][column] = 'X';
// Check if Player 1 wins
if (isWon('X')) {
toPlayer1.writeInt(PLAYER1_WON);
toPlayer2.writeInt(PLAYER1_WON);
sendMove(toPlayer2, row, column);
break; // Break the loop
}
else if (isFull()) { // Check if all cells are filled
toPlayer1.writeInt(DRAW);
toPlayer2.writeInt(DRAW);
sendMove(toPlayer2, row, column);
break;
}
else {
// Notify player 2 to take the turn - as this message is not '1' then
// this will swicth to the relevant player at the client side
toPlayer2.writeInt(CONTINUE);
// Send player 1's selected row and column to player 2
sendMove(toPlayer2, row, column);
}
// Receive a move from Player 2
row = fromPlayer2.readInt();
column = fromPlayer2.readInt();
cell[row][column] = 'O';
// Check if Player 2 wins
if (isWon('O')) {
toPlayer1.writeInt(PLAYER2_WON);
toPlayer2.writeInt(PLAYER2_WON);
sendMove(toPlayer1, row, column);
break;
}
else {
// Notify player 1 to take the turn
toPlayer1.writeInt(CONTINUE);
// Send player 2's selected row and column to player 1
sendMove(toPlayer1, row, column);
}
}
}
catch(IOException ex) {
System.err.println(ex);
}
}
/** Send the move to other player */
private void sendMove(DataOutputStream out, int row, int column) throws IOException {
out.writeInt(row); // Send row index
out.writeInt(column); // Send column index
}
/** Determine if the cells are all occupied */
private boolean isFull() {
for (int i = 0; i < 43; i++)
for (int j = 0; j < 43; j++)
if (cell[i][j] == ' ')
return false; // At least one cell is not filled
// All cells are filled
return true;
}
/** Determine if the player with the specified token wins */
private boolean isWon(char token) {
/*
int count = 0;
for (int i = 0; i < 6; ++i)
for (int j = 0; j < 7; ++j)
if (cell[i][j] == token)
++count;
if (count == 4)
return true; // found
/* else
count = 0; // reset and count again if not consecutive
*/
int count_piece = 0;
//Checking Horizontal Win
for (int i = 0; i < 6; i++) {
count_piece = 0;
for (int j = 0; j < 7; j++) {
if (cell[i][j] == 'X') {
count_piece++;
if (count_piece == 4) {
System.out.println("you win");
return true;
}
} else {
count_piece = 0;
}
}
}
return false; // no 4-in-a-line found
}
}
(I'll write in pseudocode)
Start with a simple approach: you need to check for vertical, horizontal and diagonal win, then do three separate check code blocks (you do not need to solve all the problem at once).
One for the horizontal direction:
for(every row)
count = 0;
for(each column)
if(cell value = token)
then count++;
else // reset the counting, the eventual sequence has been interrupted
count = 0;
if(count >= 4) then win = 1; // you can break out here, when improving you can break out directly in the inner for loop if count is => 4
If no win detected, go for the vertical direction:
// similar comments for the previous block apply here
for(every column)
count = 0;
for(each row)
if(cell value = token)
then count++;
else
count = 0;
if(count >= 4) then win = 1 and break;
If no win detected, go for the diagonal direction:
// a bit harder, you have to move diagonally from each cell
for(every column from the left)
for(each row from the top)
count = 0
for(delta starting from 0 to 5)
// add more checks to avoid checking outside the cell matrix bounds
// when improving the code, you can compute a better end for the delta
if(cell[row+delta][column+delta] = token)
then count++;
else
count = 0;
When you have written and tested all there three pieces, if you want you can gradually improve the algorithm, i.e. starting from the bottom instead of the top row (as most superior cells will be empty for the most part of the game); next, as 4 consecutive cells with the same element must be found, if you, e.g. haven't found enough consecutive tokens when checking a row you may stop earlier instead of going through all the 7 cells in that row.
While I'm not giving you the complete solution with working code, I hope my answer will put you on the right track.
Related
I am tasked to make a program which returns true if in a 2D array 1-s encircle 0-s.
I tried something like this, but i cant find the right solution.
public boolean checkGameState(){
for(int i=0;i<fields.length;i++){
for(int j=0;j<fields.length;j++){
if(fields[i][j]!=0){
if(row(i,j){
return true;
}
}
}
}
return false;
}
private boolean row(int a, int b){
int checkI=a;
int checkJ=b;
while(fields[checkI][checkJ]==1){
checkJ++;
}
while(fields[checkI][checkJ]==1){
checkI++;
}
while(fields[checkI][checkJ]==1){
checkJ--;
}
while(fields[checkI][checkJ]==1){
checkI--;
}
return a==checkI && b==checkJ;
}
The 2D array looks something like this:
111100
100100
100101
111100
001100
For this array the method should return true.
The easiest way might be to use a flood fill algorithm to eliminate all the zeros that are not encircled by ones, and then checking whether there are any left.
First, put all the zeros directly on the "fringe" of the 2D array into a queue. Then, use the flood fill algorithm to turn all of those into a different number (e.g., 2), and add the nodes next to them to the fringe set (either diagonally or only direct neighbours). Repeat until there are no more nodes in the fringe. Finally, check whether there are any more zeros in the array. If so, those were not connected to the fringe region and thus had to be "encircled" by ones.
// test data set up
int[][] data = {{1,1,1,1,0,0},
{1,0,0,1,0,0},
{1,0,0,1,0,1},
{1,1,1,1,0,0},
{0,0,1,1,0,0}};
int N = data.length, M = data[0].length;
// create queue of zeros on the "fringe"
Queue<int[]> fringe = new LinkedList<>();
for (int i = 0; i < N; i++) {
if (data[i][0 ] == 0) fringe.add(new int[]{i,0 });
if (data[i][M-1] == 0) fringe.add(new int[]{i,M-1});
}
for (int j = 0; j < M; j++) {
if (data[0 ][j] == 0) fringe.add(new int[]{0 ,j});
if (data[N-1][j] == 0) fringe.add(new int[]{N-1,j});
}
// do flood fill until no more zeros reachable
while (! fringe.isEmpty()) {
int[] next = fringe.poll();
int i = next[0], j = next[1];
data[i][j] = 2;
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
try {
if (data[i+di][j+dj] == 0) fringe.add(new int[]{i+di, j+dj});
} catch (ArrayIndexOutOfBoundsException e) {}
}
}
}
// check for remaining zeros
boolean encircled = false;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
System.out.print(data[i][j]);
encircled |= data[i][j] == 0;
}
System.out.println();
}
System.out.println(encircled);
Example output:
111122
100122
100121
111122
221122
true
The complexity should be on the order of O(NxM), since each of the NxM nodes can only appear once in the queue (plus a bit of overhead for constructing the queue and finding remaining zeros).
Please note that I have assumed that you need rectangle shape surrounding
You need to find sequences for 3 or more 1 in one row.
xx1111xx // x means any number
For each sequence check if there is sequence of the same length 2 or more rows lower.
xx1111xx
xxxxxxxx
xx1111xx
For each "pair" of sequences check if they are connected with 1 on the edges.
xx1111xx
xx1xx1xx
xx1111xx
This method is supposed to return true if there is more than one 1 in a column of a 2D array, yet it doesn't work. I can't figure out what's wrong with it so I thought I'd get some expert opinions.
Example:
10010
01001
10100
will return true because there are 2 ones in the first column.
Here is the code
public static boolean isVert(int[][] x) { //checks for more than one 1 in columns
int count = 0;
boolean break2 = false;
boolean check = false; //false means no 2 (or more) queens in same column
for (int i = 0; i < x.length; i++) {
count = 0;
for (int j = 0; j < x[i].length; j++) {
if (x[i][j] == 1) {
count++;
}
if (count > 1) {
break2 = true;
check = true;
break;
}
}
if (break2) {
break;
}
}
return check;
}
You break at the first occurance of 1 in whole array, which is probably not the expected result.
Explanation of how your code works:
loop until counter i is less than length of array (number of rows in array)
loop until counter j is less than length of i-th row (number of columns or elements in array)
check if element on i-th row and j-th column is 1, if true, increase variable count by one
if count is greater than 1 (this means it has to be 2 or greater) set break2 and check to true, break
if break2 is true (which is as count is > 2 and first loop breaks), break this loop too:
this happens in 1st row of your example table
end of loops, return check (which is true because 1st row contains 2 ones)
The problem in your code is that you break when you find your first row that satisfies your condition, but you do not (necessarily) check all the rows in given array.
I have corrected the code for you, hopefully it works (untested)
public static boolean isVert(int[][] x) { //checks for more than one 1 in columns
int count = 0;
for (int i = 0; i < x.length; i++) {
int rowCount = 0;
for (int j = 0; j < x[i].length; j++) {
if (x[i][j] == 1) {
rowCount++;
}
if(rowCount > 1) {
count++;
break;
}
}
}
// returns true if count of lines containing 1 equals length of array,
// if not, returns false
return count == x.length;
}
Start of by improving your naming convention. Your code has many variables named by their contents, instead of named by how they are used. For example:
boolean check = false; // false means no two queens in the same column.
instead of
boolean twoQueensInColumn = false;
and the other example
boolean break2 = false;
instead of the more reasonable
boolean continueLooking = true;
Plus, it is a very good idea to avoid using variables as place holders for loop escaping logic. For example, the two stanzas
...
if (count > 1) {
break2 = true;
check = true;
break;
}
}
if (break2) {
break;
}
are a breeding ground for bugs, requiring a lot of debugging to ensure they work "right now" which will break just as soon as you modify the code. Much better would be
boolean keepLooking = false;
for (int row = 0; keepLooking && (row < board.length); row++) {
int queensInColumn = 0;
for (int column = 0; keepLooking && (column < board[row].length, column++) {
if (board[row][column] != 0) {
queensInColumn++;
}
if (queensInColumn > 1) {
keepLooking = false;
}
}
}
The main difference being the control logic is in the loop "conditional" block, where it belongs.
I would recommend turning your integers to string and using the .contains() method and looping through that. This would make the code easier to understand.
I've a problem with my sudoku solving method. The program works like this; the board is empty when started, the users adds a couple of numbers to the board and then by hitting a Solve-button the program tries to solve it. Everything works fine besides if I put the same number in the same row. So if the user adds 1,1,0,0 ... 0. In the puzzle it can't solve it because its two 1's next to each other and will just go on forever trying to find a sulotion even though its an unsolvable puzzle. However if they were all 0's(empty) it would solve it right away, same as if Id put 1 and 2 in the top left corner. If I'd just put some random numbers in it will detect it as unsolvable (or will solve it if it's a valid puzzle)
I'm thinking around the lines of saying, when theNumber == (row, col) equals thenNumber == (row+1, col), it should return false because it's a duplicated number.
This is the code I tried to add in the solve method, obviously without success.
if ((puzzle.getNum(row, col) == a) == (puzzle.getNum(row + 1, col) == a)) {
return false;
}
Help is greatly appreciated
Validate the puzzle like this:
Create a boolean array of 9 elements.
Loop through every row, column and 9x9 box.
If you read a number, set the corresponding value in the array to true.
If it is already true throw an error (impossible puzzle).
After reading a row, column or 9x9 box reset the boolean array.
Then, if the validation succeeded call the solving method.
EDIT: Source code
public boolean checkPuzzle() {
boolean[] nums = new boolean[9];
for (int row = 0; row < panel.puzzleSize; row++) {
for (int cell = 0; cell < panel.puzzleSize; cell++) {
if (nums[puzzle[row][cell]]) return false;
nums[puzzle[row][cell]] = true;
}
nums = new boolean[9];
}
for (int col = 0; col < panel.puzzleSize; col++) {
for (int cell = 0; cell < panel.puzzleSize; cell++) {
if (nums[puzzle[cell][col]]) return false;
nums[puzzle[cell][col]] = true;
}
nums = new boolean[9];
}
for (int square = 0; square < panel.puzzleSize; square++) {
int squareCol = panel.squareSize * (square % panel.squareSize);
int squareRow = panel.squareSize * Math.floor(square / panel.squareSize);
for (int cell = 0; cell < panel.puzzleSize; cell++) {
int col = cell % panel.squareSize;
int row = Math.floor(cell / panel.squareSize);
if (nums[puzzle[squareCol + col][squareRow + row]]) return false;
nums[puzzle[squareCol + col][squareRow + row]] = true;
}
nums = new boolean[9];
}
return true;
}
Didn't have too much time to test out, but it might work (?). The row/col variable namings might be incorrect, because I didn't have time to find that in your code, but it shouldn't matter for it to work or not.
Right now I am creating a connect 4 game in swing. I did not post all the GUI components since it is not important. The game detects horizontal wins for all rows except the bottom row. Here is the code for the win detection.
boolean CheckForWin()
{
for (int row = 1; row < gameBoard.length; row++) //Plus 1 is added to prepare for dimension swap.
{
//Player 1 horizontal count
int max=0;
//Player 2 horrizontal count
int max2=0;
int count_piece=0;
for(int column=1; column<gameBoard.length; column++)
{
// check for horizontal
if(row==6)
{
break;
}
if(count_piece<max || count_piece<max2)
{
count_piece=max;
count_piece=max2;
}
if(gameBoard[row][column]=='r')
{
max++;
}
else
{
max=0;
}
if(gameBoard[row][column]=='b')
{
max2++;
}
else
{
max2=0;
}
if(max==4 || max2==4)
{
return true;
}
// check for vertical
}
}
// check for diagonal up
// check for diagonal down
return false;
}
I'm assuming gameboard is an array of arrays? You are starting rows and columns at 1 in your loops but they are 0 based in java. Do you also not detect a win in the furthest left column?
You should change your loops to for int row = 0;... and for int column = 0...
I had a class assignment (it's already past) where I had to write a Sudoku solver. I was able to create a method that can solve for each missing number. But I'm having trouble creating a method to find which cells I need to solve. I'm supposed to take a 2D array and fill in the missing number (represented by 0's).
I've put some of my code below, but not all of it (even though the assignment has passed, I respect the profs wishes).
public class SolveSudoku {
private static int[][] grid = new int[9][9];
public static int[][] getSolution(int[][] grid) {
for (int i = 0; i < 9; i++) {
System.arraycopy(grid[i], 0, SolveSudoku.grid[i], 0, 9);
}
int n = getZero();
return getSolution(n);
}
private static int[][] getSolution(int n) {
if (n == 0) {
return grid;
}
Cell cell = getZero();
int row = cell.row;
int column = cell.column;
for (int number = 1; number <= 9; number++) {
//checks cell using another method
//I have booleans that return true if the number works
}
return null;
}
private static int getZero() {
return 0;
}
private static class Cell {
int cell = 0;
int row = 0;
int column = 0;
int number;
}
}
I have the getZero method which has to find each zero in the grid (0's represent a missing number) so I can solve it. What should I do in getZero to find the cells I need to replace?
You don't check if the number is repeated in the same 3x3 square (only rows and columns).
I don't get what do you mean by "finding zeroes". In fact it is meaningless, solving the sudoku from the first row or the last one has no effect in the solution. I'll explain what did I do for the same trouble.
A cell is not an int, is an object. In the object I store a value (the number if it is found, 0 if not) and a boolean[9]. False means (index + 1) is discarded because it is in the same row/column/square, true means it is not decided yet. Also, 3 Lists (Vectors, Sets, whatever).
A list of 81 cells (you can make it a bidimensional array if you wish).
9 Lists/Vectors/Sets representing rows, 9 representing columns, 9 representing square. Each of the 81 cells is assigned to its row/column/square. Inside each cell you store the references to the list to which it belongs to.
Now comes the execution part. In a first iteration, every time you find a non-zero (a number fixed in the sudoku), you loop through the lists to which the cell belongs. For each cell in those lists, you mark the boolean assigned to that number to false.
You loop through cells, each time you find a 0 you check how many of the booleans in the cell are true. If it is 1 (all other 8 possible values have been discarded), it is the value of the cell. You set it, and as in 4) you get the list to which it belongs and mark that number to false in every cells. Loop until you get a solution, or an iteration in which you cannot set any number (no solution available directly, must start with backtracking or similar).
Remember before getting at the keyboard, to have a clear idea about what the question is and how would you resolve it without a computer. If you do not know what you want to do, the computer won't help (unless you post in stackoverflow)-
From what I can tell you want to find the first 0-valued cell in grid. I'll define first as the first zero containing column in the lowest zero-containing row.
This can be done using a naive search:
private Cell getZeroCell(){
int rz = -1;
int cz = -1;
outer: for(int row = 0; row < grid.length; row++){
for(int col = 0; col < grid[row].length; col++){
if(grid[row][col] == 0){
rz = row;
cz = col;
break outer;
}
}
}
if(rz == -1 || cz == -1){
// no zeros found
return null;
}else{
// first zero found at row `rz` and column `cz`
Cell cell = new Cell();
cell.row = rz;
cell.column = cz;
return cell;
}
}
Get the "number" of the first cell containing a zero (counting left to right, then top to bottom, 0-indexed):
private int getZeroInt(){
int rz = -1;
int cz = -1;
outer: for(int row = 0; row < grid.length; row++){
for(int col = 0; col < grid[row].length; col++){
if(grid[row][col] == 0){
rz = row;
cz = col;
break outer;
}
}
}
if(rz == -1 || cz == -1){
// no zeros found
return -1;
}else{
return rz * grid[0].length + cz;
}
}
Get the number of cells containing a zero:
private int getNumZeros(){
int count = 0;
for(int row = 0; row < grid.length; row++){
for(int col = 0; col < grid[row].length; col++){
if(grid[row][col] == 0){
count++;
}
}
}
return count;
}