package cpsc2150.extendedConnectX.models;

import org.junit.Test;
import static org.junit.Assert.*;

public class TestGameBoardMem {

    // Wrapper function to return a GameBoardMem
    private IGameBoard makeGameBoard(int r, int c, int win) {

        // Call GameBoardMem constructor with parameters
        return new GameBoardMem(r, c, win);

    }

    // Private helper method to fill up the contents of the 2d char array representing
    // tokens placed
    private char[][] initializeTestArray(int r, int c, char value) {

        // Create a new 2D char array
        char[][] testArray = new char[r][c];

        // Loop through number of rows
        for (int i = 0; i < r; i++) {

            // Loop through number of columns
            for (int j = 0; j < c; j++) {

                // Fill the current index with a space character
                testArray[i][j] = value;

            }

        }

        return testArray;

    }

    // Private helper function to generate a random number in a range
    private int getRandomNumber(int min, int max) {

        return (int)((Math.random() * (max - min)) + min);

    }

    // Private helper method to generate a string representation of a 2D char array
    // matching the implementation of toString in AbsGameBoard
    private String generateTestString(char[][] sequence) {

        // Create a string to be added on to, initialize with border character
        String testBoardView = "|";

        // Loop through number of columns, adding column numbers and border characters to the string
        for (int i = 0; i < sequence[0].length; i++) {

            // If the current value of i is single-digit, add extra space
            if (Integer.toString(i).trim().length() == 1) {

                testBoardView += (" " + i + "|");

                // Otherwise, add no extra space
            } else {

                testBoardView += (i + "|");

            }

        }

        // Move to the next line and add a border character to the string
        testBoardView += "\n|";
        // Loop through rows (starting from top)
        for (int i = sequence.length - 1; i >= 0; i--) {

            // Loop through columns (starting from left)
            for (int j = 0; j < sequence[i].length; j++) {

                // Add the token at the current position and a space and a border character to the string
                testBoardView += (sequence[i][j] + " |");

            }

            // If we're not on the last row
            if (i - 1 >= 0) {

                // Move to the next line and add a border character to the string
                testBoardView += "\n|";

            }

        }

        // Add an extra new line at the end
        testBoardView += "\n";

        return testBoardView;

    }

    @Test
    public void testConstructor_minimum_values() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(3, 3, ' ');

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testConstructor_maximum_values() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(100, 100, 100);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(100, 100, ' ');

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testConstructor_random_values() {

        // Get random values for rows, columns, and number to win between
        // the minimum for each and the maximum for each
        int rows = getRandomNumber(3, 100);
        int columns = getRandomNumber(3, 100);
        int numToWin = getRandomNumber(3, 25);

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(rows, columns, numToWin);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(rows, columns, ' ');

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testCheckIfFree_column_empty() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Check that checkIfFree returns true
        assertEquals(true, testGameBoard.checkIfFree(0));

    }

    @Test
    public void testCheckIfFree_column_full() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up a column with a token
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);

        // Check that checkIfFree returns false
        assertEquals(false, testGameBoard.checkIfFree(0));

    }

    @Test
    public void testCheckIfFree_column_partially_full() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up all the spaces in a
        // column except the last with a token
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);

        // Check that checkIfFree returns true
        assertEquals(true, testGameBoard.checkIfFree(0));

    }

    @Test
    public void testCheckHorizWin_unbroken_chain_left() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 3; i++) {

            testGameBoard.placeToken('X', i);

        }

        // Create a BoardPosition for use with checkHorizWin
        BoardPosition pos = new BoardPosition(0, 2);

        // Check that checkHorizWin returns true
        assertEquals(true, testGameBoard.checkHorizWin(pos, 'X'));

    }

    @Test
    public void testCheckHorizWin_unbroken_chain_middle() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(5, 5, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 3 + 1; i++) {

            // If i equals 0, place a different token
            if (i == 0) {

                testGameBoard.placeToken('O', i);

            } else {

                testGameBoard.placeToken('X', i);

            }

        }

        // Create a BoardPosition for use with checkHorizWin
        BoardPosition pos = new BoardPosition(0, 3);

        // Check that checkHorizWin returns true
        assertEquals(true, testGameBoard.checkHorizWin(pos, 'X'));

    }

    @Test
    public void testCheckHorizWin_unbroken_chain_right() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 3; i >= 1; i--) {

            testGameBoard.placeToken('X', i);

        }

        // Create a BoardPosition for use with checkHorizWin
        BoardPosition pos = new BoardPosition(0,  1);

        // Check that checkHorizWin returns true
        assertEquals(true, testGameBoard.checkHorizWin(pos, 'X'));

    }

    @Test
    public void testCheckHorizWin_broken_chain() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 3; i++) {

            // If i equals 1, place a different token
            // This simulates a broken sequence
            if (i == 1) {

                testGameBoard.placeToken('O', i);

                // Otherwise, place the "winning" token
            } else {

                testGameBoard.placeToken('X', i);

            }

        }

        // Create a BoardPosition for use with checkHorizWin
        BoardPosition pos = new BoardPosition(0, 2);

        // Check that checkHorizWin returns false
        assertEquals(false, testGameBoard.checkHorizWin(pos, 'X'));

    }

    @Test
    public void testCheckVertWin_unbroken_chain_bottom() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 3; i++) {

            testGameBoard.placeToken('X', 0);

        }

        // Create a BoardPosition for use with checkVertWin
        BoardPosition pos = new BoardPosition(2, 0);

        // Check that checkVertWin returns true
        assertEquals(true, testGameBoard.checkVertWin(pos, 'X'));

    }

    @Test
    public void testCheckVertWin_unbroken_chain_middle() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(5, 5, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 5; i++) {

            // If i equals 0, place a different token
            if (i == 0) {

                testGameBoard.placeToken('O', 0);

            } else {

                testGameBoard.placeToken('X', 0);

            }

        }

        // Create a BoardPosition for use with checkVertWin
        BoardPosition pos = new BoardPosition(3, 0);

        // Check that checkVertWin returns true
        assertEquals(true, testGameBoard.checkVertWin(pos, 'X'));

    }

    @Test
    public void testCheckVertWin_unbroken_chain_top() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 3 + 1; i++) {

            // If i equals 0, place a different token
            if (i == 0) {

                testGameBoard.placeToken('O', 0);

            } else {

                testGameBoard.placeToken('X', 0);

            }

        }

        // Create a BoardPosition for use with checkVertWin
        BoardPosition pos = new BoardPosition(3, 0);

        // Check that checkVertWin returns true
        assertEquals(true, testGameBoard.checkVertWin(pos, 'X'));

    }

    @Test
    public void testCheckVertWin_broken_chain() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up as many spaces in a row as are needed to win
        for (int i = 0; i < 3; i++) {

            // If i equals 1, place a different token
            // This simulates a broken sequence
            if (i == 1) {

                testGameBoard.placeToken('O', 0);

                // Otherwise, place the "winning" token
            } else {

                testGameBoard.placeToken('X', 0);

            }

        }

        // Create a BoardPosition for use with checkVertWin
        BoardPosition pos = new BoardPosition(2, 0);

        // Check that checkVertWin returns false
        assertEquals(false, testGameBoard.checkVertWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_empty_board() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(1, 1);

        // Check that checkDiagWin returns false
        assertEquals(false, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_unbroken_left_right_up() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(2, 2);

        // Check that checkDiagWin returns true
        assertEquals(true, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_unbroken_left_right_down() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(1, 2);

        // Check that checkDiagWin returns true
        assertEquals(true, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_unbroken_right_left_down() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('O', 3);
        testGameBoard.placeToken('X', 3);
        testGameBoard.placeToken('O', 3);
        testGameBoard.placeToken('X', 3);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(1, 1);

        // Check that checkDiagWin returns true
        assertEquals(true, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_unbroken_right_left_up() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(4, 4, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('X', 3);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(0, 3);

        // Check that checkDiagWin returns true
        assertEquals(true, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_unbroken_placed_last() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(2, 2);

        // Check that checkDiagWin returns true
        assertEquals(true, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckDiagWin_broken_sequence() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with checkDiagWin
        BoardPosition pos = new BoardPosition(0, 2);

        // Check that checkDiagWin returns false
        assertEquals(false, testGameBoard.checkDiagWin(pos, 'X'));

    }

    @Test
    public void testCheckTie_full_all_same_tokens() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('X', 2);

        // Check that checkTie returns true
        assertEquals(true, testGameBoard.checkTie());

    }

    @Test
    public void testCheckTie_full_different_tokens() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Check that checkTie returns true
        assertEquals(true, testGameBoard.checkTie());

    }

    @Test
    public void testCheckTie_one_empty() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('X', 2);

        // Check that checkTie returns false
        assertEquals(false, testGameBoard.checkTie());

    }

    @Test
    public void testCheckTie_all_empty() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Check that checkTie returns false
        assertEquals(false, testGameBoard.checkTie());

    }

    @Test
    public void testWhatsAtPos_empty_space() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Create a BoardPosition for use with whatsAtPos
        BoardPosition pos = new BoardPosition(1, 1);

        // Check that whatsAtPos returns a space
        assertEquals(' ', testGameBoard.whatsAtPos(pos));

    }

    @Test
    public void testWhatsAtPos_top_left_corner() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with whatsAtPos
        BoardPosition pos = new BoardPosition(2, 0);

        // Check that whatsAtPos returns 'X'
        assertEquals('X', testGameBoard.whatsAtPos(pos));

    }

    @Test
    public void testWhatsAtPos_bottom_left_corner() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with whatsAtPos
        BoardPosition pos = new BoardPosition(0, 0);

        // Check that whatsAtPos returns 'X'
        assertEquals('X', testGameBoard.whatsAtPos(pos));

    }

    @Test
    public void testWhatsAtPos_top_right_corner() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with whatsAtPos
        BoardPosition pos = new BoardPosition(2, 2);

        // Check that whatsAtPos returns 'X'
        assertEquals('X', testGameBoard.whatsAtPos(pos));

    }

    @Test
    public void testWhatsAtPos_bottom_right_corner() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with whatsAtPos
        BoardPosition pos = new BoardPosition(0, 2);

        // Check that whatsAtPos returns 'X'
        assertEquals('X', testGameBoard.whatsAtPos(pos));

    }

    @Test
    public void testIsPlayerAtPos_empty_board() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Create a BoardPosition for use with isPlayerAtPos
        BoardPosition pos = new BoardPosition(0, 0);

        // Check that isPlayerAtPos returns false
        assertEquals(false, testGameBoard.isPlayerAtPos(pos, 'X'));

    }

    @Test
    public void testIsPlayerAtPos_full_board() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with isPlayerAtPos
        BoardPosition pos = new BoardPosition(0, 0);

        // Check that isPlayerAtPos returns true
        assertEquals(true, testGameBoard.isPlayerAtPos(pos, 'X'));

    }

    @Test
    public void testIsPlayerAtPos_player_immediately_below() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a BoardPosition for use with isPlayerAtPos
        BoardPosition pos = new BoardPosition(2, 1);

        // Check that isPlayerAtPos returns false
        assertEquals(false, testGameBoard.isPlayerAtPos(pos, 'X'));

    }

    @Test
    public void testIsPlayerAtPos_player_is_only_token() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);

        // Create a BoardPosition for use with isPlayerAtPos
        BoardPosition pos = new BoardPosition(0, 0);

        // Check that isPlayerAtPos returns true
        assertEquals(true, testGameBoard.isPlayerAtPos(pos, 'X'));

    }

    @Test
    public void testIsPlayerAtPos_not_correct_token() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed
        testGameBoard.placeToken('X', 0);

        // Create a BoardPosition for use with isPlayerAtPos
        BoardPosition pos = new BoardPosition(0, 0);

        // Check that isPlayerAtPos returns false
        assertEquals(false, testGameBoard.isPlayerAtPos(pos, 'O'));

    }

    @Test
    public void testPlaceToken_bottom_row() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed (initial)
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(3, 3, ' ');

        // Set parts of test array for expected result
        testArray[0][0] = 'X';
        testArray[0][1] = 'O';
        testArray[0][2] = 'X';

        // Call placeToken to fill last token
        testGameBoard.placeToken('X', 2);

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testPlaceToken_only_one_in_row() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed (initial)
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(3, 3, ' ');

        // Set parts of test array for expected result
        testArray[0][0] = 'X';
        testArray[0][1] = 'O';
        testArray[0][2] = 'X';
        testArray[1][0] = 'X';

        // Call placeToken to fill last token
        testGameBoard.placeToken('X', 0);

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testPlaceToken_row_almost_full() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed (initial)
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('X', 0);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(3, 3, ' ');

        // Set parts of test array for expected result
        testArray[0][0] = 'X';
        testArray[0][1] = 'O';
        testArray[0][2] = 'X';
        testArray[1][0] = 'X';
        testArray[2][0] = 'O';

        // Call placeToken to fill last token
        testGameBoard.placeToken('O', 0);

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testPlaceToken_empty_board() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(3, 3, ' ');

        // Set parts of test array for expected result
        testArray[0][1] = 'X';

        // Call placeToken to fill last token
        testGameBoard.placeToken('X', 1);

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

    @Test
    public void testPlaceToken_board_almost_full() {

        // Create a GameBoard for testing
        IGameBoard testGameBoard = makeGameBoard(3, 3, 3);

        // Call placeToken to fill up spaces as needed (initial)
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 0);
        testGameBoard.placeToken('X', 0);
        testGameBoard.placeToken('O', 1);
        testGameBoard.placeToken('X', 1);
        testGameBoard.placeToken('X', 2);
        testGameBoard.placeToken('O', 2);
        testGameBoard.placeToken('X', 2);

        // Create a 2D char array filled with spaces for each index
        char[][] testArray = initializeTestArray(3, 3, ' ');

        // Set parts of test array for expected result
        testArray[0][0] = 'X';
        testArray[1][0] = 'O';
        testArray[2][0] = 'X';
        testArray[0][1] = 'O';
        testArray[1][1] = 'X';
        testArray[2][1] = 'O';
        testArray[0][2] = 'X';
        testArray[1][2] = 'O';
        testArray[2][2] = 'X';

        // Call placeToken to fill last token
        testGameBoard.placeToken('O', 1);

        // Check that GameBoard output is equal to expected output
        assertEquals(generateTestString(testArray), testGameBoard.toString());

    }

}
