Advertisement
Guest User

Untitled

a guest
Dec 21st, 2021
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.79 KB | None | 0 0
  1. // KingFlerple, 2021-12-21, Advent of Code 2021 Day 21: "Dirac Dice", Part 2.
  2. #include <iostream>
  3. #include <vector>
  4. #include <deque>
  5. #include <map>
  6. #include <set>
  7. #include <algorithm>
  8. #include <cassert>
  9.  
  10. using namespace std;
  11.  
  12. constexpr auto winningScore = 21;
  13. constexpr auto largestSquareNum = 10;
  14.  
  15. struct GameState
  16. {
  17.     int playerScores[2] = {-1,-1};
  18.     int playerPositions[2] = {-1, -1}; // 1 ... 10.
  19.     int currentPlayerTurn = -1; // {0, 1}
  20.  
  21.     // Impose some arbitrary ordering for use in set/ maps.
  22.     auto operator<=>(const GameState& other) const = default;
  23. };
  24.  
  25. ostream& operator<<(ostream& os, const GameState& gameState)
  26. {
  27.     os << "Gamestate: currentPlayerTurn: " << gameState.currentPlayerTurn << " scores: {" << gameState.playerScores[0] << ", " << gameState.playerScores[1] << "} positions: {" << gameState.playerPositions[0] << ", " << gameState.playerPositions[1] << "}";
  28.     return os;
  29. }
  30.  
  31. int newPos(int pos, int diceRoll)
  32. {
  33.     assert(diceRoll > 0);
  34.     pos += diceRoll;
  35.     while (pos > largestSquareNum)
  36.         pos -= largestSquareNum;
  37.     return pos;
  38. }
  39.  
  40. int64_t numUniversesReachingGameState(const GameState& gameState, map<GameState, int64_t>& numUniversesReachingGameStateLookup, map<GameState, vector<GameState>>& predecessorStates)
  41. {
  42.     if (numUniversesReachingGameStateLookup.contains(gameState))
  43.         return numUniversesReachingGameStateLookup[gameState];
  44.  
  45.     int64_t result = 0;
  46.  
  47.     for (const auto& predecessor : predecessorStates[gameState])
  48.     {
  49.         result += numUniversesReachingGameState(predecessor, numUniversesReachingGameStateLookup, predecessorStates);
  50.     }
  51.  
  52.     numUniversesReachingGameStateLookup[gameState] = result;
  53.     cout << "numUniversesReachingGameState: " << gameState << " result: " << result << endl;
  54.     return result;
  55. }
  56.  
  57.  
  58. int main()
  59. {
  60.     const string playerStartPosPrefix = "Player X starting position: ";
  61.  
  62.     string player1StartPosLine;
  63.     getline(cin, player1StartPosLine);
  64.     const int player1StartPos = stoi(player1StartPosLine.substr(playerStartPosPrefix.size()));
  65.  
  66.     string player2StartPosLine;
  67.     getline(cin, player2StartPosLine);
  68.     const int player2StartPos = stoi(player2StartPosLine.substr(playerStartPosPrefix.size()));
  69.  
  70.     cout << "player1StartPos: " << player1StartPos << endl;
  71.     cout << "player2StartPos: " << player2StartPos << endl;
  72.  
  73.     map<GameState, int64_t> numUniversesReachingGameStateLookup;
  74.     GameState initialGameState = {
  75.         {0, 0},
  76.         {player1StartPos, player2StartPos},
  77.         0
  78.     };
  79.     numUniversesReachingGameStateLookup[initialGameState] = 1;
  80.  
  81.     // Build the list of "predecessor states" for all reachable states.
  82.     // For each state S, this is a list of states P that can reach S by having P's current player take a turn.
  83.     // Such P will occur in predecessorStates[S] as many times as there are turns from P that reach S: could be optimised
  84.     // so that there is a _count_ of P instead of just duplicating P, but oh well :)
  85.     // S and P will alway be states that are reachable from the initialGameState via valid turns.
  86.     map<GameState, vector<GameState>> predecessorStates;
  87.     set<GameState> seenStates = { initialGameState };
  88.     deque<GameState> toProcess = { initialGameState };
  89.     while (!toProcess.empty())
  90.     {
  91.         const auto gameState = toProcess.front();
  92.         toProcess.pop_front();
  93.         const int numWinningPlayers = static_cast<int>(count_if(begin(gameState.playerScores), end(gameState.playerScores), [](const auto score) { return score >= winningScore; }));
  94.         assert(numWinningPlayers == 0 || numWinningPlayers == 1);
  95.         if (numWinningPlayers == 1)
  96.             continue;
  97.         // Take all possible turns from gameState.
  98.         // Again, could be optimised (many dice outcomes are "redundant" i.e. have the same diceSum), but don't care :p
  99.         for (int valDice1 : {1, 2, 3})
  100.         {
  101.             for (int valDice2 : {1, 2, 3})
  102.             {
  103.                 for (int valDice3 : {1, 2, 3})
  104.                 {
  105.                     const int diceSum = valDice1 + valDice2 + valDice3;
  106.                     GameState nextState = gameState;
  107.                     nextState.currentPlayerTurn = 1 - nextState.currentPlayerTurn;
  108.                     nextState.playerPositions[gameState.currentPlayerTurn] = newPos(gameState.playerPositions[gameState.currentPlayerTurn], diceSum);
  109.                     nextState.playerScores[gameState.currentPlayerTurn] += nextState.playerPositions[gameState.currentPlayerTurn];
  110.                     predecessorStates[nextState].push_back(gameState);
  111.  
  112.                     if (!seenStates.contains(nextState))
  113.                     {
  114.                         toProcess.push_back(nextState);
  115.                         seenStates.insert(nextState);
  116.                     }
  117.                 }
  118.             }
  119.         }
  120.     }
  121.  
  122.     // Now do the dynamic programming step to compute, for all valid states S, the number of universes in which S is reached.
  123.     // While we are are it, decide if each such S is a Win for either player, and update numUniversesInWhichPlayerWins accordingly.
  124.     int64_t numUniversesInWhichPlayerWins[2] = {0,0};
  125.     for (const auto& [state, predecessors] : predecessorStates)
  126.     {
  127.         for (int playerId : {0, 1})
  128.         {
  129.             if (state.playerScores[playerId] >= winningScore)
  130.             {
  131.                 numUniversesInWhichPlayerWins[playerId] += numUniversesReachingGameState(state, numUniversesReachingGameStateLookup, predecessorStates);
  132.             }
  133.         }
  134.     }
  135.     for (int playerId : {0, 1})
  136.     {
  137.         cout << "player:" << playerId << " numUniversesInWhichPlayerWins: " << numUniversesInWhichPlayerWins[playerId] << endl;
  138.     }
  139.     cout << *max_element(begin(numUniversesInWhichPlayerWins), end(numUniversesInWhichPlayerWins)) << endl;
  140. }
  141.  
  142.  
  143.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement