N-Queens In Java, Counting the Number of Probes

From dftwiki3
Jump to: navigation, search

--D. Thiebaut (talk) 09:52, 24 October 2014 (EDT)


A Java version of the N-Queens problem, that attempts to put N queens on an NxN board. It stops after the first solution is found, and reports the real time execution time. See this page for a comparison of execution times on various machines.



/**
 * NQueens.java
 * @author thiebaut
 * Positions N queens on an NxN board.
 * To compile and run:
 *      javac NQueens.java
 *      java NQueens N         
 *      (where N is the size of the board)
 */
public class NQueens {
	static int[][] board = null;
	static int counter = 0;
	
	private static void displayBoard( int N ) {
		char escCode = 0x1B;
		
		System.out.println(String.format("%c[%d;%df", escCode, 0, 0 ));
		
		for ( int c=0; c < N; c++ )
			System.out.print( "--" );
		System.out.println();
			
		
		for ( int r=0; r < N; r++ ) {
			for ( int c=0; c < N; c++ ) {
				if ( board[r][c] == -1 )
					System.out.print( "Q " );
				else if ( board[r][c] == 0 )
					System.out.print( ". " );
				else 
					System.out.print( "* " );
			}
			System.out.println();
		}
		
		try {
			Thread.sleep( 0 );//
		} catch (InterruptedException e) {
			//e.printStackTrace();
		}
	}

	private static void displayBoardNumbers( int N ) {
		
		System.out.println(String.format("%c[%d;%df", 0x1B, 0, 0 ));
		
		for ( int c=0; c < N; c++ )
			System.out.print( "--" );
		System.out.println();
			
		for ( int r=0; r < N; r++ ) {
			for ( int c=0; c < N; c++ ) 
				System.out.print( String.format( "%2d", board[r][c]) );
			System.out.println();
		}
	}

	/**
	 * recursively place N queens on the NxN board.
	 * Our job is to place a queen on Row row, anywhere we can, and then
	 * call ourself on Row row+1... until all N queens have been placed
	 * @param row: the row we're working on.
	 * @param N: the dimension of the board
	 * @return true if successfully placed all N queens.  False if we got stuck.
	 */
	private static boolean recursePlaceQueen( int row, int N ) {
		
		// We've landed on Row row.  Let's try all the columns that 
		// are open...
		for ( int col = 0; col < N; col++ ) {
			
			// is there a non-taken spot?
			counter++;
			if ( board[row][col] == 0 ) {
				
				// yes; let's put a queen here, temporarily
				board[row][col] = -1; // put a queen there
				counter++;
				
				//if we're on last row and found an empty spot, then we're done! ---
				if ( row == N-1 )
					return true;

				// otherwise, we have some more recursing to do. Mark all the 
				// cells down from this row that are taken by the queen we've just
				// added.
				updateColumnDown( row, col, N, +1 );
				updateDiagonals( row, col, N, +1 );
				
				// show the current board
				//displayBoard( N );
				
				// recurse, asking ourselves to put a new queen on the row
				// below where we are.  Keep track of whether recursion was
				// successful or not.
				boolean success = recursePlaceQueen( row+1, N );
				
				// were we successful putting all N queens?
				if ( success ) 
					// yes: then we're done, return true!
					return true;

				// No, we were not successful... could not place all N queens with 
				// our queen currently where it is.  Remove it 
				board[row][col] = 0;
				counter++;
				
				// undo the invalidation that was done earlier...
				updateColumnDown( row, col, N, -1 );
				updateDiagonals( row, col, N, -1 );
				
				// display current state of the board
				//displayBoard( N );
				
				// continue looping, looking for a new position where to put a queen
				// on Row row.
			}
		}
		// if we're here, we've tried all the possible columns on Row row, and we weren't
		// successful.  So return false to whoever called us...  No success with current 
		// position of queens on rows above us...
		return false;
	}

	/**
	 * given the position of a queen at Row row and Column col, mark all the
	 * cells on rows below and on diagonal positions relative to (row, col) as
	 * "taken", if offset is 1, or as "not taken" if offset is -1.
	 * @param row
	 * @param col
	 * @param N
	 * @param offset
	 */
	private static void updateDiagonals(int row, int col, int N, int offset) {
		int leftC = col-1, rightC = col+1;
		for ( int r=row+1; r<N; r++ ) {
			if ( leftC >= 0 ) {
				counter++;
				board[r][leftC]  += offset;
			}
			leftC--;
			if ( rightC < N ) { 
				board[r][rightC] += offset;
				counter++;
			}
			rightC++;
		}
	}

	/**
	 * given the position of a queen at Row row and Column col, mark all the
	 * cells on rows just below this cell as "taken", if offset is 1, or as 
	 * "not taken" if offset is -1.
	 * @param row
	 * @param col
	 * @param N
	 * @param offset
	 */
	private static void updateColumnDown(int row, int col, int N, int offset ) {
		for ( int r = row+1; r < N; r++ ) {
			board[ r ][ col ] += offset;
			counter++;
		}
	}

	/**
	 * create an NxN board with empty cells (containing 0)
	 * @param N
	 * @return
	 */
	private static int[][] createBoard( int N ) {
		board = new int[N][];
		
		for ( int i=0; i<N; i++ ) {
			counter++;		
			board[i] = new int[N];
		}
		
		for ( int i=0; i<N; i++ )
			for ( int j=0; j<N; j++ ) {
				counter++;
				board[i][j] = 0;
			}
		
		return board;
	}


	/**
	 * clears the screen (works only in Terminal/Console)
	 */
	private static void clearScreen() {
        System.out.print("\033[2J\033[;H");
    }
	
	/**
	 * main entry point.
	 * @param args
	 */
	public static void main(String[] args) {
		
		if ( args.length == 0 ) {
			args = new String[] { "30" };
		}
		
		//clearScreen();
		
		int N = Integer.parseInt( args[0] );
		board = createBoard( N );
		
		counter=0;
		long start = System.currentTimeMillis();
		recursePlaceQueen( /* row = */ 0, N );
		long end = System.currentTimeMillis();
		
		//displayBoard( N );
		System.out.println( String.format(
				"%dx%d board: %d probes. real time = %d ms.",
				N, N, counter, (end-start) ) );
	}
}