package finite_states.problems;

import finite_states.Action;
import finite_states.State;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.IntStream;

/**
 * The famous N-Queens problem.
 * A solution for this problem is a
 */
public class NQueens extends Problem {

    /**
     * The number of queens in play.
     */
    public final int n;

    /**
     * The possible actions, for each puzzle instance, are finite.
     * Generate them once and for all.
     */
    private final NQueensAction[][] possible_actions;

    public NQueens(@NotNull String name, int n) {
        super(name);

        assert n > 0;
        this.n = n;

        possible_actions = new NQueensAction[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                possible_actions[i][j] = new NQueensAction(
                        String.format("Queen #%d in row %d", j, i), i, j
                );
            }
        }
    }

    @Override
    public boolean isGoal(@NotNull State state) {
        // TODO: implement me!
        return false;
    }

    @Override
    public @NotNull State buildRandomState() {
        return new NQueensState();
    }

    /**
     * An action, for this problem, consists of moving a single queen through
     * her column on the chessboard, up to a specified row.
     */
    public class NQueensAction extends Action {

        /**
         * The index of the queen to be moved.
         */
        public final int column;

        /**
         * The index on which the queen will be moved.
         */
        public final int row;

        /**
         * An Action for the NQueens problem.
         *
         * @param name A human readable name for this action.
         * @param row The row to which selected queen will be moved.
         * @param column The column selecting the queen to be moved.
         */
        public NQueensAction(@NotNull String name, int row, int column) {
            super(name, 1);

            assert row < n;
            assert column < n;

            this.row = row;
            this.column = column;
        }
    }

    /**
     * Represent a possible state for the world of this problem.
     * i.e. a possible configuration of the queens on the chessboard.
     */
    public class NQueensState extends State {

        /**
         * An array keeping representing columns of the chessboard.
         * Values of this array represent the row of the i-th queen.
         */
        private final int[] positions;

        /**
         * Generate a random configuration for the queens.
         */
        public NQueensState() {
            positions = new int[n];

            Integer[] numbers;
            numbers = IntStream.range(0, positions.length)
                    .boxed().toArray(Integer[]::new);
            Collections.shuffle(Arrays.asList(numbers));
            assert numbers.length == n;

            for (int i = 0; i < n; i++) {
                this.positions[i] = numbers[i];
            }
        }

        /**
         * Build a new puzzle, starting from the current one and applying the action.
         *
         * @param positions The current position of the queens.
         * @param action    The action to be performed.
         */
        private NQueensState(int[] positions, @NotNull Action action) {
            assert Arrays.stream(positions).max().orElse(n) < n;
            assert Arrays.stream(positions).min().orElse(-1) >= 0;
            assert action instanceof NQueensAction;

            NQueensAction qAction = (NQueensAction)action;
            this.positions = positions.clone();
            this.positions[qAction.column] = qAction.row;
        }

        @Override
        public @NotNull Iterable<Action> getActions() {
            final ArrayList<Action> actions = new ArrayList<>();

            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    if (positions[j] != i) {
                        actions.add(possible_actions[i][j]);
                    }
                }
            }

            return actions;
        }

        @Override
        public @NotNull State performAction(Action action) {
            return new NQueensState(this.positions, action);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            NQueensState state = (NQueensState) o;
            return Arrays.equals(this.positions, state.positions);
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(this.positions);
        }

        @Override
        public String toString() {
            return Arrays.toString(this.positions);
        }
    }
}

@@ -1122,50 +1122,698 @@
     
-// TODO: implement me!%0A        return fals
+if (!(state instanceof NQueensState)) %7B%0A            assert false;  // This is a mistake, alert the developer.%0A            return false;%0A        %7D%0A        NQueensState qState = (NQueensState)state;%0A%0A        // Check that the queen is on a different row.%0A        if (Arrays.stream(qState.positions).distinct().count() %3C n) %7B%0A            return false;%0A        %7D%0A%0A        // Be sure that queens are on different diagonal segments.%0A        for (int i = 0; i %3C n; i++) %7B%0A            for (int j = i + 1; j %3C n; j++) %7B%0A                if (Math.abs(qState.positions%5Bi%5D - qState.positions%5Bj%5D) == j - i) %7B%0A                    return false;%0A                %7D%0A            %7D%0A        %7D%0A%0A        return tru
 e;%0A 
@@ -3226,30 +3226,29 @@
 */%0A        p
-rivate
+ublic
  final int%5B%5D

Add solution check for the NQueens problem