convert this code to rust

#undef _GLIBCXX_DEBUG                // disable run-time bound checking, etc
#pragma GCC target("bmi,bmi2,lzcnt,popcnt")                      // bit manipulation
#pragma GCC target("movbe")                                      // byte swap
#pragma GCC target("aes,pclmul,rdrnd")                           // encryption
#pragma GCC target("avx,avx2,f16c,fma,sse3,ssse3,sse4.1,sse4.2")
#pragma GCC optimize("Ofast,inline,omit-frame-pointer,unroll-loops")

#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
#include <climits>
#include <random>
#include <chrono>
std::random_device rd;
std::mt19937 g(rd());
int _count;
using namespace std;
enum
{
    EMPTY = -1,
    ME, 
    ENEMY,

};

typedef struct s_cords
{
    int x;
    int y;
} cords;
class Board;
void printBoard(Board board);

class Board
{
    public :
        int board[9][9];
        Board(){
            for (int i = 0 ; i < 9 ; i++)
            {
                for (int j = 0 ; j < 9; j++)
                {
                    this->board[i][j] = EMPTY;
                }
            }
        }
        Board( const int board[9][9])
        {
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    this->board[i][j] = board[i][j];
                }
            }
        }

        Board(const Board *b)
        {
            for (int i = 0 ; i < 9 ; i++)
            {
                for (int j = 0 ; j < 9; j++)
                {
                    this->board[i][j] = b->board[i][j];
                }
            }
        }
        Board(const Board& other)
        {
            for (int i = 0; i < 9; i++)
            {
                for (int j = 0; j < 9; j++)
                {
                    this->board[i][j] = other.board[i][j];
                }
            }
        }

        cords next_cords(int i , int j)
        {
            cords converted;
            int x = i % 3;
            int y = j % 3;
            if (x == 0)
                x = 0;
            else if (x == 1)
                x = 3;
            else if (x == 2)
                x = 6;
            if (y == 0)
                y = 0;
            else if (y == 1)
                y = 3;
            else if (y == 2)
                y = 6;
            converted.x = x;
            converted.y = y;
            return converted;
        }
        cords current_cord(int i , int j)
        {
            cords c;
            if (i >=0 && i <= 2)
                c.x = 0;
            else if (i >=3 && i <= 5)
                c.x = 3;
            else if (i >= 6 && i <= 8)
                c.x = 6;
            if (j >=0 && j <= 2)
                c.y = 0;
            else if (j >=3 && j <= 5)
                c.y = 3;
            else if (j >= 6 && j <= 8)
                c.y = 6;
            return c;
        }
        bool isEqual(int a , int b, int c)
        {
            if (a == b && b == c && c != EMPTY)
                return true;
            return false;
        }
        int isEnd(int start_x , int start_y)
        {
            for (int i = start_x ; i < 3 + start_x ; i++)
            {
                if (isEqual (board[i][0 + start_y],board[i][1 + start_y],board[i][2 + start_y]))
                    return board[i][0 + start_y];
            }
            for (int i = start_y ; i < 3 + start_y ; i++)
            {
                if (isEqual(board[0 + start_x][i],board[1 + start_x][i],board[2 + start_x][i]))
                    return board[0 + start_x][i];
            }

            if (isEqual(board[0 + start_x][0 + start_y],board[1 + start_x][1 + start_y],board[2 + start_x][2 + start_y]) 
                || isEqual(board[0 + start_x][2 + start_y],board[1 + start_x][1 + start_y],board[2 + start_x][0 + start_y]))
                return board[1 + start_x][1 + start_y];
            return EMPTY;
        }
        int basicEnd(int board[3][3])
        {
            for (int i = 0 ; i < 3 ; i++)
            {
                if (isEqual(board[0][i],board[1][i],board[2][i]))
                    return board[0][i];
                if (isEqual (board[i][0],board[i][1],board[i][2]))
                    return board[i][0];
            }
            if (isEqual(board[0][0],board[1][1],board[2][2]) || isEqual(board[0][2],board[1][1],board[2][0]))
                return board[1][1];
            return EMPTY;
        }
        int isAllEnd()
        {
            int whole_board[3][3];
            for (int i = 0 ; i < 3 ; i++)
            {
                for (int j = 0 ; j < 3 ; j++)
                {
                    cords to_check = next_cords(i,j);
                    // cout << to_check.x << " " << to_check.y << endl;
                    whole_board[i][j] = isEnd(to_check.x, to_check.y);
                }
            }
            return basicEnd(whole_board);
        }
        bool isFull()
        {
            for (int i = 0 ; i < 9; i++)
            {
                for (int j = 0 ; j < 9 ; j++)
                {
                    if (board[i][j] == EMPTY)
                        return false;
                }
            }
            return true;
        }
        bool isFull(int x , int y)
        {
            for (int i = x ; i < x + 3; i++)
            {
                for (int j = y ; j < y + 3 ; j++)
                {
                    if (board[i][j] == EMPTY)
                        return false;
                }
            }
            return true;
        }
        vector<cords> possibleMoves(int x , int y)
        {
            vector<cords> positions;
            cords play = next_cords(x, y);
            if (isFull(play.x , play.y) || isEnd(play.x ,play.y) != EMPTY || (x == -1))
            {
                for (int i = 0 ; i < 9 ; i++)
                {
                    for (int j = 0 ; j < 9 ; j++)
                    {
                        cords c = current_cord(i,j);
                        if (isFull(c.x , c.y))
                            continue;
                        if (isEnd(c.x ,c.y) != EMPTY)
                            continue;
                        if (this->board[i][j] == EMPTY)
                        {
                            cords tmp;
                            tmp.x = i;
                            tmp.y = j;
                            positions.push_back(tmp);
                        }
                    }
                }
                return positions;
            }
            for (int i = play.x ; i < play.x + 3 ; i++)
            {
                for (int j = play.y ; j < play.y + 3 ; j++)
                {
                    if (this->board[i][j] == EMPTY)
                    {
                        cords tmp;
                        tmp.x = i;
                        tmp.y = j;
                        positions.push_back(tmp);
                    }
                }
            }
            return positions;
        }
        int simulate(int player , cords last_play)
        {
            while (true)
            {
                vector<cords> moves = possibleMoves(last_play.x , last_play.y);
                if (moves.empty())
                    return EMPTY;
                if (moves.size() > 2)
                {
                    std::shuffle(moves.begin() , moves.end(),g);
                }
                player = 1 - player;
                this->board[moves[0].x][moves[0].y] = player;
                last_play.x = moves[0].x;
                last_play.y = moves[0].y;
                if (this->isAllEnd() != EMPTY)
                    return this->isAllEnd();
                if (this->isFull())
                    return EMPTY;
            }
            
            return this->isAllEnd();
        }
};

void player(int i)
{
    if (i == ME)
        cout << "X";
    else 
        cout << "O";
}
cords convertXY(int play)
{
    cords cor ;
    cor.y = (play - 1) % 9;
    if (play > 0 && play <= 9)
        cor.x = 0;
    else if (play > 9 && play <=6)
        cor.x = 1;
    else if (play > 6 && play <= 9)
        cor.x = 2;

    return cor;
}

class Node
{
    public :
        double visits = 0, uct = 0, wins = 0, draws = 0, losses = 0;
        Node *parent = nullptr;
        int winner = EMPTY;
        vector<Node*> childrens;
        Board state;
        cords move;
        int player;
       
        Node(){};
        
        Node(Board state , int player) : state(state) , player(player) {};
        Node(Board state , int player, cords last_play) : state(state) , player(player) , move(last_play) {};
        void setUct()
        {
            if (this->visits == 0)
                this->uct = numeric_limits<double>::max();
            else
            {
                double raveBonus = 0.01 * sqrt(log(parent->visits) / (visits + 1));
                this->uct = (((this->draws + this->wins) / 2) / this->visits) + sqrt(2) * sqrt(log(parent->visits) / (this->visits + 1)) + raveBonus;
            }
        }
        void addChildren(Node  *child)
        {
            this->childrens.push_back(child);
            child->parent = this;
        }

};

class Mcts
{
    public :
        Node *root_node = nullptr;
        Node *best_node = nullptr;
        Mcts() {};
        void deallocateNodes(Node* node)
        {
            if (node == nullptr)
                return;
            if (node->childrens.empty())
                return;
            for (Node* child : node->childrens)
            {
                deallocateNodes(child);
                delete child;
            }

            node->childrens.clear();
            // delete node;
        }
        ~Mcts() {
            deallocateNodes(root_node);
            delete root_node;
        }
        void generateChildren(Node* n)
        {
            if (n == nullptr)
                return;
            vector<cords> moves = n->state.possibleMoves(n->move.x , n->move.y);
            if (moves.empty())
                return;
            
            if (moves.size() > 2)
            {
                std::shuffle(moves.begin() , moves.end(),g);
            }
            for (size_t i = 0; i < moves.size(); i++)
            {
                int p = 1 - n->player;
                n->state.board[moves[i].x][moves[i].y] = p;
                Node* child = new Node(n->state.board, p);
                n->state.board[moves[i].x][moves[i].y] = EMPTY;
                child->winner = child->state.isAllEnd();
                child->move = moves[i];
                n->addChildren(child);
            }
        }
        Node* selection() 
        {
            Node* current_node = root_node;


            while (true)
            {
                if (current_node->state.isAllEnd() != EMPTY || current_node->state.isFull())
                    return current_node;

                if (current_node->childrens.empty())
                {
                    generateChildren(current_node);
                    return current_node->childrens[0];
                }
                else
                {
                    for (Node* child : current_node->childrens)
                    {
                        child->setUct();
                    }
                    std::sort(current_node->childrens.begin(), current_node->childrens.end(),
                            [](const Node* a, const Node* b) { return a->uct > b->uct; });
                    current_node = current_node->childrens[0];
                    if (current_node->visits == 0)
                        return current_node;
                    else if (current_node->childrens.empty())
                        return current_node;
                }
            }
        }
        void backPropagate(Node *node , int won)
        {
            Node *current = node;
            while (current != nullptr)
            {
                current->visits++;
                if (won == EMPTY)
                    current->draws++;
                else if (won == current->player)
                    current->wins++;
                else if (won != current->player)
                    current->losses++;
                current = current->parent;
            }
        }
        cords bestMove(cords *last_play = nullptr)
        {
            std::chrono::system_clock::time_point startTime = std::chrono::high_resolution_clock::now();
            std::chrono::system_clock::time_point endTime;
            if (_count == 0)
            {
                _count++;
                endTime = startTime + std::chrono::milliseconds(1000);
            }
            else 
                endTime = startTime + std::chrono::milliseconds(101);
            int playouts = 0;
            if (last_play != nullptr)
                root_node->move = *last_play;
            if (last_play == nullptr)
                exit(100);
            
            cerr << "last move x : " << root_node->move.x << " move y : " << root_node->move.y << endl;
            cords c = root_node->state.next_cords(root_node->move.x , root_node->move.y);
            cerr << "next moves will be x : " << c.x << " y : " << c.y<< endl;
     
            while (std::chrono::high_resolution_clock::now() < endTime)
            {
                playouts++;
                Node *leaf_to_roulout = selection();
                Board save_state = leaf_to_roulout->state;
                int won = leaf_to_roulout->state.simulate(leaf_to_roulout->player,leaf_to_roulout->move);
                leaf_to_roulout->state = save_state;
                backPropagate(leaf_to_roulout,won);
            }
            double visits_count = 0;
            for (Node* child : this->root_node->childrens)
            {
                if (child != nullptr && child->visits > visits_count)
                {
                    this->best_node = child;
                    visits_count = child->visits;
                }
            }

            cerr << "playouts " << playouts << endl;
            cerr << "bestnode found draws : " << best_node->draws << " wins : " << best_node->wins << " loses : " << best_node->losses << " uct : " << best_node->uct << endl;
            cerr << "correct move is : " << best_node->move.x << " " <<  best_node->move.y << " playouts " << playouts << endl;
            return best_node->move;
        }
};
void printBoard(Board board)
{
    (void) board;
    int row = 0;
    for (int i = 0 ; i < 9; i++)
    {
        for (int j = 0 ; j < 9;j++)
        {
            cout << "|";
            if (board.board[i][j] != EMPTY)
                player(board.board[i][j]);
            else
                cout << i + j + 1 + row;
            cout << "|";
        }
        row += 2;
        cout << endl;
    }  
    cout << "\n-------------\n";
}
int main()
{

    // game loop
    Board board;
    while (1) {
        Mcts mcts;
        int opponent_row;
        int opponent_col;
        cin >> opponent_row >> opponent_col; cin.ignore();
        int valid_action_count;
        cin >> valid_action_count; cin.ignore();
        for (int i = 0; i < valid_action_count; i++) {
            int row;
            int col;
            cin >> row >> col; cin.ignore();
        }
        if (opponent_row != -1)
            board.board[opponent_col][opponent_row] = ME;
        cords last_play;
        last_play.x = opponent_col;
        last_play.y = opponent_row;
        mcts.root_node = new Node(board , ENEMY , last_play);
        cords pos = mcts.bestMove(&last_play);
        board.board[pos.x][pos.y] = ENEMY;
        cout << pos.y << " " << pos.x << endl;
    }
}