fireside said:
Something you might want to think about for future projects is a path class. That's what I did, anyway. I instantiate it with the width and the height and then fill in an array with positions of walls, etc, and have a findPath function in it which returns an array of squares/nodes whatever.
This game is written using purely ASCII art. I believe I stated that in the first post.
The console has a set "grid" which at the time, I didn't think of. I don't know why.
It's an 80 x 50 grid (80 width, 50 height). The Snake is an "array" and every element of the Snake contains the X or Y position. Here's a quickie example:
SnakeX[0] is the head
SnakeY[0] is the head
If SnakeX[0] = 60 then it's on the 60th grid piece (width) - which is the far right
If SnakeX[0] = 0 then it's on the 0th grid piece (height) - which is the very top
I know what I have to do now, and I'm trying to do it. It's like the answer's on the tip of my tongue, but I just can't grasp it!
Again: This project is written in C++, not Java. I'm not a fan of OOP, nor have I ever been. Everytime I try writting something using objects, people tell me how wrong I am, and never show me the proper way (by example), rather, they attempt to lecture me and do an extremely poor job at it, or just simply say "Go spend money on this and that" and sir, I'm not spending money that I don't have to. This is a hobby for me, not a job, not a school thing, although I don't mind learning, I don't like spending money I don't have to.
Back to the point: The only thing that's wrong with the Snake right now, is the fact that he isn't exactly "looking ahead" by one square, and I know how to fix that, because I've done that in the past (before I tried making him have a predetermined "path" to the food). With the prepathing + avoiding the body, he should be able to (in theory) go further than just checking ahead of his body by one square. What I'm hoping is, if he ends up in a rut (all possible moves are crashes), I'll have him back up and take a different route, and so fourth.
I'm starting to understand the theory of using "Nodes" and I watched that old old link I posted in here, the CS Class showing the "Game-Tree-Algorithm" using Java with the Tic-Tac-Toe example. It really made me think, about how the whole "Fitness" and "Children" or "nodes" would work, and paticuarly how well they'd work with Snake - but as I've said before: Before I do that, I'd like to see if I can do this all on my own. It's an obsessive thing, that I'm actively and consciously doing, and since it's doing no harm, it's only making me think and learn, perhaps I'll be able to see how well an "Algorithm" such as the A* would work in a situation like this.
I can't quite remember all that much about the A* algorithm, but yes, I read a heck of a lot of tutorials, documentation (articles/"documentation") on it, and I don't know why it didn't sink in then, but hopefully it will now.
Again: Thank you for the support. :)
EDIT: Oh my God, I'm at like the LAST part (minute bug?)...! This works perfectly. 0 collisions for the AI Snake/real Snake!
if (FoodY < TempY[0]) {
Instructions.push_back(UP);
if (!CrashAt(TempX, TempY, UP)) { TempY[0]--; }
while (CrashAt(TempX, TempY, UP)) {
for (int x = 0; x < 4; x++) {
PlaceCursor(0, 5);
SetColor(GREEN, BLACK);
printf("%d, %d ", TempX[0], TempY[0]);
Sleep(1000);
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 (Crash(TempX, TempY)) {
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(250);
}
PlaceCursor(0, 5);
SetColor(GREEN, BLACK);
printf("%d, %d ", TempX[0], TempY[0]);
}
/* THIS HAS TO DO WITH ONLY UPDATING THE SNAKE'S POSITION AND DRAWING HIM */
/**************************************************************************/
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(" ");
}
}
}
EDIT: This is my last edit on this post I think... Last edited by Hyper : Today at 06:27 PM.
A'lass! Victory is mine! :)
#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 showing the working code above:
Thank you a lot, fireside. I'll try to convert this into an A* algorithm next.
I just wanted to see if I could do this on my own (and I did actually!). :)













