Jump to content


Is this considered "AI?"


43 replies to this topic

#1 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 14 March 2009 - 11:27 PM

This is the most basic (pure logic of just X vs. Y coordinates) AI I created on a whim for Snake:

void CREATE_SNAKE::AI() {


    if (Food[1] > Vertical[0]) { if (LastMove != CONTROL[0]) { LastMove = CONTROL[1]; } else { LastMove = CONTROL[2]; } } else

    if (Food[1] < Vertical[0]) { if (LastMove != CONTROL[1]) { LastMove = CONTROL[0]; } else { LastMove = CONTROL[2]; } } else

    if (Food[0] > Horizontal[0]) { if (LastMove != CONTROL[2]) { LastMove = CONTROL[3]; } else { LastMove = CONTROL[0]; } } else

    if (Food[0] < Horizontal[0]) { if (LastMove != CONTROL[3]) { LastMove = CONTROL[2]; } else { LastMove = CONTROL[0]; } }


    return;

}

Is it considered AI? Also, I have a book on AI (from 2001 I believe, "AI Techniques for Game Programming"), I've never really read it or put that much time/thought into it because I've been rather busy with school (was at the time), anyhow.

It has the typical, "Help Bob Through a Maze." Would that be more probable than what I have now? More or less, I'm clueless when it comes to AI and am looking for opinions on what to do. :)

Thanks!

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 5306 posts
  • LocationBellevue, WA

Posted 14 March 2009 - 11:58 PM

Sure. What you've created there is obviously very basic, but it's AI. To be specific, it's a rule-based system, because it consists of a set of rules that say "under these conditions, do this". There are many much larger research and industrial AI systems that are also rule-based, though with thousands or tens of thousands of rules.

Rule-based systems have the advantage that their behavior is more precisely controllable than with other approaches like fuzzy logic or neural networks; on the other hand, they are utterly unable to generalize to situations they haven't been designed for, and you need someone to build all the individual rules.
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 15 March 2009 - 12:06 AM

The main issue with the current AI (rule-based system) is: The Snake has a tendancy to crash into himself, like so:
Posted Image

How exactly would I go about having him plot (ahead of time) a "safe" path to the food where he doesn't hit himself (or a very low percentage of hitting himself so he isn't undefeatable)?

#4 Reedbeta

    DevMaster Staff

  • Administrators
  • 5306 posts
  • LocationBellevue, WA

Posted 15 March 2009 - 12:16 AM

For that you probably want a path-finding algorithm such as A* (pronounced A-star). You can treat the squares occupied by the snake as impassible. Unfortunately, that "AI Techniques for Game Programming" book doesn't seem to have anything about pathfinding in it (a very curious omission...) but there is a lot of information about it on the Web.
reedbeta.com - developer blog, OpenGL demos, and other projects

#5 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 15 March 2009 - 12:38 AM

That book does have something relating to "Pathfinding," but I think it was on the low standard/"quality" end of the scale (not because it's old but!) because it was a very simple example of how you could do it.

"C:\AI Techniques\example_code\Chapter03\Pathfinder" - Help Bob Through a Maze

However, it's not applicable to my problem (not that I'm aware of) because how I have the Snake game made.

const int CBobsMap::map[MAP_HEIGHT][MAP_WIDTH] = 

{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,

 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,

 8, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1,

 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1,

 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1,

 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1,

 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1,

 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 5,

 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,

 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

Is from one of the source-code files from the example. The Snake map isn't an array, the Snake itself is an array that travels about, and comparisons determine whether or not his array occupied the same space or if he passed out of bounds (same goes with hitting other Snakes).

I'm aware this is the web, it's why I'm asking here. :p

#6 Reedbeta

    DevMaster Staff

  • Administrators
  • 5306 posts
  • LocationBellevue, WA

Posted 15 March 2009 - 01:41 AM

I'm not sure what you mean about the map not being an array. From the code sample you posted, it sure looks like an array to me. :)

Anyway, as I've said, I think A* search is an appropriate algorithm for this. You should now go onto google and do some reading about it - then you can come back here if you have questions. No offense, but I'm not going to spoon-feed you the details. B)
reedbeta.com - developer blog, OpenGL demos, and other projects

#7 TheNut

    Senior Member

  • Moderators
  • 1699 posts
  • LocationThornhill, ON

Posted 15 March 2009 - 02:05 AM

AI Techniques for Game Programming is a book on adaptive and evolutionary AIs such as Neural Networks and Genetic Algorithms. It's a great book and in my opinion deals more with "AI" than simply resorting to "algorithmic" solutions, although that's not to discourage use of them ;) Do finish that book and when you're done read up on Evolutionary Neural Networks here http://www.cs.ucf.edu/~kstanley/. Good stuff.

Back to the OT, you can use an ANN or GA to move the snake, but you will need to train it (lookup supervised neural networks in the book). A good fitness function would be to reward the snake for moving towards the pebble and deduct points if it moves away from if or if it rams into itself or a wall. Although you will need to train it for each level, especially if you throw some weird things in to confuse the ANN. Most will agree using either of these techniques for this game is not a good idea.

Using a rule based system for this kind of game is possible, but would require tons of code and could still lead to collisions. Like Reedbeta said, using the A* algorithm would work very well for this game. I learned that algorithm from this guys webpage: http://www.geocities...ones/astar.html. I don't know if it's the original article I read, but it was very clear when I did read it. Just a place for you to start.
http://www.nutty.ca - Being a nut has its advantages.

#8 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 15 March 2009 - 02:23 AM

Reedbeta, I wasn't asking you to. You mis understand where I'm coming from apparently, but that's my fault for not being clear.
Speaking of being foggy: That was the example map from "Help Bob Through a Maze" not Snake. In Snake, there is no defined array (map) for the Snake to go through. He floats in space. He is an array himself (which holds the values of where his body is/how long).

This is how he is moved through space:
            for (int x = SnakeSize; x > 0; x--) { Horizontal[x] = Horizontal[x - 1]; Vertical[x] = Vertical[x - 1]; }

            for (int x = SnakeSize; x >= 0; x--) { PlaceCursor(Horizontal[x], Vertical[x]); printf("*"); }

But that isn't exactly relevant here, other than the fact that I don't quite see how the books example of Bob and the maze could fit with Snake and pathfinding to the food (avoiding only the exterior walls (simple) and his body (not simple)).

Thank you, TheNut, that link was helpful, is a start to finding an answer.
Yes, I'm aware google is my friend, I use him often enough to find links for other people. :)
(That and MSDN links)

#9 Reedbeta

    DevMaster Staff

  • Administrators
  • 5306 posts
  • LocationBellevue, WA

Posted 15 March 2009 - 03:33 AM

Pathfinding doesn't require all the locations of the map to be written out in advance and stored in an array. The game space of Snake is still a grid, so you can pathfind through it like through any grid, regardless of what kind of data structure is used to represent it. In this case you'd generate the grid locations on an as-needed basis during the search.
reedbeta.com - developer blog, OpenGL demos, and other projects

#10 imerso

    Senior Member

  • Members
  • PipPipPipPip
  • 431 posts
  • LocationBrasil

Posted 15 March 2009 - 03:45 AM

Just as a note to the topic: you could map your snake's position to that grid used by the book you're reading.

Lets say you have a screen resolution of 640x480. Lets say too that you're using a grid of 16x16 for the IA.

Simplifying, if you divide the X position of the snake by 40 (that is the same as 640 / 16), you get the X position scaled to the grid resolution that is 16. Dividing the Y position of the snake by 30 (that is, 480/16), you scale the Y coordinate of the snake to the grid as well.

So,


// better if you use reciprocal, but I'm keeping divisions

// to simplify an idea explanation.

float scaleX = (float)screenWidth / gridWidth;

float scaleY = (float)screenHeight / gridHeight;

int scaledSnakeX = (int)(snakeX / scaleX);  // discard precision

int scaledSnakeY = (int)(snakeY / scaleY) // discard precision


You get the position of the snake inside the grid that you'be using for the IA. The features (fruits, for instance) should then be positioned in coordinates that snap entirely to the grid, like 0x0, 16x16, 32x32, etc.

With this you could implement the book's algorithm with little modifications to your existing code.

EDIT: the code will work for screen of 80x25 as well, if you're in text mode.

#11 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 15 March 2009 - 07:54 PM

Yes, thank you for the comments and posts. I'm just an idiot. I don't have the map itself (yes, it's in "Text mode" - DOS shell) as an array, but the Snake itself is an array! Meaning if I passed it as a copy (I'm so used to doing it by reference I forget you can copy things), I could "Move in space ahead of time" without actually moving the Snake or taking 5 years to make a decision.

Thanks again for all the posts, and yes, I'm aware the "playing board" is still a grid (80 width by 25 height or 50 if it's full screen (true DOS)).

#12 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 18 March 2009 - 05:03 AM

Sorry for posting again so early, but! I'm curious... Isn't this a "dumbed down" version of A*?

#13 fireside

    Senior Member

  • Members
  • PipPipPipPip
  • 1586 posts

Posted 18 March 2009 - 06:54 AM

From what I've seen of the video, A* is a more specific application of that only used for finding the shortest path. I don't think you would need anything that complex for a snake game. If it was me I would have a head class and a body segment class. The head would respond to keyboard commands, each body segment would simply follow it's leader, or rather the last position of the leader since you would only make changes at the center point of each grid space, and move so many pixels each game loop. Decisions would only need to be made based on the collisions involving the head.
Currently using Blender and Unity.

#14 Nodlehs

    Valued Member

  • Members
  • PipPipPip
  • 152 posts

Posted 18 March 2009 - 04:36 PM

I find Amit explains things quite well, with lots of examples. Check out his site for information on path finding.

http://theory.stanfo...ameProgramming/

#15 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 19 March 2009 - 05:26 AM

fireside, I'm sorry, I mis interpreted what A* was specifically, and now I know (I think I do!). The Professor talking in the video is explaining a tree of possible moves (not path-finding). Sorry again.

Nodlehs, thank you. I'll be reading that for a while (lots to read! Many pages).
Before I came on here today, I decided to sit down and actually think on my own without trying to jumble my mind with "designing" one from somebody elses examples n' whatever else like I usually try and do (learn from the best... or second best).

Here's my out-come:
bool CREATE_SNAKE::CrashAt(const int Direction) {


    int CHECK_H = 0;

    int CHECK_V = 0;


    if (Direction == CONTROL[0]) { CHECK_V = Vertical[0] - 1; CHECK_H = Horizontal[0]; } else

    if (Direction == CONTROL[1]) { CHECK_V = Vertical[0] + 1; CHECK_H = Horizontal[0]; } else

    if (Direction == CONTROL[2]) { CHECK_H = Horizontal[0] - 1; CHECK_V = Vertical[0]; } else

    if (Direction == CONTROL[3]) { CHECK_H = Horizontal[0] + 1; CHECK_V = Vertical[0]; } else

    { MessageBox(NULL, "ERROR", "ERROR", MB_OK); exit(0); }


    /* Hit self */

    for (int x = SnakeSize - 1; x > 1; x--) {

        if (CHECK_H == Horizontal[x] && CHECK_V == Vertical[x]) {

            return true;

        }

    }


    /* Hit wall */

    if (CHECK_V == PlaceY || CHECK_V == Height + PlaceY - 1 ||

        CHECK_H == PlaceX || CHECK_H == Width + PlaceX - 1) {

            return true;

    }


    return false;

}


void CREATE_SNAKE::AI() {


/*

    Up    = CONTROL[0]

    Down  = CONTROL[1]

    Left  = CONTROL[2]

    Right = CONTROL[3]

*/


    int Move = 0;

    if (Food[1] > Vertical[0]) { Move = 1; }   /* The food is below */

    if (Food[1] < Vertical[0]) { Move = 0; }   /* The food is above */

    if (Food[0] > Horizontal[0]) { Move = 3; } /* The food is right */

    if (Food[0] < Horizontal[0]) { Move = 2; } /* The food is left  */


    /* Initially move towards the closest position to the food */

    if (LastMove != CONTROL[Move]) { LastMove = CONTROL[Move]; }


    /* If the Snake continues to hit himself, try another move */

    while (CrashAt(CONTROL[Move])) {


        /* Cycle through all four possible moves (North, South, West, and East) */

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

            if (x == 4) { LastMove = CONTROL[Move]; return; }

            Move = x;


            /* If you found a move that results in NO hit, use it */

            if (!CrashAt(CONTROL[Move])) { break; }

        }


        /* Assign the move */

        LastMove = CONTROL[Move];

    }


    return;

}

It took an over-all average of 60 (1 point per food) to about 120-200+! It's amazing really. I can't believe it took me SO long to think of it. Although.. I don't think that's considered (or even is) "path-finding," but more or less avoiding obstacles (which is still considered AI, right?).

Thanks again for the help (and links)... and putting up with my dumb questions. :) Appreciated!

EDIT: Wow! That was alot to read... But says alot too.

Why does this:

Quote

Recalculating paths

As time passes we expect the game world to change. A path found some time ago may no longer be the optimal path. It may be worth updating old paths with new information. Listed below are some criteria that could be used for determining when a recalculation is needed:
Every N steps: this guarantees that the information used to calculate the path is not more than N steps old.
Whenever extra CPU time is available: this allows dynamic adjustment of path quality; as more units are deployed, or if the game is running on a slower computer, CPU usage per unit can be decreased.
Whenever the unit turns a corner or passes a waypoint.
Whenever the world near the unit has changed.

The main drawback of path recalculation is that a lot of path information is thrown away. For example, if the path is 100 steps long and it is recalculated every 10 steps, the total number of path steps is 100+90+80+70+60+50+40+30+20+10 = 550. For a path of M steps, approximately M^2 path steps are computed over time. Therefore path recalculation is not a good idea if you expect to have many long paths. It would be better to reuse the path information instead of throwing it away.

Say that (the bolded text)? Why couldn't you just hold onto the old path information and just check for the units coordinates? It said earlier that the D* and LPA* algorithms weren't "Applicable" (for something) and quote, "besides, storing units information doesn't take that much, only unit coordinates." I mean, if you already have a path made, and only want to redo the entire! Path-finding again, just to avoid units.. Why not keep the old path and check if the units have crossed into it, and adjust accordingly (like slewing to the left or right by 1 or 2 squares so he's out of their way)?

Sorry, curious.

EDIT #2: Here's what I was talking about (part of it):

Quote

For a game with lots of moving units, you usually don't want to keep all that information around, so D* and LPA* aren't applicable

Earlier above that it said this though:

Quote

I personally don't see much need for ID-A* for finding paths on game maps. ID algorithms tend to increase computation time while reducing memory requirements. In map pathfinding, however, the "nodes" are very small―they are simply coordinates. I don't see a big win from not storing those nodes.

Why couldn't you just store the units positions and check them instead of trying to find a whole new path from scratch? :/

#16 fireside

    Senior Member

  • Members
  • PipPipPipPip
  • 1586 posts

Posted 19 March 2009 - 01:15 PM

Quote

fireside, I'm sorry, I mis interpreted what A* was specifically, and now I know (I think I do!). The Professor talking in the video is explaining a tree of possible moves (not path-finding). Sorry again.

No problem. I guess I should have read your posts more because I thought you were making a standard snake game, but you're making an automatic snake game that finds his own food. A* would be useful for that purpose and it's something you might want to look at later. Nice to see someone having some fun programming a game instead of trying to make the next big MMO.
Currently using Blender and Unity.

#17 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 19 March 2009 - 04:32 PM

fireside said:

No problem. I guess I should have read your posts more because I thought you were making a standard snake game, but you're making an automatic snake game that finds his own food. A* would be useful for that purpose and it's something you might want to look at later. Nice to see someone having some fun programming a game instead of trying to make the next big MMO.

I created Snake in less than a day back in December, just to see if I could (all on my own!), and it worked great! It had menus, cheat codes, etc. It was 100% structural/procedural. After a long while (somewhere in mid-February) of thinking on it, I converted it to a more object-oriented method, so I could easily add X Snakes to it. But I was over-tasking myself because I was trying to do too much at once (priorities! Snake == last not first), so I quit messing with it 'til early March when I had more time.

But since then, I wanted to make a smarter Snake (opponent), something that isn't dumb as simply follows X, Y coordinates, because (as shown above) he crashes into himself often and it's very discouraging (as a programmer) and disappointing (as a player). I've never been proficient with OOP either, so I saw it as a great opportunity to learn more about the object-oriented paradgim of programming. :)

Most things I write, I end up hard-coding because I'm a hobbyist programmer; I've never had a formal education on programming although I've programmed for a long time. I've never advanced far in the depths of knowledge of programming because I never knew where to look. It's like being in a library and having no map to say where the books are and what they contain! (Yes, I'm aware of google/MSDN now)

Sorry for the long reply, and being off-topic.. But before I go back on-topic, I have one really important question:
Did I comment my code well enough?

Thanks.

#18 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 15 June 2009 - 05:59 AM

I apologize if I came off as being condescending or arrogant 3-4 months ago. It's how people usually perceive me.

This is rather delayed, which I also apologize for! I have been having personal and private issues which is why I quit logging on so often as well, but that's aside the point of this post! :)

Here is the best A* based algorithm (I think that's how you'd refer to it) I could create! It isn't much of an improvement of what I had prior, but it is an improvement non-the-less. I will post the "required" functions from TextControl after the code, so you can compile it, along with a screen shot of it.

#include <iostream>

#include <vector>

#include <TextControl.h>

using namespace std;


int FoodX = rand() % 70 + 5;

int FoodY = rand() % 25 + 5;


int SnakeX[100];

int SnakeY[100];


vector<int> Instructions;


void CreatePath() {


    int DestX = FoodX;

    int DestY = FoodY;


    int TempX = SnakeX[0];

    int TempY = SnakeY[0];


    while (TempX != DestX || TempY != DestY) {

        if (DestX > TempX) { TempX++; Instructions.push_back(3); }

        if (DestX < TempX) { TempX--; Instructions.push_back(2); }

        if (DestY > TempY) { TempY++; Instructions.push_back(1); }

        if (DestY < TempY) { TempY--; Instructions.push_back(0); }

    }


    return;

}


void DrawSnake(const int color) {


    SetColor(color, BLACK);

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

    for (int x = 10; x >= 0; x--) { PlaceCursor(SnakeX[x], SnakeY[x]); printf("*"); }


    SetColor(GREEN, BLACK);

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

    if (SnakeX[0] != SnakeX[10] || SnakeY[0] != SnakeY[10]) { PlaceCursor(SnakeX[10], SnakeY[10]); SetColor(BLACK, BLACK); printf(" "); }


    return;

}


void RunPath(const int color) {


    /* It'll crash sometimes if it randomly is empty */

    if (!Instructions.empty()) {


        PlaceCursor(0, 1);

        SetColor(WHITE, BLACK);

        printf("Total moves: %i ", Instructions.size());


        PlaceCursor(0, 0);

        printf("Initial Snake position: %i, %i", SnakeX[0], SnakeY[0]);


        int X = SnakeX[0];

        int Y = SnakeY[0];


        //PlaceCursor(SnakeX[0], SnakeY[0]);

        //SetColor(RED, BLACK);

        //printf("x");


        SetColor(color, BLACK);

        for (int x = 0; x < Instructions.size() - 1; x++) {


            switch (Instructions.at(x)) {

                case 0: SnakeY[0]--; break;

                case 1: SnakeY[0]++; break;

                case 2: SnakeX[0]--; break;

                case 3: SnakeX[0]++; break;

            }


            Sleep(25);

            

            DrawSnake(color);


            //PlaceCursor(SnakeX[0], SnakeY[0]);

            //printf("x");

        }


        switch (Instructions.front()) {

            case 0: SnakeY[0]--; break;

            case 1: SnakeY[0]++; break;

            case 2: SnakeX[0]--; break;

            case 3: SnakeX[0]++; break;

        }


        PlaceCursor(FoodX, FoodY);

        SetColor(YELLOW, BLACK);

        printf("x");


        PlaceCursor(31, 0);

        SetColor(WHITE, BLACK);

        printf("- Current Snake position: %i, %i  ", SnakeX[0], SnakeY[0]);


        //Sleep(1000);


        /* To show the old path in a faded grey */

        SetColor(BLACK, BLACK);

        for (int x = 0; x < Instructions.size(); x++) {


            PlaceCursor(X, Y);

            printf("x");


            switch (Instructions.at(x)) {

                case 0: Y--; break;

                case 1: Y++; break;

                case 2: X--; break;

                case 3: X++; break;

            }

        }


        /* To ensure the food is erased */

        PlaceCursor(FoodX, FoodY);

        SetColor(BLACK, BLACK);

        printf(" ");

    }


    return;

}


void NewRun() {


    FoodX = rand() % 70 + 5;

    FoodY = rand() % 25 + 5;


    PlaceCursor(FoodX, FoodY);

    SetColor(YELLOW, BLACK);

    printf("x");


    Instructions.clear();


    return;

}


int main() {


    ClearConsole(BLACK, BLACK);

    RemoveCursor();


    srand(time(NULL));


    SnakeX[0] = 5;

    SnakeY[0] = 5;


    while (true) {

        CreatePath();

        RunPath(BLUE);

        NewRun();

    }


    cin.get();

    return 0;

}

- RemoveCursor
void RemoveCursor() {


    /* Remove the cursor (does not work in full screen) */

    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    CONSOLE_CURSOR_INFO CursoInfo;

    CursoInfo.dwSize = 1;         /* The size of caret */

    CursoInfo.bVisible = false;   /* Caret is visible? */

    SetConsoleCursorInfo(hConsole, &CursoInfo);


    return;

}

- SetColor
enum {

    BLACK       = 0,

    DARK_BLUE   = 1,

    DARK_GREEN  = 2,

    TEAL        = 3,

    DARK_RED    = 4,

    DARK_PURPLE = 5,

    GOLD        = 6,

    GREY        = 7,

    DARK_WHITE  = 8,

    BLUE        = 9,

    GREEN       = 10,

    CYAN        = 11,

    RED         = 12,

    PURPLE      = 13,

    YELLOW      = 14,

    WHITE       = 15

};


void SetColor(const int foreground) {


    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    SetConsoleTextAttribute(hConsole, foreground);


    return;

}


void SetColor(const int foreground, const int background) {


    int Color = foreground + (background * 16);

    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

    SetConsoleTextAttribute(hConsole, Color);


    return;

}

- PlaceCursor
void PlaceCursor(const int x, const int y) {


    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);


    COORD PlaceCursorHere;

    PlaceCursorHere.X = x;

    PlaceCursorHere.Y = y;


    SetConsoleCursorPosition(hConsole, PlaceCursorHere);

    return;

}

- ClearConsole
void ClearConsole(const int foreground, const int background) {


    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);


    COORD coordScreen = { 0, 0 };

    DWORD cCharsWritten;

    CONSOLE_SCREEN_BUFFER_INFO csbi;

    DWORD dwConSize;


    if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) { return; }

    dwConSize = csbi.dwSize.X * csbi.dwSize.Y;


    SetColor(foreground, background);

    if (!FillConsoleOutputCharacter(hConsole, (TCHAR) ' ', dwConSize, coordScreen, &cCharsWritten)) { return; }

    if (!GetConsoleScreenBufferInfo(hConsole, &csbi)) { return; }

    if (!FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten)) { return; }


    return;

}

As promised! The picture:
Posted Image

I hope to get some real feedback on this. It's quite appreciated (no matter how my mannerism is). :)
“You may be disappointed if you fail, but you are doomed if you don't try.”
Beverly Sills

#19 Hyper

    Valued Member

  • Members
  • PipPipPip
  • 195 posts

Posted 25 September 2009 - 06:56 AM

Almost a year later and basically nothing has changed: I can't think of anything else.

This is what I have (the entire main.cpp):
#include <iostream>

#include <vector>

#include <TextControl.h>

using namespace std;


int TotalRuns = 0;

int TotalCrashes = 0;


int FoodX = rand() % 70 + 5;

int FoodY = rand() % 25 + 5;


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

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


vector<int> Instructions;


void ShowPath() {


    int TempX = SnakeX[0];

    int TempY = SnakeY[0];


    SetColor(GREEN, BLACK);


    for (int x = 0; x < Instructions.size() - 1; x++) {


        switch (Instructions.at(x)) {

            case 0: TempY--; break;

            case 1: TempY++; break;

            case 2: TempX--; break;

            case 3: TempX++; break;

        }


        PlaceCursor(TempX, TempY);

        printf("!");

    }


    return;

}


bool Crash(const int X[], const int Y[]) {


    for (int x = 9; x > 1; x--) {

        if (X[0] == X[x] && Y[0] ==Y[x]) {

            //PlaceCursor(X[x], Y[x]);

            //SetColor(RED, BLACK);

            //printf("X");

            //Sleep(500);

            //TotalCrashes++;

            return true;

        }

    }


    return false;

}


void DrawSnake(const int color, int SnakeX[], int SnakeY[]);


void CreatePath() {


    /* Copy food location */

    int DestX = FoodX;

    int DestY = FoodY;


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

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


    /* Copy Snake location */

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

        TempX[x] = SnakeX[x];

        TempY[x] = SnakeY[x];

    }


    while (TempX[0] != DestX || TempY[0] != DestY) {

        if (DestX > TempX[0]) { TempX[0]++; Instructions.push_back(3); }

        if (DestX < TempX[0]) { TempX[0]--; Instructions.push_back(2); }

        if (DestY > TempY[0]) { TempY[0]++; Instructions.push_back(1); }

        if (DestY < TempY[0]) { TempY[0]--; Instructions.push_back(0); }


Sleep(5);


        DrawSnake(RED, TempX, TempY);


        /* Move the rest of the body */

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


        static int NewMove = 0;


        /* Oh noes! He crashed! Randomize a movement */

        while (Crash(TempX, TempY)) {


            SetColor(CYAN, BLACK);

            PlaceCursor(TempX[0], TempY[0]); printf("x");

            Sleep(500);


            /* Remove the OLD move */

            switch (Instructions.back()) {

                case 0: TempY[0]++; break;

                case 1: TempY[0]--; break;

                case 2: TempX[0]++; break;

                case 3: TempX[0]--; break;

            }


            NewMove++;


            /* Add the NEW move and ensure there's no crash... */

            switch (NewMove) {

                case 0: TempY[0]--; break;

                case 1: TempY[0]++; break;

                case 2: TempX[0]--; break;

                case 3: TempX[0]++; break;

            }


            Instructions.back() = NewMove;


            if (NewMove > 4) { NewMove = 0; goto DEAD_SNAKE; }

        }


/*

        for (int x = 9; x > 1; x--) {

            if (TempX[0] == TempX[x] && TempY[0] == TempY[x]) {

                PlaceCursor(TempX[x], TempY[x]);

                SetColor(RED, BLACK);

                printf("X");

                Sleep(500);

                TotalCrashes++;

            }

        }

*/

        //Sleep(5);

    }


DEAD_SNAKE:


    //ShowPath();


    return;

}


void DrawSnake(const int color, int SnakeX[], int SnakeY[]) {


    SetColor(color, BLACK);

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

    for (int x = 10; x >= 0; x--) { PlaceCursor(SnakeX[x], SnakeY[x]); printf("*"); }


    SetColor(GREEN, BLACK);

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

    if (SnakeX[0] != SnakeX[10] || SnakeY[0] != SnakeY[10]) { PlaceCursor(SnakeX[10], SnakeY[10]); SetColor(BLACK, BLACK); printf(" "); }


    return;

}


void RunPath(const int color) {


    /* It'll crash sometimes if it randomly is empty */

    if (!Instructions.empty()) {


        PlaceCursor(0, 1);

        SetColor(WHITE, BLACK);

        printf("Total moves: %i ", Instructions.size());


        PlaceCursor(0, 0);

        printf("Initial Snake position: %i, %i", SnakeX[0], SnakeY[0]);


        //PlaceCursor(SnakeX[0], SnakeY[0]);

        //SetColor(RED, BLACK);

        //printf("x");


        SetColor(color, BLACK);

        for (int x = 0; x < Instructions.size() - 1; x++) {


            switch (Instructions.at(x)) {

                case 0: SnakeY[0]--; break;

                case 1: SnakeY[0]++; break;

                case 2: SnakeX[0]--; break;

                case 3: SnakeX[0]++; break;

            }


            Sleep(15);


            DrawSnake(color, SnakeX, SnakeY);



        for (int x = 9; x > 2; x--) {

            if (SnakeX[0] == SnakeX[x] && SnakeY[0] == SnakeY[x]) {

                SetColor(WHITE, BLACK);

                PlaceCursor(SnakeX[x], SnakeY[x]);

                printf("X");

                //Sleep(500);

                TotalCrashes++;

            }

        }


            //PlaceCursor(SnakeX[0], SnakeY[0]);

            //printf("x");

        }


        switch (Instructions.front()) {

            case 0: SnakeY[0]--; break;

            case 1: SnakeY[0]++; break;

            case 2: SnakeX[0]--; break;

            case 3: SnakeX[0]++; break;

        }


        PlaceCursor(FoodX, FoodY);

        SetColor(YELLOW, BLACK);

        printf("x");


        PlaceCursor(31, 0);

        SetColor(WHITE, BLACK);

        printf("- Current Snake position: %i, %i  ", SnakeX[0], SnakeY[0]);


        //Sleep(500);


        /* To show the old path in a faded grey */

/*

        SetColor(BLACK, BLACK);

        for (int x = 0; x < Instructions.size(); x++) {


            PlaceCursor(X, Y);

            printf("x");


            switch (Instructions.at(x)) {

                case 0: Y--; break;

                case 1: Y++; break;

                case 2: X--; break;

                case 3: X++; break;

            }

        }


        /* To ensure the food is erased */

        PlaceCursor(FoodX, FoodY);

        SetColor(BLACK, BLACK);

        printf(" ");

    }


    return;

}


void NewRun() {


    FoodX = rand() % 70 + 5;

    FoodY = rand() % 25 + 5;


    PlaceCursor(FoodX, FoodY);

    SetColor(YELLOW, BLACK);

    printf("x");


    Instructions.clear();


    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));


    SnakeX[0] = 5;

    SnakeY[0] = 5;


    while (true) {

        CreatePath();

        RunPath(BLUE);

        NewRun();


        PlaceCursor(0, 3);

        SetColor(WHITE, BLACK);

        printf("Total runs: ");

        SetColor(CYAN, BLACK);

        printf("%d", TotalRuns);


        PlaceCursor(0, 4);

        SetColor(WHITE, BLACK);

        printf("Total crashes: ");

        SetColor(RED, BLACK);

        printf("%d", TotalCrashes);


        TotalRuns++;

    }


    cin.get();

    return 0;

}

He has a 1/8th collect/hit ratio (think that's how you'd say it).
Every 800,000 foods he collects, he has around 100,000 collisions.

If anybody has any suggestions/changes, let me know. :(
“You may be disappointed if you fail, but you are doomed if you don't try.”
Beverly Sills

#20 poita

    Senior Member

  • Members
  • PipPipPipPip
  • 322 posts

Posted 27 September 2009 - 10:11 PM

Well I would suggest using A* like everyone else has said. What you have there is nothing like A*.

What was it about A* that you didn't understand?





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users