I've been working on the code for alpha-beta pruning for a Connect Four game and I can't seem to get it right. I thought I implemented it correctly at first, but the algorithm would return the column 1 every time. I have two ints val and nodeVal that I use to determine my alpha and beta values. When I instantiate them as 0 inside my alphaBetaPruning() algorithm, the column that is returned by the alphaBetaSearch() method is always 1. If I instantiate these values outside of the alphaBetaPruning() algorithm, the alphaBetaSearch() algorithm results in a Null Pointer Exception
.
Note: the ConnectFourBoard class holds the best child of a board state.
Any help would be appreciated!
public class ConnectFourPlayer
{
private final int columns = 7;
private char currentPlayer;
private char computer = 'C';
private char opponent = 'P';
private int depth = 8;
//private int val = 0;
//private int nodeVal = 0;
private int minMax;
/**
* Constructor method creates the computer
* player.
*/
public ConnectFourPlayer()
{
}
/**
* Method returns the number of children
* given a board state.
*/
public int numberOfChildren(ConnectFourBoard board)
{
int count = 0;
//count number of columns with empty slots
for (int col = 0; col < columns; col++)
{
for (int row = 0; row < 6; row++)
{
if ( board.board[row][col] == ' ')
{
count++;
break;
}
}
}
return count;
}
/**
* Method generates the children of a given
* board state and stores them in the array
* boardChildren[]. The array boardChildren
* is the size of the number of possible
* children.
*/
public void generateChildren(ConnectFourBoard board)
{
board.boardChildren = new ConnectFourBoard[this.numberOfChildren(board)];
int childNumber = 0;
for (int boardCol = 0; boardCol < columns; boardCol++)
{
for (int boardRow = 0; boardRow < 6; boardRow++)
{
if ( board.board[boardRow][boardCol] == ' ' )
{
ConnectFourBoard childBoard = new ConnectFourBoard();
for(int row = 0; row < 6; row++)
{
for(int col = 0; col < columns; col++)
{
childBoard.board[row][col] = board.board[row][col];
}
}
childBoard.row = boardRow;
childBoard.column = boardCol;
childBoard.board[boardRow][boardCol] = currentPlayer;
board.boardChildren[childNumber] = childBoard;
childNumber++;
break;
}
}
}
}
/**
* Method implements the alphaBetaPruning algorithm.
* Returns the column that corresponds to that
* column with the highest alphaBetaPruning value.
*/
public int alphaBetaSearch(ConnectFourBoard board)
{
minMax = 1;
currentPlayer = computer;
alphaBetaPruning(board, this.depth, Integer.MIN_VALUE, Integer.MAX_VALUE);
return board.best.column;
}
/**
* Method returns the static evaluation function value of the
* best possible move that will maximize the computer player's
* move value and also minimize the opponent's move value,
* given a depth limit.
*/
public int alphaBetaPruning(ConnectFourBoard board, int depth, int alpha, int beta)
{
//create StaticEvaluationFunction object to evaluate each possible move
StaticEvaluationFunction evaluator = new StaticEvaluationFunction();
int val = 0;
int nodeVal = 0;
//check if the computer is in a goal state
if(board.gameOver() == 'C')
{
board.best = null;
return evaluator.evaluate(board, this.currentPlayer);
}
//check if depth limit was reached
if(depth == 0)
{
board.best = null;
return evaluator.evaluate(board, this.currentPlayer);
}
//fill the boardChildren[] array with all children of the current board
this.generateChildren(board);
int width = this.numberOfChildren(board);
//check if the current board state has any children
if(width == 0)
{
board.best = null;
return evaluator.evaluate(board, this.currentPlayer);
}
else
{
int i = 0;
boolean prune = false;
while((!prune) && (i < width))
{
//recursive call to alphaBetaPruning()
val = alphaBetaPruning(board.boardChildren[i], (depth - 1), alpha, beta);
if(i == 0)
nodeVal = val;
//check if current board is a minNode
if(minNode(board))
{
//check current child < smallest child so far
if(val < nodeVal)
{
//set nodeVal to smaller child
nodeVal = val;
//set best to this child board
board.best = board.boardChildren[i];
}
//check current child < smallest child so far (local)
if(val < beta)
//set beta to smaller child
beta = val;
}
//check if current board is a maxNode
if(maxNode(board))
{
//check current child > biggest child so far
if(val > nodeVal)
{
//set nodeVal to bigger child
nodeVal = val;
//set best to this child board
board.best = board.boardChildren[i];
}
//check current child > biggest child so far (local)
if(val > alpha)
//set alpha to bigger child
alpha = val;
}
//check max so far > min so far
if(alpha > beta)
{
//prune
prune = true;
return nodeVal;
}
else i++;
}//end while
}
return nodeVal;
}
/**
* Method returns true if the given board state is
* a max node.
*/
public boolean maxNode(ConnectFourBoard board)
{
if(minMax == 1)
{
minMax = 0;
currentPlayer = opponent;
return true;
}
return false;
}
/**
* Method returns true if the given board state is
* a min node.
*/
public boolean minNode(ConnectFourBoard board)
{
if(minMax == 0)
{
minMax = 1;
currentPlayer = computer;
return true;
}
return false;
}
}
/**Holds the state of the board*/
public class ConnectFourBoard
{
public final int rows = 6;
public final int columns = 7;
public char board[][] = new char[rows][columns];
public ConnectFourBoard boardChildren[];
public ConnectFourBoard best;
public int column;
public int row;
...
/**
* Method makes the computer player's move. First,
* checks if a move is allowed. If it is, that
* slot is set to the computer player's character.
* Returns true is a move is made and false
* otherwise.
*/
public int generateComputerPlayerMove(ConnectFourPlayer computer)
{
int column = computer.alphaBetaSearch(this);
//update grid
for(int row = 0; row < board.length; row++)
{
if(board[row][column] == ' ')
{
board[row][column] = 'C';
break;
}
}
return column;
}
}
Related
I'm currently developing an algorithm for my Tictactoe Android App (using Java) with Minimax, but first, I just want to make sure the algorithm works, so I made the algorithm to "suggests" the next move to the player.
The player is declared as String player = "x"; in the Minimax class.
Minimax.java
package com.firaz.tictactoe;
public class Minimax {
public static class Move {
int row, col;
}
static String player = "x";
static String opponent = "o";
String[][] board = new String[3][3];
// This function returns true if there are moves
// remaining on the board. It returns false if
// there are no moves left to play.
static Boolean isMovesLeft(String[][] board) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[i][j].equals("_"))
return true;
return false;
}
// This is the evaluation function
static int evaluate(String[][] b) {
// Checking for Rows for X or O victory.
for (int row = 0; row < 3; row++) {
if (b[row][0].equals(b[row][1]) &&
b[row][1].equals(b[row][2])) {
if (b[row][0].equals(player))
return +10;
else if (b[row][0].equals(opponent))
return -10;
}
}
// Checking for Columns for X or O victory.
for (int col = 0; col < 3; col++) {
if (b[0][col].equals(b[1][col])
&& b[1][col].equals(b[2][col])) {
if (b[0][col].equals(player))
return +10;
else if (b[0][col].equals(opponent))
return -10;
}
}
// Checking for Diagonals for X or O victory.
if (b[0][0].equals(b[1][1])
&& b[1][1].equals(b[2][2])) {
if (b[0][0].equals(player))
return +10;
else if (b[0][0].equals(opponent))
return -10;
}
if (b[0][2].equals(b[1][1])
&& b[1][1].equals(b[2][0])) {
if (b[0][2].equals(player))
return +10;
else if (b[0][2].equals(opponent))
return -10;
}
// Else if none of them have won then return 0
return 0;
}
// This is the minimax function. It considers all
// the possible ways the game can go and returns
// the value of the board
static int minimax(String[][] board,
int depth, Boolean isMax) {
int score = evaluate(board);
// If Maximizer has won the game
// return his/her evaluated score
if (score == 10)
return score;
// If Minimizer has won the game
// return his/her evaluated score
if (score == -10)
return score;
// If there are no more moves and
// no winner then it is a tie
if (isMovesLeft(board) == false)
return 0;
// If this maximizer's move
if (isMax) {
int best = -1000;
// Traverse all cells
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j].equals("_")) {
// Make the move
board[i][j] = player;
// Call minimax recursively and choose
// the maximum value
best = Math.max(best, minimax(board,
depth + 1, !isMax));
// Undo the move
board[i][j] = "_";
}
}
}
return best;
}
// If this minimizer's move
else {
int best = 1000;
// Traverse all cells
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j].equals("_")) {
// Make the move
board[i][j] = opponent;
// Call minimax recursively and choose
// the minimum value
best = Math.min(best, minimax(board,
depth + 1, !isMax));
// Undo the move
board[i][j] = "_";
}
}
}
return best;
}
}
// This will return the best possible
// move for the player
static Move findBestMove(String[][] board) {
int bestVal = -1000;
Move bestMove = new Move();
bestMove.row = -1;
bestMove.col = -1;
// Traverse all cells, evaluate minimax function
// for all empty cells. And return the cell
// with optimal value.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// Check if cell is empty
if (board[i][j].equals("_")) {
// Make the move
board[i][j] = player;
// compute evaluation function for this
// move.
int moveVal = minimax(board, 0, false);
// Undo the move
board[i][j] = "_";
// If the value of the current move is
// more than the best value, then update
// best/
if (moveVal > bestVal) {
bestMove.row = i;
bestMove.col = j;
bestVal = moveVal;
}
}
}
}
return bestMove;
}
public void setBoard(String[][] Board){
this.board = Board;
Move bestMove = findBestMove(board);
PlayerVersusComputer playerVersusComputer = new PlayerVersusComputer();
playerVersusComputer.setRow(bestMove.row);
playerVersusComputer.setCol(bestMove.col);
}
}
I'm expecting a return integer (row and col) from the Minimax.java to the PlayerVersusComputer.java in this code snippet.
PlayerVersusComputer.java
String buttonID = getResources().getResourceEntryName(v.getId()); //button_ij
int pointerOne = Integer.parseInt(buttonID.substring(7, buttonID.length()-1)); //obtain i
int pointerTwo = Integer.parseInt(buttonID.substring(buttonID.length()-1)); //obtain j
if (player1Turn) {
((Button) v).setText("x");
board[pointerOne][pointerTwo] = "x";
} else {
((Button) v).setText("o");
board[pointerOne][pointerTwo] = "o";
checkBestMove();
}
protected void checkBestMove() {
minimax.setBoard(board);
row = getCol();
col = getRow();
tvAI.setText(row+"R"+" "+col+"C");
}
This is the board Array. The board will "update" itself every time I pressed the ((Button) v).setText("x"); or ("o"), then passed to Minimax.java when it's O's turn.
String[][] board = {{"_","_","_"},
{"_","_","_"},
{"_","_","_"}};
Since the row and col is not returned to the checkBestMove() function (I'm not sure if it's not returned or just can't "update" itself), the tvAI.setText keeps saying "0R0C" instead of updating itself (to 2R2C, then 0R1C, etc).
Update to the row and col are expected because the tvAI.setText is intended to suggest the player's next move. So why it's not updating?
In function setBoard is the call of findBestMove(board); but the return value of type Move is ignored. Maybe you can assign it to a variable.
this.bestMove = findBestMove(board);
So, I have been making this Tic Tac Toe program for a while now.
It's a basic Tic Tac Toe game but the game board is scalable. The program is almost finished but one little feature is missing.
I have to make the game end if player gets five or more marks in a row when the game board is larger than 4x4.
F.E. If the game board is 9x9 the game has to end when the player or the computer gets five marks in a row.
(Mark = "O" or "X").
The game now ends when someone gets marks in a row equals to the size of the board (if 9x9 you need 9 marks in a row to win).
I have to implement a feature in the playerHasWon and I've been having a lot of trouble finding out how. I think it's a simple to implement thing but I have not found out how to do it.
Hope my explanation is easy enough to understand. Here's the code:
package tictac;
import java.util.Scanner;
import java.util.Random;
public class Tictac {
public static final int DRAW = 0; // game ends as a draw
public static final int COMPUTER = 1; // computer wins
public static final int PLAYER = 2; // player wins
public static final char PLAYER_MARK = 'X'; // The "X"
public static final char COMPUTER_MARK = 'O'; // The "O"
public static int size; // size of the board
public static String[][] board; // the board itself
public static int score = 0; // game win score
public static Scanner scan = new Scanner(System.in); // scanner
/**
* Builds the board with the integer size and user input.
*
* Displays game win message and switches play turns.
*
* #param args the command line parameters. Not used.
*/
public static void main(String[] args) {
while (true) {
System.out.println("Select board size");
System.out.print("[int]: ");
try {
size = Integer.parseInt(scan.nextLine());
} catch (Exception e) {
System.out.println("You can't do that.");
continue; // after message, give player new try
}
break;
}
int[] move = {};
board = new String[size][size];
setupBoard();
int i = 1;
loop: // creates the loop
while (true) {
if (i % 2 == 1) {
displayBoard();
move = getMove();
} else {
computerTurn();
}
switch (isGameFinished(move)) {
case PLAYER:
System.err.println("YOU WIN!");
displayBoard();
break loop;
case COMPUTER:
System.err.println("COMPUTER WINS!");
displayBoard();
break loop;
case DRAW:
System.err.println("IT'S A DRAW");
displayBoard();
break loop;
}
i++;
}
}
/**
* Checks for game finish.
*
* #param args command line parameters. Not used.
*
* #return DRAW the game ends as draw.
* #return COMPUTER the game ends as computer win.
* #return PLAYERE the game ends as player win.
*/
private static int isGameFinished(int[] move) {
if (isDraw()) {
return DRAW;
} else if (playerHasWon(board, move,
Character.toString(COMPUTER_MARK))) {
return COMPUTER;
} else if (playerHasWon(board, move,
Character.toString(PLAYER_MARK))) {
return PLAYER;
}
return -1; // can't be 0 || 1 || 2
}
/**
* Checks for win for every direction on the board.
*
* #param board the game board.
* #param move move on the board.
* #param playerMark mark on the board "X" or "O".
* #return the game is won.
*/
public static boolean playerHasWon(String[][] board, int[] move,
String playerMark) { //playermark x || o
// horizontal check
for (int i = 0; i < size; i++) {
if (board[i][0].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[i][j].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// vertical check
for (int i = 0; i < size; i++) {
if (board[0][i].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[j][i].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// diagonals check
int i;
for (i = 0; i < size; i++) {
if (!board[i][i].equals(playerMark)) {
break;
}
}
if (i == size) {
return true;
}
for (i = 0; i < size; i++) {
if (!board[i][(size - 1) - i].equals(playerMark)) {
break;
}
}
return i == size;
}
/**
* Checks for draws.
*
* #return if this game is a draw.
*/
public static boolean isDraw() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (board[i][j] == " ") {
return false;
}
}
}
return true;
}
/**
* Displays the board.
*
*
*/
public static void displayBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
System.out.printf("[%s]", board[i][j]);
}
System.out.println();
}
}
/**
* Displays the board.
*
*
*/
public static void setupBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
board[i][j] = " ";
}
}
}
/**
* Takes in user input and sends it to isValidPlay.
*
* #return null.
*/
public static int[] getMove() {
Scanner sc = new Scanner(System.in);
System.out.println("Your turn:");
while (true) {
try {
System.out.printf("ROW: [0-%d]: ", size - 1);
int x = Integer.parseInt(sc.nextLine());
System.out.printf("COL: [0-%d]: ", size - 1);
int y = Integer.parseInt(sc.nextLine());
if (isValidPlay(x, y)) {
board[x][y] = "" + PLAYER_MARK;
return new int[]{x, y};
} else { // if input is unallowed
System.out.println("You can't do that");
continue; // after message, give player new try
}
} catch (Exception e) {
System.out.println("You can't do that.");
}
return null;
}
}
/*
* Randomizes computer's turn, where it inputs the mark 'O'.
*
*
*/
public static void computerTurn() {
Random rgen = new Random(); // Random number generator
while (true) {
int x = (int) (Math.random() * size);
int y = (int) (Math.random() * size);
if (isValidPlay(x, y)) {
board[x][y] = "" + COMPUTER_MARK;
break;
}
}
}
/**
* Checks if a move is possible.
*
* #param inX x-move is out of bounds.
* #param inY y-move is out of bounds.
* #return false
*/
public static boolean isValidPlay(int inX, int inY) {
// Play is out of bounds and thus not valid.
if ((inX >= size) || (inY >= size)) {
return false;
}
// Checks if a play have already been made at the location,
// and the location is thus invalid.
return (board[inX][inY] == " ");
}
}
// End of file
Took a quick look, detected the problem and came up with a quick fix:
public static boolean checkDiagonal(String markToLook) {
// how many marks are we looking for in row?
int sizeToWin = Math.min(size, 5);
// running down and right
// don't need to iterate rows that can't be the starting point
// of a winning diagonal formation, thus can exlude some with
// row < (size - (sizeToWin - 1))
for (int row = 0; row < (size - (sizeToWin - 1)); row++) {
for (int col = 0; col < size; col++) {
int countOfMarks = 0;
// down and right
for (int i = row; i < size; i++) {
if (board[i][i] == null ? markToLook == null :
board[i][i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
countOfMarks = 0;
// down and left
for (int i = row; i < size; i++) {
if (board[i][size - 1 - i] == null ? markToLook == null :
board[i][size - 1 - i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
}
}
return false;
}
And call it from your PlayerHasWon method instead of performign the checks there. Basically we iterate each possible starting square on the board for a diagonal winning formation, and run check down+left and down+right for each of the squares.
I am in awful hurry and did not test it much, but will return in couple of hours to improve this solution. Seems to work.
Edit: My previous solution I found lacking in further tests, I've updated the above code to function as desired.
First, I think playerMark should be a char and not a String. That said, let's go for the answer. The "horizontal" case would be:
// This is the number of marks in a row required to win
// Adjust formula if necessary
final int required = size > 4 ? 5 : 3;
for (int i = 0; i < size; i++) {
int currentScore = 0;
for (j = 0; j < size; j++) {
if (board[i][j].equals(playerMark)) {
currentScore++;
if (currentScore >= required)
return true;
}
else {
currentScore = 0;
}
}
}
}
The vertical case would be analogous. The diagonal one is a bit trickier as now it would require board[i][i+k] for the main diagonal and board[i][k-i] for the secondary; and it may not be obvious which values k and i must traverse. Here's my attempt (variable required as in horizontal case):
Note: everything from here down has been completely rewritten on 2015-12-16. The previous versions didn't work and the algorithm was not explained.
After two failed attempts I decided to do my homework and actually sort things out instead of doing everything in my head thinking I can keep track of all variables. The result is this picture:
Main diagonals are painted in blue, secondary diagonals in green.
Each diagonal is identified by a value of k, with k=0 being always being the longest diagonal of each set. Values of k grow as diagonals move down, so diagonals above the longest one have negative k while diagonals below the longest one have positive k.
Things that hold for both diagonals:
Diagonal contains size-abs(k) elements. Diagonals in which size-abs(k) is less than required need not be searched. This means that, for board size size and required length required, we'll search values of k from required-size to size-required. Notice that these have the same absolute value, with the first being <=0 and the second >=0. These values are both zero only when required==size, i.e. when we need the full diagonal to claim a win, i.e. when we only need to search k=0.
For k<=0, possible values of i (row) go from 0 to size+k. Values greater than or equal to size+k cross the right edge of the board and are thus outside the board.
For k>=0, possible values of i (row) go from k to size. Values below k cross the left edge of the board and are thus outside the board.
Only for main (blue) diagonals:
Value of j (column) is k+i.
Only for secondary (green) diagonals:
Value of j (column) is size-1+k-i. In case this is not obvious, just pick the top right corner (k=0,i=0) and notice j=size-1. Then notice that adding 1 to k (keeping i constant) always moves j by 1 right (it would go out of the board if done from k=0,i=0, just think about the intersection of horizontal line i=0 with diagonal k=1), and adding 1 to i (keeping k constant) always moves j by 1 to the left.
The ressulting code would be:
// Main diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][k+i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
// Secondary diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][size-1+k-i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
At this point, the code is nearly identical in both cases, changing only the j index in board[i][j]. In fact, both loops could be merged, taking care only of keeping two currentScore variables, one for the main (blue) diagonal and the other for the secondary (green) diagonal.
I am doing an algorithm for a hill climbing search, and for some reason, the stack that I'm supposed to have at the end of the loop seems to be overwritten with the last iteration of the state that the loop generated.
Basically, here is a rundown of what this algorithm is doing:
This algorithm is being used to solve the N queens problem. All of the underlying code with the state class works perfectly fine. With this algorithm, it iterates through all possible successor states of the current state. It stores the next successor state within the neighborState variable (as seen in the code below). If a state cost is less than the current cost, it will add the neighborState with that new low cost into a neighborNode and store that into a stack. Any new min values that get detected will wipe the stack and insert the new lowest minimum nodes.
I've done a few tests within the loop to see what the outputs look like from what is being inserted into the stack. All of them seem to be correctly outputting. However, when I am outside the loop and check the stack, all the nodes in the stack have their states replaced to the last generated successor state from the loop. It seems that in every node that has the neighborState stored, each time the neighborState updates, it changes all the node neighborState values as well. I just can't seem to find a way to fix this though.
Some advice as to how I can fix this would be greatly appreciated!
*Note: Disregard the code after the for loop starting at the if statement, as it is still incomplete.
Here is the code:
import java.util.Random;
import java.util.Stack;
public class HillClimber {
private LocalSearchNode _current;
private int _shoulderSearchStepsAllowed;
// may need more instance variables
public HillClimber(LocalSearchNode initial, int searchShoulder) {
_current = initial;
_shoulderSearchStepsAllowed = searchShoulder;
}
public LocalSearchNode findSolution() {
LocalSearchNode neighborNode = null;
//Stack <LocalSearchNode> nodeStack;
State currentState = null;
//State neighborState = null;
Double val = 0.0;
boolean start = true;
while (true) {
currentState = _current.getState();
Stack<LocalSearchNode> nodeStack = new Stack<LocalSearchNode>();
// finds the highest valued successor of current
for (String s : currentState.actions()) {
State neighborState = currentState.successor(s);
Double cost = neighborState.estimatedDistance(neighborState);
// execute this for the first successor found
if (start) {
val = cost;
System.out.println("Started with " + val);
neighborNode = new LocalSearchNode(neighborState,
s, val, 0);
nodeStack.push(neighborNode);
start = false;
((QState) nodeStack.peek().getState()).test();
System.out.println(nodeStack.size());
}
// resets node array if new min found and adds it to the array
else if (cost < val) {
System.out.println("Reset " + val + " with " + cost);
val = cost;
nodeStack = new Stack<LocalSearchNode>();
neighborNode= new LocalSearchNode(neighborState,
s, val, 0);
nodeStack.push(neighborNode);
((QState) nodeStack.peek().getState()).test();
System.out.println(nodeStack.size());
}
// if cost is the same as current min val, add it to the array
else if (cost.equals(val)) {
val = cost;
System.out.println("Added " + val);
neighborNode = new LocalSearchNode(neighborState,
s, val, 0);
nodeStack.push(neighborNode);
((QState) nodeStack.peek().getState()).test();
System.out.println(nodeStack.size());
}
}
System.out.println("Final min " + val);
System.out.println(nodeStack.size());
((QState) nodeStack.elementAt(0).getState()).test();
((QState) nodeStack.elementAt(1).getState()).test();
// returns current state if no better state found
if (_current.getValue() < val) {
// System.out.println(val);
// ((QState) _current.getState()).test();
return _current;
} else {
if (nodeStack.size() > 1) {
Random generator = new Random();
int i = generator.nextInt(nodeStack.size());
_current = nodeStack.elementAt(i);
} else {
_current = nodeStack.firstElement();
}
start = true;
}
}
}
}
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class QState implements State {
private List<String> _list;
private int[][] _state;
private int[] _qlist;
/**
* Constructor takes in the board and row index value corresponding to the
* queens at their respective column index
*
* #param state
* #param queens
*/
public QState(int[][] state, int[] queens) {
_state = state;
_qlist = queens;
_list = new ArrayList<String>();
// generates a list of all possible actions for this state
for (int i = 0; i < _qlist.length; i++) {
for (int j = 0; j < _qlist.length; j++) {
if (_state[i][j] != -1) {
_list.add("Move queen " + j + " to row " + i);
}
}
}
}
/**
* Returns a list of N * (N - 1) actions
*/
public List<String> actions() {
return _list;
}
/**
* Returns the matrix board configuration of this state
*
* #return
*/
public int[][] getMatrix() {
return _state;
}
/**
* Returns the array of queens row index for the board configuration
*
* #return
*/
public int[] getQList() {
return _qlist;
}
/**
* Parses the action and moves the queen to the new board location
*/
public State successor(String action) {
State temp = null;
int[][] newState = _state;
int[] newQList = _qlist;
String[] vals = action.split("\\s");
int i = Integer.parseInt(vals[5]); // parses the row index
int j = Integer.parseInt(vals[2]); // parses the column index
newState[_qlist[j]][j] = 0; // clears the old queen
newState[i][j] = -1; // sets the new queen
newQList[j] = i; // adds the new queen to the list
temp = new QState(newState, newQList);
return temp;
};
/**
* Returns the default step cost of 1.0
*/
public Double stepCost(String action) {
return 1.0;
}
// overrides the built-in Java equals method
#Override
public boolean equals(Object s) {
if (s == null) {
return false;
}
if (this.getClass() != s.getClass()) {
return false;
}
if (!Arrays.equals(this.getMatrix(), ((QState) s).getMatrix())) {
return false;
}
return true;
}
/**
* Returns the queen conflicts for the particular board
*/
public Double estimatedDistance(State s) {
double conflicts = 0.0;
int col = 0;
int row = 0;
for (int j = 0; j < _qlist.length; j++) {
row = _qlist[j] - 1;
col = j + 1;
// checks the upper right diagonal for queen conflicts
while (row >= 0 && col < _qlist.length) {
if (_state[row][col] == -1) {
conflicts++;
}
row--;
col++;
}
row = _qlist[j] + 1;
col = j + 1;
// checks the lower right diagonal for queen conflicts
while (row < _qlist.length && col < _qlist.length) {
if (_state[row][col] == -1) {
conflicts++;
}
row++;
col++;
}
row = _qlist[j];
col = j + 1;
// checks the sideways right for queen conflicts
while (col < _qlist.length) {
if (_state[row][col] == -1) {
conflicts++;
}
col++;
}
}
// test();
return conflicts;
}
public void test() {
for (int i = 0; i < _qlist.length; i++) {
for (int j = 0; j < _qlist.length; j++) {
if (_state[i][j] == -1) {
System.out.print("Q");
} else {
System.out.print("-");
}
}
System.out.println("");
}
System.out.println("\n");
}
}
If you look at successor, this looks suspicious to me:
int[][] newState = _state;
int[] newQList = _qlist;
Here, it looks like you're sharing these arrays between objects. Without knowing much about what the program is doing, this kind of thing is typically the cause of the "shared update" behavior you've observed.
So updating the array from the returned successor will also change the state of the object that returned it (and so on).
There are a couple of easy ways to copy an array, namely System#arraycopy, Arrays#copyOf and clone. (All arrays are cloneable.) For the 2D array you might want to make a helper method since you'd probably need to make a deep copy. Something like:
static int[][] copyState(int[][] toCopy) {
int[][] copy = new int[toCopy.length][];
for(int i = 0; i < copy.length; i++) {
// or = Arrays.copyOf(toCopy[i], toCopy[i].length);
copy[i] = toCopy[i].clone();
}
return copy;
}
I didn't spend a whole lot of time really parsing the code--there's a lot to go through, sorry--but I don't see you making a copies anywhere, just mutating them, so I'd put my bet on this.
I am making a tic tac toe game for n number of players on a nxn board, but the winning condition is aways 3 on a row. My so far solution to the problem is: when a move is made the program will check the following square for 3 on a row.
(x-1,y+1) (x,y+1) (x+1,y+1)
(x-1,y) (x,y) (x+1,y)
(x-1,y-1) (x,y-1) (x+1,y-1)
It will check the top (x-1,y+1) (x,y+1) (x+1,y+1) bottom(x-1,y-1) (x,y-1) (x+1,y-1)
sides(x+1,y+1) (x+1,y) (x+1,y-1) , (x-1,y+1) (x-1,y) (x-1,y-1) , the diagonals and the ones going through the middle(x,y).
my code so far is:
public int checkWinning() {
for(int a = 1; a < size-1; a++){
for(int b = 1; b < size-1; b++){
if (board[a][b] == board[a+1][b] && board[a][b] == board[a-1][b]){
return board[a][b];
}else if(board[a][b] == board[a][b+1] && board[a][b] == board[a][b-1]){
return board[a][b];
}else if(board[a][b] == board[a+1][b-1] && board[a][b] == board[a-1][b+1]){
return board[a][b];
}else if(board[a][b] == board[a+1][b+1] && board[a][b] == board[a-1][b-1]){
return board[a][b];
}
}
}
for(int d = 1; d < size-1; d++){
if (board[0][d] == board[0][d-1] && board[0][d] == board[0][d+1]){
return board[0][d];
} else if (board[size-1][d] == board[size-1][d-1] && board[size-1][d] == board[size-1][d+1]){
return board[size-1][d];
}
}
for(int c = 1; c < size-1; c++){
if (board[c][0] == board[c-1][0] && board[c][0] == board[c+1][0]){
return board[c][0];
}else if(board[c][size-1] == board[c-1][size-1] && board[c][size-1] == board[c+1][size-1]){
return board[c][size-1];
}
}
return 0;
}
where the first section is where I check the ones through the middle and diagonals. the second section I check the top an bottom and the top and the thrid section checks the sides.
When it returns 0 is means that there are no winner yet.
#override
public void checkResult() {
int winner = this.board.checkWinning();
if (winner > 0) {
this.ui.showResult("Player "+winner+" wins!");
}
if (this.board.checkFull()) {
this.ui.showResult("This is a DRAW!");
}
}
Board[x][y] -> 2-dimensional array representing the board, The coordinates are counted from top-left (0,0) to bottom-right (size-1, size-1), board[x][y] == 0 signifies free at position (x,y), board[x][y] == i for i > 0 signifies that Player i made a move on (x,y), just so you know it.
my problem is that when i expands the board to a size larger than 3x3 the program somehow overwrites it self or a does not check every thing sides top and bottom every time, and I can't seem too se why.
EDIT:
played with the app for a few minutes... interesting results
java -jar tic-tac-toe.jar 5 20
It was a cats game!!
|1|1|5|5|1|3|5|3|1|5|2|5|1|1|2|
|2|3|2|3|1|5|3|5|3|2|3|1|5|2|2|
|5|4|5|4|1|5|5|4|2|1|4|5|4|2|2|
|3|2|1|5|5|5|2|4|5|3|4|1|2|4|2|
|3|4|1|2|5|4|1|1|4|5|1|3|3|4|1|
|1|5|4|4|3|2|5|1|3|5|1|3|5|3|4|
|2|5|1|4|3|3|3|5|3|1|1|4|3|4|4|
|1|4|5|1|1|5|4|5|2|4|1|1|5|4|3|
|1|3|2|1|4|2|4|3|3|4|5|2|4|3|3|
|5|1|1|3|3|4|4|4|2|2|1|4|3|2|5|
|2|2|3|1|5|5|4|1|3|5|3|2|3|3|2|
|2|4|2|4|4|1|3|1|1|3|1|2|1|2|2|
|2|5|5|1|4|3|4|5|5|4|5|3|3|5|2|
|4|5|2|1|5|3|2|1|3|2|2|2|2|4|4|
|4|1|1|4|5|4|5|4|2|2|3|3|2|2|3|
Played 100 games:
Number wins by Player1: 0
Number wins by Player2: 0
Number wins by Player3: 0
Number wins by Player4: 0
Number wins by Player5: 0
Number of ties: 100
didn't scroll through all 100 games to find the winning board, but I thought this was interesting:
java -jar tic-tac-toe.jar 2 10
Player2 won the game!
|1|1|2|1|2|2| |2|1|2|
|2|2|2|2|2|2|2|2|2|2|
|2|1|2|2|2|1|1|1|1|1|
|1|1|1|1|2|1|2|1|1|1|
|2|2| |1|2|1|1|1|1|2|
|2|2|2|1|1|1| |1|2|2|
|2|2|1|2|2|2|2|2|1|1|
| | |2|2|2|2| |1|1|1|
|1|1|2|2|2|1|1|1|1| |
| | |1|1|1|1|1|2|1| |
Played 100 games:
Number wins by Player1: 0
Number wins by Player2: 1
Number of ties: 99
This does answer your question... but I took it a bit far... decided to implement the solution.
Instead of counting matches... I just check from teh point the last player plays, if all marks in a row column and diagnal match the players, he wins.
package com.clinkworks.example;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TicTacToe {
private static final String TIE = "TIE";
private static final Map<String, Integer> gamesToWinsMap = new HashMap<String, Integer>();
/**
* accepts input in the following format:
*
* playerCount rowCount columnCount (sets the game with the n players, n columns, and n rows)
* - java -jar tic-tac-toe.jar 2 3 3
* PlayerCount squareSize (defaults to a game with rows and cols the same as squareSize and the player count given)
* - java -jar tic-tac-toe.jar 2 3
* PlayerCount (defaults to a 3 by 3 game)
* - java -jar tic-tac-toe.jar 2
* no input (defaults to a 3 by 3 game with 2 players)
* - java -jar tic-tac-toe.jar
* #param args
*/
public static void main(String[] args) {
int playerCount = 2;
int rows = 3;
int cols = 3;
if(args.length == 3){
playerCount = Integer.valueOf(args[0]);
rows = Integer.valueOf(args[1]);
cols = Integer.valueOf(args[2]);
}
if(args.length == 2){
playerCount = Integer.valueOf(args[0]);
rows = Integer.valueOf(args[1]);
cols = rows;
}
if(args.length == 1){
playerCount = Integer.valueOf(args[0]);
}
for(int i = 1; i <= playerCount; i++){
gamesToWinsMap.put("Player" + i, 0);
}
//lets play 100 games and see the wins and ties
playGames(100, playerCount, rows, cols);
for(int i = 1; i <= playerCount; i++){
System.out.println("Number wins by Player" + i + ": " + gamesToWinsMap.get("Player" + i));
}
System.out.println("Number of ties: " + gamesToWinsMap.get(TIE));
}
public static void playGames(int gamesToPlay, int playerCount, int rows, int cols) {
//play a new game each iteration, in our example, count = 100;
for (int i = 0; i < gamesToPlay; i++) {
playGame(playerCount, rows, cols);
}
}
public static void playGame(int playerCount, int rows, int cols) {
//create a new game board. this initalizes our 2d array and lets the complexity of handling that
// array be deligated to the board object.
Board board = new Board(playerCount, rows, cols);
//we are going to generate a random list of moves. Heres where we are goign to store it
List<Move> moves = new ArrayList<Move>();
//we are creating moves for each space on the board.
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
moves.add(new Move(row, col));
}
}
//randomize the move list
Collections.shuffle(moves);
//do each move
for (Move move : moves) {
board.play(move);
if(gameOver(board)){
break;
}
}
}
public static boolean gameOver(Board board){
if (board.whoWon() != null) {
System.out.println(board.whoWon() + " won the game!");
System.out.println(board);
Integer winCount = gamesToWinsMap.get(board.whoWon());
winCount = winCount == null ? 1 : winCount + 1;
gamesToWinsMap.put(board.whoWon(), winCount);
return true;
} else if (board.movesLeft() == 0) {
System.out.println("It was a cats game!!");
System.out.println(board);
Integer tieCount = gamesToWinsMap.get(TIE);
tieCount = tieCount == null ? 1 : tieCount + 1;
gamesToWinsMap.put(TIE, tieCount);
return true;
}
return false;
}
public static class Move {
private int row;
private int column;
public Move(int row, int column) {
this.row = row;
this.column = column;
}
public int getRow() {
return row;
}
public int getColumn() {
return column;
}
}
public static class Board {
private final int rowSize;
private final int columnSize;
private final Integer[][] gameBoard;
private int playerCount;
private int currentPlayer;
private String winningPlayer;
public Board() {
gameBoard = new Integer[3][3];
currentPlayer = 1;
winningPlayer = null;
this.rowSize = 3;
this.columnSize = 3;
playerCount = 2;
}
public Board(int players) {
gameBoard = new Integer[3][3];
currentPlayer = 1;
winningPlayer = null;
this.rowSize = 3;
this.columnSize = 3;
playerCount = players;
}
public Board(int rowSize, int columnSize) {
gameBoard = new Integer[rowSize][columnSize];
currentPlayer = 1;
winningPlayer = null;
playerCount = 2;
this.rowSize = rowSize;
this.columnSize = columnSize;
}
public Board(int players, int rowSize, int columnSize) {
gameBoard = new Integer[rowSize][columnSize];
currentPlayer = 1;
winningPlayer = null;
playerCount = players;
this.rowSize = rowSize;
this.columnSize = columnSize;
}
/**
*
* #return the amount of empty spaces remaining on the game board, or if theres a winning player, zero.
*/
public int movesLeft() {
if(whoWon() != null){
return 0;
}
int moveCount = 0;
for (int x = 0; x < getRowSize(); x++) {
for (int y = 0; y < getColumnSize(); y++) {
moveCount += getMoveAt(x, y) == null ? 1 : 0;
}
}
return moveCount;
}
/**
* If someone won, this will return the winning player.
*
* #return the winning player
*/
public String whoWon() {
return winningPlayer;
}
/**
* This move allows the next player to choose where to place their mark.
*
* #param row
* #param column
* #return if the game is over, play will return true, otherwise false.
*/
public boolean play(Move move) {
if (!validMove(move)) {
// always fail early
throw new IllegalStateException("Player " + getCurrentPlayer() + " cannot play at " + move.getRow() + ", " + move.getColumn() + "\n" + toString());
}
doMove(move);
boolean playerWon = isWinningMove(move);
if (playerWon) {
winningPlayer = "Player" + getCurrentPlayer();
return true;
}
shiftPlayer();
boolean outOfMoves = movesLeft() <= 0;
return outOfMoves;
}
public int getRowSize() {
return rowSize;
}
public int getColumnSize() {
return columnSize;
}
public int getCurrentPlayer() {
return currentPlayer;
}
public Integer getMoveAt(int row, int column) {
return gameBoard[row][column];
}
private void doMove(Move move) {
gameBoard[move.getRow()][move.getColumn()] = getCurrentPlayer();
}
private void shiftPlayer() {
if(getCurrentPlayer() == getPlayerCount()){
currentPlayer = 1;
}else{
currentPlayer++;
}
}
private int getPlayerCount() {
return playerCount;
}
private boolean validMove(Move move) {
boolean noMoveAtIndex = false;
boolean indexesAreOk = move.getRow() >= 0 || move.getRow() < getRowSize();
indexesAreOk = indexesAreOk && move.getColumn() >= 0 || move.getColumn() < getColumnSize();
if (indexesAreOk) {
noMoveAtIndex = getMoveAt(move.getRow(), move.getColumn()) == null;
}
return indexesAreOk && noMoveAtIndex;
}
private boolean isWinningMove(Move move) {
// since we check to see if the player won on each move
// we are safe to simply check the last move
return winsDown(move) || winsAcross(move) || winsDiagnally(move);
}
private boolean winsDown(Move move) {
boolean matchesColumn = true;
for (int i = 0; i < getColumnSize(); i++) {
Integer moveOnCol = getMoveAt(move.getRow(), i);
if (moveOnCol == null || getCurrentPlayer() != moveOnCol) {
matchesColumn = false;
break;
}
}
return matchesColumn;
}
private boolean winsAcross(Move move) {
boolean matchesRow = true;
for (int i = 0; i < getRowSize(); i++) {
Integer moveOnRow = getMoveAt(i, move.getColumn());
if (moveOnRow == null || getCurrentPlayer() != moveOnRow) {
matchesRow = false;
break;
}
}
return matchesRow;
}
private boolean winsDiagnally(Move move) {
// diagnals we only care about x and y being teh same...
// only perfect squares can have diagnals
// so we check (0,0)(1,1)(2,2) .. etc
boolean matchesDiagnal = false;
if (isOnDiagnal(move.getRow(), move.getColumn())) {
matchesDiagnal = true;
for (int i = 0; i < getRowSize(); i++) {
Integer moveOnDiagnal = getMoveAt(i, i);
if (moveOnDiagnal == null || moveOnDiagnal != getCurrentPlayer()) {
matchesDiagnal = false;
break;
}
}
}
return matchesDiagnal;
}
private boolean isOnDiagnal(int x, int y) {
if (boardIsAMagicSquare()) {
return x == y;
} else {
return false;
}
}
private boolean boardIsAMagicSquare() {
return getRowSize() == getColumnSize();
}
public String toString() {
StringBuffer stringBuffer = new StringBuffer();
for(int y = 0; y < getColumnSize(); y++) {
for(int x = 0; x < getRowSize(); x++) {
Integer move = getMoveAt(x, y);
String moveToPrint = "";
if (move == null) {
moveToPrint = " ";
} else {
moveToPrint = move.toString();
}
stringBuffer.append("|").append(moveToPrint);
}
stringBuffer.append("|\n");
}
return stringBuffer.toString();
}
}
}
I have to revise my answer. If you want to have three in a row regardless of your board size, your loop code might be sufficient, but you are always checking whether the values of the fields are the same but never make a difference between empty and non-empty fields.
So “empty” can win too, which would effectively hide a possible win of a player. In other words, your code does not work correctly, even for a field size of three. You didn’t test it enough.
If I initialize the board as
int[][] board={
{ 1, 1, 1 },
{ 0, 0, 0 },
{ 0, 0, 0 },
};
your code returns 0 as the second row contains three zeros. I assumed that 0 represents the empty field but the actual value for “empty” doesn’t matter. You have to exclude empty fields from the three-in-a-row check.
You can simplify this a fair amount by breaking the logic up a bit.
First realize that you only need to check for a win around the piece you just placed.
Now we need a way to check whether that move is a winner.
First we need a simple function to check whether a cell matches a given value, returning true if its within bounds and matches.
private boolean cellMatches(int x, int y, int val) {
if (x<0||x>boardWidth)
return false;
if (y<0||y>boardHeight)
return false;
return board[x][y]==val;
}
Now a function that you give a starting position (x and y) and a delta (dx, dy) and it checks up to two cells in that direction returning a count of how many in a row matched value. The for loop may be overkill for two checks but it would easily allow you to expand up to longer lines being used.
private int countMatches(int x, int y, int dx, int dy, int val) {
int count = 0;
for (int step=1;step<=2;step++) {
if (cellMatches(x+dx*step, y+dy*step, val) {
count++;
} else {
return count;
}
}
return count;
}
Now we can use the previous method. When we place a new piece we can just count out in each matching pair of directions. The combined count is the total number in a row. (i.e. two in a row top + 1 bot = a total run length of 4). If any of those run lengths is three then it is a winning move.
private boolean makeMove(int x, int y, int val) {
board[x][y] = val;
int runlength=countMatches(x,y,0,1,val) + countMatches(x,y,0,-1,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,0,val) + countMatches(x,y,-1,0,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,1,val) + countMatches(x,y,-1,-1,val);
if (runLength >= 2)
return true;
int runlength=countMatches(x,y,1,-1,val) + countMatches(x,y,-1,1,val);
if (runLength >= 2)
return true;
return false;
}
Note that because we need to count the center piece that we placed we just need a run length of two or more.
I am currently doing Algorithms in collage and we are asked to make a hex game using Weighted quick union, we were given most of the code for the project by the lecturer. But im running into a problem here.`public class Hex implements BoardGame {
private int[][] board; // 2D Board. 0 - empty, 1 - Player 1, 2 - Player 2
private int n1, n2; // height and width of board
private WeightedQuickUnionUF wqu; // Union Find data structure to keep track
// of unions and calculate winner
private int currentPlayer; // Current player in the game, initialised to 1
public Hex(int n1, int n2) // create N-by-N grid, with all sites blocked
{
this.n1 = n1;
this.n2 = n2;
currentPlayer = 1;
// TODO: Create instance of board
// TODO: Create instance WeightedQuickUnionUF class
wqu = new WeightedQuickUnionUF(14);
board = new int[n1][n2];
for(int i=0; i < n1 ; i++){
for(int j = 0; j < n2; j++){
board[i][j] = 0;
}
}
}
/*
* (non-Javadoc)
*
* #see BoardGame#takeTurn(int, int)
*/
#Override
public void takeTurn(int x, int y) {
if(((x > n1) || (x < 0)) || ((y > n2) || (y < 0)))
{
StdOut.println("Wrong");
}
else{
if(board[x][y] == 0){
board[x][y] = currentPlayer;
}
else{
StdOut.println("Taken");
}
}
// TODO: check coords are valid
// TODO: check if location is free and set to player's value(1 or 2).
// TODO: calculate location and neighbours location in
// WeightedQuickUnionUF data structure
// TODO: create unions to neighbour sites in WeightedQuickUnionUF that
// also contain current players value
// TODO: if no winner get the next player
}
/*
* (non-Javadoc)
*
* #see BoardGame#getCurrentPlayer()
*/
#Override
public int getCurrentPlayer() {
return currentPlayer;
}
public void setCurrentPlayer(int currentPlayer) {
this.currentPlayer = currentPlayer;
}
/*
* (non-Javadoc)
*
* #see BoardGame#getBoard()
*/
#Override
public int[][] getBoard() {
return board;
}
private void nextPlayer() {
if (currentPlayer == 1)
currentPlayer = 2;
else
currentPlayer = 1;
}
/*
* (non-Javadoc)
*
* #see BoardGame#isWinner()
*/
#Override
public boolean isWinner() {
// TODO:check if there is a connection between either side of the board.
// You can do this by using the 'virtual site' approach in the
// percolation test.
return false;
}
/**
* THIS IS OPTIONAL:
* Modify the main method if you wish to suit your implementation.
* This is just an example of a test implementation.
* For example you may want to display the board after each turn.
* #param args
*
*/
public static void main(String[] args) {
BoardGame hexGame = new Hex(4, 4);
while (!hexGame.isWinner()) {
System.out.println("It's player " + hexGame.getCurrentPlayer()
+ "'s turn");
System.out.println("Enter x and y location:");
int x = StdIn.readInt();
int y = StdIn.readInt();
hexGame.takeTurn(x, y);
}
System.out.println("It's over. Player " + hexGame.getCurrentPlayer()
+ " wins!");
}
}
`
I have already checked if the coordinates are valid and if the place on the board is free. But I can seem to get my head around finding the location and neighbours locations using WeightedQuickUnionUF. Any help would be great as I have tried everything I know so far. Here is the WeightedQuickUnionUF class.
public class WeightedQuickUnionUF {
private int[] id;
private int[] sz;
private int count;
public WeightedQuickUnionUF(int N){
count = N;
id = new int[N];
sz = new int[N];
for(int i = 0 ; i < N; i++){
id[i] = i;
sz[i] = i;
}
}
public int count(){
return count;
}
public int find(int p){
while(p != id[p])
p = id[p];
return p;
}
public boolean connected(int p, int q ){
return find(p) == find(q);
}
public void union(int p, int q){
int i = find(p);
int j = find(q);
if(i == j) return;
if(sz[i] < sz[j]){id[i] = j; sz[j] += sz[i];}
else {id[j] = i; sz[i] += sz[j];}
count--;
}
public static void main(String[] args) {
int N = StdIn.readInt();
WeightedQuickUnionUF uf = new WeightedQuickUnionUF(N);
while(!StdIn.isEmpty()){
int p = StdIn.readInt();
int q = StdIn.readInt();
if(uf.connected(p,q)) continue;
uf.union(p, q);
StdOut.println(p + " " + q);
}
StdOut.println(uf.count() + "components");
}
}
You have a bug in the initialization code for sz[]
It should be:
for(int i = 0 ; i < N; i++){
id[i] = i;
sz[i] = 1; // changed to 1 so it indicates the number of nodes for this 'root'
}