// The following code contains unit test function that test the functionality 
// of the above classes and functions.

// Test 1 runs the NextLocation on a map with some occupied locations and checks
// if it returns the index of the first empty location.
function test1() {
    let map = new Map_Backtracking.Map([[1, 2, 3, 4, 0, 0, 0, 0]], 8);

    let actual = new EightQueens(map).domain.getNextLocation().getIndices().i;
    let expected = 4;

    compareAndReport('Test 1', actual, expected);
}

// Test 2 runs the NextLocation on a map with all locations occupied and checks
// if it returns null.
function test2() {
    let map = new Map_Backtracking.Map([[1, 2, 3, 4, 5, 6, 7, 8]], 8);

    let actual = new EightQueens(map).domain.getNextLocation();
    let expected = null;

    compareAndReport('Test 2', actual, expected);
}

// Test 3 runs the NextCandidate on a map with some occupied locations and checks
// if it returns the next valid candidate for a specified location. The test also
// checks if the returned candidate does not violate the rules of the Eight Queens
// puzzle (i.e., no two queens threaten each other).
function test3() {
    let map = new Map_Backtracking.Map([[1, 3, 0, 0, 0, 0, 0, 0]], 8);

    let actual = new EightQueens(map).domain.getNextCandidate(
        new EightQueens.Domain.Location(map, 2));
    let expected = 5;

    // 1 3 5 0 0 0 0 0

    // Q 0 0 0 0 0 0 0  1
    // 0 0 0 0 0 0 0 0  2
    // 0 Q 0 0 0 0 0 0  3
    // 0 0 0 0 0 0 0 0  4
    // 0 0 Q 0 0 0 0 0  5

    compareAndReport('Test 3', actual, expected);
}

// Test 4 runs the NextCandidate on a map where the specified location has no 
// valid candidates and checks if it returns AI::Map<int>::EMPTY to indicate 
// that no candidate is found.
function test4() {
    let map = new Map_Backtracking.Map([[1, 2, 4, 8, 0, 0, 0, 0]], 8);

    let actual = new EightQueens(map).domain.getNextCandidate(
        new EightQueens.Domain.Location(map, 3));
    let expected = 0;

    compareAndReport('Test 4', actual, expected);
}

// Test 5 runs the Backtracking algorithm on an empty map and checks if it 
// finds a valid solution.
function test5() {
    let map = new Map_Backtracking.Map([[0, 0, 0, 0, 0, 0, 0, 0]], 8);

    new EightQueens(map).run();

    let actual = map.toString();
    let expected = '1,5,8,6,3,7,2,4';

    // 1 5 8 6 3 7 2 4

    // Q • • • • • • • 1
    // • • • • • • Q • 2
    // • • • • Q • • • 3
    // • • • • • • • Q 4
    // • Q • • • • • • 5
    // • • • Q • • • • 6
    // • • • • • Q • • 7
    // • • Q • • • • • 8

    compareAndReport('Test 5', actual, expected);
}

// Test 6 runs the Backtracking algorithm twice on the same map. The first run
// should find a solution and fill the map with it. The second run should find 
// the next solution and fill the map with it. The test checks if the second
// solution is different from the first one.
function test6() {
    let map = new Map_Backtracking.Map([[0, 0, 0, 0, 0, 0, 0, 0]], 8);

    let eightQueens = new EightQueens(map);

    eightQueens.run();
    eightQueens.run();

    let actual = map.toString();
    let expected = '1,6,8,3,7,4,2,5';

    // 1 6 8 3 7 4 2 5

    // Q • • • • • • • 1
    // • • • • • • Q • 2
    // • • • Q • • • • 3
    // • • • • • Q • • 4
    // • • • • • • • Q 5
    // • Q • • • • • • 6
    // • • • • Q • • • 7
    // • • Q • • • • • 8

    compareAndReport('Test 6', actual, expected);
}

// Test 7 runs the Backtracking algorithm multiple times to checks if it finds 
// 10 first solutions of the Eight Queens puzzle.
function test7() {
    let map = new Map_Backtracking.Map([[0, 0, 0, 0, 0, 0, 0, 0]], 8);

    let eightQueens = new EightQueens(map);

    let actual = '';
    let i = 0;
    while (eightQueens.run() && ++i <= 10)
        actual += '' + (i-1) + ': ' + map.toString() + '\n';

    let expected = 
`0: 1,5,8,6,3,7,2,4
1: 1,6,8,3,7,4,2,5
2: 1,7,4,6,8,2,5,3
3: 1,7,5,8,2,4,6,3
4: 2,4,6,8,3,1,7,5
5: 2,5,7,1,3,8,6,4
6: 2,5,7,4,1,8,6,3
7: 2,6,1,7,4,8,3,5
8: 2,6,8,3,1,4,7,5
9: 2,7,3,6,8,5,1,4
`;

    compareAndReport('Test 7', actual, expected);
}

// Test 8 runs the NextLocation for Sudoku on a 9x9 map with one empty cell and 
// checks if it returns the index of that empty cell.
function test8() {
    let size = 9;
    let map = new Map_Backtracking.Map([
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 0, 1, 1, 1, 1],   // <--
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1]
    ], size, size);

    let indices = new Sudoku(map).domain.getNextLocation().getIndices();
    let actual = indices.i + ',' + indices.j;
    let expected = '4,5';

    compareAndReport('Test 8', actual, expected);
}

// Test 9 runs the NextCandidate on a 9x9 map with one empty cell and checks if it
// returns the next valid candidate for that cell. The test also checks if the
// returned candidate does not violate the rules of Sudoku (i.e., no duplicate
// numbers in the same row, column, or 3x3 subgrid).
function test9() {
    let size = 9;
    let map = new Map_Backtracking.Map([
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 5, 1, 1, 1],
        [1, 1, 1, 1, 6, 1, 1, 1, 1],
        [1, 4, 1, 1, 1, 3, 1, 2, 1],   // <--
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1]
    ], size, size);

    let actual = new Sudoku(map).domain.getNextCandidate(
        new Sudoku.Domain.Location(map, 5, 4));
    let expected = 7;

    compareAndReport('Test 9', actual, expected);
}

// Test 10 runs the Backtracking algorithm on an empty 9x9 map and checks if it
// finds a valid solution that satisfies the rules of Sudoku. The test also checks
// if the solution is correct by comparing the output with a known valid solution.
function test10() {
    let size = 9;
    let map = new Map_Backtracking.Map([
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0]
    ], size, size);

    new Sudoku(map).run();

    let actual = map.toString();
    let expected = '\
1,2,3,4,5,6,7,8,9\n\
4,5,6,7,8,9,1,2,3\n\
7,8,9,1,2,3,4,5,6\n\
2,1,4,3,6,5,8,9,7\n\
3,6,5,8,9,7,2,1,4\n\
8,9,7,2,1,4,3,6,5\n\
5,3,1,6,4,2,9,7,8\n\
6,4,2,9,7,8,5,3,1\n\
9,7,8,5,3,1,6,4,2';

    compareAndReport('Test 10', actual, expected);
}