RosettaCodeData/Task/N-queens-problem/SQL-PL/n-queens-problem.sql

277 lines
5.7 KiB
SQL

-- A column of a matrix.
CREATE TYPE INTEGER_ARRAY AS INTEGER ARRAY[]@
-- The whole matrix of any size.
CREATE TYPE INTEGER_MATRIX AS INTEGER_ARRAY ARRAY[]@
/**
* Retrieves the value from a matrix at a specific position.
*
* IN X: Row number.
* IN Y: Column number.
* IN M: Matrix.
* RETURN the integer value at that position.
*/
CREATE OR REPLACE FUNCTION GET_INTEGER_VALUE(
IN X SMALLINT,
IN Y SMALLINT,
IN M INTEGER_MATRIX)
RETURNS INTEGER
F_GET_INTEGER_VALUE: BEGIN
DECLARE A INTEGER_ARRAY;
DECLARE RET INTEGER;
SET A = M[X];
SET RET = A[Y];
RETURN RET;
END F_GET_INTEGER_VALUE
@
/**
* Establishes the given value at a specific position in the matrix.
*
* IN X: Row number.
* IN Y: Column number.
* INOUT M: Matrix.
* IN VAL: Value to set in the matrix.
*/
CREATE OR REPLACE PROCEDURE SET_INTEGER_VALUE(
IN X SMALLINT,
IN Y SMALLINT,
INOUT M INTEGER_MATRIX,
IN VAL INTEGER)
P_SET_INTEGER_VALUE: BEGIN
DECLARE A INTEGER_ARRAY;
SET A = M[X];
SET A[Y] = VAL;
SET M[X] = A;
END P_SET_INTEGER_VALUE
@
/**
* Initializes the matriz at a given size with the same value in all positions.
*
* INOUT M: Matrix.
* IN X: Number of rows.
* IN Y: Number of columns per row.
* IN VAL: Value to set in the matrix.
*/
CREATE OR REPLACE PROCEDURE INIT_INTEGER_MATRIX(
INOUT M INTEGER_MATRIX,
IN X SMALLINT,
IN Y SMALLINT,
IN VAL INTEGER)
P_INIT_INTEGER_MATRIX: BEGIN
DECLARE I SMALLINT DEFAULT 1;
DECLARE J SMALLINT;
DECLARE A INTEGER_ARRAY;
WHILE (I <= X) DO
SET A = ARRAY[];
SET J = 1;
WHILE (J <= Y) DO
SET A[J] = VAL;
SET J = J + 1;
END WHILE;
SET M[I] = A;
SET I = I + 1;
END WHILE;
END P_INIT_INTEGER_MATRIX
@
/**
* Prints the content of the matrix to the standard output.
*
* INOUT M: Matrix.
*/
CREATE OR REPLACE PROCEDURE PRINT_INTEGER_MATRIX(
IN M INTEGER_MATRIX)
P_PRINT_INTEGER_MATRIX: BEGIN
DECLARE I SMALLINT DEFAULT 1;
DECLARE J SMALLINT;
DECLARE X SMALLINT;
DECLARE Y SMALLINT;
DECLARE VAL INTEGER;
DECLARE A INTEGER_ARRAY;
DECLARE RET VARCHAR(256);
SET X = CARDINALITY(M);
CALL DBMS_OUTPUT.PUT_LINE('>>>>>');
WHILE (I <= X) DO
SET A = M[I];
SET RET = '[';
SET Y = CARDINALITY(A);
SET J = 1;
WHILE (J <= Y) DO
SET VAL = A[J];
SET RET = RET || VAL;
SET J = J + 1;
IF (J <= Y) THEN
SET RET = RET || ',';
END IF;
END WHILE;
SET RET = RET || ']';
CALL DBMS_OUTPUT.PUT_LINE(RET);
SET I = I + 1;
END WHILE;
CALL DBMS_OUTPUT.PUT_LINE('<<<<<');
END P_PRINT_INTEGER_MATRIX
@
/**
* Checks if a queen is safe in the given position.
*
* IN M: Matrix representing the chessboard.
* IN ROW: Row of the queen.
* IN COL: Column in the row for the queen.
* IN SIZE: Size of the chessboard (max row, max col).
* RETURNS true if the position is safe.
*/
CREATE OR REPLACE FUNCTION IS_SAFE(
IN M INTEGER_MATRIX,
IN ROW SMALLINT,
IN COL SMALLINT,
IN SIZE SMALLINT)
MODIFIES SQL DATA
RETURNS BOOLEAN
F_IS_SAFE: BEGIN
DECLARE I SMALLINT;
DECLARE J SMALLINT;
DECLARE VAL INTEGER;
-- Debug purposes.
--CALL SET_INTEGER_VALUE(ROW, COL, M, -1);
--CALL PRINT_INTEGER_MATRIX(M);
--CALL SET_INTEGER_VALUE(ROW, COL, M, 0);
SET I = 1;
WHILE (I <= COL) DO
SET VAL = GET_INTEGER_VALUE(ROW, I, M);
IF (VAL = 1) THEN
RETURN FALSE;
END IF;
SET I = I + 1;
END WHILE;
SET I = ROW;
SET J = COL;
WHILE (I >= 1 AND J >= 1) DO
SET VAL = GET_INTEGER_VALUE(I, J, M);
IF (VAL = 1) THEN
CALL SET_INTEGER_VALUE(ROW, COL, M, 0);
RETURN FALSE;
END IF;
SET I = I - 1;
SET J = J - 1;
END WHILE;
SET I = ROW;
SET J = COL;
WHILE (J >= 1 AND I <= SIZE) DO
SET VAL = GET_INTEGER_VALUE(I, J, M);
IF (VAL = 1) THEN
RETURN FALSE;
END IF;
SET I = I + 1;
SET J = J - 1;
END WHILE;
RETURN TRUE;
END F_IS_SAFE
@
/**
* Dummy procedure for the recurssion.
*
* IN SIZE: Size of the chessboard (max row, max col).
* IN COL: Column to analyse.
* OUT RET: True if it was possible to put all queens
*/
CREATE OR REPLACE PROCEDURE SOLVE_N_QUEENS(
INOUT M INTEGER_MATRIX,
IN SIZE SMALLINT,
IN COL SMALLINT,
OUT RET BOOLEAN)
P_SOLVE_N_QUEENS: BEGIN
END P_SOLVE_N_QUEENS
@
/**
* Solves the n-queens algoritm.
*
* IN SIZE: Size of the chessboard (max row, max col).
* IN COL: Column to analyse.
* OUT RET: True if it was possible to put all queens
*/
CREATE OR REPLACE PROCEDURE SOLVE_N_QUEENS(
INOUT M INTEGER_MATRIX,
IN SIZE SMALLINT,
IN COL SMALLINT,
OUT RET BOOLEAN)
MODIFIES SQL DATA
P_SOLVE_N_QUEENS: BEGIN
DECLARE I SMALLINT;
DECLARE SAFE BOOLEAN;
DECLARE SOLVED BOOLEAN;
-- Debug purposes.
--CALL PRINT_INTEGER_MATRIX(M);
SET RET = FALSE;
IF (COL > SIZE) THEN
SET RET = TRUE;
ELSE
SET I = 1;
WHILE (I <= SIZE AND NOT RET) DO
SET SAFE = IS_SAFE(M, I, COL, SIZE);
IF (SAFE) THEN
CALL SET_INTEGER_VALUE(I, COL, M, 1);
CALL SOLVE_N_QUEENS(M, SIZE, COL + 1, SOLVED);
IF (SOLVED) THEN
SET RET = TRUE;
ELSE
CALL SET_INTEGER_VALUE(I, COL, M, 0); -- Backtrack.
END IF;
END IF;
SET I = I + 1;
END WHILE;
END IF;
END P_SOLVE_N_QUEENS
@
/**
* Main procedure to solve the n-queen algoritm.
*
* IN SIZE: Size of the chessboard. The bigger it is, the more time it takes.
*/
CREATE OR REPLACE PROCEDURE N_QUEENS(
IN SIZE SMALLINT)
P_N_QUEENS: BEGIN
DECLARE M INTEGER_MATRIX;
DECLARE SOL BOOLEAN DEFAULT FALSE;
CALL INIT_INTEGER_MATRIX(M, SIZE, SIZE, 0);
CALL SOLVE_N_QUEENS(M, SIZE, 1, SOL);
IF (SOL = TRUE) THEN
CALL PRINT_INTEGER_MATRIX(M);
ELSE
CALL DBMS_OUTPUT.PUT_LINE('Solution does not exist.');
END IF;
END P_N_QUEENS
@
--#SET TERMINATOR ;
-- Activates the standard output for the current session.
SET SERVEROUTPUT ON;
CALL N_QUEENS(4);
CALL N_QUEENS(8);
CALL N_QUEENS(16);