diff --git a/AdversarialSearch/Minimax/BitMath.h b/AdversarialSearch/Minimax/BitMath.h new file mode 100644 index 0000000..753649e --- /dev/null +++ b/AdversarialSearch/Minimax/BitMath.h @@ -0,0 +1,24 @@ +#include + +namespace BitMath +{ + int reverseBits(const int value, int maxBits) + { + int reversedBits = 0; + maxBits = (maxBits % 2 == 0) ? maxBits : maxBits - 1; + int halfBits = floor(maxBits / 2); + for (int bit = 0; bit < halfBits; bit++) + { + int transposeDifference = (maxBits - (bit * 2) - 1); + + int rBit = value & (1 << bit); + int lBit = rBit << transposeDifference; + reversedBits |= lBit; + + lBit = value & (1 << (maxBits - 1 - bit)); + rBit = lBit >> transposeDifference; + reversedBits |= rBit; + } + return reversedBits; + }; +}; diff --git a/AdversarialSearch/Minimax/Main.cpp b/AdversarialSearch/Minimax/Main.cpp new file mode 100644 index 0000000..40c7080 --- /dev/null +++ b/AdversarialSearch/Minimax/Main.cpp @@ -0,0 +1,21 @@ +#include "MinimaxBenchmarker.h" +#include "BitMath.h" +#include + +int main(int argc, char *argv[]) +{ + TicTacToeMinimax* minimax = new TicTacToeMinimax(); + MinimaxBenchmarker* benchmarker = new MinimaxBenchmarker(); + + if (argc > 1) + { + int boardState = atoi(argv[1]); + benchmarker -> benchmarkEvaluate(minimax, boardState, true); + } + else + { + benchmarker -> benchmarkMinimaxVsMinimax(minimax, 0, true); + } + + return 0; +}; diff --git a/AdversarialSearch/Minimax/Makefile b/AdversarialSearch/Minimax/Makefile new file mode 100644 index 0000000..4efa104 --- /dev/null +++ b/AdversarialSearch/Minimax/Makefile @@ -0,0 +1,8 @@ +build: + cl /EHsc *.cpp /link /out:Main.exe + +clean: + @IF EXIST "Main.exe" del "Main.exe" > NUL + @IF EXIST "Main.obj" del "Main.obj" > NUL + @IF EXIST "MinimaxBenchmarker.obj" del "MinimaxBenchmarker.obj" > NUL + @IF EXIST "TicTacToeMinimax.obj" del "TicTacToeMinimax.obj" > NUL \ No newline at end of file diff --git a/AdversarialSearch/Minimax/MinimaxBenchmarker.cpp b/AdversarialSearch/Minimax/MinimaxBenchmarker.cpp new file mode 100644 index 0000000..d0e5852 --- /dev/null +++ b/AdversarialSearch/Minimax/MinimaxBenchmarker.cpp @@ -0,0 +1,103 @@ +#include "MinimaxBenchmarker.h" + +void MinimaxBenchmarker::benchmarkMinimaxVsMinimax(TicTacToeMinimax* ticTacToeMinimax, int board, bool isMaxTurn) +{ + auto start = chrono::high_resolution_clock::now(); + int firstBoard = board; + + srand (time(NULL)); + + int currentBoard = firstBoard; + int nextBoard; + + int state = ticTacToeMinimax -> getState(currentBoard); + while (state == Playing) + { + vector bestMoves = ticTacToeMinimax -> evaluateAll(currentBoard, isMaxTurn); + isMaxTurn = !isMaxTurn; + + int randomIndex = rand() % bestMoves.size(); + currentBoard = bestMoves[randomIndex]; + + state = ticTacToeMinimax -> getState(currentBoard); + assert(state == Playing || state == Draw); + } + + nextBoard = currentBoard; + auto finish = chrono::high_resolution_clock::now(); + + printBoard(firstBoard); + printBoard(nextBoard); + printState(ticTacToeMinimax -> getState(nextBoard)); + + auto milliseconds = chrono::duration_cast(finish - start); + auto nanoseconds = chrono::duration_cast(finish - start); + cout << "benchmarkMinimaxVsMinimax: " << milliseconds.count() << "ms / " << nanoseconds.count() << "ns" << endl; + cout << endl << endl; +}; + +void MinimaxBenchmarker::benchmarkEvaluate(TicTacToeMinimax* ticTacToeMinimax, int board, bool isMaxTurn) +{ + auto start = chrono::high_resolution_clock::now(); + int nextBoard = ticTacToeMinimax -> evaluate(board, isMaxTurn); + auto finish = chrono::high_resolution_clock::now(); + auto milliseconds = chrono::duration_cast(finish - start); + auto nanoseconds = chrono::duration_cast(finish - start); + + printBoard(board); + printBoard(nextBoard); + cout << endl << endl; + + cout << "benchmarkEvaluate: " << milliseconds.count() << "ms / " << nanoseconds.count() << "ns" << endl; +}; + +void MinimaxBenchmarker::benchmarkEvaluateAll(TicTacToeMinimax* ticTacToeMinimax, int board, bool isMaxTurn) +{ + auto start = chrono::high_resolution_clock::now(); + vector bestBoards = ticTacToeMinimax -> evaluateAll(board, isMaxTurn); + auto finish = chrono::high_resolution_clock::now(); + + cout << "Found '" << bestBoards.size() << "' possibilities." << endl; + //printBoard(board); + for (int x = 0; x < bestBoards.size(); x++) + { + //printBoard(bestBoards[x]); + } + + auto milliseconds = chrono::duration_cast(finish - start); + auto nanoseconds = chrono::duration_cast(finish - start); + cout << "benchmarkEvaluateAll: " << milliseconds.count() << "ms / " << nanoseconds.count() << "ns" << endl; + cout << endl << endl; +}; + +void MinimaxBenchmarker::printBoard(int board) +{ + int crossMask = 3; + + cout << endl; + for (int x = 0; x < 9; x++) + { + if (x > 0 && x % 3 == 0) cout << endl; + + if ((board & crossMask) == 0) + { + cout << "[ ]"; + } + else + { + if ((board & crossMask) == crossMask) cout << "[X]"; + else cout << "[O]"; + } + + crossMask <<= 2; + } + cout << endl; +}; + +void MinimaxBenchmarker::printState(int state) +{ + if (state == Playing) cout << "Playing" << endl; + else if (state == Draw) cout << "Draw" << endl; + else if (state == CrossWins) cout << "CrossWins" << endl; + else cout << "CircleWins" << endl; +}; diff --git a/AdversarialSearch/Minimax/MinimaxBenchmarker.h b/AdversarialSearch/Minimax/MinimaxBenchmarker.h new file mode 100644 index 0000000..be8aa62 --- /dev/null +++ b/AdversarialSearch/Minimax/MinimaxBenchmarker.h @@ -0,0 +1,24 @@ +#ifndef _MINIMAX_BENCHMARKER_H +#define _MINIMAX_BENCHMARKER_H + +#include +#include +#include +#include +#include +#include +#include +#include "TicTacToeMinimax.h" + +using namespace std; + +class MinimaxBenchmarker +{ + public: + void benchmarkMinimaxVsMinimax(TicTacToeMinimax* ticTacToeMinimax, int board, bool isMaxTurn); + void benchmarkEvaluate(TicTacToeMinimax* ticTacToeMinimax, int board, bool isMaxTurn); + void benchmarkEvaluateAll(TicTacToeMinimax* ticTacToeMinimax, int board, bool isMaxTurn); + void printBoard(int board); + void printState(int state); +}; +#endif diff --git a/AdversarialSearch/Minimax/TicTacToeMinimax.cpp b/AdversarialSearch/Minimax/TicTacToeMinimax.cpp new file mode 100644 index 0000000..2756de5 --- /dev/null +++ b/AdversarialSearch/Minimax/TicTacToeMinimax.cpp @@ -0,0 +1,218 @@ +#include "TicTacToeMinimax.h" + +const int DrawBoardState = 0b101010101010101010; + +const int winnerMasks[8] = +{ + 0b101010000000000000, + 0b000000101010000000, + 0b000000000000101010, + 0b100000100000100000, + 0b001000001000001000, + 0b000010000010000010, + 0b100000001000000010, + 0b000010001000100000 +}; + +int TicTacToeMinimax::evaluate(const int board, const bool isMaxTurn) +{ + return isMaxTurn ? evaluateMaxDecision(board) + : evaluateMinDecision(board); +}; + +std::vector TicTacToeMinimax::evaluateAll(const int board, const bool isMaxTurn) +{ + return isMaxTurn ? evaluateMaxDecisions(board) + : evaluateMinDecisions(board); +}; + +std::vector TicTacToeMinimax::evaluateMaxDecisions(const int board) +{ + std::vector bestMoves; + float bestMoveScore = -999.0; + int mask = 3; + + for (int x = 0; x < 9; x++) + { + if ((board & mask) == 0) + { + const int newBoard = (board | mask); + float score = getMinTreeScore(newBoard, -999.0, 999.0, 1); + + if (score > bestMoveScore) + { + bestMoveScore = score; + bestMoves.clear(); + bestMoves.push_back(newBoard); + } + else if (score == bestMoveScore) + { + bestMoves.push_back(newBoard); + } + } + mask <<= 2; + } + return bestMoves; +}; + +std::vector TicTacToeMinimax::evaluateMinDecisions(const int board) +{ + std::vector bestMoves; + float bestMoveScore = 999.0; + int mask = 2; + + for (int x = 0; x < 9; x++) + { + if ((board & mask) == 0) + { + const int newBoard = (board | mask); + float score = getMaxTreeScore(newBoard, -999.0, 999.0, 1); + + if (score < bestMoveScore) + { + bestMoveScore = score; + bestMoves.clear(); + bestMoves.push_back(newBoard); + } + else if (score == bestMoveScore) + { + bestMoves.push_back(newBoard); + } + } + mask <<= 2; + } + return bestMoves; +}; + +int TicTacToeMinimax::evaluateMaxDecision(const int board) +{ + int bestMove = 0; + float bestMoveScore = -999.0; + int mask = 3; + + for (int x = 0; x < 9; x++) + { + if ((board & mask) == 0) + { + float score = getMinTreeScore(board | mask, -999.0, 999.0, 1); + + if (score > bestMoveScore) + { + bestMoveScore = score; + bestMove = (board | mask); + } + } + mask <<= 2; + } + return bestMove; +}; + +int TicTacToeMinimax::evaluateMinDecision(const int board) +{ + int bestMove = 0; + float bestMoveScore = 999.0; + int mask = 2; + + for (int x = 0; x < 9; x++) + { + if ((board & mask) == 0) + { + float score = getMaxTreeScore(board | mask, -999.0, 999.0, 1); + if (score < bestMoveScore) + { + bestMoveScore = score; + bestMove = (board | mask); + } + } + mask <<= 2; + } + return bestMove; +}; + +float TicTacToeMinimax::getMaxTreeScore(const int board, float alpha, float beta, int depth) +{ + int state = getState(board); + if (state != Playing) + { + return ((float) getScore(state) / depth); + } + + float bestScore = -999.0; + int mask = 3; + + for (int x = 0; x < 9; x++) + { + if ((board & mask) == 0) + { + float score = getMinTreeScore(board | mask, alpha, beta, depth + 1); + if (score >= beta) + return score; + if (score > alpha) + alpha = score; + if (score > bestScore) + bestScore = score; + } + mask <<= 2; + } + return bestScore; +}; + +float TicTacToeMinimax::getMinTreeScore(const int board, float alpha, float beta, int depth) +{ + int state = getState(board); + if (state != Playing) + { + return ((float) getScore(state) / depth); + } + + float bestScore = 999.0; + int mask = 2; + + for (int x = 0; x < 9; x++) + { + if ((board & mask) == 0) + { + float score = getMaxTreeScore(board | mask, alpha, beta, depth + 1); + if (score <= alpha) + return score; + if (score < beta) + beta = score; + if (score < bestScore) + bestScore = score; + } + mask <<= 2; + } + return bestScore; +}; + +int TicTacToeMinimax::getState(const int board) +{ + for(int x = 0; x < 8; x++) + { + // check if the board represent a winner state + int winnerMask = winnerMasks[x]; + if ((winnerMask & board) != winnerMask) + continue; + + // check if all pieces are the same + winnerMask = winnerMask >> 1; + int piecesXor = (winnerMask ^ board) & winnerMask; + + if (piecesXor == 0) + return CrossWins; + if (piecesXor == winnerMask) + return CircleWins; + } + if ((board & DrawBoardState) == DrawBoardState) + return Draw; + return Playing; +}; + +int TicTacToeMinimax::getScore(const int state) +{ + if (state == CrossWins) + return 1; + if (state == CircleWins) + return -1; + return 0; +}; diff --git a/AdversarialSearch/Minimax/TicTacToeMinimax.h b/AdversarialSearch/Minimax/TicTacToeMinimax.h new file mode 100644 index 0000000..dd4700b --- /dev/null +++ b/AdversarialSearch/Minimax/TicTacToeMinimax.h @@ -0,0 +1,39 @@ +#ifndef _TIC_TAC_TOE_MINIMAX_H +#define _TIC_TAC_TOE_MINIMAX_H + +#include +#include +#include +#include + +enum ETicTacToeState +{ + Playing, + Draw, + CrossWins, + CircleWins +}; + +enum ETurnOwner +{ + Cross, + Circle +}; + +class TicTacToeMinimax +{ + friend class MinimaxBenchmarker; + private: + float getMaxTreeScore(const int board, float alpha, float beta, int depth); + float getMinTreeScore(const int board, float alpha, float beta, int depth); + int evaluateMaxDecision(const int board); + int evaluateMinDecision(const int board); + std::vector evaluateMaxDecisions(const int board); + std::vector evaluateMinDecisions(const int board); + int getScore(const int state); + int getState(const int board); + public: + int evaluate(const int board, const bool isMaxTurn); + std::vector evaluateAll(const int board, const bool isMaxTurn); +}; +#endif diff --git a/AdversarialSearch/Minimax/vsvarsall.bat b/AdversarialSearch/Minimax/vsvarsall.bat new file mode 100644 index 0000000..c1d5e66 --- /dev/null +++ b/AdversarialSearch/Minimax/vsvarsall.bat @@ -0,0 +1 @@ +cmd /K "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall" x64 \ No newline at end of file diff --git a/Strategy/Main.cpp b/Strategy/Main.cpp new file mode 100644 index 0000000..597393c --- /dev/null +++ b/Strategy/Main.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct Decision +{ + string Id; + int Probability; + + Decision() + { + // + }; + + Decision(string id, int probability) + { + Id = id; + Probability = probability; + }; +}; + +//Decision getProbability(Decision* probabilities, int size, int probability); + +int main() +{ + Decision decisions[3]; + decisions[0] = Decision("ID_0", 25); + decisions[1] = Decision("ID_1", 50); + decisions[2] = Decision("ID_2", 25); + + int maxProbability = 0; + for (int i = 0; i < 3; i++) + { + maxProbability += decisions[i].Probability; + } + + std::mt19937 gen(time(NULL)); + std::uniform_int_distribution<> dis(0, maxProbability); + int finalProbability = dis(gen); + Decision finalDecision; + + int finalProbabilityCountdown = finalProbability; + + for (int i = 0; i < 3; i++) + { + finalProbabilityCountdown -= decisions[i].Probability; + if (finalProbabilityCountdown <= 0) + { + cout << finalProbability << " - " << decisions[i].Probability << endl; + break; + } + } + return 0; +};