Why my game keep crashing when when i click the button

Why my game keep crashing when when i click the button ?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


public class GameController : MonoBehaviour
{
    public string[,] tictactoeArray = new string[5, 5];
    public GameObject[] tictactoeButtons;
    public GameObject X;
    public GameObject O; 

    // public GameObject playAgainButtonObj;
    public GameObject playAgainButtonObj;
    public Button playAgainButton;
    public Text message;

    bool gameEnd;
    int round = 0;

    // Start is called before the first frame update
    void Start()
    {
        X = new GameObject();
        O = new GameObject();
        Debug.Log("GameController Start");
        message.enabled = false;
        playAgainButtonObj.SetActive(false);
        // playAgainButton.enabled = false;
        gameEnd = false;

        // Inisialisasi array
        for (int i = 0; i < 5; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                // Debug.Log("GameController loop arr");
                tictactoeArray[i, j] = "?";
            }
        }
    }

    // Update is called once per frame
    void Update()
{ 
    Debug.Log("GameController Update");
    if (Input.GetMouseButtonDown(0) && !gameEnd)
    {
        // Mengambil titik di layar yang di-klik dan mengubahnya menjadi ray
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        
        // Menjalankan raycast dan menyimpan hasil dalam variabel 'hit'
        if (Physics.Raycast(ray, out hit))
        {
            // Mendapatkan GameObject yang terkena raycast
            GameObject hitButton = hit.transform.gameObject;
            
            // Mencari indeks tombol dalam array 'tictactoeButtons'
            int index = System.Array.IndexOf(tictactoeButtons, hitButton);
            int row = index / 5; // Menghitung baris berdasarkan indeks
            int col = index % 5; // Menghitung kolom berdasarkan indeks
            
            // Memeriksa apakah sel dalam array 'tictactoeArray' belum diisi
            if (tictactoeArray[row, col] == "?")
            {
                // Memilih simbol (X atau O) berdasarkan giliran ('round')
                if (round % 2 == 0)
                {
                    tictactoeArray[row, col] = "X"; // Mengisi sel dengan X
                    round++;
                    
                    // Membuat objek visualisasi X dan menempatkannya di lokasi yang sesuai
                    Instantiate(X, new Vector3(hitButton.transform.position.x, hitButton.transform.position.y, 
                    hitButton.transform.position.z), Quaternion.identity);
                }
                else
                {
                    tictactoeArray[row, col] = "O"; // Mengisi sel dengan O
                    round++;
                    
                    // Membuat objek visualisasi O dan menempatkannya di lokasi yang sesuai
                    Instantiate(O, new Vector3(hitButton.transform.position.x - 3.3f, hitButton.transform.position.y, 
                    hitButton.transform.position.z), Quaternion.identity);
                }
                
                // Memeriksa apakah ada pemenang setelah setiap langkah
                checkWinner();
                
                // Jika permainan belum berakhir, melakukan langkah AI
                if (!gameEnd)
                    PerformAIMove();
            }
        }
    }
}


    
void PerformAIMove()
{
    Debug.Log("PerformAIMove called");
    int bestScore = int.MinValue; // Inisialisasi skor terbaik dengan nilai terendah
    int bestMove = -1; // Inisialisasi gerakan terbaik dengan nilai tidak valid
    int alpha = int.MinValue; // Inisialisasi nilai alpha dengan nilai terendah

    for (int i = 0; i < 25; i++) // Loop melalui semua kemungkinan langkah pada papan 5x5
    {
        int row = i / 5; // Hitung baris berdasarkan indeks i
        int col = i % 5; // Hitung kolom berdasarkan indeks i

        if (tictactoeArray[row, col] == "?") // Jika sel kosong
        {
            tictactoeArray[row, col] = "O"; // Isi sel dengan "O"
            int score = Minimax(tictactoeArray, 0, false, alpha, int.MaxValue); // Panggil Minimax untuk menghitung skor langkah
            tictactoeArray[row, col] = "?"; // Kembalikan sel ke keadaan semula

            if (score > bestScore) // Jika skor langkah lebih baik dari skor terbaik sebelumnya
            {
                bestScore = score; // Perbarui skor terbaik
                bestMove = i; // Simpan gerakan terbaik
            }
        }
    }

    if (bestMove != -1) // Jika gerakan terbaik valid
    {
        int row = bestMove / 5; // Hitung baris dari gerakan terbaik
        int col = bestMove % 5; // Hitung kolom dari gerakan terbaik

        tictactoeArray[row, col] = "O"; // Isi sel dengan "O" untuk melakukan gerakan terbaik
        round++; // Tambahkan satu putaran

        int buttonIndex = row * 5 + col; // Hitung indeks tombol yang sesuai
        GameObject button = tictactoeButtons[buttonIndex]; // Dapatkan referensi tombol dari array
        Instantiate(O, button.transform.position, Quaternion.identity); // Buat simbol "O" di posisi tombol

        checkWinner(); // Periksa apakah pemain AI menang setelah gerakan
    }
}


int Minimax(string[,] board, int depth, bool isMaximizingPlayer, int alpha, int beta)
{
    Debug.Log("Minimax called with depth: " + depth);
    // Menilai kondisi saat ini pada papan permainan
    int score = Evaluate(board);

    // Jika pemain maksimal menang
    if (score == 10)
        return score - depth;
    // Jika pemain minimal menang
    if (score == -10)
        return score + depth;
    // Jika papan penuh dan permainan seri
    if (IsBoardFull(board))
        return 0;

    if (isMaximizingPlayer)
    {
        int bestScore = int.MinValue;

        // Mengulangi semua langkah mungkin
        for (int i = 0; i < 5; i++)
        {
        for (int j = 0; j < 5; j++)
        {
            // int row = i / 5;
            // int col = i % 5;

            // Memeriksa apakah sel saat ini kosong
            if (board[i, j] == "?")
            {
                // Melakukan langkah untuk pemain maksimal
                board[i, j] = "O";
                
                // Mengevaluasi rekursif posisi dengan langkah yang diambil
                int currentScore = Minimax(board, depth + 1, false, alpha, beta);
                
                // Mengembalikan langkah
                board[i, j] = "?";
                
                // Memperbarui skor terbaik untuk pemain maksimal
                bestScore = Mathf.Max(bestScore, currentScore);
                
                // Memperbarui nilai alfa untuk pemangkasan (pruning)
                alpha = Mathf.Max(alpha, bestScore);
                
                // Melakukan pemangkasan jika beta kurang dari atau sama dengan alfa
                if (beta <= alpha)
                    break;
            }
        }
    }
        return bestScore;
    }
    else
    {
        int bestScore = int.MaxValue;

        for (int i = 0; i < 5; i++)
        {
        for (int j = 0; j < 5; j++)
        {
            // int row = i / 5;
            // int col = i % 5;

            // Memeriksa apakah sel saat ini kosong
            if (board[i, j] == "?")
            {
                // Melakukan langkah untuk pemain maksimal
                board[i, j] = "X";
                
                // Mengevaluasi rekursif posisi dengan langkah yang diambil
                int currentScore = Minimax(board, depth + 1, false, alpha, beta);
                
                // Mengembalikan langkah
                board[i, j] = "?";
                
                // Memperbarui skor terkecil untuk pemain maksimal
                bestScore = Mathf.Min(bestScore, currentScore);
                
                // Memperbarui nilai beta untuk pemangkasan (pruning)
                beta = Mathf.Min(beta, bestScore);
                
                // Melakukan pemangkasan jika beta kurang dari atau sama dengan alfa
                if (beta <= alpha)
                    break;
            }
        }
    }

        return bestScore;
    }
}


int Evaluate(string[,] board)
{
    Debug.Log("Evaluate called");
    // Periksa baris
    for (int i = 0; i < 5; i++)
    {
        // Periksa apakah semua elemen dalam baris saat ini memiliki nilai yang sama
        if (board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2] && board[i, 2] == board[i, 3] && board[i, 3] == board[i, 4])
        {
            // Periksa nilai elemen pertama dalam baris untuk menentukan pemenang
            if (board[i, 0] == "O")
                return 10; // Pemain O menang
            else if (board[i, 0] == "X")
                return -10; // Pemain X menang
        }
    }
    
    // Periksa kolom
    for (int j = 0; j < 5; j++)
    {
        // Periksa apakah semua elemen dalam kolom saat ini memiliki nilai yang sama
        if (board[0, j] == board[1, j] && board[1, j] == board[2, j] && board[2, j] == board[3, j] && board[3, j] == board[4, j])
        {
            // Periksa nilai elemen pertama dalam kolom untuk menentukan pemenang
            if (board[0, j] == "O")
                return 10; // Pemain O menang
            else if (board[0, j] == "X")
                return -10; // Pemain X menang
        }
    }
    
    // Periksa diagonal
    // Periksa diagonal utama (dari kiri atas ke kanan bawah)
    if (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2] && board[2, 2] == board[3, 3] && board[3, 3] == board[4, 4])
    {
        // Periksa nilai elemen kiri atas dalam diagonal untuk menentukan pemenang
        if (board[0, 0] == "O")
            return 10; // Pemain O menang
        else if (board[0, 0] == "X")
            return -10; // Pemain X menang
    }
    
    // Periksa diagonal sekunder (dari kanan atas ke kiri bawah)
    if (board[0, 4] == board[1, 3] && board[1, 3] == board[2, 2] && board[2, 2] == board[3, 1] && board[3, 1] == board[4, 0])
    {
        // Periksa nilai elemen kanan atas dalam diagonal untuk menentukan pemenang
        if (board[0, 4] == "O")
            return 10; // Pemain O menang
        else if (board[0, 4] == "X")
            return -10; // Pemain X menang
    }
    
    // Jika tidak ada pemenang yang ditemukan dalam baris, kolom, atau diagonal, kembalikan 0
    return 0; // Tidak ada pemenang
}

bool IsBoardFull(string[,] board)
{
    Debug.Log("IsBoardFull called");
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 5; j++)
        {
            // Periksa apakah ada sel kosong ("?") di baris i dan kolom j
            if (board[i, j] == "?")
                return false; // Mengembalikan false jika ditemukan setidaknya satu sel kosong
        }
    }
    return true; // Mengembalikan true jika tidak ditemukan sel kosong, artinya papan telah terisi penuh
}



public void checkWinner() {
    Debug.Log("checkWinner called");
    // Check diagonal wins
    // Memeriksa apakah ada pemenang pada garis diagonal pertama (kiri atas ke kanan bawah)
    if (
    //diagonal
    (tictactoeArray[0, 0] == "X" && tictactoeArray[0, 1] == "X" && tictactoeArray[0, 2] == "X" && tictactoeArray[0, 3] == "X") ||
    (tictactoeArray[1, 0] == "X" && tictactoeArray[1, 1] == "X" && tictactoeArray[1, 2] == "X" && tictactoeArray[1, 3] == "X") ||
    (tictactoeArray[2, 0] == "X" && tictactoeArray[2, 1] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[2, 3] == "X") ||
    (tictactoeArray[3, 0] == "X" && tictactoeArray[3, 1] == "X" && tictactoeArray[3, 2] == "X" && tictactoeArray[3, 3] == "X") ||
    (tictactoeArray[4, 0] == "X" && tictactoeArray[4, 1] == "X" && tictactoeArray[4, 2] == "X" && tictactoeArray[4, 3] == "X") ||
    (tictactoeArray[0, 4] == "X" && tictactoeArray[0, 1] == "X" && tictactoeArray[0, 2] == "X" && tictactoeArray[0, 3] == "X") ||
    (tictactoeArray[1, 4] == "X" && tictactoeArray[1, 1] == "X" && tictactoeArray[1, 2] == "X" && tictactoeArray[1, 3] == "X") ||
    (tictactoeArray[2, 4] == "X" && tictactoeArray[2, 1] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[2, 3] == "X") ||
    (tictactoeArray[3, 4] == "X" && tictactoeArray[3, 1] == "X" && tictactoeArray[3, 2] == "X" && tictactoeArray[3, 3] == "X") ||
    (tictactoeArray[4, 4] == "X" && tictactoeArray[4, 1] == "X" && tictactoeArray[4, 2] == "X" && tictactoeArray[4, 3] == "X") ||
    //vertikal
    (tictactoeArray[0, 0] == "X" && tictactoeArray[1, 0] == "X" && tictactoeArray[2, 0] == "X" && tictactoeArray[3, 0] == "X") ||
    (tictactoeArray[0, 1] == "X" && tictactoeArray[1, 1] == "X" && tictactoeArray[2, 1] == "X" && tictactoeArray[3, 1] == "X") ||
    (tictactoeArray[0, 2] == "X" && tictactoeArray[1, 2] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[3, 2] == "X") ||
    (tictactoeArray[0, 3] == "X" && tictactoeArray[1, 3] == "X" && tictactoeArray[2, 3] == "X" && tictactoeArray[3, 3] == "X") ||
    (tictactoeArray[0, 4] == "X" && tictactoeArray[1, 4] == "X" && tictactoeArray[2, 4] == "X" && tictactoeArray[3, 4] == "X") ||
    (tictactoeArray[4, 0] == "X" && tictactoeArray[1, 0] == "X" && tictactoeArray[2, 0] == "X" && tictactoeArray[3, 0] == "X") ||
    (tictactoeArray[4, 1] == "X" && tictactoeArray[1, 1] == "X" && tictactoeArray[2, 1] == "X" && tictactoeArray[3, 1] == "X") ||
    (tictactoeArray[4, 2] == "X" && tictactoeArray[1, 2] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[3, 2] == "X") ||
    (tictactoeArray[4, 3] == "X" && tictactoeArray[1, 3] == "X" && tictactoeArray[2, 3] == "X" && tictactoeArray[3, 3] == "X") ||
    (tictactoeArray[4, 4] == "X" && tictactoeArray[1, 4] == "X" && tictactoeArray[2, 4] == "X" && tictactoeArray[3, 4] == "X") ||
    //diagonal
    (tictactoeArray[0, 0] == "X" && tictactoeArray[1, 1] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[3, 3] == "X") ||
    (tictactoeArray[4, 4] == "X" && tictactoeArray[1, 1] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[3, 3] == "X") ||
    (tictactoeArray[0, 4] == "X" && tictactoeArray[1, 3] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[3, 1] == "X") ||
    (tictactoeArray[4, 0] == "X" && tictactoeArray[1, 3] == "X" && tictactoeArray[2, 2] == "X" && tictactoeArray[3, 1] == "X") ||
    (tictactoeArray[1, 0] == "X" && tictactoeArray[2, 1] == "X" && tictactoeArray[3, 2] == "X" && tictactoeArray[4, 3] == "X") ||
    (tictactoeArray[0, 1] == "X" && tictactoeArray[1, 2] == "X" && tictactoeArray[2, 3] == "X" && tictactoeArray[3, 4] == "X") ||
    (tictactoeArray[1, 4] == "X" && tictactoeArray[2, 3] == "X" && tictactoeArray[3, 2] == "X" && tictactoeArray[4, 1] == "X") ||
    (tictactoeArray[1, 4] == "X" && tictactoeArray[2, 3] == "X" && tictactoeArray[3, 2] == "X" && tictactoeArray[4, 1] == "X") ||
    (tictactoeArray[0, 3] == "X" && tictactoeArray[1, 2] == "X" && tictactoeArray[2, 1] == "X" && tictactoeArray[3, 0] == "X")) 
    {
        playAgainButtonObj.SetActive(true);  // Mengaktifkan tombol untuk bermain lagi
        message.enabled = true;  // Mengaktifkan pesan untuk ditampilkan
        message.text = "Winner is X";  // Menampilkan pesan bahwa pemenangnya adalah X
        gameEnd = true;  // Menandakan bahwa permainan telah berakhir
    } else if (    
    //diagonal
    (tictactoeArray[0, 0] == "O" && tictactoeArray[0, 1] == "O" && tictactoeArray[0, 2] == "O" && tictactoeArray[0, 3] == "O") ||
    (tictactoeArray[1, 0] == "O" && tictactoeArray[1, 1] == "O" && tictactoeArray[1, 2] == "O" && tictactoeArray[1, 3] == "O") ||
    (tictactoeArray[2, 0] == "O" && tictactoeArray[2, 1] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[2, 3] == "O") ||
    (tictactoeArray[3, 0] == "O" && tictactoeArray[3, 1] == "O" && tictactoeArray[3, 2] == "O" && tictactoeArray[3, 3] == "O") ||
    (tictactoeArray[4, 0] == "O" && tictactoeArray[4, 1] == "O" && tictactoeArray[4, 2] == "O" && tictactoeArray[4, 3] == "O") ||
    (tictactoeArray[0, 4] == "O" && tictactoeArray[0, 1] == "O" && tictactoeArray[0, 2] == "O" && tictactoeArray[0, 3] == "O") ||
    (tictactoeArray[1, 4] == "O" && tictactoeArray[1, 1] == "O" && tictactoeArray[1, 2] == "O" && tictactoeArray[1, 3] == "O") ||
    (tictactoeArray[2, 4] == "O" && tictactoeArray[2, 1] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[2, 3] == "O") ||
    (tictactoeArray[3, 4] == "O" && tictactoeArray[3, 1] == "O" && tictactoeArray[3, 2] == "O" && tictactoeArray[3, 3] == "O") ||
    (tictactoeArray[4, 4] == "O" && tictactoeArray[4, 1] == "O" && tictactoeArray[4, 2] == "O" && tictactoeArray[4, 3] == "O") ||
    //vertikal
    (tictactoeArray[0, 0] == "O" && tictactoeArray[1, 0] == "O" && tictactoeArray[2, 0] == "O" && tictactoeArray[3, 0] == "O") ||
    (tictactoeArray[0, 1] == "O" && tictactoeArray[1, 1] == "O" && tictactoeArray[2, 1] == "O" && tictactoeArray[3, 1] == "O") ||
    (tictactoeArray[0, 2] == "O" && tictactoeArray[1, 2] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[3, 2] == "O") ||
    (tictactoeArray[0, 3] == "O" && tictactoeArray[1, 3] == "O" && tictactoeArray[2, 3] == "O" && tictactoeArray[3, 3] == "O") ||
    (tictactoeArray[0, 4] == "O" && tictactoeArray[1, 4] == "O" && tictactoeArray[2, 4] == "O" && tictactoeArray[3, 4] == "O") ||
    (tictactoeArray[4, 0] == "O" && tictactoeArray[1, 0] == "O" && tictactoeArray[2, 0] == "O" && tictactoeArray[3, 0] == "O") ||
    (tictactoeArray[4, 1] == "O" && tictactoeArray[1, 1] == "O" && tictactoeArray[2, 1] == "O" && tictactoeArray[3, 1] == "O") ||
    (tictactoeArray[4, 2] == "O" && tictactoeArray[1, 2] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[3, 2] == "O") ||
    (tictactoeArray[4, 3] == "O" && tictactoeArray[1, 3] == "O" && tictactoeArray[2, 3] == "O" && tictactoeArray[3, 3] == "O") ||
    (tictactoeArray[4, 4] == "O" && tictactoeArray[1, 4] == "O" && tictactoeArray[2, 4] == "O" && tictactoeArray[3, 4] == "O") ||
    //diagonal
    (tictactoeArray[0, 0] == "O" && tictactoeArray[1, 1] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[3, 3] == "O") ||
    (tictactoeArray[4, 4] == "O" && tictactoeArray[1, 1] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[3, 3] == "O") ||
    (tictactoeArray[0, 4] == "O" && tictactoeArray[1, 3] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[3, 1] == "O") ||
    (tictactoeArray[4, 0] == "O" && tictactoeArray[1, 3] == "O" && tictactoeArray[2, 2] == "O" && tictactoeArray[3, 1] == "O") ||
    (tictactoeArray[1, 0] == "O" && tictactoeArray[2, 1] == "O" && tictactoeArray[3, 2] == "O" && tictactoeArray[4, 3] == "O") ||
    (tictactoeArray[0, 1] == "O" && tictactoeArray[1, 2] == "O" && tictactoeArray[2, 3] == "O" && tictactoeArray[3, 4] == "O") ||
    (tictactoeArray[1, 4] == "O" && tictactoeArray[2, 3] == "O" && tictactoeArray[3, 2] == "O" && tictactoeArray[4, 1] == "O") ||
    (tictactoeArray[1, 4] == "O" && tictactoeArray[2, 3] == "O" && tictactoeArray[3, 2] == "O" && tictactoeArray[4, 1] == "O") ||
    (tictactoeArray[0, 3] == "O" && tictactoeArray[1, 2] == "O" && tictactoeArray[2, 1] == "O" && tictactoeArray[3, 0] == "O")) 
    {
        playAgainButtonObj.SetActive(true);  // Mengaktifkan tombol untuk bermain lagi
        message.enabled = true;  // Mengaktifkan pesan untuk ditampilkan
        message.text = "Winner is O";  // Menampilkan pesan bahwa pemenangnya adalah O
        gameEnd = true;  // Menandakan bahwa permainan telah berakhir
    }
}

    public void PlayAgain()
    {
        Debug.Log("PlayAgain called");
        // Reset game state
        round = 0;
        gameEnd = false;
        message.enabled = false;
        playAgainButtonObj.SetActive(false);

        // Clear the board
        for (int i = 0; i < 5; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                tictactoeArray[i, j] = "?";
            }
        }

        // Destroy existing X and O game objects
        GameObject[] xObjects = GameObject.FindGameObjectsWithTag("X");
        foreach (GameObject xObject in xObjects)
        {
            Destroy(xObject);
        }

        GameObject[] oObjects = GameObject.FindGameObjectsWithTag("O");
        foreach (GameObject oObject in oObjects)
        {
            Destroy(oObject);
        }
    }

}

I created the same one but in 3x3 it doesn’t crash like this one