I have a bunch of maths manipulations that have thresholds, yet no matter what I change, the if statements always return true. No compile errors, can't get the debugger to work. This is a function, the X Y and Z arrays are all correct (I printed them to check earlier), the maths is right at least for the blade distance check, yet that always returns true. I ran the same code (rewritten obviously) through matlab and that returns true or false depending on my data, so clearly its something with the way I've written this that's wrong. Also is there any way to slimline this?
bool Device::_SafeD(char _Type, float _Data[20][3]) {
bool S;
double x[20], y[20], z[20];
for (int i=0; i<20; i++) {
x[i] = _Data[i][0];
y[i] = _Data[i][1];
z[i] = _Data[i][2];
}
// Check angles for needle
if (_Type == 'n') {
for (int i=0; i<20; i++) {
float dot, moda, modb, c, angle;
dot = ((x[i]*x[i+1]) + (y[i]*y[i+1]) + (z[i]*z[i+1]));
moda = sqrt(pow(x[i],2)+pow(y[i],2)+pow(z[i],2));
modb = sqrt(pow(x[i+1],2)+(y[i+1],2)+(z[i+1],2));
c = dot/(moda*modb);
angle = acos(c);
if (angle > 45){
S = 0;
} else {
S = 1;
}
}
}
// Check distance for blade
if (_Type == 'b'){
for (int i=0; i<19; i++) {
float distance = (x[i+1]-x[i]) + (y[i+1]-y[i]) + (z[i+1]-z[i]);
cout << "distance " << distance << endl;
if (distance > 5.0) {
S = 0;
} else {
S = 1;
}
}
}
if (S == 0) {
return 0;
}
if(S == 1) {
return 1;
}
}
Cheers
The most likely error is that you are comparing angle to an angle in degree while the return value of acos is in radians.
if (angle > 45){
Convert the angle to degrees before comparing to 45 and you should be OK.
if (radians_to_degrees(angle) > 45){
where
double radians_to_degrees(double in)
{
return (in*180/M_PI);
}
Another option is to compute the equivalent of 45 degrees in radians and compare it with angle.
double const radian_45 = M_PI*45.0/180;
and use
if (angle > radian_45){
The other error is spelled out clearly in a comment by #Bob__:
OP uses two loops to check the angle and the distance, inside those loops a flag (S) is set to 0 or 1, but it's done for every index in the array, so it's overwritten. The return value of the entire function (in the provided code) depends only by the last two elements in the array.
Related
I have a code that is supposed to reduce a variable, by one in each iteration, in a loop. The problem is that it does not, it remains the same. Here is the code I have:
bool IsRed(int speed, int distance, int time)
{
if ((18 * distance) % (10 * speed * time) >= (5 * distance * time)) {return true;}
return false;
}
std::vector<std::pair<int, int> > DistanceTime = {{ 300,10 }, { 1500,10 }, { 3000,10 }};
int maxSpeed = 90 * 0.277778;
int traficLights = 3;
for (int i = 0; i < traficLights; i++)
{
for (auto j = DistanceTime.begin(); j != DistanceTime.end(); ++j)
{
int distance = j->first;
int time = j->second;
if (IsRed(maxSpeed, distance, time) == true)
{
maxSpeed--; //should get reduced
i = 0;
j = DistanceTime.begin();
}
//debug line
std::cout << maxSpeed * 3.6 << std::endl; //didn't change
}
}
The variable maxSpeed should get reduced every time the IsRed() function returns "true". Then the first and second for loop should re-initialize and test the new reduced speed again. At the end, the maxSpeed that returns "false", for all the pairs of the vector, in the IsRed() function, should be printed (did not do it in the code above).
Have I messed up the for loops? Any help would be very welcome!
In order for any code within:
if (IsRed(maxSpeed, distance, time) == true)
{
//stuff
}
to be executed, your function IsRed(...) needs to return true, and with the inputs you provided it doesn't.
you also don't need to specify == true in your if statement you can simply write:
if (IsRed(maxSpeed, distance, time))
{
//stuff
}
because of the return type of the function IsRed.
Pardon me if this question already exists, I've searched a lot but I haven't gotten the answer to the question I want to ask. So, basically, I'm trying to implement a Tic-Tac-Toe AI that uses the Minimax algorithm to make moves.
However, one thing I don't get is, that when Minimax is used on an empty board, the value returned is always 0 (which makes sense because the game always ends in a draw if both players play optimally).
So Minimax always chooses the first tile as the best move when AI is X (since all moves return 0 as value). Same happens for the second move and it always chooses the second tile instead. How can I fix this problem to make my AI pick the move with the higher probability of winning? Here is the evaluation and Minimax function I use (with Alpha-Beta pruning):
int evaluate(char board[3][3], char AI)
{
for (int row = 0; row<3; row++)
{
if (board[row][0] != '_' && board[row][0] == board[row][1] && board[row][1] == board[row][2])
{
if (board[row][0]==AI)
{
return +10;
}
else
{
return -10;
}
}
}
for (int col = 0; col<3; col++)
{
if (board[0][col] != '_' && board[0][col] == board[1][col] && board[1][col] == board[2][col])
{
if (board[0][col]==AI)
{
return +10;
}
else
{
return -10;
}
}
}
if (board[1][1] != '_' && ((board[0][0]==board[1][1] && board[1][1]==board[2][2]) || (board[0][2]==board[1][1] && board[1][1]==board[2][0])))
{
if (board[1][1]==AI)
{
return +10;
}
else
{
return -10;
}
}
return 0;
}
int Minimax(char board[3][3], bool AITurn, char AI, char Player, int depth, int alpha, int beta)
{
bool breakout = false;
int score = evaluate(board, AI);
if(score == 10)
{
return score - depth;
}
else if(score == -10)
{
return score + depth;
}
else if(NoTilesEmpty(board))
{
return 0;
}
if(AITurn == true)
{
int bestvalue = -1024;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j<3; j++)
{
if(board[i][j] == '_')
{
board[i][j] = AI;
bestvalue = max(bestvalue, Minimax(board, false, AI, Player, depth+1, alpha, beta));
alpha = max(bestvalue, alpha);
board[i][j] = '_';
if(beta <= alpha)
{
breakout = true;
break;
}
}
}
if(breakout == true)
{
break;
}
}
return bestvalue;
}
else if(AITurn == false)
{
int bestvalue = +1024;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j<3; j++)
{
if(board[i][j] == '_')
{
board[i][j] = Player;
bestvalue = min(bestvalue, Minimax(board, true, AI, Player, depth+1, alpha, beta));
beta = min(bestvalue, beta);
board[i][j] = '_';
if(beta <= alpha)
{
breakout = true;
break;
}
}
}
if(breakout == true)
{
break;
}
}
return bestvalue;
}
}
Minimax assumes optimal play, so maximizing "probability of winning" is not a meaningful notion: Since the other player can force a draw but cannot force a win, they will always force a draw. If you want to play optimally against a player who is not perfectly rational (which, of course, is one of the only two ways to win*), you'll need to assume some probability distribution over the opponent's moves and use something like ExpectMinimax, where with some probability the opponent's move is overridden by a random mistake. Alternatively, you can deliberately restrict the ply of the minimax search, using a heuristic for the opponent's play beyond a certain depth (but still searching the game tree for your own moves.)
* The other one is not to play.
Organize your code into smaller routines so that it looks tidier and easier to debug. Apart from the recursive minimax function, an all-possible-valid-move generation function and a robust evaluation sub-routine are essential ( which seems lacking here).
For example, at the beginning of the game, the evaluation algorithm should return a non-zero score, every position should have a relative scoring index ( eg middle position may have slightly higher weightage than the corners).
Your minimax boundary condition - return if there is no empty cell positions ; is flawed as it will evaluate even when a winning/losing move occurred in the preceding ply. Such conditions will aggravate in more complex AI games.
If you are new to minimax, you can find plenty of ready to compile sample codes on CodeReview
I'm trying to implement NegaMax ai for Connect 4. The algorithm works well some of the time, and the ai can win. However, sometimes it completely fails to block opponent 3 in a rows, or doesn't take a winning shot when it has three in a row.
The evaluation function iterates through the grid (horizontally, vertically, diagonally up, diagonally down), and takes every set of four squares. It then checks within each of these sets and evaluates based on this.
I've based the function on the evaluation code provided here: http://blogs.skicelab.com/maurizio/connect-four.html
My function is as follows:
//All sets of four tiles are evaluated before this
//and values for the following variables are set.
if (redFoursInARow != 0)
{
redScore = INT_MAX;
}
else
{
redScore = (redThreesInARow * threeWeight) + (redTwosInARow * twoWeight);
}
int yellowScore = 0;
if (yellowFoursInARow != 0)
{
yellowScore = INT_MAX;
}
else
{
yellowScore = (yellowThreesInARow * threeWeight) + (yellowTwosInARow * twoWeight);
}
int finalScore = yellowScore - redScore;
return turn ? finalScore : -finalScore; //If this is an ai turn, return finalScore. Else return -finalScore.
My negamax function looks like this:
inline int NegaMax(char g[6][7], int depth, int &bestMove, int row, int col, bool aiTurn)
{
{
char c = CheckForWinner(g);
if ('E' != c || 0 == depth)
{
return EvaluatePosition(g, aiTurn);
}
}
int bestScore = INT_MIN;
for (int i = 0; i < 7; ++i)
{
if (CanMakeMove(g, i)) //If column i is not full...
{
{
//...then make a move in that column.
//Grid is a 2d char array.
//'E' = empty tile, 'Y' = yellow, 'R' = red.
char newPos[6][7];
memcpy(newPos, g, sizeof(char) * 6 * 7);
int newRow = GetNextEmptyInCol(g, i);
if (aiTurn)
{
UpdateGrid(newPos, i, 'Y');
}
else
{
UpdateGrid(newPos, i, 'R');
}
int newScore = 0; int newMove = 0;
newScore = NegaMax(newPos, depth - 1, newMove, newRow, i, !aiTurn);
newScore = -newScore;
if (newScore > bestScore)
{
bestMove = i;
bestScore = newScore;
}
}
}
}
return bestScore;
}
I'm aware that connect four has been solved are that there are definitely better ways to go about this, but any help or suggestions with fixing/improving this will be greatly appreciated. Thanks!
I have 2 vectors, one (vector1 of structs (Point)) is filled with X amount of points and another (vector2 of structs (PrimeTemplate)) is filled with Y amount of points. I want to find all values below a threshold and I feel like my code just doesn't do that. For now I'll just ignore if one point maps to more than 1 other. What am I missing? I only generate a few points and I know I should be getting more.
struct Template{
int tempX;
int tempY;
};
struct PrimeTemplate{
double tempX;
double tempY;
};
int matches = 0;
for (int outerLoop = 0; outerLoop < vector1 .size(); outerLoop++)
{
for (int innerLoop = 0; innerLoop < vector2.size(); innerLoop++)
{
double tempEuclidianX = std::pow(abs(vector1 [outerLoop].tempX - vector2[innerLoop].tempX), 2.0);
double tempEuclidianY = std::pow(abs(vector1 [outerLoop].tempY - vector2[innerLoop].tempY), 2.0);
double Euclidian = sqrt(tempEuclidianX + tempEuclidianY);
if (Euclidian <= 5) //less than threshold
{
matches++;
}
}
}
Sample input from a file would look like this (two different files, random numbers) (no worries about getting data, it's all there)
245 21
452 54
124 68
485 78
111 29
97 75
78 113
300 124
411 101
What is wrong with your code is that you use abs() before squaring.
It isn't necessary to take the absolute value at all before squaring, of course, but if you are going to then you want to use fabs, as just abs takes and returns an integer. This extra rounding off might be why are not getting the right answer.
This was the method I used for calculating the shortest distances between a pair. It loops through a text file and loads up the vectors you see. Turned out the issue with the points was in my implementation before this code which was some the normalization of Biometric points.
for (int outerLoop = 0; outerLoop < Tvector.size(); outerLoop++)
{
for (int innerLoop = 0; innerLoop < QPrimeVector.size(); innerLoop++)
{
double tempEuclidianX = std::pow((QPrimeVector[innerLoop].tempX - Tvector[outerLoop].tempX), 2.0);
double tempEuclidianY = std::pow((QPrimeVector[innerLoop].tempY - Tvector[outerLoop].tempY), 2.0);
double Euclidian = sqrt(tempEuclidianX + tempEuclidianY);
if (Euclidian <= THRESHOLD) //less than threshold and not taken already
{
if (Euclidian < minEuclidian)
{
minEuclidian = Euclidian;
if (!Tvector[outerLoop].marked)
{
matched = innerLoop;
}
}
}
if (matched != -1)
{
matches++;
}
matched = -1;
minEuclidian = 10;
}
if (matches > masterMatchCount)
{
masterMatchCount = matches;
deltaThetaMaster = deltaTheta;
deltaXMaster = deltaX;
deltaYMaster = deltaY;
}
}
for (int reset = 0; reset < Tvector.size(); reset++)
{
Tvector[reset].marked = false; //reset all the matches
}
QPrimeVector.clear();
}
Edit: to clarify, the problem is with the second algorithm.
I have a bit of C++ code that samples cards from a 52 card deck, which works just fine:
void sample_allcards(int table[5], int holes[], int players) {
int temp[5 + 2 * players];
bool try_again;
int c, n, i;
for (i = 0; i < 5 + 2 * players; i++) {
try_again = true;
while (try_again == true) {
try_again = false;
c = fast_rand52();
// reject collisions
for (n = 0; n < i + 1; n++) {
try_again = (temp[n] == c) || try_again;
}
temp[i] = c;
}
}
copy_cards(table, temp, 5);
copy_cards(holes, temp + 5, 2 * players);
}
I am implementing code to sample the hole cards according to a known distribution (stored as a 2d table). My code for this looks like:
void sample_allcards_weighted(double weights[][HOLE_CARDS], int table[5], int holes[], int players) {
// weights are distribution over hole cards
int temp[5 + 2 * players];
int n, i;
// table cards
for (i = 0; i < 5; i++) {
bool try_again = true;
while (try_again == true) {
try_again = false;
int c = fast_rand52();
// reject collisions
for (n = 0; n < i + 1; n++) {
try_again = (temp[n] == c) || try_again;
}
temp[i] = c;
}
}
for (int player = 0; player < players; player++) {
// hole cards according to distribution
i = 5 + 2 * player;
bool try_again = true;
while (try_again == true) {
try_again = false;
// weighted-sample c1 and c2 at once
// h is a number < 1325
int h = weighted_randi(&weights[player][0], HOLE_CARDS);
// i2h uses h and sets temp[i] to the 2 cards implied by h
i2h(&temp[i], h);
// reject collisions
for (n = 0; n < i; n++) {
try_again = (temp[n] == temp[i]) || (temp[n] == temp[i+1]) || try_again;
}
}
}
copy_cards(table, temp, 5);
copy_cards(holes, temp + 5, 2 * players);
}
My problem? The weighted sampling algorithm is a factor of 10 slower. Speed is very important for my application.
Is there a way to improve the speed of my algorithm to something more reasonable? Am I doing something wrong in my implementation?
Thanks.
edit: I was asked about this function, which I should have posted, since it is key
inline int weighted_randi(double *w, int num_choices) {
double r = fast_randd();
double threshold = 0;
int n;
for (n = 0; n < num_choices; n++) {
threshold += *w;
if (r <= threshold) return n;
w++;
}
// shouldn't get this far
cerr << n << "\t" << threshold << "\t" << r << endl;
assert(n < num_choices);
return -1;
}
...and i2h() is basically just an array lookup.
Your reject collisions are turning an O(n) algorithm into (I think) an O(n^2) operation.
There are two ways to select cards from a deck: shuffle and pop, or pick sets until the elements of the set are unique; you are doing the latter which requires a considerable amount of backtracking.
I didn't look at the details of the code, just a quick scan.
you could gain some speed by replacing the all the loops that check if a card is taken with a bit mask, eg for a pool of 52 cards, we prevent collisions like so:
DWORD dwMask[2] = {0}; //64 bits
//...
int nCard;
while(true)
{
nCard = rand_52();
if(!(dwMask[nCard >> 5] & 1 << (nCard & 31)))
{
dwMask[nCard >> 5] |= 1 << (nCard & 31);
break;
}
}
//...
My guess would be the memcpy(1326*sizeof(double)) within the retry-loop. It doesn't seem to change, so should it be copied each time?
Rather than tell you what the problem is, let me suggest how you can find it. Either 1) single-step it in the IDE, or 2) randomly halt it to see what it's doing.
That said, sampling by rejection, as you are doing, can take an unreasonably long time if you are rejecting most samples.
Your inner "try_again" for loop should stop as soon as it sets try_again to true - there's no point in doing more work after you know you need to try again.
for (n = 0; n < i && !try_again; n++) {
try_again = (temp[n] == temp[i]) || (temp[n] == temp[i+1]);
}
Answering the second question about picking from a weighted set also has an algorithmic replacement that should be less time complex. This is based on the principle of that which is pre-computed does not need to be re-computed.
In an ordinary selection, you have an integral number of bins which makes picking a bin an O(1) operation. Your weighted_randi function has bins of real length, thus selection in your current version operates in O(n) time. Since you don't say (but do imply) that the vector of weights w is constant, I'll assume that it is.
You aren't interested in the width of the bins, per se, you are interested in the locations of their edges that you re-compute on every call to weighted_randi using the variable threshold. If the constancy of w is true, pre-computing a list of edges (that is, the value of threshold for all *w) is your O(n) step which need only be done once. If you put the results in a (naturally) ordered list, a binary search on all future calls yields an O(log n) time complexity with an increase in space needed of only sizeof w / sizeof w[0].