-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMinimax.cpp
160 lines (149 loc) · 5.37 KB
/
Minimax.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include<iostream>
#include"Minimax.h"
struct MinimaxParams
{
StrategicBoardGame* current_;
Turn AITurn_;
int maxLevel_, moveAccuracy_, chooseAccuracy_, alpha_, beta_, level_;
MinimaxParams(StrategicBoardGame* current, Turn AITurn = Turn::second, int maxLevel = 3,
int moveAccuracy = 100, int chooseAccuracy = 100, int alpha = -1000, int beta = 1000, int level = 0);
};
namespace
{
std::pair<StrategicBoardGame*, int> miniMax(MinimaxParams args)
{
// we have to store next moves
StrategicBoardGame** nextLevel = args.current_->getPossibleMove();
int nextLevelSize = 0;
// we will count size of nextLevel moves
for (nextLevelSize = 0; nextLevel[nextLevelSize] != nullptr; nextLevelSize++);
int* nextLevelScores = new int[nextLevelSize];
// best move will store index of best move in nextLevel and worst score just stores the worst score
int bestMove = 0, worstScore = args.beta_;
// just because we want nextLevelScores[bestMove] to be min, otherwize it wont calculate true maximum
// next level will be pruned at 0 (;
nextLevelScores[0] = args.alpha_;
// move accuracy will proune some nodes before we see them, looks likes we didnt noticed them
int chanceToProune = rand() % 100;
// we wrote the code like this you see blow because we want to if we set moveAcc for e.x. 60
// and maxDepth 8 , it will proune no node in first level , 5 percent nodes in second level ,
// 10 percent in third and so on
if (chanceToProune < 100 - ((100 - args.moveAccuracy_)*(args.level_ + 1) / args.maxLevel_))
{
for (int i = 0; i < nextLevelSize; i++)
{
// if we are at a leaf , we have to evaluate it
if (nextLevel[i]->checkEnd() || args.maxLevel_ == args.level_ + 1)
{
// evaluate the game
nextLevelScores[i] = -1 * nextLevel[i]->evaluate(Turn(int(args.AITurn_) * -1));
}
// otherwize we run minimax an other time
else
{
MinimaxParams nextLevelArgs(nextLevel[i], Turn(-1 * int(args.AITurn_)), args.maxLevel_,
args.moveAccuracy_, args.chooseAccuracy_, -1 * args.beta_,
-1 * nextLevelScores[bestMove], args.level_ + 1);
std::pair<StrategicBoardGame*, int> nextLevelMiniMax = miniMax(nextLevelArgs);
nextLevelScores[i] = -1 * nextLevelMiniMax.second;
if (nextLevelMiniMax.first != nextLevel[i])
{
delete nextLevelMiniMax.first;
nextLevelMiniMax.first = nullptr;
}
}
// determines best score
if (nextLevelScores[i] > nextLevelScores[bestMove])
{
bestMove = i;
}
// determines worst score
if (nextLevelScores[i] < worstScore)
{
worstScore = nextLevelScores[i];
}
// proune nodes if they are out of window
if (nextLevelScores[i] > args.beta_)
{
break;
}
}
if (args.chooseAccuracy_ != 100 && args.level_ == 0)
{
// we chose an score acording to best score and scores window and chooseAcc
float chosenScore = ((nextLevelScores[bestMove] - worstScore) * args.chooseAccuracy_) / 100.0;
// delta to fined best match to chosen score
float delta = abs((nextLevelScores[bestMove] - worstScore) - chosenScore);
for (int i = 0; i < nextLevelSize; i++)
{
float tempDelta = abs((nextLevelScores[i] - worstScore) - chosenScore);
if (delta > tempDelta)
{
bestMove = i;
delta = tempDelta;
}
}
}
}
else
{
// if we didnt see a node , we set its score to hiest possible score so in upper node it will
// be lowest possible score and it wont be chosen
nextLevelScores[bestMove] = args.beta_;
}
//hashes[level].push_back(pair<int, int>(gameHash, bestScore));
StrategicBoardGame* bestGame = nextLevel[bestMove];
for (int i = 0; i < nextLevelSize; i++)
{
if (i != bestMove)
{
delete nextLevel[i];
nextLevel[i] = nullptr;
}
}
int bestScore = nextLevelScores[bestMove];
delete[] nextLevelScores;
return std::pair<StrategicBoardGame*, int>(bestGame, bestScore);
}
}
MinimaxParams::MinimaxParams(StrategicBoardGame * current, Turn AITurn, int maxLevel, int moveAccuracy, int chooseAccuracy, int alpha, int beta, int level)
{
current_ = current;
AITurn_ = AITurn;
maxLevel_ = maxLevel;
moveAccuracy_ = moveAccuracy;
chooseAccuracy_ = chooseAccuracy;
alpha_ = alpha;
beta_ = beta;
level_ = level;
}
StrategicBoardGame* runMinimax(const Turn &turn,StrategicBoardGame* game,const int &intelligence,
const int &moveAccuracy, const int &chooseAccuracy, int &scot, const bool &negaMax)
{
std::pair<StrategicBoardGame*, int> minimax;// to store outcome of the minimax function
if (negaMax == true)
{
// so we will first run the minimax whit tight window
minimax = miniMax(MinimaxParams(game, turn, intelligence, moveAccuracy,
chooseAccuracy, scot - 1000, scot + 1000));
if (minimax.second > scot + 1000 || minimax.second < scot - 1000)
{
// outcome is out of our tight window. so negascot have failed
delete minimax.first;
// we have to run minimax whit a wide window
minimax = miniMax(MinimaxParams(game, turn, intelligence, moveAccuracy, chooseAccuracy));
}
scot = minimax.second;// set scot for next time
}
else {
// normal minimax
minimax = miniMax(MinimaxParams(game, turn, intelligence, moveAccuracy, chooseAccuracy));
}
if (minimax.second <= -1000 || minimax.second >= 1000)
{
// if we will lose whatever we do, so let fight. try not to lose in next put
delete minimax.first;
minimax = miniMax(MinimaxParams(game, turn, 2));
}
return minimax.first;
}