Advertisement
Garey

Kursova_3_1

Feb 28th, 2018
307
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.91 KB | None | 0 0
  1. #include <algorithm>
  2. #include <array>
  3. #include <iostream>
  4. #include <iterator>
  5. #include <map>
  6. #include <tuple>
  7. #include <vector>
  8.  
  9.  
  10.  
  11. /* Error handling made easy. */
  12. [[noreturn]] void fail(char const * message) {
  13.     std::cerr << message << std::endl;
  14.     std::exit(EXIT_FAILURE);
  15. }
  16.  
  17.  
  18.  
  19. /* A mark at a place on the board.
  20. *
  21. * - None:     No one has placed a mark here yet.
  22. * - AI: This place has been marked by the magnificient AI.
  23. * - Human:    The dirty human has touched this place.
  24. */
  25. enum class Mark {
  26.     None,
  27.     AI,
  28.     Human
  29. };
  30.  
  31. std::ostream & operator<< (std::ostream & s, Mark m) {
  32.     switch (m) {
  33.     case Mark::None:     return (s << "_");
  34.     case Mark::AI:       return (s << "O");
  35.     case Mark::Human:    return (s << "X");
  36.     }
  37.     fail("Not reached");
  38. }
  39.  
  40.  
  41.  
  42. /* A board is just 3 rows of each 3 marks. */
  43. using Board = std::array<Mark, 3 * 3>;
  44.  
  45. Board make_empty_board(void) {
  46.     Board b;
  47.     std::fill(std::begin(b), std::end(b), Mark::None);
  48.     return b;
  49. }
  50.  
  51. std::ostream & operator<<(std::ostream & o, Board const & b) {
  52.     auto from = std::begin(b);
  53.     auto to = from;
  54.     for (unsigned i = 0; i < 3; ++i) {
  55.         std::advance(to, 3);
  56.         std::copy(from, to, std::ostream_iterator<Mark>(o, "|"));
  57.         o << '\n';
  58.         std::advance(from, 3);
  59.     }
  60.     return o;
  61. }
  62.  
  63.  
  64.  
  65. /* A state consists of the board (state) and a boolean to tell
  66. * us who's turn it is.
  67. */
  68. struct State {
  69.     Board board;
  70.     bool AITurn;
  71. };
  72.  
  73. bool operator<(State const & lhs, State const & rhs) {
  74.     if (lhs.AITurn == rhs.AITurn) {
  75.         return lhs.board < rhs.board;
  76.     }
  77.     return lhs.AITurn; /* The states waiting for the AI to
  78.                               * make a turn are ordered before the
  79.                               * states waiting for the human to make a
  80.                               * turn. */
  81. }
  82.  
  83. std::ostream & operator<<(std::ostream & stream, State const & state) {
  84.     stream << (state.AITurn ? "AI" : "HUMAN") << '\n';
  85.     return stream << state.board;
  86. }
  87.  
  88.  
  89.  
  90. /* A move is just the index on which to place a mark. */
  91. using Move = unsigned;
  92.  
  93. std::vector<Move> possible_moves(State const & state) {
  94.     std::vector<Move> moves;
  95.     unsigned index = 0;
  96.     for (auto const place : state.board) {
  97.         if (place == Mark::None) {
  98.             moves.emplace_back(index);
  99.         }
  100.         ++index;
  101.     }
  102.     return moves;
  103. }
  104.  
  105. State make_move(State state, Move move) {
  106.     if (move >= state.board.size() || state.board[move] != Mark::None) {
  107.         fail("Invalid move");
  108.     }
  109.     state.board[move] = state.AITurn ? Mark::AI : Mark::Human;
  110.     state.AITurn = !state.AITurn;
  111.     return state;
  112. }
  113.  
  114.  
  115.  
  116. /* Possible winners:
  117. * - Human: The human won (or is about to win).
  118. * - AI: The AI won (or is about to win).
  119. * - None: Its a tie or it's not yet sure who's about to win.
  120. */
  121. enum class Winner {
  122.     Human,
  123.     AI,
  124.     None
  125. };
  126.  
  127. Winner determine_winner(State const & state) {
  128.     Board const & b = state.board;
  129.     Mark const lines[][3] = {
  130.         /* Horizontal */
  131.         { b[0], b[1], b[2] },
  132.         { b[3], b[4], b[5] },
  133.         { b[6], b[7], b[8] },
  134.         /* Vertical */
  135.         { b[0], b[3], b[6] },
  136.         { b[1], b[4], b[7] },
  137.         { b[2], b[5], b[8] },
  138.         /* Cross */
  139.         { b[0], b[4], b[8] },
  140.         { b[6], b[4], b[2] }
  141.     };
  142.     for (auto line : lines) {
  143.         if (std::count(line, line + 3, Mark::Human) == 3) {
  144.             return Winner::Human;
  145.         }
  146.         if (std::count(line, line + 3, Mark::AI) == 3) {
  147.             return Winner::AI;
  148.         }
  149.     }
  150.     return Winner::None;
  151. }
  152.  
  153.  
  154. /* Partition the given range into 3 groups, according to the given selector.
  155. *
  156. * Let r be the result of partition3(from, to, selector); Then the
  157. * first group is in the range [from, std::get<0>(r)) and consists of
  158. * those elements for which the selector returned a negative
  159. * value. The "mid" group is in the range [std::get<0>(r),
  160. * std::get<1>(r)) and consists of those elements for which the
  161. * selector returned 0. The "end" group is in the range
  162. * [std::get<1>(r), to) and consists of those elements which are not
  163. * part of another group.
  164. *
  165. * For each element the selector will be called exactly once.
  166. */
  167. template<typename Iterator, typename Selector>
  168. std::tuple<Iterator, Iterator> partition3(Iterator from,
  169.     Iterator to,
  170.     Selector selector) {
  171.     Iterator startMidGroup = from;
  172.     Iterator startEndGroup = to;
  173.     while (from != startEndGroup) {
  174.         auto const selection = selector(*from);
  175.         if (selection < 0) {
  176.             std::iter_swap(from, startMidGroup);
  177.             ++startMidGroup;
  178.             ++from;
  179.         }
  180.         else if (selection == 0) {
  181.             ++from;
  182.         }
  183.         else {
  184.             --startEndGroup;
  185.             std::iter_swap(from, startEndGroup);
  186.         }
  187.     }
  188.     return std::make_tuple(startMidGroup, startEndGroup);
  189. }
  190.  
  191.  
  192.  
  193. /* An advice, to be stored as "answer" to a State in the Plan.
  194.  
  195. * The move is the Move that is recommended, and the winner member
  196. * shows whether it can be said for sure who is going to win (if the
  197. * aforementioned move is made).
  198. */
  199. struct Advice {
  200.     Move move;
  201.     Winner winner;
  202. };
  203.  
  204.  
  205.  
  206. /* The Plan is just a mapping from States to Advices. */
  207. using Plan = std::map<State, Advice>;
  208.  
  209.  
  210.  
  211. /* Core function, decides which move is best in the given state and
  212. * stores this information in the Plan.
  213. *
  214. * Furthermore, the function computes the outcome of the game assuming
  215. * perfect play of both parties. This is stored in the Plan, too, and
  216. * also returned (to be used in the very same function, as this
  217. * function recursively calls itself).
  218. */
  219. Winner solve_state(Plan & plan, State const & state) {
  220.     /* Look whether this particular state has already been
  221.     * considered. If so, bail out.
  222.     */
  223.     auto const precomputed = plan.find(state);
  224.     if (precomputed != plan.end()) {
  225.         return precomputed->second.winner;
  226.     }
  227.     /* Look whether the game is over. Either because somebody won, or
  228.     * because there are no possible moves anymore.
  229.     */
  230.     auto winner = determine_winner(state);
  231.     auto moves = possible_moves(state);
  232.     if (winner != Winner::None || moves.empty()) {
  233.         return winner;
  234.     }
  235.     /* Make all possible moves, and solve the resulting states. The
  236.     * moves are partitioned according to the outcome of the game after
  237.     * the respective move.
  238.     */
  239.     auto const selector = [&plan, &state](auto const move) {
  240.         auto const result = solve_state(plan, make_move(state, move));
  241.         switch (result) {
  242.         case Winner::AI: return -1;
  243.         case Winner::None:     return  0;
  244.         case Winner::Human:    return  1;
  245.         }
  246.         fail("Not reached");
  247.     };
  248.     auto const partitioning =
  249.         partition3(std::begin(moves), std::end(moves), selector);
  250.     /* [begin(moves), begin_tie_moves): Moves where the AI wins.
  251.     * [begin_tie_moves, begin_human_moves): Moves which results in a tie.
  252.     * [begin_human_moves, end(moves)): Moves where the human wins.
  253.     */
  254.     auto const begin_tie_moves = std::get<0>(partitioning);
  255.     auto const begin_human_moves = std::get<1>(partitioning);
  256.     /* Try to make a move that lets the current player win. If that's
  257.     * not possible chose a move that results in a tie. Else there's no
  258.     * choice but to lose.
  259.     */
  260.     Move move;
  261.     if (state.AITurn) {
  262.         if (begin_tie_moves != std::begin(moves)) {
  263.             winner = Winner::AI;
  264.         }
  265.         else if (begin_human_moves != std::begin(moves)) {
  266.             winner = Winner::None;
  267.         }
  268.         else {
  269.             winner = Winner::Human;
  270.         }
  271.         move = moves[0];
  272.     }
  273.     else {
  274.         if (begin_human_moves != std::end(moves)) {
  275.             move = *begin_human_moves;
  276.             winner = Winner::Human;
  277.         }
  278.         else if (begin_tie_moves != std::end(moves)) {
  279.             move = *begin_tie_moves;
  280.             winner = Winner::None;
  281.         }
  282.         else {
  283.             move = moves[0];
  284.             winner = Winner::AI;
  285.         }
  286.     }
  287.     /* Store the results in the plan, to be used by the AI during the
  288.     * game or in other solve_state calls.
  289.     */
  290.     plan[state] = { move, winner };
  291.     return winner;
  292. }
  293.  
  294.  
  295.  
  296. /* Solve every possible state, return the plan that's build up in the
  297. * course of this.
  298. */
  299. Plan calculate_plan(void) {
  300.     Plan plan;
  301.     solve_state(plan, { make_empty_board(), true });
  302.     solve_state(plan, { make_empty_board(), false });
  303.     return plan;
  304. }
  305.  
  306.  
  307.  
  308. void report_winner(Winner winner) {
  309.     std::cout << "AAAAAAnd the winner is ";
  310.     switch (winner) {
  311.     case Winner::Human: {
  312.         std::cout << "the human";
  313.         break;
  314.     }
  315.     case Winner::AI: {
  316.         std::cout << "the AI";
  317.         break;
  318.     }
  319.     case Winner::None: {
  320.         std::cout << " ... ehm .. seems like there's no winner";
  321.     }
  322.     }
  323.     std::cout << std::endl;
  324. }
  325.  
  326.  
  327.  
  328. int tictactoe(int, char**) {
  329.     auto const plan = calculate_plan();
  330.     /* Play one round of the game. */
  331.     State state = { make_empty_board(), false };
  332.     auto moves = possible_moves(state);
  333.     auto winner = determine_winner(state);
  334.     while (moves.size() != 0 && winner == Winner::None) {
  335.         std::cout << state << std::endl;
  336.         if (state.AITurn) {
  337.             auto const advice = plan.find(state);
  338.             if (advice == std::end(plan)) {
  339.                 fail("No idea what to do?");
  340.             }
  341.             std::cout << "Making move " << advice->second.move << std::endl;
  342.             state = make_move(state, advice->second.move);
  343.         }
  344.         else {
  345.             int choice = 0;
  346.             std::cout << "Where do you place your mark? ";
  347.             std::cin >> choice;
  348.             if (!std::cin) {
  349.                 fail("Ok, just forget it!");
  350.             }
  351.             state = make_move(state, choice);
  352.         }
  353.         moves = possible_moves(state);
  354.         winner = determine_winner(state);
  355.     }
  356.     report_winner(winner);
  357.     return EXIT_SUCCESS;
  358. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement