Why my minimax is not working - c++

I already have tried to modify my algorithm to work better, but I haven't achieved any result. My problem is that after the first moves, if I have, for example:
XX.
OO.
...
The Computer, instead of choosing 0 2, choses for example 1 2 and sometimes tries to go for position it can't.
My code:
#include "game.hpp"
pair<int,int> winner;
int m = INT_MAX;
pair<int,int> game::minimax(State ini) {
int v = maxValue(ini);
cout << v << endl;
return winner;
}
int game::maxValue(State u){
int check = u.getUtility();
if( check % 700 == 0 ) {
if( u.moves < m ) {
winner = u.move;
m = u.moves;
}
return check;
}
int v = INT_MIN;
u.makeDescendents();
while( !u.ls.empty() ) {
v = max(v,minValue(u.ls.front()));
u.ls.pop_front();
}
return v;
}
int game::minValue(State u) {
int check = u.getUtility();
if( check % 700 == 0 )
return check;
int v = INT_MAX;
u.makeDescendents();
while( !u.ls.empty() ) {
v = min(v,maxValue(u.ls.front()));
u.ls.pop_front();
}
return v;
}
For you can help me better I will make clear the meaning of some variables:
winner: is the position the computer will move
u.moves: is the depth on the search tree , for root is 0
m: supposed to save the less depth state solution , for that way filter solutions and computer must play the move more close of solution.
check: save utility value at this moment for known if is a terminal state
utility for win is 700 for tie is 0 and for defeat is -700
u.ls: list of children states
Something else , I think use m and winner global and return a global on minimax is a poor solution , do you can see some way to improve this?
Thanks very much.

First things first, what does u.getUtility() return if the state is not terminal? If it returns 0, well then 0 % 700 == 0 is true, so it's just finding the first move it expanded and selecting it. Since I can't see the u.makeDescendents() algorithm, I can't rule this out.
If that's not the case, then almost certainly your u.getUtility() function is making the assumption that it is only ever being called for the same max player. i.e. It's returning 700 if X wins and -700 if X loses. If you're running both sides through the same minimax, then when you evaluate O as max, it's still trying to find a win for X because that's the only time it will see the evaluation as a win.
If this is the case, the fix is simple, determine which player's turn it is from the state and return the win/loss evaluation as if it were that player (which is typically always a loss in TicTacToe because you cannot make a move which loses you the game, you can only win by making a move and the previous player made the last move).
If neither of these suggestions solve the problem, the typical way to debug minimax problems is to step through the game tree one level deep at a time, exploring the path that is returning known invalid evaluations until you find the point at which is generates an incorrect value. Then you have to inspect it as to find out why. This is trivial for small games like tic tac toe, because it only goes 9 levels deep and you can get a perfect minimax value, but for any non trivial game, you generally have to look at your evaluation function to determine where the discrepancy is occurring.

Related

Why does sorting call the comparison function less often than a linear minimum search algorithm?

I'll start by giving some context. I'm learning to write a raytracer, a very simple one. I don't have any acceleration structures yet, so the code in question is intended to find the closest object that the ray hits. Since I'm learning yet, I'd greatly appreciate if the answers concentrated on the seemingly strange problem that I'm observing - I know the RT logic is very wrong as it is right now. It produces correct results, anyway.
1. The first approach: for every hit, add a hit-result structure object to the list, then apply std::sort with a predicate that compares the distance form the hit point to the ray origin. Should be O(N log N) according to the textbook, and I think it is suboptimal, since I only need the first result, not the whole sorted list.
2. The second approach: whenever there is a hit, take the distance and compare it to the minimum, which is first initialized to std::numeric_limits<float>::max(). Well, your standard "find min in the array" algorithm. Should be O(N) and thus faster.
These pieces of code reside in a recursive function. Tested on the very same scene of 10 spheres, 1 is faster by an order of magnitude. The amount of calls to the distance function is a few times less than in 2. What am I missing?
I'm not sure if the context is required, in case there are "branches to be cut" off this question, tell me.
Code piece 1:
result rt_function(...) {
static int count{};
std::vector<result> hitList;
for(const auto& obj : objList) {
const result res = obj->testOuter(ray);
if ( res.hit ) {
hitList.push_back(res);
}
}
if (!hitList.empty()) {
sort(hitList.begin(), hitList.end(), [=](result& hit1, result& hit2) -> bool {
std::cerr << ++count << '\n';
return cv::norm(hit1.point - ray.origin) <
cv::norm(hit2.point - ray.origin);
});
const result res = hitList.front();
const SceneObject* near = res.obj;
// the raytracing continues...
count == 180771
Code piece 2:
result rt_function(...) {
static int count{};
float min_distance = std::numeric_limits<float>::max(), distance{};
result closest_res{}; bool have_hit{};
for(const auto& obj : objList) {
const result res = obj->testOuter(ray);
if ( res.hit ) {
have_hit = true;
std::cerr << ++count << '\n';
distance = cv::norm(res.point - ray.origin);
if (distance < min_distance) {
min_distance = distance; closest_res = res;
}
}
}
if (have_hit) {
const result res = closest_res;
const SceneObject* near = res.obj;
// the raytracing continues...
count == 349633
I want to (a) understand why there are less comparisons and (b) where the bottleneck is, since the run time is significantly higher, as I've noted above.
Statements like O(N²) are like a dimension; double the number of points and time taken quadruples. An O(log N) algorithm can be slow for small N , the point being if N doubles or is increased by a factor of 10 running time doesn't.
Compare with finding a specific word in a 1000 page dictionary and one in a 20 word sentence. Sorting a 20 word sentence before finding a specific word takes longer than reading it straight through once.

knights tour in c++ using recursion

I have created a class Board which deals with 2d vectors specifically for this purpose. I am trying to solve the Knight's Tour. I want to print out the thing when it is done. Using the recursive voyagingKnight() function I find that it does not do anything, does not print the result. It seems that I would want to increment the step number for the recursive call but this is not working.
The vector argument incs is a 2d vector of increments for moving the knight, in each row a row move in the first colum and a column move in the second column.
Does anyone have any suggestions as to a flaw in my reasoning here?
The relevant code
bool voyaging_knight( Board &board, int i, int j, int steps ,vector< vector<int> > &increments)
{
if( !newplace(theboard, i, j) ) return false;
board.setval(i,j,step);
if( gone_everywhere( board, steps) )
{
cout <<"DONE" << endl;
board.showgrid();
return true;
}
int n;
int in, jn;
for(n=0; n<8; n++ )
{
in = i + increments[n][0];
jn = j + increments[n][1];
if( inboard(board, i, j)&& newplace(board,i,j) )
{
voyaging_knight( board, in, jn, steps+1 ,increments);
return true;
}
}
theboard.setval(i,j,-1);
}
Yes, change this:
voyagingKnight( theboard, inext, jnext, step+1 ,incs);
return true;
To this:
return voyagingKnight( theboard, inext, jnext, step+1 ,incs);
In addition, it seems that you need to return something (probably false) at the end of the function.
BTW, I'm assuming that you have all the entries in theboard initialized to -1.
I'm guessing that you want 1 continuous path made by horse movements on a (chess)-board found by backtracking. In that case you have to pass the board by value, so each path you take has its own instance to fill. By passing by reference, every path fills the same board, so you can never take all the steps.
Also you should pass a result by value and fill it with the positions you visited and return that from the recursive function, so each path has its own instance of resulting positions and by returning it, you end up with the final result.
You should not pass inc because that is just a helper container that doesn't change.
Make the board a global variable, and build up a sequence of visited squares in a global variable too. Make sure that when retracting each tentative step you undo any changes (square visited, last step of sequence). Call your knight's tour function, make it return success if it reaches the end, and do any output after finishing.
Package the whole shebang in a file or as a class, so as to not expose private details to prying eyes.

Possible segmentation fault: Am I using the "this->" operator correctly?

I am doing a homework problem that I have a question about. If you don't feel comfortable assisting with a homework problem, I should say that my instructor has encouraged us to ask for help on this site when we are completely stumped. Also, I have completed the basic portion of the assignment on my own, and am now doing an optional challenge problem. Anyway, on to the problem!
Being new to OOP and C++ in general, I am having trouble understanding the "this->" operator. We haven't covered it in class, but I have seen it elsewhere and I am sort-of guessing how it is meant to be used.
For the assignment, I have to create a console based Tic-Tac-Toe game. Only the challenge portion of the assignment wants us to create an AI opponent, and we don't get any extra credit for doing the challenge, I just want to know how to do it. I am studying things like minimax and game trees, but for now I just wanted to create a "pick a random, open spot" function.
I have a class called TicTacToe which is basically the entire program. I will post it below with the parts that are relevant to the question, but part that is giving me an error is this subroutine:
void TicTacToe::makeAutoMove(){
srand(time(NULL));
int row = rand() % 3 + 1;
int col = rand() % 3 + 1;
if(this->isValidMove(row, col)){
this->makeMove(row, col);
}else{
this->makeAutoMove();
}
}
The only thing that this function is meant to do is make a move on the board, assuming that it is open. The board is set up like:
char board[4][4];
and when I print it, it looks like:
1 2 3
1 - - -
2 - - -
3 - - -
The problem, is that on occasion a move is made by the computer that gives me an error that is difficult to track down because of the random nature of the function. I believe it is a segfault error, but I can't tell because I can't replicate it in my debugger.
I think that the "this->" operator functions as a pointer, and if a pointer is NULL and it is accessed it could give me this problem. Is this correct? Is there a way to fix this?
I understand that this may be a very low-level question to many of the members of the community, but I would appreciate your help as long as it doesn't come with snide remarks about how trivial this is, or how stupid I must be. I'm LEARNING, which means that I am going to have some silly questions sometimes.
Here is more of my .cpp file if it helps:
TicTacToe::TicTacToe()
{
for(int row = 0; row < kNumRows; row++){
for(int col = 0; col < kNumCols; col++){
if(col == 0 && row == 0){
board[row][col] = ' ';
}else if(col == 0){
board[row][col] = static_cast<char>('0' + row);
}else if(row == 0){
board[row][col] = static_cast<char>('0' + col);
}else{
board[row][col] = '-';
}
}
}
currentPlayer = 'X';
}
char TicTacToe::getCurrentPlayer(){
return currentPlayer;
}
char TicTacToe::getWinner(){
//Check for diagonals (Only the middle square can do this)
char middle = board[2][2];
if(board[1][1] == middle && board[3][3] == middle && middle != '-'){
return middle;
}else if(middle == board[3][1] && middle == board[1][3] && middle != '-'){
return middle;
}
//Check for horizontal wins
for(int row = 1; row < kNumRows; row++){
if(board[row][1] == board[row][2] && board[row][2] == board[row][3] && board[row][1] != '-'){
return board[row][1];
}
}
//Check for vertical wins
for(int col = 1; col < kNumCols; col++){
if(board[1][col] == board[2][col] && board[2][col] == board[3][col] && board[1][col] != '-'){
return board[1][col];
}
}
//Otherwise, in the case of a tie game, return a dash.
return '-';
}
void TicTacToe::makeMove(int row, int col){
board[row][col] = currentPlayer;
if(currentPlayer == 'X'){
currentPlayer = 'O';
}else if(currentPlayer == 'O'){
currentPlayer = 'X';
}
}
//TODO: Make sure this works after you make the make-move function
bool TicTacToe::isDone(){
bool fullBoard = true;
//First check to see if the board is full
for(int col = 1; col < kNumCols; col++){
for(int row = 1; row < kNumRows; row++){
if(board[row][col] == '-'){
fullBoard = false;
}
}
}
//If the board is full, the game is done. Otherwise check for consecutives.
if(fullBoard){
return true;
}else{
//Check for diagonals (Only the middle square can do this)
char middle = board[2][2];
if(board[1][1] == middle && board[3][3] == middle && middle != '-'){
return true;
}else if(middle == board[3][1] && middle == board[1][3] && middle != '-'){
return true;
}
//Check for horizontal wins
for(int row = 1; row < kNumRows; row++){
if(board[row][1] == board[row][2] && board[row][2] == board[row][3] && board[row][1] != '-'){
return true;
}
}
//Check for vertical wins
for(int col = 1; col < kNumCols; col++){
if(board[1][col] == board[2][col] && board[2][col] == board[3][col] && board[1][col] != '-'){
return true;
}
}
}
//If all other tests fail, then the game is not done
return false;
}
bool TicTacToe::isValidMove(int row, int col){
if(board[row][col] == '-' && row <= 3 && col <= 3){
return true;
}else{
//cout << "That is an invalid move" << endl;
return false;
}
}
void TicTacToe::print(){
for(int row = 0; row < kNumRows; row++){
for(int col = 0; col < kNumCols; col++){
cout << setw(3) << board[row][col];
}
cout << endl;
}
}
A general preface: you almost never need to use this explicitly. In a member function, in order to refer to member variables or member methods, you simply name the variable or method. As with:
class Foo
{
int mN;
public:
int getIt()
{
return mN; // this->mN legal but not needed
}
};
I think that the "this->" operator functions as a pointer, and if a
pointer is NULL and it is accessed it could give me this problem. Is
this correct? Is there a way to fix this?
this is a pointer, yes. (Actually, it's a keyword.) If you call a non-static member function of a class, this points to the object. For instance, if we were to call getIt() above:
int main()
{
Foo f;
int a = f.getIt();
}
then this would point to f from main().
Static member functions do not have a this pointer. this cannot be NULL, and you cannot change the value of this.
There are several cases in C++ where using this is one way to solve a problem, and other cases where this must be used. See this post for a list of these situations.
I could reproduce the bug on coliru's g++4.8.1 when not compiling with optimizations. As I said in a comment, the problem is the srand combined with time and the recursion:
The return value of time is often the Unix time, in seconds. That is, if you call time within the same second, you'll get the same return value. When using this return value to seed srand (via srand(time(NULL))), you'll therefore set the same seed within this second.
void TicTacToe::makeAutoMove(){
srand(time(NULL));
int row = rand() % 3 + 1;
int col = rand() % 3 + 1;
if(this->isValidMove(row, col)){
this->makeMove(row, col);
}else{
this->makeAutoMove();
}
}
If you don't compile with optimizations, or the compiler otherwise needs to use stack space to do an iteration of makeAutoMove, each call will occupy a bit of the stack. Therefore, when called often enough, this will produce a Stack Overflow (luckily, you went to the right site).
As the seed doesn't change within the same second, the calls to rand will also produce the same values within that second - for each iteration, the first rand will always produce some value X and the second always some value Y within that second.
If X and Y lead to an invalid move, you'll get infinite recursion until the seeding changes. If your computer is fast enough, it might call makeAutoMove often enough to occupy enough stack space within that second to cause a Stack Overflow.
Note that it's not required to seed the Pseudo-Random Number Generator used by rand more than once. Typically, you do only seed once, to initialize the PRNG. Subsequent calls to rand then produce pseudo-random numbers.
From cppreference:
Each time rand() is seeded with srand(), it must produce the same sequence of values.
cppreference: rand, srand
Here is the first pass:
Arrays start counting from zero. So you do not need the +1 in lines like rand() % 3 + 1;
Indeed this is a point to the current object. Usually you do not need to use it. i.e. this->makeMove(row, col); and makeMove(row, col); work the same
char board[4][4];1 should bechar board[3][3];` as you want a 3x3 board. See 1) above
board[row][col] = static_cast<char>('0' + row); - You do not need the static cast '0' + row will suffice
You need to take account of (1) in the rest of your code
If you get segmentation problems it is best to use the debugger. A very skill to learn
Anyway - Good luck with your studies. It is refreshing to get a new poster on this web site that is keen to learn
Just a side note about recursion, efficiency, robust coding and how being paranoid can help.
Here is a "cleaned up" version of your problematic function.
See other answers for explanations about what went wrong with the original.
void TicTacToe::makeAutoMove() {
// pick a random position
int row = rand() % 3;
int col = rand() % 3;
// if it corresponds to a valid move
if (isValidMove(row, col)){
// play it
makeMove(row, col);
}else{
// try again
makeAutoMove(); // <-- makeAutoMove is calling itself
}
}
Recursion
In plain English you could describe what the code does as:
pick a random (row, col) couple.
if this couple represents a valid move position, play that move
else try again
Calling makeAutoMove() is indeed a very logical way of trying again, but a not so efficient one programming-wise.
Each new call will cause some memory allocation on the stack:
4 bytes for each local variable (8 bytes in total)
4 bytes for the return address
So the stack consumption will look like:
makeAutoMove <-- 12 bytes
makeAutoMove <-- 24
makeAutoMove <-- 36
makeAutoMove <-- 48
<-- etc.
Imagine for a second that you inadvertently call this function in a situation where it cannot succeed (when a game has ended and no more valid moves are available).
The function will then call itself endlessly. It will be only a matter of time before stack memory gets exhausted and the program crashes. And, given the computing power of your average PC, the crash will occur in the blink of an eye.
This extreme case illustrates the (hidden) cost of using recursive calls. But even if the function eventually succeeds, the cost of each retry is still there.
The things we can learn from there:
recursive calls have a cost
they can lead to crashes when the termination conditions are not met
a lot of them (but not all of them) can easily be replaced by loops, as we will see
As a side note within the side note, as dyp duly noted, modern compilers are so smart they can, for various reasons, detect some patterns within the code that allow them to eliminate such kind of recursive calls.
Nevertheless, you never know if your particular compiler will be smart enough to remove banana peels from under your sloppy feets, so better avoid slopiness altogether, if you ask me.
Avoiding recursion
To get rid of that naughty recursion, we could implement the try again like so:
void TicTacToe::makeAutoMove() {
try_again:
int row = rand() % 3;
int col = rand() % 3;
if (isValidMove(row, col)){
makeMove(row, col);
}else{
goto try_again; // <-- try again by jumping to function start
}
}
After all, we don't really need to call our function again. Jumping back to the start of it will be enough. That's what the goto does.
Good news is, we got rid of the recursion without changing much of the code.
Not so good news is, we used an ugly construct to do so.
Preserving regular program flow
We don't want to keep that ungainly goto since it breaks the usual control flow and makes the code very difficult to understand, maintain and debug *.
We can, however, replace it easily with a conditional loop:
void TicTacToe::makeAutoMove() {
// while a valid move has not been found
bool move_found = false;
while (! move_found) {
// pick a random position
int row = rand() % 3;
int col = rand() % 3;
// if it corresponds to a valid move
if (isValidMove(row, col)){
// play it
makeMove(row, col);
move_found = true; // <-- stop trying
}
}
}
The good: bye bye Mr goto
The bad : hello Mrs move_found
Keeping the code sleek
We swapped the goto for a flag.
It's already better (the program flow is not broken anymore), but we have added some complexity to the code.
We can relatively easily get rid of the flag:
while (true) { // no way out ?!?
// pick a random position
int row = rand() % 3;
int col = rand() % 3;
// if it corresponds to a valid move
if (isValidMove(row, col)){
// play it
makeMove(row, col);
break; // <-- found the door!
}
}
}
The good: bye bye Mrs move_found
The bad : we use a break, that is little more than a tamed goto (something like "goto the end of the loop").
We could end the improvements there, but there is still something annoying with this version: the exit condition of the loop is hidden within the code, which makes it more difficult to understand at first glance.
Using explicit exit conditions
Exit conditions are especially important to figure whether a piece of code will work or not (the reason why our function gets stuck forever is precisely that there are some cases where the exit condition is never met).
So it's always a good idea to make exit conditions stand out as clearly as possible.
Here is a way to make the exit condition more apparent:
void TicTacToe::makeAutoMove() {
// pick a random valid move
int row, col;
do {
row = rand() % 3;
col = rand() % 3;
} while (!isValidMove (row, col)); // <-- if something goes wrong, it's here
// play it
makeMove(row, col);
}
You could probably do it a bit differently. It does not matter as long as we achieve all of these goals:
no recursion
no extraneous variables
meaningful exit condition
sleek code
When you compare the latest refinement with the original version, you can see that it has mutated to something significantly different.
Code robustness
As we have seen, this function can never succeed in case no more legal moves are available (i.e. the game has ended).
This design can work, but it requires the rest of your algorithm to make sure end game conditions are properly checked before this function is called.
This makes your function dependent on external conditions, with nasty consequences if these conditions are not met (a program hangup and/or crash).
This makes this solution a fragile design choice.
Paranoia to the rescue
You might want to keep this fragile design for various reasons. For instance, you might prefer to wine & dine your g/f rather than dedicating your evening to software robustness improvements.
Even if your g/f eventually learns how to cope with geeks, there will be cases when the best solution you can think of will have inherent potential inconsistencies.
This is perfectly OK, as long as these inconsistencies are spotted and guarded against.
A first step toward code robustness is to make sure a potentially dangerous design choice will be detected, if not corrected altogether.
A way of doing so is to enter a paranoid state of mind, imagining that every system call will fail, the caller of any of your function will do its best to make it crash, every user input will come from a rabid Russian hacker, etc.
In our case, we don't need to hire a rabid Russian hacker and there is no system call in sight. Still, we know how an evil programmer could get us in trouble, so we will try to guard against that:
void TicTacToe::makeAutoMove() {
// pick a random valid move
int row, col;
int watchdog = 0; // <-- let's get paranoid
do {
row = rand() % 3;
col = rand() % 3;
assert (watchdog++ < 1000); // <-- inconsistency detection
} while (!isValidMove (row, col));
// play it
makeMove(row, col);
}
assert is a macro that will force a program exit if the condition passed as parameter is not met, with a console message and/or popup window saying something like assertion "watchdog++ < 1000" failed in tictactoe.cpp line 238.
You can see it as a way to bail out of a program if a fatal algorithmic flaw (i.e. the kind of flaw that will require a source code overhaul, so there is little point in keeping this inconsistent version of the program running) has been detected.
By adding the watchdog, we make sure the program will explicitely exit if it detects an abnormal condition, indicating gracefully the location of the potential problem (tictactoe.cpp line 238 in our case).
While refactoring your code to eliminate inconsistencies can be difficult or even impossible, detecting inconsistencies is most often very easy and cheap.
The condition have not to be very precise, the only point is to make sure your code is executing in a "reasonably" consistent context.
In this example, the actual number of trials to get a legit move is not easy to estimate (it's based on cumulative probabilities to hit a cell where a move is forbidden), but we can easily figure that failing to find a legit move after 1000 tries means something went seriously wrong with the algorithm.
Since this code is just there to increase robustness, it does not have to be efficient. It's just a means to go from the "why the hell does my program hang?!?" situation to the "dang, I must have called makeAutoMove after end game" (near) immediate realization.
Once you've tested and proved your program, and if you have really good reasons for that (namely, if your paranoid checks cause serious performance issues) you can take the decision to cleanup that paranoid code, leaving very explicit comments in your source about the way this particular piece of code shall be used.
Actually there are means to keep the paranoid code live without sacrificing efficiency, but that's another story.
What it boils down to is:
get used to notice potential inconsistencies in your code, especially when these inconsistencies can have serious consequences
try to make sure as many pieces of your code as possible can detect inconsistencies
sprinkle your code with paranoid checks to increase your chances of detecting wrong moves early
Code refactoring
In an ideal world, each function should give a consistent result and leave the system in a consistent state. That rarely happens in real life, unless you accept some limitations to your creativity.
However, it could be interesting to see what you could achieve if you designed a tic-tac-toe 2.0 with these guidelines in mind. I'm sure you would find a lot of helpful reviewers here on StackOverflow.
Feel free to ask if you found some points of interest in all these rants, and welcome to the wonderful world of geeks :)
(kuroi dot neko at wanadoo dot fr)
* goto might look harmless enough in such a small example, but you can trust me on this: abusing goto will lead you to a world of pain. Just don't do it unless you have a very, very good reason.

C++ do while loop

I have a vector holding 10 items (all of the same class for simplicity call it 'a'). What I want to do is to check that 'A' isn't either a) hiding the walls or b) hiding another 'A'. I have a collisions function that does this.
The idea is simply to have this looping class go though and move 'A' to the next position, if that potion is causing a collision then it needs to give itself a new random position on the screen. Because the screen is small, there is a good chance that the element will be put onto of another one (or on top of the wall etc). The logic of the code works well in my head - but debugging the code the object just gets stuck in the loop, and stay in the same position. 'A' is supposed to move about the screen, but it stays still!
When I comment out the Do while loop, and move the 'MoveObject()' Function up the code works perfectly the 'A's are moving about the screen. It is just when I try and add the extra functionality to it is when it doesn't work.
void Board::Loop(void){
//Display the postion of that Element.
for (unsigned int i = 0; i <= 10; ++i){
do {
if (checkCollisions(i)==true){
moveObject(i);
}
else{
objects[i]->ResetPostion();
}
}
while (checkCollisions(i) == false);
objects[i]->SetPosition(objects[i]->getXDir(),objects[i]->getYDir());
}
}
The class below is the collision detection. This I will expand later.
bool Board::checkCollisions(int index){
char boundry = map[objects[index]->getXDir()][objects[index]->getYDir()];
//There has been no collisions - therefore don't change anything
if(boundry == SYMBOL_EMPTY){
return false;
}
else{
return true;
}
}
Any help would be much appreciated. I will buy you a virtual beer :-)
Thanks
Edit:
ResetPostion -> this will give the element A a random position on the screen
moveObject -> this will look at the direction of the object and adjust the x and Y cord's appropriately.
I guess you need: do { ...
... } while (checkCollisions(i));
Also, if you have 10 elements, then i = 0; i < 10; i++
And btw. don't write if (something == true), simply if (something) or if (!something)
for (unsigned int i = 0; i <= 10; ++i){
is wrong because that's a loop for eleven items, use
for (unsigned int i = 0; i < 10; ++i){
instead.
You don't define what 'doesn't work' means, so that's all the help I can give for now.
There seems to be a lot of confusion here over basic language structure and logic flow. Writing a few very simple test apps that exercise different language features will probably help you a lot. (So will a step-thru debugger, if you have one)
do/while() is a fairly advanced feature that some people spend whole careers never using, see: do...while vs while
I recommend getting a solid foundation with while and if/else before even using for. Your first look at do should be when you've just finished a while or for loop and realize you could save a mountain of duplicate initialization code if you just changed the order of execution a bit. (Personally I don't even use do for that any more, I just use an iterator with while(true)/break since it lets me pre and post code all within a single loop)
I think this simplifies what you're trying to accomplish:
void Board::Loop(void) {
//Display the postion of that Element.
for (unsigned int i = 0; i < 10; ++i) {
while(IsGoingToCollide(i)) //check is first, do while doesn't make sense
objects[i]->ResetPosition();
moveObject(i); //same as ->SetPosition(XDir, YDir)?
//either explain difference or remove one or the other
}
}
This function name seems ambiguous to me:
bool Board::checkCollisions(int index) {
I'd recommend changing it to:
// returns true if moving to next position (based on inertia) will
// cause overlap with any other object's or structure's current location
bool Board::IsGoingToCollide(int index) {
In contrast checkCollisions() could also mean:
// returns true if there is no overlap between this object's
// current location and any other object's or structure's current location
bool Board::DidntCollide(int index) {
Final note: Double check that ->ResetPosition() puts things inside the boundaries.

Optimizing C++ Tree Generation

I'm generating a Tic-Tac-Toe game tree (9 seconds after the first move), and I'm told it should take only a few milliseconds. So I'm trying to optimize it, I ran it through CodeAnalyst and these are the top 5 calls being made (I used bitsets to represent the Tic-Tac-Toe board):
std::_Iterator_base::_Orphan_me
std::bitset<9>::test
std::_Iterator_base::_Adopt
std::bitset<9>::reference::operator bool
std::_Iterator_base::~_Iterator_base
void BuildTreeToDepth(Node &nNode, const int& nextPlayer, int depth)
{
if (depth > 0)
{
//Calculate gameboard states
int evalBoard = nNode.m_board.CalculateBoardState();
bool isFinished = nNode.m_board.isFinished();
if (isFinished || (nNode.m_board.isWinner() > 0))
{
nNode.m_winCount = evalBoard;
}
else
{
Ticboard tBoard = nNode.m_board;
do
{
int validMove = tBoard.FirstValidMove();
if (validMove != -1)
{
Node f;
Ticboard tempBoard = nNode.m_board;
tempBoard.Move(validMove, nextPlayer);
tBoard.Move(validMove, nextPlayer);
f.m_board = tempBoard;
f.m_winCount = 0;
f.m_Move = validMove;
int currPlay = (nextPlayer == 1 ? 2 : 1);
BuildTreeToDepth(f,currPlay, depth - 1);
nNode.m_winCount += f.m_board.CalculateBoardState();
nNode.m_branches.push_back(f);
}
else
{
break;
}
}while(true);
}
}
}
Where should I be looking to optimize it? How should I optimize these 5 calls (I don't recognize them=.
The tic-tac-toe game tree is very redundant. Eliminating rotated and mirrored boards will reduce the final ply of the game tree by 3 or 4 orders of magnitude. No amount of optimizations will make bubblesort as fast as introsort.
struct Game_board;
struct Node
{
Game_board game_board;
Node* parent;
std::vector<Node*> children;
enum { X_Win, Y_Win, Draw, Playing } outcome;
};
// returns the same hash value for all "identical" boards.
// ie boards that can be rotated or mirrored to look the
// same will have the same hash value
int hash( const Game_board& game_board );
// uses hash() function to generate hashes from Node*
struct Hash_functor;
// nodes yet to be explored.
std::hash_set<Node*,Hash_functor> open;
//nodes already explored.
std::hash_set<Node*,Hash_functor> closed;
while( ! open.empty() )
{
Node* node_to_expore = get_a_node( open );
assert( node_to_expore not in close or open sets )
if( node_to_expore is win lose or draw )
{
Mark node as win lose or draw
add node to closed set
}
loop through all children of node_to_expore
{
if( child in close )
{
add node from closed set to children list of node_to_expore
}
else if( child in open )
{
add node from open set to children list of node_to_explore
}
else
{
add child to open set
add child to children list of node_to_expore
}
}
}
Those functions are typically trivial. That means that an optimized ("release") build will typically have them inlined. However, in a debug build they're not. The result is that a debug build is slower, but allows you to set breakpoints on those functions. So, the "milliseconds comment" should be applied to the release build, where you wouldn't even have those functions anymore.
You're getting all wrapped up in data structure.
Don't build the tree, just walk it. Have only one copy of the board. At each node in the search tree, just modify the board, and on the way back out, un-modify it.
And if you want to know what it's doing, just hit the pause button at random. It will show you why it's in those routines you don't recognize that are taking all the time.
Honestly, and I don't mean this as a slam against you, you're asking us to examine a poorly documented piece of code that is a smaller part to a larger code base. We don't have the context that gives much information. I personally am also turned off by examining others' code when it doesn't appear that they've done all they can do to examine it themselves yet (and I don't mean this to say I'm annoyed at you or anything, just that my eyes are glazing over looking at your code).
I recommend you run your code through a profiler and determine what exactly is taking so much time. Treat this profiling like you're debugging. When you find a module taking a long time, examine that module in small sections (as if you're hunting for a bug) to see why.
This will allow you to ask a much more informed question if you still need to ask something.
You've posted far too little of your code.
You are asking how to optimize the code however you should also be asking how to optimize the algorithm.
There are two things that I immediately see.
As "Michael Dorgan" stated generate the tree of moves once.
How many broads are you generating in your tree? 362880? Your code appears to be generating redundant entries. For example, with an empty board there are actually three moves not nine moves. All other combinations are the board rotated (which are equal). You can reduce the number of boards that needs to be generated and speed up the generation of the tree.
Here are the three first moves(rotate the last two board to generate the other boards)
| |
|X|
| |
|X|
| |
| |
X| |
| |
| |
Let me add that if your system is taking 9 seconds to do its work, that means that something is being called billions and billions of times more than it should. If you don't have release level profiling abilities, place a few global counters in your code and increment them every time the code they are in is called. This will give you a poor man's profile that will work on release builds. If you see a billions calls somewhere you don't expect, you now have a place to look closer.
In reality, Tic-Tac-Toe's entire move tree should be trivial to store as a hash if you need the speed. 9! moves just isn't that big of a domain (anymore). 362k moves shouldn't break the bank, and that's a brute force analysis. That domain can be cut way down when you take into consideration all the various symetries of data.
Bah, here's how I would do it if I was coding it since people have latched onto my back of the envelope math:
I wouldn't even go the tree route, just some rules and be done.
Turn 1. Go in center.
Turn 2. If center unoccupied, go there, else go in corner
Turn 3. If opponent filled a corner, go in opposite corner, else go in corner - you've won.
Turn 4. Block if needed. If Xs occupy opposite corners, fill edge. If Xs occupy center and opposite corner, fill corner. If Xs occupy opposite edges, fill corner and win. If Xs fill adjacent edges, Fill corner betweem them.
Turn 5 Win if possible. Block if needed. Else go in corner opposite of adjacent edge move and win.
Turn 6-9 Win if possible. Block if needed. Else, place random towards draw.