1/*
2 * ๊ฒ์ ์ด๋ก (Game Theory)
3 * Nim, Sprague-Grundy, Minimax, Alpha-Beta Pruning
4 *
5 * ๊ฒ์์์ ์ต์ ์ ๋ต์ ์ฐพ๋ ์๊ณ ๋ฆฌ์ฆ์
๋๋ค.
6 */
7
8#include <iostream>
9#include <vector>
10#include <algorithm>
11#include <unordered_set>
12#include <functional>
13#include <climits>
14
15using namespace std;
16
17// =============================================================================
18// 1. Nim Game
19// =============================================================================
20
21// ๋ชจ๋ ๋๋ฏธ์ XOR์ด 0์ด๋ฉด ํ๊ณต ์น๋ฆฌ, ์๋๋ฉด ์ ๊ณต ์น๋ฆฌ
22bool canWinNim(const vector<int>& piles) {
23 int xorSum = 0;
24 for (int pile : piles) {
25 xorSum ^= pile;
26 }
27 return xorSum != 0;
28}
29
30// ์น๋ฆฌ๋ฅผ ์ํ ์ฒซ ์ ์ฐพ๊ธฐ
31pair<int, int> findNimMove(const vector<int>& piles) {
32 int xorSum = 0;
33 for (int pile : piles) {
34 xorSum ^= pile;
35 }
36
37 if (xorSum == 0) {
38 return {-1, -1}; // ์ด๊ธธ ์ ์์
39 }
40
41 for (int i = 0; i < (int)piles.size(); i++) {
42 int target = piles[i] ^ xorSum;
43 if (target < piles[i]) {
44 return {i, piles[i] - target}; // i๋ฒ ๋๋ฏธ์์ ์ด๋งํผ ์ ๊ฑฐ
45 }
46 }
47
48 return {-1, -1};
49}
50
51// =============================================================================
52// 2. Sprague-Grundy ์ ๋ฆฌ
53// =============================================================================
54
55// ๊ทธ๋ฐ๋ ์ ๊ณ์ฐ
56int grundyNumber(int n, function<vector<int>(int)> getMoves, vector<int>& memo) {
57 if (memo[n] != -1) return memo[n];
58
59 unordered_set<int> reachable;
60 for (int next : getMoves(n)) {
61 reachable.insert(grundyNumber(next, getMoves, memo));
62 }
63
64 // MEX (Minimum Excludant) ๊ณ์ฐ
65 int mex = 0;
66 while (reachable.count(mex)) mex++;
67
68 return memo[n] = mex;
69}
70
71// ์ฌ๋ฌ ๊ฒ์์ ๊ทธ๋ฐ๋ ์๋ XOR
72int multiGameGrundy(const vector<int>& grundyNumbers) {
73 int result = 0;
74 for (int g : grundyNumbers) {
75 result ^= g;
76 }
77 return result;
78}
79
80// =============================================================================
81// 3. Minimax
82// =============================================================================
83
84class TicTacToe {
85private:
86 vector<vector<int>> board; // 0: ๋น์นธ, 1: X, 2: O
87 const int EMPTY = 0, X = 1, O = 2;
88
89 int checkWin() {
90 // ๊ฐ๋ก
91 for (int i = 0; i < 3; i++) {
92 if (board[i][0] != EMPTY &&
93 board[i][0] == board[i][1] && board[i][1] == board[i][2]) {
94 return board[i][0];
95 }
96 }
97 // ์ธ๋ก
98 for (int j = 0; j < 3; j++) {
99 if (board[0][j] != EMPTY &&
100 board[0][j] == board[1][j] && board[1][j] == board[2][j]) {
101 return board[0][j];
102 }
103 }
104 // ๋๊ฐ์
105 if (board[0][0] != EMPTY &&
106 board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
107 return board[0][0];
108 }
109 if (board[0][2] != EMPTY &&
110 board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
111 return board[0][2];
112 }
113 return 0;
114 }
115
116 bool isFull() {
117 for (int i = 0; i < 3; i++) {
118 for (int j = 0; j < 3; j++) {
119 if (board[i][j] == EMPTY) return false;
120 }
121 }
122 return true;
123 }
124
125public:
126 TicTacToe() : board(3, vector<int>(3, 0)) {}
127
128 // Minimax: ์ต์ ์ ์ฐพ๊ธฐ
129 int minimax(bool isMaximizing) {
130 int winner = checkWin();
131 if (winner == X) return 1;
132 if (winner == O) return -1;
133 if (isFull()) return 0;
134
135 if (isMaximizing) {
136 int bestScore = INT_MIN;
137 for (int i = 0; i < 3; i++) {
138 for (int j = 0; j < 3; j++) {
139 if (board[i][j] == EMPTY) {
140 board[i][j] = X;
141 bestScore = max(bestScore, minimax(false));
142 board[i][j] = EMPTY;
143 }
144 }
145 }
146 return bestScore;
147 } else {
148 int bestScore = INT_MAX;
149 for (int i = 0; i < 3; i++) {
150 for (int j = 0; j < 3; j++) {
151 if (board[i][j] == EMPTY) {
152 board[i][j] = O;
153 bestScore = min(bestScore, minimax(true));
154 board[i][j] = EMPTY;
155 }
156 }
157 }
158 return bestScore;
159 }
160 }
161
162 pair<int, int> findBestMove(int player) {
163 int bestScore = (player == X) ? INT_MIN : INT_MAX;
164 pair<int, int> bestMove = {-1, -1};
165
166 for (int i = 0; i < 3; i++) {
167 for (int j = 0; j < 3; j++) {
168 if (board[i][j] == EMPTY) {
169 board[i][j] = player;
170 int score = minimax(player == O);
171 board[i][j] = EMPTY;
172
173 if (player == X && score > bestScore) {
174 bestScore = score;
175 bestMove = {i, j};
176 } else if (player == O && score < bestScore) {
177 bestScore = score;
178 bestMove = {i, j};
179 }
180 }
181 }
182 }
183
184 return bestMove;
185 }
186
187 void makeMove(int row, int col, int player) {
188 board[row][col] = player;
189 }
190
191 void print() {
192 for (int i = 0; i < 3; i++) {
193 cout << " ";
194 for (int j = 0; j < 3; j++) {
195 char c = board[i][j] == EMPTY ? '.' : (board[i][j] == X ? 'X' : 'O');
196 cout << c << " ";
197 }
198 cout << endl;
199 }
200 }
201};
202
203// =============================================================================
204// 4. Alpha-Beta Pruning
205// =============================================================================
206
207int alphabeta(vector<vector<int>>& board, int depth, int alpha, int beta,
208 bool isMax, function<int(const vector<vector<int>>&)> evaluate,
209 function<vector<pair<int,int>>(const vector<vector<int>>&, int)> getMoves,
210 int player) {
211
212 if (depth == 0) {
213 return evaluate(board);
214 }
215
216 auto moves = getMoves(board, player);
217 if (moves.empty()) {
218 return evaluate(board);
219 }
220
221 if (isMax) {
222 int maxEval = INT_MIN;
223 for (auto [r, c] : moves) {
224 board[r][c] = player;
225 int eval = alphabeta(board, depth - 1, alpha, beta, false,
226 evaluate, getMoves, 3 - player);
227 board[r][c] = 0;
228 maxEval = max(maxEval, eval);
229 alpha = max(alpha, eval);
230 if (beta <= alpha) break; // ๊ฐ์ง์น๊ธฐ
231 }
232 return maxEval;
233 } else {
234 int minEval = INT_MAX;
235 for (auto [r, c] : moves) {
236 board[r][c] = player;
237 int eval = alphabeta(board, depth - 1, alpha, beta, true,
238 evaluate, getMoves, 3 - player);
239 board[r][c] = 0;
240 minEval = min(minEval, eval);
241 beta = min(beta, eval);
242 if (beta <= alpha) break; // ๊ฐ์ง์น๊ธฐ
243 }
244 return minEval;
245 }
246}
247
248// =============================================================================
249// 5. ๋ ๊ฒ์ ๋ณํ
250// =============================================================================
251
252// 1, 2, 3๊ฐ์ฉ ๊ฐ์ ธ๊ฐ ์ ์๋ ๋ ๊ฒ์
253bool stoneGame123(int n) {
254 // n์ด 4์ ๋ฐฐ์์ด๋ฉด ํ๊ณต ์น๋ฆฌ
255 return n % 4 != 0;
256}
257
258// 2์ ๊ฑฐ๋ญ์ ๊ณฑ ๊ฐ์ฉ ๊ฐ์ ธ๊ฐ ์ ์๋ ๋ ๊ฒ์
259bool stoneGamePowerOf2(int n) {
260 vector<int> grundy(n + 1, 0);
261
262 for (int i = 1; i <= n; i++) {
263 unordered_set<int> reachable;
264 for (int p = 1; p <= i; p *= 2) {
265 reachable.insert(grundy[i - p]);
266 }
267 int mex = 0;
268 while (reachable.count(mex)) mex++;
269 grundy[i] = mex;
270 }
271
272 return grundy[n] != 0;
273}
274
275// =============================================================================
276// 6. Bash Game
277// =============================================================================
278
279// ์ต๋ k๊ฐ์ฉ ๊ฐ์ ธ๊ฐ ์ ์๋ ๋ ๊ฒ์
280bool bashGame(int n, int k) {
281 return n % (k + 1) != 0;
282}
283
284// =============================================================================
285// 7. Wythoff's Game
286// =============================================================================
287
288// ๋ ๋๋ฏธ, ํ ๋๋ฏธ์์ ๊ฐ์ ธ๊ฐ๊ฑฐ๋ ์์ชฝ์์ ๊ฐ์ ์ ๊ฐ์ ธ๊ฐ๊ธฐ
289bool wythoffGame(int a, int b) {
290 if (a > b) swap(a, b);
291
292 const double phi = (1 + sqrt(5)) / 2;
293 int coldA = (int)(phi * (b - a));
294
295 return a != coldA;
296}
297
298// =============================================================================
299// ํ
์คํธ
300// =============================================================================
301
302int main() {
303 cout << "============================================================" << endl;
304 cout << "๊ฒ์ ์ด๋ก ์์ " << endl;
305 cout << "============================================================" << endl;
306
307 // 1. Nim Game
308 cout << "\n[1] Nim Game" << endl;
309 vector<int> piles = {3, 4, 5};
310 cout << " ๋๋ฏธ: [3, 4, 5]" << endl;
311 cout << " XOR: " << (3 ^ 4 ^ 5) << endl;
312 cout << " ์ ๊ณต ์น๋ฆฌ: " << (canWinNim(piles) ? "์" : "์๋์ค") << endl;
313
314 auto [pileIdx, removeCount] = findNimMove(piles);
315 if (pileIdx != -1) {
316 cout << " ์ต์ ์: ๋๋ฏธ " << pileIdx << "์์ " << removeCount << "๊ฐ ์ ๊ฑฐ" << endl;
317 }
318
319 // 2. Sprague-Grundy
320 cout << "\n[2] Sprague-Grundy" << endl;
321 // 1, 2, 3๊ฐ์ฉ ๊ฐ์ ธ๊ฐ ์ ์๋ ๊ฒ์์ ๊ทธ๋ฐ๋ ์
322 auto moves123 = [](int n) -> vector<int> {
323 vector<int> result;
324 if (n >= 1) result.push_back(n - 1);
325 if (n >= 2) result.push_back(n - 2);
326 if (n >= 3) result.push_back(n - 3);
327 return result;
328 };
329
330 vector<int> memo(20, -1);
331 memo[0] = 0;
332 cout << " ๋ ๊ฒ์ (1,2,3๊ฐ) ๊ทธ๋ฐ๋ ์:" << endl;
333 cout << " ";
334 for (int i = 0; i < 10; i++) {
335 cout << "G(" << i << ")=" << grundyNumber(i, moves123, memo) << " ";
336 }
337 cout << endl;
338
339 // 3. Minimax (Tic-Tac-Toe)
340 cout << "\n[3] Tic-Tac-Toe Minimax" << endl;
341 TicTacToe game;
342 game.makeMove(0, 0, 1); // X
343 game.makeMove(1, 1, 2); // O
344 cout << " ํ์ฌ ์ํ:" << endl;
345 game.print();
346
347 auto [bestRow, bestCol] = game.findBestMove(1); // X์ ์ต์ ์
348 cout << " X์ ์ต์ ์: (" << bestRow << ", " << bestCol << ")" << endl;
349
350 // 4. ๋ ๊ฒ์ ๋ณํ
351 cout << "\n[4] ๋ ๊ฒ์ ๋ณํ" << endl;
352 cout << " ๋ 10๊ฐ (1,2,3๊ฐ ๊ฐ๋ฅ): " << (stoneGame123(10) ? "์ ๊ณต ์น" : "ํ๊ณต ์น") << endl;
353 cout << " ๋ 12๊ฐ (1,2,3๊ฐ ๊ฐ๋ฅ): " << (stoneGame123(12) ? "์ ๊ณต ์น" : "ํ๊ณต ์น") << endl;
354
355 // 5. Bash Game
356 cout << "\n[5] Bash Game" << endl;
357 cout << " ๋ 10๊ฐ, ์ต๋ 3๊ฐ: " << (bashGame(10, 3) ? "์ ๊ณต ์น" : "ํ๊ณต ์น") << endl;
358 cout << " ๋ 12๊ฐ, ์ต๋ 3๊ฐ: " << (bashGame(12, 3) ? "์ ๊ณต ์น" : "ํ๊ณต ์น") << endl;
359
360 // 6. Wythoff's Game
361 cout << "\n[6] Wythoff's Game" << endl;
362 cout << " ๋๋ฏธ (3, 5): " << (wythoffGame(3, 5) ? "์ ๊ณต ์น" : "ํ๊ณต ์น") << endl;
363 cout << " ๋๋ฏธ (1, 2): " << (wythoffGame(1, 2) ? "์ ๊ณต ์น" : "ํ๊ณต ์น") << endl;
364
365 // 7. ๊ฒ์ ์ด๋ก ์์ฝ
366 cout << "\n[7] ๊ฒ์ ์ด๋ก ์์ฝ" << endl;
367 cout << " | ๊ฒ์ | ์น๋ฆฌ ์กฐ๊ฑด |" << endl;
368 cout << " |---------------|------------------------|" << endl;
369 cout << " | Nim | XOR != 0 |" << endl;
370 cout << " | Bash (k) | n % (k+1) != 0 |" << endl;
371 cout << " | 1,2,3 ๋ | n % 4 != 0 |" << endl;
372 cout << " | Wythoff | a != floor(phi*(b-a)) |" << endl;
373 cout << " | ์ผ๋ฐ ๊ฒ์ | Grundy != 0 |" << endl;
374
375 cout << "\n============================================================" << endl;
376
377 return 0;
378}