d2jsp
Log InRegister
d2jsp Forums > Off-Topic > Computers & IT > Programming & Development > Artifical Intelligence > What Gives?
Prev12
Add Reply New Topic New Poll
Member
Posts: 31,680
Joined: Nov 10 2007
Gold: 1.00
Oct 7 2009 11:53pm
Code
#include <iostream>
#include <vector>
#include <TextControl.h>
using namespace std;

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

int FoodX = 0;
int FoodY = 0;

void RunPath(const int, int*, int*);
void SpawnFood(const int, const int);
void DrawSnake(const int, int*, int*, int);
bool Crash(const int*, const int*, const int);

vector<int>Instructions;

bool Crash(const int *SnakeX, const int *SnakeY, const int SnakeSize) {

   for (int x = SnakeSize; x > 1; x--) {
       if (SnakeX[0] == SnakeX[x] && SnakeY[0] == SnakeY[x]) {
           PlaceCursor(SnakeX[x], SnakeY[x]);
           SetColor(RED, BLACK);
           printf("X");

           PlaceCursor(0, 45);
           SetColor(RED, BLACK);
           printf("X: %d  ", SnakeX[0]);

           PlaceCursor(0, 46);
           SetColor(RED, BLACK);
           printf("Y: %d  ", SnakeY[0]);

           Sleep(250);
           return true;
       }
   }

   return false;
}

void RunPath(const int color, int *SnakeX, int *SnakeY, vector<int> Instructions) {

   /* It'll crash sometimes if it randomly is empty */
   if (!Instructions.empty()) {

       SetColor(color, BLACK);
       for (int x = 0; x < Instructions.size(); x++) {

           switch (Instructions.at(x)) {
               case 0: SnakeY[0]--; break; // UP
               case 1: SnakeY[0]++; break; // DOWN
               case 2: SnakeX[0]--; break; // LEFT
               case 3: SnakeX[0]++; break; // RIGHT
           }

           Sleep(50);
           DrawSnake(color, SnakeX, SnakeY, 10);

           if (Crash(SnakeX, SnakeY, 10)) { Sleep(1000); }
       }
   }

   return;
}

void DrawSnake(const int color, int *SnakeX, int *SnakeY, int SnakeSize) {

   SetColor(color, BLACK);
   for (int x = SnakeSize; x > 0; x--) { SnakeX[x] = SnakeX[x - 1]; SnakeY[x] = SnakeY[x - 1]; }
   for (int x = SnakeSize; x > 0; x--) { PlaceCursor(SnakeX[x], SnakeY[x]); printf("*"); }

   SetColor(GREEN, BLACK);
   PlaceCursor(SnakeX[0], SnakeY[0]); printf("@");

   // If his head, is NOT on his tail (specifically his "tail") - draw a black square
   if (SnakeX[0] != SnakeX[SnakeSize] || SnakeY[0] != SnakeY[SnakeSize]) {
       PlaceCursor(SnakeX[SnakeSize], SnakeY[SnakeSize]);
       SetColor(BLACK, BLACK);
       printf(" ");
   }

   PlaceCursor(0, 48);
   SetColor(RED, BLACK);
   printf("Snake's current size: ");
   SetColor(WHITE, BLACK);
   printf("%d", SnakeSize);

   PlaceCursor(0, 49);
   SetColor(WHITE, BLACK);
   for (int x = SnakeSize; x > 0; x--) {
       printf("[");
       SetColor(CYAN, BLACK);
       printf("%d", SnakeX[x]);
       SetColor(WHITE, BLACK);
       printf(",");
       SetColor(GREEN, BLACK);
       printf("%d", SnakeY[x]);
       SetColor(WHITE, BLACK);
       printf("] ");
   }

   return;
}

void SpawnFood(const int X, const int Y) {

       FoodX = X;
       FoodY = Y;

       PlaceCursor(FoodX, FoodY);
       SetColor(YELLOW, BLACK);
       printf("x");

   return;
}

int main() {

   HANDLE OutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

   SetConsoleTitle("Snake - AI Module");
   SMALL_RECT windowSize = {0, 0, 79, 49};
   SetConsoleWindowInfo(OutHandle, TRUE, &windowSize);

   ClearConsole(BLACK, BLACK);
   RemoveCursor();

   srand(time(NULL));

   int SnakeX[100] = {'\0'};
   int SnakeY[100] = {'\0'};

   for (int x = 0; x < 10; x++) {
       SnakeX[x] = 35 + x;
       SnakeY[x] = 25;
   }

   SpawnFood(15, 15);

   Instructions.clear();

   int NewMove = 0;
   int OldMove = 0;

   for (int x = 0; x < 100; x++) {

       NewMove = rand() % 4;

       while (NewMove == LEFT && OldMove == RIGHT) { NewMove = rand() % 4; }
       while (NewMove == RIGHT && OldMove == LEFT) { NewMove = rand() % 4; }
       while (NewMove == UP && OldMove == DOWN) { NewMove = rand() % 4; }
       while (NewMove == DOWN && OldMove == UP) { NewMove = rand() % 4; }

       OldMove = NewMove;
       Instructions.push_back(NewMove);
   }

   RunPath(BLUE, SnakeX, SnakeY, Instructions);
   RunPath(RED, SnakeX, SnakeY, Instructions);

   cin.get();
   return 0;
}


This is just something funny for you to compile & run if you really want to - the Snake will randomly pick 100 directions and move in them.
The only "avoidance" he has right there, is turning "into" himself. If his last (old) move was left, he cannot move to the right (because going left <--- then right --> would make his head hit his body).

Here's a video (YouTube, horray!) of the compiled and ran version (currently posted above) of Snake: http://www.youtube.com/watch?v=08X9HFMtrJ0&feature=channel
Everytime you compile it however, it'll change his movements 100% due to the srand function used - the only reason I'm using it is because I like to watch him do different stuff.

EDIT: I modified the code (just reposted the entire program above) - added the "Crash" function so you can see what/how it works real-time ;)

This post was edited by Muted on Oct 8 2009 12:22am
Member
Posts: 6,953
Joined: Sep 27 2003
Gold: 518.50
Oct 8 2009 01:35am
Quote (Muted @ Wed, Oct 7 2009, 11:51pm)
I've read God knows how many "tutorials" on the A* Algorithm, and quite frankly, I don't understand in the slightest how that would apply.
In one respect, I can see how it might fit. In another, I don't. Because it's not what I want, per se.

A* is a graph-search algorithm that can be easily adapted for 2D graph traversal. It is one of the most helpful and applicable algorithms in all of computer science (not just 2D pathfinding as some believe)
Member
Posts: 31,680
Joined: Nov 10 2007
Gold: 1.00
Oct 8 2009 01:41am
Quote (ASBands @ Thu, 8 Oct 2009, 02:35)
A* is a graph-search algorithm that can be easily adapted for 2D graph traversal.  It is one of the most helpful and applicable algorithms in all of computer science (not just 2D pathfinding as some believe)


I'm not saying A* algorithm is not proper for this "job." I'm well aware of its capabilities. There's plenty proof that it'd be extremely applicable in the case of Snake (this console-based game I've written).
Speaking of which, there's a rather interesting video of somebody who apparently won some tournie using Java, Mario Brothers, and the A* algorithm: http://www.youtube.com/watch?v=qYluZRwrw9w&feature=related

However! I've actually managed to find one of the major bugs that's been driving me crazy this entire time (past few months and extremely tonight). I haven't sat down eight hours straight like this since January.
Here's the final elimination of all logical bugs I've had in my AI program: http://www.youtube.com/watch?v=R891r2NwENY - as you can see, he is no longer moving angular or doing "arbitrary" behavoir.
The real Snake is finally following the AI Snake as it should! I'll be hopefully finding a way to tell him to avoid his body by either tonight, or tomorrow.

Here's the source code (current):
Code
#include <iostream>
#include <vector>
#include <TextControl.h>
using namespace std;

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

int FoodX = 0;
int FoodY = 0;

void CreatePath(int*, int*); // THE LOGIC ERROR LIES HERE
void RunPath(const int, int*, int*);
void SpawnFood(const int, const int);
void DrawSnake(const int, int*, int*, int);
bool Crash(const int*, const int*, const int);

vector<int>Instructions;

void CreatePath(int *SnakeX, int *SnakeY) {

   Instructions.clear();

   int TempX[100] = {'\0'};
   int TempY[100] = {'\0'};

   /* Copy Snake location */
   for (int x = 0; x < 10; x++) {
       TempX[x] = SnakeX[x];
       TempY[x] = SnakeY[x];
   }

   while (TempX[0] != FoodX || TempY[0] != FoodY) {
       if (FoodX > TempX[0]) {
           Instructions.push_back(RIGHT);
           TempX[0]++;

           Sleep(50);
           DrawSnake(RED, TempX, TempY, 10);
       }

       if (FoodX < TempX[0]) {
           Instructions.push_back(LEFT);
           TempX[0]--;

           Sleep(50);
           DrawSnake(RED, TempX, TempY, 10);
       }

       if (FoodY > TempY[0]) {
           Instructions.push_back(DOWN);
           TempY[0]++;

           Sleep(50);
           DrawSnake(RED, TempX, TempY, 10);
       }

       if (FoodY < TempY[0]) {
           Instructions.push_back(UP);
           TempY[0]--;

           Sleep(50);
           DrawSnake(RED, TempX, TempY, 10);
       }
   }

   SetColor(CYAN, BLACK);
   PlaceCursor(25, 0);
   printf("AI PATH COMPLETED");
   Sleep(500);
   PlaceCursor(25, 0);
   printf("                 ");

   return;
}

bool Crash(const int *SnakeX, const int *SnakeY, const int SnakeSize) {

   for (int x = SnakeSize; x > 1; x--) {
       if (SnakeX[0] == SnakeX[x] && SnakeY[0] == SnakeY[x]) {
           PlaceCursor(SnakeX[x], SnakeY[x]);
           SetColor(RED, BLACK);
           printf("X");

           PlaceCursor(0, 45);
           SetColor(RED, BLACK);
           printf("X: %d  ", SnakeX[0]);

           PlaceCursor(0, 46);
           SetColor(RED, BLACK);
           printf("Y: %d  ", SnakeY[0]);

           Sleep(50);
           return true;
       }
   }

   return false;
}

void RunPath(const int color, int *SnakeX, int *SnakeY, vector<int> Instructions) {

   /* It'll crash sometimes if it randomly is empty */
   if (!Instructions.empty()) {

       SetColor(color, BLACK);
       for (int x = 0; x < Instructions.size(); x++) {

           switch (Instructions.at(x)) {
               case 0: SnakeY[0]--; break; // UP
               case 1: SnakeY[0]++; break; // DOWN
               case 2: SnakeX[0]--; break; // LEFT
               case 3: SnakeX[0]++; break; // RIGHT
           }

           Sleep(50);
           DrawSnake(color, SnakeX, SnakeY, 10);

           if (Crash(SnakeX, SnakeY, 10)) { Sleep(250); }
       }
   }

   return;
}

void DrawSnake(const int color, int *SnakeX, int *SnakeY, int SnakeSize) {

   SetColor(color, BLACK);
   for (int x = SnakeSize; x > 0; x--) { SnakeX[x] = SnakeX[x - 1]; SnakeY[x] = SnakeY[x - 1]; }
   for (int x = SnakeSize; x > 0; x--) { PlaceCursor(SnakeX[x], SnakeY[x]); printf("*"); }

   SetColor(GREEN, BLACK);
   PlaceCursor(SnakeX[0], SnakeY[0]); printf("@");

   // If his head, is NOT on his tail (specifically his "tail") - draw a black square
   if (SnakeX[0] != SnakeX[SnakeSize] || SnakeY[0] != SnakeY[SnakeSize]) {
       PlaceCursor(SnakeX[SnakeSize], SnakeY[SnakeSize]);
       SetColor(BLACK, BLACK);
       printf(" ");
   }

   PlaceCursor(0, 47);
   SetColor(RED, BLACK);
   printf("Snake's current size: ");
   SetColor(WHITE, BLACK);
   printf("%d", SnakeSize);

   PlaceCursor(0, 48);
   SetColor(WHITE, BLACK);
   for (int x = SnakeSize; x > 0; x--) {
       printf("[");
       SetColor(CYAN, BLACK);
       printf("%d", SnakeX[x]);
       SetColor(WHITE, BLACK);
       printf(",");
       SetColor(GREEN, BLACK);
       printf("%d", SnakeY[x]);
       SetColor(WHITE, BLACK);
       printf("] ");
   }

   return;
}

void SpawnFood(const int X, const int Y) {

   FoodX = X;
   FoodY = Y;

   PlaceCursor(FoodX, FoodY);
   SetColor(YELLOW, BLACK);
   printf("x");

   return;
}

int main() {

   HANDLE OutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

   SetConsoleTitle("Snake - AI Module");
   SMALL_RECT windowSize = {0, 0, 79, 49};
   SetConsoleWindowInfo(OutHandle, TRUE, &windowSize);

   ClearConsole(BLACK, BLACK);
   RemoveCursor();

   srand(time(NULL));

   int SnakeX[100] = {'\0'};
   int SnakeY[100] = {'\0'};

   /* Spawn point for the Snake */
   SnakeX[0] = 35;
   SnakeY[0] = 25;

   SpawnFood(15, 15);
   CreatePath(SnakeX, SnakeY);
   RunPath(BLUE, SnakeX, SnakeY, Instructions);

   SpawnFood(45, 15);
   CreatePath(SnakeX, SnakeY);
   RunPath(BLUE, SnakeX, SnakeY, Instructions);

   cin.get();
   return 0;
}


Thank you for the encouragement, llamaoo7. :)

You have no idea how much you did just by taking a few minutes to actually look at my Pong game and change something...
Including showing an interest in the Snake game/AI. It's given me a tiny bit of hope that somebody actually cares other than me; Which gives me reason to actually work on it again.
I usually don't get so down and distraught but, I hate it when things I write suddenly go berserk and improper and just, shit hits the fan basically. Makes me really sad and nobody helps.
Everybody just says something general like "Go learn A*" or "A* will do this" and posts the same link I've read years ago, and aside from that, I've read many documentaries/posts/tutorials on A*.

I don't think I quite understand or at least remember the A* algorithm or any algorithm for that matter. The entire 'vector of instructions' idea, came from my AI book, and it works extremely well.

<3 llamaoo7 <3



Quote (llamaoo7 @ Thu, 8 Oct 2009, 00:51)
It appears to be exactly what you need.  You have a starting point, an end point, and you need a path.  The different part about your situation is that your obstacle tends to move as you move along the path.  (Think about how this could be handled after you understand A*.)


Oh, and here's something for you to think about: Everybody learns differently.
Some people learn from stubbornness (trial & error). Some people learn from observing other people (seeing their errors, or perhaps mimicking their successes).
Me personally? I learn from trial & error mostly. But sometimes, I do need to learn from example (which is basically "observing" other peoples successes and mimicking them).

I can't solve this puzzle of Snake using the A* algorithm, because I don't understand the A* algorithm.
I do however, know what I've written. I also know what I've written in the past is successful.
I've slowly been building an AI that's more and more advanced everytime I compile it (insignificantly "improved," but improved non-the-less).

When he avoids his own body, and I get him to avoid other players bodies, I'll be happy and try this from an entirely different structure (the A* algorithm if possible).
There's one faint thing I can remember, and that's something about Djinn's A* algorithim, or something on the lines of that. It calculates every possible move and saves them all.

Anyways, thanks again! :)

This post was edited by Muted on Oct 8 2009 01:51am
Member
Posts: 31,680
Joined: Nov 10 2007
Gold: 1.00
Oct 8 2009 02:51am
Snake successfully avoids his own body now: http://www.youtube.com/watch?v=hOlbHzQ5vlw

I'll post the relevant source-code here (it's not exactly "bullet-proof," but it's a start!):
Code
while (TempX[0] != FoodX || TempY[0] != FoodY) {
       if (FoodX > TempX[0]) {
           Instructions.push_back(RIGHT);
           TempX[0]++;

           if (Crash(TempX, TempY, 10)) {
               TempX[0]--;
               Instructions.back() = UP;
               TempY[0]--;
           }

           for (int x = 10; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = 10; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");
               if (TempX[0] != TempX[10] || TempY[0] != TempY[10]) { PlaceCursor(TempX[10], TempY[10]); SetColor(BLACK, BLACK); printf(" "); }
           }
       }

       if (FoodX < TempX[0]) {
           Instructions.push_back(LEFT);
           TempX[0]--;

           if (Crash(TempX, TempY, 10)) {
               TempX[0]++;
               Instructions.back() = UP;
               TempY[0]--;
           }

           for (int x = 10; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = 10; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");
               if (TempX[0] != TempX[10] || TempY[0] != TempY[10]) { PlaceCursor(TempX[10], TempY[10]); SetColor(BLACK, BLACK); printf(" "); }
           }
       }

       if (FoodY > TempY[0]) {
           Instructions.push_back(DOWN);
           TempY[0]++;

           if (Crash(TempX, TempY, 10)) {
               TempY[0]--;
               Instructions.back() = UP;
               TempY[0]--;
           }

           for (int x = 10; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = 10; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");
               if (TempX[0] != TempX[10] || TempY[0] != TempY[10]) { PlaceCursor(TempX[10], TempY[10]); SetColor(BLACK, BLACK); printf(" "); }
           }
       }

       if (FoodY < TempY[0]) {
           Instructions.push_back(UP);
           TempY[0]--;

           if (Crash(TempX, TempY, 10)) {
               TempY[0]++;
               Instructions.back() = UP;
               TempY[0]++;
           }

           for (int x = 10; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = 10; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");
               if (TempX[0] != TempX[10] || TempY[0] != TempY[10]) { PlaceCursor(TempX[10], TempY[10]); SetColor(BLACK, BLACK); printf(" "); }
           }
       }

       NewMove = 4;
   }


I still think it should be 100% randomized but, for some reason, the "real Snake" and the "AI Snake" split off like mirror images... It's really trippy.
EDIT: Here's a newer version of Snake (he avoids his body for the most part, it isn't randomized which is why he'll freeze up): http://www.youtube.com/watch?v=vMGwzDAuHr4

This AI could use some improving! :(

This post was edited by Muted on Oct 8 2009 03:21am
Member
Posts: 13,425
Joined: Sep 29 2007
Gold: 0.00
Warn: 20%
Oct 8 2009 07:57am
dont want to read source code cuz i dont have the time but... is it trying to find the x,y coordanates of the food before the food is placed? i could see that pausing it then it rechecks again for food x moments later

just a guess by looking at the video
Member
Posts: 31,680
Joined: Nov 10 2007
Gold: 1.00
Oct 8 2009 01:51pm
Quote (AbDuCt @ Thu, 8 Oct 2009, 08:57)
dont want to read source code cuz i dont have the time but... is it trying to find the x,y coordanates of the food before the food is placed? i could see that pausing it then it rechecks again for food x moments later

just a guess by looking at the video


If you take the time, you'll have a more clear answer; If you had time to watch a 34 second video, you had time to read the code.
Code
if Food is left of Snake {
          Instruction: GO UP
          Move AI Snake UP

          if Crashed {
              Reverse last move
              New instruction: GO UP
              Move AI Snake UP
          }

          Move the rest of the body (following the head)

          Change color to RED
          Display the AI Snake's body (asterisks (*))

          Change color to GREEN
          Draw the AI Snake's head (at sign (@))
          Make the last body part a blank spot (so there isn't a long trail of asterisks showing the trail of the Snake)
      }


This is an English summarization of the code above.

Now back to what I was doing (posting updates as I go!) - this is what I currently have (and it seems to be faulty at some times):
Code
if (FoodX > TempX[0]) {
           Instructions.push_back(RIGHT);
           if (!Crash(TempX, TempY)) { TempX[0]++; }

           while (Crash(TempX, TempY)) {

               NewMove = rand() % 4;

               switch (NewMove) {
                   case UP: TempY[0]--; Instructions.back() = UP; break;
                   case DOWN: TempY[0]++; Instructions.back() = DOWN; break;
                   case LEFT: TempX[0]--; Instructions.back() = LEFT; break;
                   case RIGHT: TempX[0]++; Instructions.back() = RIGHT; break;
               }

               /* CRASHED STILL - REVERSE */
               if (Crash(TempX, TempY)) {
                   switch (NewMove) {
                       case UP: TempY[0]++; break;
                       case DOWN: TempY[0]--; break;
                       case LEFT: TempX[0]++; break;
                       case RIGHT: TempX[0]--; break;
                   }
               }
           }

           for (int x = SnakeSize; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = SnakeSize; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");

               // If his head, is NOT on his tail (specifically his "tail") - draw a black square
               if (TempX[0] != TempX[SnakeSize] || TempY[0] != TempY[SnakeSize]) {
                   PlaceCursor(TempX[SnakeSize], TempY[SnakeSize]);
                   SetColor(BLACK, BLACK);
                   printf(" ");
               }
           }
       }


The AI Snake will sometimes arbitrarly crash into himself when there's clearly an open spot, then (and this is what's odd), he'll CONTINUE to move even though it's a crash, and he KNOWS it's a crash.
Here's a video (I'm so abusing YouTube) demonstrating what I mean, and I'll even put an annotation or whatever to point out when it occurs, because I don't feel like freezing Snake: http://www.youtube.com/watch?v=o9t8bOkp4lI

Thanks for the feedback (I hope).

EDIT: OK, there's something extremely flawed with how I have this setup using the randomized movements... I'm going to look back at my old old code from 2008 now, also...
With the following code, he fails utterly. He simulates 40+ crashes, and has 1-2 REAL crashes (blue Snake), and even then, he somehow "misses" the food but "hits" it in his mind.
Code
   //while (true) {
       SpawnFood(15, 15);
       //NewRun();
       CreatePath(SnakeX, SnakeY);
       RunPath(BLUE, SnakeX, SnakeY, Instructions);

       SpawnFood(65, 15);
       CreatePath(SnakeX, SnakeY);
       RunPath(BLUE, SnakeX, SnakeY, Instructions);
   //}


:confused: :mad:

This post was edited by Muted on Oct 8 2009 02:09pm
Member
Posts: 9,475
Joined: Mar 14 2005
Gold: 110.00
Oct 8 2009 06:07pm
Quote (Muted @ Thu, 8 Oct 2009, 14:51)
If you take the time, you'll have a more clear answer; If you had time to watch a 34 second video, you had time to read the code.


Bad code takes a long time to read. I'd show you some shitty legacy code I have at work that would demonstrate this point well.

Quote

This is an English summarization of the code above.


This will help a lot because if your code isn't working, then it may be hard to see your intention. Your intention isn't really any known algorithm, more just ad-hoc code so solve a couple of test cases that won't solve the complete problem.

The problem you are facing is a known and well solved problem. Seriously, read that A* article and ask questions where you get lost. Other than this, I can't really help you anymore.

Member
Posts: 31,680
Joined: Nov 10 2007
Gold: 1.00
Oct 8 2009 06:14pm
You've helped plenty, thanks again. I'll try to convert this (start from scratch?) to the A* algorithm.
Thanks again! You have no idea how much you've been a help. I've been working on this for over a year.
Now it finally works, and I have what I've always wanted: My own personal project, by myself, for myself, 100% success rate, pathed! :D

Here's the working code (see for yourself):
Code
#include <iostream>
#include <vector>
#include <TextControl.h>
using namespace std;

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3

int SnakeSize = 10;

int SimulatedCrashes = 0;
int TotalCrashes = 0;

int FoodX = 0;
int FoodY = 0;

bool DisplayAISnake = true;
int AIWait = 50;

void CreatePath(int*, int*);
void RunPath(const int, int*, int*);
void DrawSnake(const int, int*, int*);
bool Crash(const int*, const int*);
bool CrashAt(int*, int*, const int);

vector<int>Instructions;

bool CrashAt(int *SnakeX, int *SnakeY, const int Direction) {

   int CHECK_H = 0;
   int CHECK_V = 0;

   if (Direction == UP) { CHECK_V = SnakeY[0] - 1; CHECK_H = SnakeX[0]; } else
   if (Direction == DOWN) { CHECK_V = SnakeY[0] + 1; CHECK_H = SnakeX[0]; } else
   if (Direction == LEFT) { CHECK_H = SnakeX[0] - 1; CHECK_V = SnakeY[0]; } else
   if (Direction == RIGHT) { CHECK_H = SnakeX[0] + 1; CHECK_V = SnakeY[0]; } else
   { MessageBox(NULL, "ERROR", "ERROR", MB_OK); exit(0); }

   /* Hit self */
   for (int x = SnakeSize - 1; x > 1; x--) {
       if (CHECK_H == SnakeX[x] && CHECK_V == SnakeY[x]) {
           return true;
       }
   }

   return false;
}

void CreatePath(int *SnakeX, int *SnakeY) {

   Sleep(50);
   PlaceCursor(0, 46);
   SetColor(YELLOW, BLACK);
   cout << "Instruction size:   ";

   Instructions.clear();

   int TempX[100] = {'\0'};
   int TempY[100] = {'\0'};

   /* Copy Snake location */
   for (int x = 0; x < SnakeSize; x++) {
       TempX[x] = SnakeX[x];
       TempY[x] = SnakeY[x];
   }

   while (TempX[0] != FoodX || TempY[0] != FoodY) {
       if (FoodX > TempX[0]) {

           Instructions.push_back(RIGHT);

           if (!CrashAt(TempX, TempY, RIGHT)) { TempX[0]++; } else {
               while (CrashAt(TempX, TempY, RIGHT)) {
                   for (int x = 0; x < 4; x++) {
                       switch (x) {
                           case UP: TempY[0]--; Instructions.back() = UP; break;
                           case DOWN: TempY[0]++; Instructions.back() = DOWN; break;
                           case LEFT: TempX[0]--; Instructions.back() = LEFT; break;
                           case RIGHT: TempX[0]++; Instructions.back() = RIGHT; break;
                       }

                       PlaceCursor(TempX[0], TempY[0]);
                       SetColor(GREEN, BLACK);
                       printf("X");

                       /* CRASHED STILL - REVERSE */
                       if (CrashAt(TempX, TempY, x)) {
                           switch (x) {
                               case UP: TempY[0]++; break;
                               case DOWN: TempY[0]--; break;
                               case LEFT: TempX[0]++; break;
                               case RIGHT: TempX[0]--; break;
                           }
                       } else { break; }

                       Sleep(500);
                   }
               }
           }

           for (int x = SnakeSize; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = SnakeSize; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");

               // If his head, is NOT on his tail (specifically his "tail") - draw a black square
               if (TempX[0] != TempX[SnakeSize] || TempY[0] != TempY[SnakeSize]) {
                   PlaceCursor(TempX[SnakeSize], TempY[SnakeSize]);
                   SetColor(BLACK, BLACK);
                   printf(" ");
               }
           }
       }

       if (FoodX < TempX[0]) {

           Instructions.push_back(LEFT);

           if (!CrashAt(TempX, TempY, LEFT)) { TempX[0]--; } else {
               while (CrashAt(TempX, TempY, LEFT)) {

                   for (int x = 0; x < 4; x++) {
                       switch (x) {
                           case UP: TempY[0]--; Instructions.back() = UP; break;
                           case DOWN: TempY[0]++; Instructions.back() = DOWN; break;
                           case LEFT: TempX[0]--; Instructions.back() = LEFT; break;
                           case RIGHT: TempX[0]++; Instructions.back() = RIGHT; break;
                       }

                       PlaceCursor(TempX[0], TempY[0]);
                       SetColor(GREEN, BLACK);
                       printf("X");

                       /* CRASHED STILL - REVERSE */
                       if (CrashAt(TempX, TempY, x)) {
                           switch (x) {
                               case UP: TempY[0]++; break;
                               case DOWN: TempY[0]--; break;
                               case LEFT: TempX[0]++; break;
                               case RIGHT: TempX[0]--; break;
                           }
                       } else { break; }

                       Sleep(500);
                   }
               }
           }

           for (int x = SnakeSize; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = SnakeSize; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");

               // If his head, is NOT on his tail (specifically his "tail") - draw a black square
               if (TempX[0] != TempX[SnakeSize] || TempY[0] != TempY[SnakeSize]) {
                   PlaceCursor(TempX[SnakeSize], TempY[SnakeSize]);
                   SetColor(BLACK, BLACK);
                   printf(" ");
               }
           }

       }

       if (FoodY > TempY[0]) {

           Instructions.push_back(DOWN);

           if (!CrashAt(TempX, TempY, DOWN)) { TempY[0]++; } else {
               while (CrashAt(TempX, TempY, DOWN)) {

                   for (int x = 0; x < 4; x++) {
                       switch (x) {
                           case UP: TempY[0]--; Instructions.back() = UP; break;
                           case DOWN: TempY[0]++; Instructions.back() = DOWN; break;
                           case LEFT: TempX[0]--; Instructions.back() = LEFT; break;
                           case RIGHT: TempX[0]++; Instructions.back() = RIGHT; break;
                       }

                       PlaceCursor(TempX[0], TempY[0]);
                       SetColor(GREEN, BLACK);
                       printf("X");

                       /* CRASHED STILL - REVERSE */
                       if (CrashAt(TempX, TempY, x)) {
                           switch (x) {
                               case UP: TempY[0]++; break;
                               case DOWN: TempY[0]--; break;
                               case LEFT: TempX[0]++; break;
                               case RIGHT: TempX[0]--; break;
                           }
                       } else { break; }

                       Sleep(500);
                   }
               }
           }

           for (int x = SnakeSize; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = SnakeSize; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");

               // If his head, is NOT on his tail (specifically his "tail") - draw a black square
               if (TempX[0] != TempX[SnakeSize] || TempY[0] != TempY[SnakeSize]) {
                   PlaceCursor(TempX[SnakeSize], TempY[SnakeSize]);
                   SetColor(BLACK, BLACK);
                   printf(" ");
               }
           }
       }

       if (FoodY < TempY[0]) {

           Instructions.push_back(UP);

           if (!CrashAt(TempX, TempY, UP)) { TempY[0]--; } else {
               while (CrashAt(TempX, TempY, UP)) {

                   for (int x = 0; x < 4; x++) {
                       switch (x) {
                           case UP: TempY[0]--; Instructions.back() = UP; break;
                           case DOWN: TempY[0]++; Instructions.back() = DOWN; break;
                           case LEFT: TempX[0]--; Instructions.back() = LEFT; break;
                           case RIGHT: TempX[0]++; Instructions.back() = RIGHT; break;
                       }

                       PlaceCursor(TempX[0], TempY[0]);
                       SetColor(GREEN, BLACK);
                       printf("X");

                       /* CRASHED STILL - REVERSE */
                       if (CrashAt(TempX, TempY, x)) {
                           switch (x) {
                               case UP: TempY[0]++; break;
                               case DOWN: TempY[0]--; break;
                               case LEFT: TempX[0]++; break;
                               case RIGHT: TempX[0]--; break;
                           }
                       } else { break; }

                       Sleep(500);
                   }
               }
           }

           for (int x = SnakeSize; x > 0; x--) { TempX[x] = TempX[x - 1]; TempY[x] = TempY[x - 1]; }

           if (DisplayAISnake) {
               Sleep(AIWait);

               SetColor(RED, BLACK);
               for (int x = SnakeSize; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

               SetColor(GREEN, BLACK);
               PlaceCursor(TempX[0], TempY[0]); printf("@");

               // If his head, is NOT on his tail (specifically his "tail") - draw a black square
               if (TempX[0] != TempX[SnakeSize] || TempY[0] != TempY[SnakeSize]) {
                   PlaceCursor(TempX[SnakeSize], TempY[SnakeSize]);
                   SetColor(BLACK, BLACK);
                   printf(" ");
               }
           }
       }

       PlaceCursor(0, 46);
       SetColor(YELLOW, BLACK);
       cout << "Instruction size: " << Instructions.size();
   }

       if (DisplayAISnake) {
           SetColor(BLACK, BLACK);
           for (int x = SnakeSize; x > 0; x--) { PlaceCursor(TempX[x], TempY[x]); printf("*"); }

           PlaceCursor(TempX[0], TempY[0]);
           SetColor(YELLOW, BLACK);
           printf("x");
       }

   return;
}

bool Crash(const int *X, const int *Y) {

   for (int x = SnakeSize - 1; x > 1; x--) {
       if (X[0] == X[x] && Y[0] == Y[x]) {
           SimulatedCrashes++;
           PlaceCursor(0, 0);
           SetColor(RED, BLACK);
           printf("%d", SimulatedCrashes);

           return true;
       }
   }

   return false;
}

void RunPath(const int color, int *SnakeX, int *SnakeY, vector<int> Instructions) {

   /* It'll crash sometimes if it randomly is empty */
   if (!Instructions.empty()) {

       SetColor(color, BLACK);
       for (int x = 0; x < Instructions.size(); x++) {

           switch (Instructions.at(x)) {
               case UP: SnakeY[0]--; break;
               case DOWN: SnakeY[0]++; break;
               case LEFT: SnakeX[0]--; break;
               case RIGHT: SnakeX[0]++; break;
           }

           Sleep(50);
           DrawSnake(color, SnakeX, SnakeY);

           if (Crash(SnakeX, SnakeY)) { TotalCrashes++; PlaceCursor(0, 1); SetColor(GREEN, BLACK); printf("%d", TotalCrashes); Sleep(250); }
       }
   }

   return;
}

void DrawSnake(const int color, int *SnakeX, int *SnakeY) {

   SetColor(color, BLACK);
   for (int x = SnakeSize; x > 0; x--) { SnakeX[x] = SnakeX[x - 1]; SnakeY[x] = SnakeY[x - 1]; }
   for (int x = SnakeSize; x > 0; x--) { PlaceCursor(SnakeX[x], SnakeY[x]); printf("*"); }

   SetColor(GREEN, BLACK);
   PlaceCursor(SnakeX[0], SnakeY[0]); printf("@");

   // If his head, is NOT on his tail (specifically his "tail") - draw a black square
   if (SnakeX[0] != SnakeX[SnakeSize] || SnakeY[0] != SnakeY[SnakeSize]) {
       PlaceCursor(SnakeX[SnakeSize], SnakeY[SnakeSize]);
       SetColor(BLACK, BLACK);
       printf(" ");
   }

   PlaceCursor(0, 47);
   SetColor(RED, BLACK);
   printf("Snake's current size: ");
   SetColor(WHITE, BLACK);
   printf("%d", SnakeSize);

   PlaceCursor(0, 48);
   SetColor(WHITE, BLACK);
   for (int x = SnakeSize; x > 0; x--) {
       printf("[");
       SetColor(CYAN, BLACK);
       printf("%d", SnakeX[x]);
       SetColor(WHITE, BLACK);
       printf(",");
       SetColor(GREEN, BLACK);
       printf("%d", SnakeY[x]);
       SetColor(WHITE, BLACK);
       printf("] ");
   }

   return;
}

void NewRun() {

   FoodX = rand() % 70 + 5;
   FoodY = rand() % 25 + 5;

   PlaceCursor(FoodX, FoodY);
   SetColor(YELLOW, BLACK);
   printf("x");

   return;
}

int main() {

   HANDLE OutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

   SetConsoleTitle("Snake - AI Module");
   SMALL_RECT windowSize = {0, 0, 79, 49};
   SetConsoleWindowInfo(OutHandle, TRUE, &windowSize);

   ClearConsole(BLACK, BLACK);
   RemoveCursor();

   srand(time(NULL));

   int SnakeX[100] = {'\0'};
   int SnakeY[100] = {'\0'};

   /* Spawn point for the Snake */
   SnakeX[0] = 35;
   SnakeY[0] = 25;

   while (true) {
       NewRun();
       CreatePath(SnakeX, SnakeY);
       RunPath(BLUE, SnakeX, SnakeY, Instructions);
   }

   cin.get();
   return 0;
}


Video of it running PROPERLY: http://www.youtube.com/watch?v=vi050-Y2v-4

This post was edited by Muted on Oct 8 2009 06:33pm
Go Back To Programming & Development Topic List
Prev12
Add Reply New Topic New Poll