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

// Tests for MinimMax search using NIM game as a domain.

// Test 1 checks that the terminal state of the game has no next moves.
function test1() {
    let nim = new NIM([0, 0, 0], MiniMax.PLAYER1, MiniMax.PLAYER1);
    nim.setNextMoves();

    let actual = nim.next.map(nim => nim.toString()).join('\n');
    let expected = '';

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

// Test 2 checks that the non-terminal state of the game has correct next moves.
function test2() {
    let nim = new NIM([2, 2, 2], MiniMax.PLAYER1, MiniMax.PLAYER1);
    nim.setNextMoves();

    let actual = nim.next.map(nim => nim.toString()).join('\n');
    let expected = '\
Map: 0,2,2\n\
Index: 0. Turn: player 2. Maximizer: player 1. Utility: 0. # of moves: 0\n\
Map: 1,2,2\n\
Index: 1. Turn: player 2. Maximizer: player 1. Utility: 0. # of moves: 0';

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

// Test 3 checks that the terminal state of the game is correctly identified as 
// a winning state for the player whose turn it is, and as a non-winning state 
// for the other player.
function test3() {
    let nim1 = new NIM([0, 0, 0], MiniMax.PLAYER1, MiniMax.PLAYER1);
    let winning1 = nim1.isWinner(MiniMax.PLAYER1);
    let nim2 = new NIM([0, 0, 1], MiniMax.PLAYER1, MiniMax.PLAYER1);
    let winning2 = nim2.isWinner(MiniMax.PLAYER1);
    let actual = '' + winning1 + ' ' + winning2;
    let expected = 'true false';

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

// Test 4 checks that a close to a terminal state of the game is correctly
// identified as a winning state.
function test4() {
    let nim = new NIM([0, 1, 1], MiniMax.PLAYER1, MiniMax.PLAYER1, 0);
    nim.run();
    let actual = nim.toString();
    let expected = '\
Map: 0,1,1\n\
Index: 0. Turn: player 1. Maximizer: player 1. Utility: 10. # of moves: 1';

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

// Test 5 checks that the MiniMax search correctly identifies the best move for
// both players.
function test5() {
    let nim1 = new NIM([5, 5, 5], MiniMax.PLAYER1, MiniMax.PLAYER1, 0);
    nim1.run();
    let actual1 = nim1.getBest().toString();
    let expected1= '\
Map: 0,5,5\n\
Index: 0. Turn: player 2. Maximizer: player 1. Utility: 10. # of moves: 5';

    let nim2 = new NIM([0, 5, 5], MiniMax.PLAYER2, MiniMax.PLAYER2, 0);
    nim2.run();
    let actual2 = nim2.getBest().toString();
    let expected2= '\
Map: 0,0,5\n\
Index: 0. Turn: player 1. Maximizer: player 2. Utility: -10. # of moves: 5';

    let nim3 = new NIM([0, 0, 5], MiniMax.PLAYER1, MiniMax.PLAYER1, 0);
    nim3.run();
    let actual3 = nim3.getBest().toString();
    let expected3= '\
Map: 0,0,1\n\
Index: 1. Turn: player 2. Maximizer: player 1. Utility: 10. # of moves: 1';

    compareStringsAndReport('Test 5', 
        actual1 + '\n' + actual2 + '\n' + actual3, 
        expected1 + '\n' + expected2 + '\n' + expected3);
}

// Tests for MinimMax search using Tic-Tac-Toe game as a domain.

// Grid's elements
const x = MiniMax.PLAYER1;
const o = MiniMax.PLAYER2;
const _ = MiniMax.EMPTY;

// Test 6 checks that the terminal state of the game is correctly identified as
// a drawn state when the grid is full and there is no winner.
function test6() {
    let tictactoe = new TicTacToe([
        o, x, o,
        o, x, o,
        x, o, x
    ], o, o, 0);
    tictactoe.run();
    let actual = tictactoe.toString();
    let expected = '\
Map:\n\
  o x o\n\
  o x o\n\
  x o x\n\
Index: 0. Turn: player 2. Maximizer: player 2. Utility: 0. # of moves: 0';

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

// Test 7 checks that the terminal state of the game is correctly identified as 
// a winning or non-winning state for players.
function test7() {
    let tictactoe1 = new TicTacToe([
        _, x, _,
        _, x, _,
        _, x, _ ], x, x, 1);
    tictactoe1.run();
    let actual1 = tictactoe1.toString();
    let expected1 = '\
Map:\n\
  _ x _\n\
  _ x _\n\
  _ x _\n\
Index: 1. Turn: player 1. Maximizer: player 1. Utility: 10. # of moves: 0';

    let tictactoe2 = new TicTacToe([
        _, _, _,
        _, _, _,
        o, o, o ], x, x, 2);
    tictactoe2.run();
    let actual2 = tictactoe2.toString();
    let expected2 = '\
Map:\n\
  _ _ _\n\
  _ _ _\n\
  o o o\n\
Index: 2. Turn: player 1. Maximizer: player 1. Utility: -10. # of moves: 0';

    let tictactoe3 = new TicTacToe([
        x, o, o,
        o, x, o,
        o, o, x ], o, x, 3);
    tictactoe3.run();
    let actual3 = tictactoe3.toString();
    let expected3 = '\
Map:\n\
  x o o\n\
  o x o\n\
  o o x\n\
Index: 3. Turn: player 2. Maximizer: player 1. Utility: 10. # of moves: 0';

    let tictactoe4 = new TicTacToe([
        x, x, o,
        x, o, x,
        o, x, x ], x, x, 4);
    tictactoe4.run();
    let actual4 = tictactoe4.toString();
    let expected4 = '\
Map:\n\
  x x o\n\
  x o x\n\
  o x x\n\
Index: 4. Turn: player 1. Maximizer: player 1. Utility: -10. # of moves: 0';

    compareStringsAndReport('Test 7',
        actual1 + '\n' + actual2 + '\n' + actual3 + '\n' + actual4,
        expected1 + '\n' + expected2 + '\n' + expected3 + '\n' + expected4);
}

// Test 8 checks that a close to the terminal state of the game is correctly 
// identified.
function test8() {
    let tictactoe = new TicTacToe([
        o, x, o,
        x, _, x,
        o, x, o ], o, x);
    tictactoe.run();
    let actual = tictactoe.next[0].toString();
    let expected = '\
Map:\n\
  o x o\n\
  x o x\n\
  o x o\n\
Index: 4. Turn: player 1. Maximizer: player 1. Utility: -10. # of moves: 0';
    compareStringsAndReport('Test 8', actual, expected);
}

// Test 9 checks that the MiniMax search correctly identifies the next moves 
// for the player.
function test9() {
    let tictactoe = new TicTacToe([
        o, x, o,
        o, x, o,
        _, _, _ ], o, o);
    tictactoe.run();
    let actual = tictactoe.next.map(tictactoe => tictactoe.toString()).join('\n');
    let expected = '\
Map:\n\
  o x o\n\
  o x o\n\
  o _ _\n\
Index: 6. Turn: player 1. Maximizer: player 2. Utility: 10. # of moves: 0\n\
Map:\n\
  o x o\n\
  o x o\n\
  _ o _\n\
Index: 7. Turn: player 1. Maximizer: player 2. Utility: 10. # of moves: 2\n\
Map:\n\
  o x o\n\
  o x o\n\
  _ _ o\n\
Index: 8. Turn: player 1. Maximizer: player 2. Utility: 10. # of moves: 0';

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

// Test 10 checks that the MiniMax search correctly evaluate the utility value 
// of the initial state of the game.
function test10() {
    let tictactoe = new TicTacToe([
        _, _, _,
        _, _, _,
        _, _, _ ], x, x, 0);
    tictactoe.run();
    let actual = tictactoe.toString();
    let expected = '\
Map:\n\
  _ _ _\n\
  _ _ _\n\
  _ _ _\n\
Index: 0. Turn: player 1. Maximizer: player 1. Utility: 0. # of moves: 9';

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