private function calculateBestMove($player, $stopOpponentPlayer = true, &$playerMoveScore = 0)
    {
        $winningMoves = []; // the best tile(s)

        $opponentMoveScore = 0; // the higher moveScore of the opponent

        $opponentMove = []; // keep opponent best move

        if (!$this->checkPlayer($player) || $this->isFinished()) {
            return $winningMoves;
        }

        // try to stop the opponent from winning
        if ($stopOpponentPlayer) {
            // get the opponent's best move
            $opponentMove = $this->calculateBestMove($this->getOpponentPlayer($player), false, $opponentMoveScore);
        }

        foreach ($this->board->getBoard() as $positionX => $col) {

            foreach ($col as $positionY => $tile) {

                $moveScore = $this->calculateMoveScore($player, [$positionX, $positionY]);

                if (!is_null($moveScore)) {
                    if ($moveScore > $playerMoveScore) {

                        $winningMoves = [[$positionX, $positionY]];
                        $playerMoveScore = $moveScore;

                        continue;
                    }

                    if ($moveScore == $playerMoveScore) {

                        array_push($winningMoves, [$positionX, $positionY]);

                        continue;
                    }
                }
            }
        }

        if ($stopOpponentPlayer && $opponentMoveScore >= 10 && $playerMoveScore < 10) {
            return $opponentMove;
        }

        if (count($winningMoves)) {

            return $winningMoves[array_rand($winningMoves)];
        }

        return $winningMoves;
    }