The5threic1-1

Tic-Tac-Toe MCTSish

May 4th, 2024
156
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.32 KB | Source Code | 0 0
  1. #include<unordered_map>
  2. #include<iostream>
  3. #include<math.h>
  4. #include<string>
  5. #include<vector>
  6. #include<algorithm>
  7. /* FEN[10] --> would be like "XOX_OXX_O"
  8. "w" denotes the number of wins/points from randomly generated games from this position,
  9. N denotes the no. of randomly generated games.
  10. node_exploration --> Tendency to explore given node.
  11. if explored once or not... maybe we'll not need that.*/
  12. struct Position
  13. {
  14.     std::string FEN="_________";
  15.     float w=0, N=0;                
  16.     float node_exploration = INFINITY;         
  17.     bool explored=false;   
  18.     bool chd_fd = false;
  19.     Position* child[10] = { 0 };
  20. };
  21. bool new_posn_enc(std::unordered_map<std::string, Position>& all_positions_now, std::string positionFEN )
  22. {
  23.     if (all_positions_now.find(positionFEN) == all_positions_now.end())
  24.     {
  25.         return true;
  26.     }
  27.     return false;
  28. }
  29. //computes node exploration thing.
  30. int find_node_exp(Position* parent, Position* child)
  31. {
  32.     child->node_exploration = child->w / child->N + sqrt(2 * log(parent->N) / child->N);       
  33.     return 0;
  34. }
  35. //plays given move onto the given board.
  36. int play(int move, char* FENtmpPar, int mvno)              
  37. {
  38.     if (FENtmpPar[move] == '_')
  39.     {
  40.         if (mvno % 2 == 1)
  41.         {
  42.             FENtmpPar[move] = 'X';
  43.         }
  44.         else
  45.         {
  46.             FENtmpPar[move] = 'O';
  47.         }
  48.     }
  49.     else
  50.     {
  51.         std::cerr << "Error 001: The program inherited an invalid move"<<std::endl;
  52.     }
  53.     return 0;
  54. }
  55. //If X wins win()=-1, if O wins win()=1, if it's a dead draw win()=0.
  56. int win(std::string posn)
  57. {
  58.     for (int i = 0; i < 3; i++)
  59.     {
  60.         if (posn[3*i] != '_')
  61.         {
  62.             if (posn[3 * i] == posn[3 * i + 1] && posn[3 * i] == posn[3 * i + 2])
  63.             {
  64.                 if (posn[3 * i] == 'X')
  65.                 {
  66.                     return -1;
  67.                 }
  68.                 return 1;
  69.             }
  70.         }
  71.         if (posn[i] != '_')
  72.         {
  73.             if (posn[i] == posn[3 + i] && posn[i] == posn[6 + i])
  74.             {
  75.                 if (posn[i] == 'X')
  76.                 {
  77.                     return -1;
  78.                 }
  79.                 return 1;
  80.             }
  81.         }
  82.     }
  83.     if (posn[0] != '_')
  84.     {
  85.         if (posn[0] == posn[4] && posn[0] == posn[8])
  86.         {
  87.             if (posn[0] == 'X')
  88.             {
  89.                 return -1;
  90.             }
  91.             return 1;
  92.         }
  93.     }
  94.     if (posn[2] != '_')
  95.     {
  96.         if (posn[2] == posn[4] && posn[2] == posn[6])
  97.         {
  98.             if (posn[2] == 'X')
  99.             {
  100.                 return -1;
  101.             }
  102.             return 1;
  103.         }
  104.     }
  105.     for (int i = 0; i < 9; i++)
  106.     {
  107.         if (posn[i] == '_')
  108.         {
  109.             return 2;
  110.         }
  111.     }
  112.     return 0;
  113. }
  114. //Plays some random games from "Present" position and updates accordingly
  115. int play_rand_frm(Position** history, int mvpld, int nofgames)
  116. {
  117.     int move, Nog = nofgames;
  118.     float wins = 0;
  119.     bool to_be_played = true;
  120.     if (win(history[mvpld]->FEN) != 2)
  121.     {
  122.         wins += float(nofgames)*float(win(history[mvpld]->FEN) + 1) / 2;
  123.         //std::cout << wins << std::endl;
  124.         to_be_played = true;
  125.         //if (win(history[mvpld]->FEN) != 0)
  126.         //{
  127.         //  history[mvpld - 1]->N = INFINITY;
  128.         //}
  129.     }
  130.     if (to_be_played)
  131.     {
  132.         std::vector<int> legal, inileg;
  133.         char FENtmp1[9], FENtmp2[9];
  134.         for (int i = 0; i < 9; i++)
  135.         {
  136.             FENtmp1[i] = history[mvpld]->FEN[i];
  137.             FENtmp2[i] = FENtmp1[i];
  138.             if (history[mvpld]->FEN[i] == '_')
  139.             {
  140.                 inileg.push_back(i);
  141.                 legal.push_back(i);
  142.             }
  143.         }
  144.         while (Nog--)
  145.         {
  146.             /*Testing code in this section------
  147.                                                 |
  148.                                                 |
  149.                                                 v  *//*
  150.             std::cout << "Game number: " << nofgames - Nog << std::endl;
  151.             std::cout << "===================================================" << std::endl;*/
  152.             //Testing ends.
  153.             while (win(FENtmp2) == 2)
  154.             {
  155.                 move = rand() % legal.size();
  156.                 play(legal[move], FENtmp2, 10 - legal.size());
  157.                 /*Testing code in this section------
  158.                                                     |
  159.                                                     |
  160.                                                     v  *//*
  161.                 std::cout << "Continuation... ";
  162.                 for (int i = 0; i < 9; i++)
  163.                 {
  164.                     std::cout << FENtmp2[i];
  165.                 }
  166.                 std::cout << std::endl;*/
  167.                 //Testing ends.
  168.                 legal.erase(legal.begin() + move);
  169.             }
  170.             //std::cout << std::endl << std::endl;
  171.             /*while none has won, a random move shall be chosen to be played.
  172.             The chosen move is occupied so it can no longer be played hence erasing it.*/
  173.             wins += float(win(FENtmp2) + 1) / 2;
  174.             std::copy(FENtmp1, FENtmp1 + 10, FENtmp2);
  175.             legal.clear();
  176.             std::copy(inileg.begin(), inileg.end(), back_inserter(legal));
  177.             //scores updated (internally), board position reset, legal moves reset.
  178.         }
  179.     }
  180.     history[mvpld]->w += wins+(mvpld%2)*(nofgames-2*wins);
  181.     history[mvpld]->N += nofgames;
  182.     /*Testing code in this section------
  183.                                         |
  184.                                         |
  185.                                         v  *//*
  186.     std::cout << "Scores recorded: " << std::endl;
  187.     std::cout << "w--> " << history[mvpld]->w<< std::endl;
  188.     std::cout << "N--> " << history[mvpld]->N<< std::endl;*/
  189.     //Testing ends.
  190.     for (int i = mvpld-1; i>=0; i--)
  191.     {
  192.         history[i]->w += wins + (i % 2) * (nofgames - 2 * wins);
  193.         history[i]->N += nofgames;
  194.         find_node_exp(history[i], history[i+1]);
  195.     }
  196.     history[mvpld]->explored = true;
  197.     //The results are updated into data structures.
  198.     return 0;
  199. }
  200. //Finding possible child nodes and adding them to the vector containing all board positions.
  201. int fd_child_nodes(Position* present, std::unordered_map<std::string, Position>& all_positions_now)
  202. {
  203.     Position next;
  204.     //std::cout << "initial value of *next.FEN*: " << next.FEN << std::endl;
  205.     int count=0, j=0;
  206.     char marker;
  207.     if (present->chd_fd)
  208.     {
  209.         return 0;
  210.     }
  211.     for (int i = 0; i < 9; i++)
  212.     {
  213.         //FENtmp1[i] = present->FEN[i];
  214.         //std::cout << FENtmp1[i] << std::endl;
  215.         if (present->FEN[i] == '_')
  216.         {
  217.             count++;
  218.         }
  219.     }
  220.     //std::cout << "henno";
  221.     next.FEN = present->FEN;
  222.     //std::cout << "value of *next.FEN*: " << next.FEN <<std::endl;
  223.     if (count % 2 == 1)
  224.     {
  225.         marker = 'X';
  226.     }
  227.     else
  228.     {
  229.         marker = 'O';
  230.     }
  231.     for (int i = 0; i < 9; i++)
  232.     {
  233.         if (next.FEN[i] == '_')
  234.         {
  235.             next.FEN[i] = marker;
  236.             if (new_posn_enc(all_positions_now, next.FEN))
  237.             {
  238.                 all_positions_now[next.FEN] = next;
  239.                 present->child[j] = &all_positions_now[next.FEN];
  240.                 /*Testing code in this section------
  241.                                                 |
  242.                                                 |
  243.                                                 v  *//*
  244.                 std::cout << "Child node no. " << j << " : ";
  245.                 std::cout << present->child[j]->FEN;
  246.                 std::cout << std::endl;*/
  247.                 //testing ends
  248.             }
  249.             else
  250.             {
  251.                 present->child[j] = &all_positions_now[next.FEN];
  252.                 /*Testing code in this section------
  253.                                                 |
  254.                                                 |
  255.                                                 v  *//*
  256.                 std::cout << "Child node no. (child already exists): " << j << " : ";
  257.                 std::cout << present->child[j]->FEN;
  258.                 std::cout << std::endl;*/
  259.                 //testing ends
  260.             }
  261.             if (win(present->child[j]->FEN) != 2)
  262.             {
  263.                 if (win(present->child[j]->FEN) != 0)
  264.                 {
  265.                     present->N = INFINITY;
  266.                 }
  267.                 else
  268.                 {
  269.                     present->child[j]->N = 200;
  270.                     present->child[j]->w = 100;
  271.                 }
  272.             }
  273.             j++;
  274.             next.FEN[i] = '_';
  275.         }
  276.     }
  277.     present->chd_fd = true;
  278.     return 0;
  279. }
  280. //Finding best child node, you'll have to add them to history though
  281. int explore_aor_best_continuation(Position** history, int mvpld, int mv_rand_play, bool fd_continuation)
  282. {
  283.     int i = 0, j = 0;
  284.     int index_number=0;
  285.     float exp_tendency=0;
  286.     for (i = 0; i < 9-mvpld; i++)
  287.     {
  288.         if (!(history[mvpld]->child[i]->explored))
  289.         {
  290.             history[mvpld + 1] = history[mvpld]->child[i];
  291.             play_rand_frm(history, mvpld + 1, mv_rand_play);
  292.             history[mvpld]->child[i]->explored = true;
  293.         }
  294.         if (fd_continuation)
  295.         {
  296.             if (history[mvpld]->child[i]->node_exploration > exp_tendency)
  297.             {
  298.                 exp_tendency = history[mvpld]->child[i]->node_exploration;
  299.                 index_number = i;
  300.             }
  301.         }
  302.     }
  303.     return index_number;
  304. }
  305. int fd_best_play(Position** history, int mvpld, int mv_rand_play)
  306. {
  307.     int i = 0, j = 0;
  308.     int index_number = 0;
  309.     float win_tendency = -1;
  310.     for (i = 0; i < 9 - mvpld; i++)
  311.     {
  312.         //std::cout << "Value of i: " << i << std::endl;
  313.         //if (!(history[mvpld]->child[i]->explored))
  314.         //{
  315.             //history[mvpld + 1] = history[mvpld]->child[i];
  316.             //play_rand_frm(history, mvpld + 1, mv_rand_play);
  317.             //history[mvpld]->child[i]->explored = true;
  318.         //}
  319.         /*std::cout << "The move being judged: " << history[mvpld]->child[i]->FEN << " -> " << history[mvpld]->child[i]->w << "/" << history[mvpld]->child[i]->N;
  320.         std::cout << "-> Probability: " << history[mvpld]->child[i]->w / history[mvpld]->child[i]->N<<std::endl;
  321.         */
  322.         if (history[mvpld]->child[i]->w / history[mvpld]->child[i]->N > win_tendency && history[mvpld]->child[i]->N<INFINITY)
  323.         {
  324.             win_tendency = history[mvpld]->child[i]->w / history[mvpld]->child[i]->N;
  325.             index_number = i;
  326.         }
  327.     }
  328.     return index_number;
  329. }
  330. bool analyse(std::string posn, bool analyse_prnt = true, bool mode_play = false, char* FEN = nullptr, int playouts_primary=10, int playouts_secondary=10)
  331. {
  332.     int count1 = 0;
  333.     char marker;
  334.     bool no_victor=true;
  335.     for (int i = 0; i < 9; i++)
  336.     {
  337.         if (posn[i] == '_')
  338.         {
  339.             count1++;
  340.         }
  341.     }
  342.     srand(time(0));
  343.     Position testboard[9];
  344.     Position* historytmp[10];
  345.     int expl, depth, count = 0, best;
  346.     std::unordered_map<std::string, Position> all_positions_yet;
  347.     for (int i = 0; i < 9 - count1; i++)
  348.     {
  349.         historytmp[i] = &testboard[i];
  350.     }
  351.     testboard[9 - count1].FEN = posn;
  352.     historytmp[9 - count1] = &testboard[9 - count1];
  353.     all_positions_yet[testboard[9 - count1].FEN] = testboard[9 - count1];
  354.     fd_child_nodes(historytmp[9 - count1], all_positions_yet);
  355.     //std::cout << "Been there done that " << count1 << std::endl;
  356.     if (analyse_prnt)
  357.     {
  358.         std::cout << "Best Continuation: ";
  359.     }
  360.     for (int mvpld = 9 - count1; mvpld < 9; mvpld++)
  361.     {
  362.         depth = mvpld;
  363.         while (depth < 9 && count < 1000)
  364.         {
  365.             count++;
  366.             expl = explore_aor_best_continuation(historytmp, depth, playouts_primary, true);
  367.             depth++;
  368.             historytmp[depth] = historytmp[depth - 1]->child[expl];
  369.             if (win(historytmp[depth]->FEN) != 2)
  370.             {
  371.                 depth = mvpld;
  372.                 continue;
  373.             }
  374.             if (historytmp[depth - 1]->child[expl]->chd_fd)
  375.             {
  376.                 continue;
  377.             }
  378.             fd_child_nodes(historytmp[depth], all_positions_yet);
  379.             explore_aor_best_continuation(historytmp, depth, playouts_secondary, false);
  380.             depth = mvpld;
  381.         }
  382.         best = fd_best_play(historytmp, mvpld, playouts_primary);
  383.         if (analyse_prnt)
  384.         {
  385.             std::cout << historytmp[mvpld]->child[best]->FEN << "   ";
  386.         }
  387.         if (mode_play && mvpld == 9 - count1)
  388.         {
  389.             for (int i = 0; i < 9; i++)
  390.             {
  391.                 FEN[i] = historytmp[mvpld]->child[best]->FEN[i];
  392.             }
  393.         }
  394.         historytmp[mvpld + 1] = historytmp[mvpld]->child[best];
  395.         if (win(historytmp[mvpld]->child[best]->FEN) != 2 && win(historytmp[mvpld]->child[best]->FEN) != 0)
  396.         {
  397.             if (analyse_prnt)
  398.             {
  399.                 std::cout << std::endl;
  400.                 std::cout << "The final outcome considering, perfect players, is inescapable ";
  401.                 if (win(historytmp[mvpld]->child[best]->FEN) == -1)
  402.                 {
  403.                     marker = 'X';
  404.                 }
  405.                 else
  406.                 {
  407.                     marker = 'O';
  408.                 }
  409.                 std::cout << marker << " should win" << std::endl;
  410.                 std::cout << "semi-random playout gives the weighted probability as : " << historytmp[9 - count1]->w / historytmp[9 - count1]->N;
  411.                 std::cout << " For the losing side " << std::endl;
  412.             }
  413.             no_victor = false;
  414.             break;
  415.         }
  416.         if (analyse_prnt)
  417.         {
  418.             if (win(historytmp[mvpld]->child[best]->FEN) == 0)
  419.             {
  420.                 if (count1 % 2 == 0)
  421.                 {
  422.                     marker = 'X';
  423.                 }
  424.                 else
  425.                 {
  426.                     marker = 'O';
  427.                 }
  428.                 std::cout << std::endl;
  429.                 std::cout << "The final outcome considering, perfect players is DRAW " << std::endl;
  430.                 std::cout << "semi-random playout gives the weighted probability as : " << historytmp[9 - count1]->w / historytmp[9 - count1]->N;
  431.                 std::cout << " For " << marker << std::endl;
  432.             }
  433.             count = 0;
  434.             no_victor = false;
  435.         }
  436.         else
  437.         {
  438.             break;
  439.         }
  440.     }
  441.     return no_victor;
  442.  
  443. }
  444. int play_against(char FEN[], bool analyse_prnt, int playouts_primary, int playouts_secondary)
  445. {
  446.     char marker;
  447.     int playmv, mv = 10;
  448.     std::string board;
  449.     for (int i = 0; i < 9; i++)
  450.     {
  451.         if (FEN[i] == '_')
  452.         {
  453.             mv--;
  454.         }
  455.     }
  456.     board = FEN;
  457.     std::cout << "Marker of the opponent: >> ";
  458.     std::cin >> marker;
  459.     if (marker != 'O' && marker != 'X')
  460.     {
  461.         std::cout << "Error 002: Invalid marker (markers are supposed to be uppercase):" << std::endl;
  462.         return 0;
  463.     }
  464.     if ( (marker == 'X' && mv%2 == 1) || (marker == 'O' && mv%2 == 0))
  465.     {
  466.         std::cout << board << std::endl;
  467.         for (int i = 0; i < 9; i++)
  468.         {
  469.             std::cout << board[i] << " ";
  470.             if (i % 3 == 2)
  471.             {
  472.                 std::cout << std::endl;
  473.             }
  474.         }
  475.     }
  476.     else
  477.     {
  478.         analyse(board, analyse_prnt, true, FEN, playouts_primary, playouts_secondary);
  479.         board = FEN;
  480.         std::cout << board << std::endl<<std::endl;
  481.         for (int i = 0; i < 9; i++)
  482.         {
  483.             std::cout << board[i] << " ";
  484.             if (i % 3 == 2)
  485.             {
  486.                 std::cout << std::endl;
  487.             }
  488.         }
  489.         mv++;
  490.     }
  491.     while (true)
  492.     {
  493.         std::cout << ">> ";
  494.         std::cin >> playmv;
  495.         play(playmv, FEN, mv);
  496.         board = FEN;
  497.         std::cout << board << std::endl<<std::endl;
  498.         for (int i = 0; i < 9; i++)
  499.         {
  500.             std::cout << board[i] << " ";
  501.             if (i % 3 == 2)
  502.             {
  503.                 std::cout << std::endl;
  504.             }
  505.         }
  506.         if (win(board) != 2)
  507.         {
  508.             if (win(board) == -1)
  509.             {
  510.                 std::cout << "X has won" << std::endl;
  511.             }
  512.             if (win(board) == 1)
  513.             {
  514.                 std::cout << "O has won" << std::endl;
  515.             }
  516.             if (win(board) == 0)
  517.             {
  518.                 std::cout << "DRAW" << std::endl;
  519.             }
  520.             break;
  521.         }
  522.         analyse(board, analyse_prnt, true, FEN, playouts_primary, playouts_secondary);
  523.         board = FEN;
  524.         std::cout << board << std::endl;
  525.         for (int i = 0; i < 9; i++)
  526.         {
  527.             std::cout << board[i] << " ";
  528.             if (i % 3 == 2)
  529.             {
  530.                 std::cout << std::endl;
  531.             }
  532.         }
  533.         if (win(board) != 2)
  534.         {
  535.             if (win(board) == -1)
  536.             {
  537.                 std::cout << "X has won" << std::endl;
  538.             }
  539.             if (win(board) == 1)
  540.             {
  541.                 std::cout << "O has won" << std::endl;
  542.             }
  543.             if (win(board) == 0)
  544.             {
  545.                 std::cout << "DRAW" << std::endl;
  546.             }
  547.             break;
  548.         }
  549.         mv += 2;
  550.     }
  551. }
  552. int help()
  553. {
  554.     std::cout << "play_start().ON/OFF    -> Allows to play against the engine from start" << std::endl;
  555.     std::cout << "                          Toggle to ON if you want to see analysis side by side" << std::endl;
  556.     std::cout << "play_from().ON/OFF     -> Allows you to play against the engine from a position you'll enter" << std::endl;
  557.     std::cout << "                          Toggle to ON if you want to see analysis side by side" << std::endl;
  558.     std::cout << "analyse()              -> Allows you to see engine continuations from present move" << std::endl;
  559.     std::cout << "help()                 -> Shows the list of engine commands" << std::endl;
  560.     std::cout << "set playouts_primary   -> set number of primary playouts" << std::endl;
  561.     std::cout << "set playouts_secondary -> set number of secondary playouts" << std::endl;
  562.     std::cout << "terminate()            -> close application" << std::endl;
  563.     return 0;
  564. }
  565. bool text_recognizer(std::string command, int *playouts_primary, int *playouts_secondary)
  566. {
  567.     if (command == "help()")
  568.     {
  569.         help();
  570.         return true;
  571.     }
  572.     if (command == "play_start().ON")
  573.     {
  574.         char start[10] = "_________";
  575.         play_against(start, true, *playouts_primary, *playouts_secondary);
  576.         return true;
  577.     }
  578.     if (command == "play_start().OFF")
  579.     {
  580.         char start[10] = "_________";
  581.         play_against(start, false, *playouts_primary, *playouts_secondary);
  582.         return true;
  583.     }
  584.     if (command == "play_from().ON")
  585.     {
  586.         char start[10];
  587.         std::cout << ">> ";
  588.         std::cin >> start;
  589.         play_against(start, true, *playouts_primary, *playouts_secondary);
  590.         return true;
  591.     }
  592.     if (command == "play_from().OFF")
  593.     {
  594.         char start[10];
  595.         std::cout << ">> ";
  596.         std::cin >> start;
  597.         play_against(start, false, *playouts_primary, *playouts_secondary);
  598.         return true;
  599.     }
  600.     if (command == "analyse()")
  601.     {
  602.         std::string start;
  603.         std::cout << ">> ";
  604.         std::cin >> start;
  605.         analyse(start, true, false, nullptr , *playouts_primary, *playouts_primary);
  606.         return true;
  607.     }
  608.     if (command == "set playouts_primary")
  609.     {
  610.         std::cout << ">> ";
  611.         std::cin >> *playouts_primary;
  612.         return true;
  613.     }
  614.     if (command == "set playouts_secondary")
  615.     {
  616.         std::cout << ">> ";
  617.         std::cin >> *playouts_secondary;
  618.         return true;
  619.     }
  620.     if (command == "terminate()")
  621.     {
  622.         return false;
  623.     }
  624.     std::cout << "Unknown command " << "\"" << command << "\"" << std::endl;
  625.     return true;
  626. }
  627. int main()
  628. {
  629.     std::string command;
  630.     int playouts_primary=10, playouts_secondary=10;
  631.     bool ready = true;
  632.     std::cout << "Tic-Tac-Toe engine (2022) version 1.0.0" << std::endl;
  633.     std::cout << "=================================================" << std::endl;
  634.     while (ready)
  635.     {
  636.         std::cout << ">> ";
  637.         std::getline(std::cin, command);
  638.         if (command == "")
  639.         {
  640.             std::getline(std::cin, command);
  641.         }
  642.         ready = text_recognizer(command, &playouts_primary, &playouts_secondary);
  643.     }
  644.     return 0;
  645. }
Advertisement
Add Comment
Please, Sign In to add comment