Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about writing a program for placing some modified queen-type pieces on an 8 x 8 board

Tags:

java

algorithm

To this question:

The superqueen is a chess piece that can move like a queen, but also like a knight. What is the maximal number of superqueens on an 8X8 chessboard such that no one can capture an other?

I want to write a brute force algorithm to find the maximum. Here's what I wrote:

public class Main {

    public static boolean chess[][];

    public static void main(String[] args) throws java.lang.Exception {
        chess = new boolean[8][8];
        chess[0][0] = true;

        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                /*Loop to check various possibilities*/
                if (!checkrow(i) && !checkcolumn(j) && !checkdiagonals(i, j) && !checkknight(i, j)) {
                    if (i != 0 || j != 0) {
                        chess[i][j] = true;
                    }
                }
            }
        }/*printing the array*/

        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                System.out.print(((chess[i][j]) ? "T" : "x") + "|");
            }
            System.out.println();
        }
    }

    /*All working fine here*/
    public static boolean checkrow(int a) {

        for (int i = 0; i < 8; i++) {
            if (chess[a][i]) {
                return true;
            }
        }
        return false;
    }

    /*All working fine here*/
    public static boolean checkcolumn(int a) {

        for (int i = 0; i < 8; i++) {
            if (chess[i][a]) {
                return true;
            }
        }
        return false;
    }

    /*All working fine here*/
    public static boolean checkdiagonals(int pi, int pj) {

        int i = pi - Math.min(pi, pj);
        int j = pj - Math.min(pi, pj);

        for (int k = i, l = j; k < 8 && l < 8; k++, l++) {
            if (chess[k][l]) {
                return true;
            }
        }

        int i_2 = pi - Math.min(pi, pj);
        int j_2 = pj + Math.min(pi, pj);

        for (int k = i_2, l = j_2; k < 8 && l > 1; k++, l--) {
            if (chess[k][l]) {
                return true;
            }
        }
        return false;
    }

    /*Not All working fine here try commenting out this method above so that that it doesn't run during the check*/
    public static boolean checkknight(int pi, int pj) {

        for (int i = -1; i <= 1; i++) {
            for (int j = -1; j <= 1; j++) {
                if (0 <= pi + 2 * i && pi + 2 * i <= 8 && 0 <= pj + j && pj + j <= 8) {
                    if (chess[pi + 2 * i][pj + j]) {
                        return true;
                    }
                }

                if (0 <= pi + i && pi + i <= 8 && 0 <= pj + 2 * j && pj + 2 * j <= 8) {
                    if (chess[pi + i][pj + 2 * i]) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

I have two questions:

  • My algorithm for checkknight looks for all knight positions, is it wrong? or there is some coding error.Everything is working fine when I comment out it and I get a nice solution.
  • Secondly it'll result only in one solution.For other solutions I have to offset(or change position) of other pieces bit by bit after each mega-loop of this, I am confused about implementing it. My instincts guide me that I need to change whole of the code. Is there a modification or a way to do it?

Additional Thoughts: I think we would add to a counter each time we place a piece and add to a long array and output the maximum and array after storing the relevant data.

Code Location: You may view/edit/fork/download it at http://ideone.com/gChD8a

like image 265
RE60K Avatar asked Sep 10 '14 19:09

RE60K


1 Answers

This a rough brute-force method starting from the opposite direction, i.e. from the solved eight-queens puzzle. This will allow us to find a bunch of viable solutions.

The brute-force technique for going from a single superqueen to potentially 8 seems to be especially complex due to the knight's traversal. Based on the runs, about 60% of the viable paths for normal queens are invalid with superqueens. So if we were to instead brute force normal queens, and then work backwards, that is potential time saved for finding a solution, and we can better determine the run-time. Because we know normal queens is easier.

We start off with the 12 fundamental solutions, we would then use these as inputs. Solving normal queens is outside this, but the wiki page has a fantastic article describing everything.

In my case, I stored them as Strings representing the coordinate of the queen (the rows are indices).

So: "17468253" = A1, B7, C4, D6, E8, F2, G5, H3

By brute-forcing the opposite direction from solved queens, we only have to test at most 12 x 8! possible solutions. Because order doesn't matter, additional optimization could occur by eliminating duplicate boards and solutions for processing.

First up, checkKnight, which appears to be your source of confusion. Using absolute values, you can reasonably determine whether or not a piece is within knight's range by checking whether the X offset is 2 and Y offset is 1, or vice versa. You've made a complex checkKnight function to check each individual location and whether or not a piece is on the border. Working the other way by hitscanning each queen to each other queen is logically simpler and less of a nightmare to debug.

Queen class

public class Queen {
    int i, j;

    public Queen(int i, int j) {
        this.i = i;
        this.j = j;
    }

    public boolean checkKnight(Queen queen) { // if any queen meets another
                                                // queen at 2 and 1 offset, we
                                                // eliminate it.
        return (Math.abs(i - queen.i) == 2 && Math.abs(j - queen.j) == 1)
                || (Math.abs(i - queen.i) == 1 && Math.abs(j - queen.j) == 2);
    }
}

This board has been modified since I originally posted. It takes a String input and converts it to a full chessboard. It has some minor work towards the potential any-size board, but right now it handles child board creation. When a child board is created, the queens are passed by reference rather than making a whole new set of queens. A total of 96 queens are stored in memory, 1 for each one on the original 12-board solution. Not perfectly optimized, but better than 96 -> 672 -> 4032 -> ...

Board class

public class Board {
    static int boardSize = 8;
    ArrayList<Queen> queens = new ArrayList<Queen>();

    public Board(String s) {
        for (int i = 0; i < s.length(); i++) {
            queens.add(new Queen(i, s.charAt(i) - 49)); // you could implement
                                                        // base 16 here, for
                                                        // example, for a 15x15
                                                        // board
        }
    }

    public Board(Board b) { // duplicates the board, but keeps references to
                            // queens to conserve memory, only 96 total queens
                            // in existence through search!
        for (Queen q : b.queens) {
            queens.add(q);
        }
    }

    public boolean checkForImpact() {
        for (int i = 0; i < queens.size(); i++) {
            for (int j = i + 1; j < queens.size(); j++) {
                if (queens.get(i).checkKnight(queens.get(j))) { // just check
                                                                // for any
                                                                // queens
                                                                // intersecting,
                                                                // one hit is
                                                                // enough
                    return true;
                }
            }
        }
        return false;
    }

    public ArrayList<Board> getChildBoards() { // create child boards with a
                                                // single queen removed
        ArrayList<Board> boards = new ArrayList<Board>();
        for (int i = 0; i < queens.size(); i++) {
            boards.add(new Board(this));
        }
        int i = 0;
        for (Board b : boards) {
            b.queens.remove(i);
            i++;
        }
        return boards;
    }

    public String drawBoard() {
        String s = "";
        char[][] printableBoard = new char[boardSize][boardSize];
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                printableBoard[i][j] = '_';
            }
        }
        for (Queen q : queens) {
            printableBoard[q.i][q.j] = 'Q';
        }
        s += "  A B C D E F G H\n";
        for (int i = 0; i < 8; i++) {
            s += (8 - i) + "|";
            for (int j = 0; j < boardSize; j++) {
                s += printableBoard[i][j];
                s += "|";
            }
            s += "\n";
        }
        return s;
    }
}

Test class

import java.util.ArrayList;

public class Test {
    static String[] boards = { "24683175", "17468253", "17582463", "41582736",
            "51842736", "31758246", "51468273", "71386425", "51863724",
            "57142863", "63184275", "53172864" }; // all 12 solutions for the 8
                                                    // queens problem
    static ArrayList<Board> boardObjects = new ArrayList<Board>();

    public static void main(String[] args) {
        for (String queens : boards) { // create starter boards
            boardObjects.add(new Board(queens));
        }

        int i;
        ArrayList<Board> foundBoards = null;
        for (i = 8; i > 0; i--) {
            ArrayList<Board> newBoards = new ArrayList<Board>();
            foundBoards = new ArrayList<Board>();
            for (Board b : boardObjects) {
                if (b.checkForImpact()) { // if any queen intercepts we get
                                            // children
                    ArrayList<Board> boardsToBeAdded = b.getChildBoards(); // pass
                                                                            // all
                                                                            // permutations
                                                                            // of
                                                                            // queens
                                                                            // once
                                                                            // removed
                    for (Board bo : boardsToBeAdded) {
                        newBoards.add(bo); // add it in to the next list
                    }
                } else {
                    foundBoards.add(b); // if we have no impact, we have a
                                        // solution
                }
            }
            if (!foundBoards.isEmpty())
                break;
            boardObjects.clear();
            boardObjects = newBoards;
        }
        System.out.println("The maximum number of super-queens is: " + i);
        ArrayList<String> winningCombinations = new ArrayList<String>();
        for (Board board : foundBoards) {
            String createdBoard = board.drawBoard();
            boolean found = false;
            for (String storedBoard : winningCombinations) {
                if (storedBoard.equals(createdBoard))
                    found = true;
            }
            if (!found)
                winningCombinations.add(createdBoard);
        }
        for (String board : winningCombinations) {
            System.out.println(board);
        }
    }
}

The end output is:

The maximum number of super-queens is: 6
  A B C D E F G H
8|Q|_|_|_|_|_|_|_|
7|_|_|_|_|_|_|Q|_|
6|_|_|_|Q|_|_|_|_|
5|_|_|_|_|_|_|_|_|
4|_|_|_|_|_|_|_|Q|
3|_|Q|_|_|_|_|_|_|
2|_|_|_|_|Q|_|_|_|
1|_|_|_|_|_|_|_|_|

  A B C D E F G H
8|Q|_|_|_|_|_|_|_|
7|_|_|_|_|_|_|_|_|
6|_|_|_|_|Q|_|_|_|
5|_|_|_|_|_|_|_|Q|
4|_|Q|_|_|_|_|_|_|
3|_|_|_|_|_|_|_|_|
2|_|_|_|_|_|Q|_|_|
1|_|_|Q|_|_|_|_|_|

  A B C D E F G H
8|_|_|_|_|Q|_|_|_|
7|Q|_|_|_|_|_|_|_|
6|_|_|_|_|_|_|_|Q|
5|_|_|_|Q|_|_|_|_|
4|_|_|_|_|_|_|_|_|
3|_|_|_|_|_|_|_|_|
2|_|_|Q|_|_|_|_|_|
1|_|_|_|_|_|Q|_|_|

  A B C D E F G H
8|_|_|_|_|Q|_|_|_|
7|Q|_|_|_|_|_|_|_|
6|_|_|_|_|_|_|_|Q|
5|_|_|_|Q|_|_|_|_|
4|_|_|_|_|_|_|_|_|
3|_|_|_|_|_|_|Q|_|
2|_|_|Q|_|_|_|_|_|
1|_|_|_|_|_|_|_|_|

  A B C D E F G H
8|_|_|_|_|Q|_|_|_|
7|Q|_|_|_|_|_|_|_|
6|_|_|_|_|_|_|_|Q|
5|_|_|_|_|_|_|_|_|
4|_|_|Q|_|_|_|_|_|
3|_|_|_|_|_|_|Q|_|
2|_|_|_|_|_|_|_|_|
1|_|_|_|Q|_|_|_|_|

I've removed the duplicates and made a nice board printing method. don't remember the exact math, but this highlights 40 possible locations. There are others, just by looking, but we've found a fair chunk of them already! From here, we can gently shift individual queens around. From a cursory look, each board has a single piece that can be moved to 3 additional spaces, so now we know there are probably about 160 solutions.

Conclusions

With this application, the run-time on my machine was less than a second, meaning that if we attached this to a standard queens application, the additional knight's brute-forcing would have no impact on that process and have almost the same run-time. In addition, because only 6-piece puzzles are possible, we know that your eventual application run will finish its finding at the 6th piece being placed, as no more solutions are possible, since there are no viable 7-piece and 8-piece solutions.

In other words, finding the maximum super-queen layout is likely actually shorter than the maximum queen layout due to the additional restrictions!

like image 132
Compass Avatar answered Sep 27 '22 21:09

Compass