I have recently finished developing my ASCII based snake game. As a newbie to C++ I would appreciate it if I was given feedback as to how I could improve my code when it comes to efficiency and readability.
#include <iostream>
#include <cstdlib>
#include <conio.h>
#include <vector>
using std::cout; using std::cin; using std::endl;
const int width = 20;
const int height = 20;
bool gameState = true;
int score;
struct fruitClass {
int x;
int y;
};
struct Node {
int x;
int y;
struct Node *next;
};
Node *newNode(int x, int y) {
Node *link = new(Node);
link->x = x;
link->y = y;
return link;
}
void rotateList(Node *head, int x, int y) {
int i;
Node *current = head;
int tempX[2], tempY[2];
for (i = 0; current != nullptr; i++) {
if (i == 0) {
tempX[0] = current->x;
tempY[0] = current->y;
current->x = x;
current->y = y;
}
else {
tempX[(i % 2 == 0) ? 0 : 1] = current->x;
tempY[(i % 2 == 0) ? 0 : 1] = current->y;
current->x = tempX[(i % 2 == 0) ? 1 : 0];
current->y = tempY[(i % 2 == 0) ? 1 : 0];
}
current = current->next;
}
}
void append(Node *&head, Node *&link) {
Node *ptr;
ptr = head;
head = link;
head->next = ptr;
}
Node *head;
fruitClass fruit;
class snakeClass {
private:
int posX;
int posY;
int tailLen = 0;
enum eDirection{STOP = 0, UP, LEFT, RIGHT, DOWN};
eDirection dir;
public:
void init () {
//Initialise snake's position
posX = width/2;
posY = height/2;
//Initialise first tail node
head = newNode(posX, posY);
//Spawn fruit
fruit.x = rand() % width;
fruit.y = rand() % height;
//Initialise score
score = 0;
//Initialise direction
dir = STOP;
}
void draw() {
system("cls");
//Draw top of playing field
for (int i = 0; i < width+2; i++)
cout << '#';
cout << '\n';
//Main draw
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
bool found = false;
//Draw left of playing field
if (j == 0)
cout << '#';
//Draw snake's head
if (i == posY && j == posX) {
cout << 'O';
found = true;
}
Node *current = head;
//Draw snake's body by extracting data from a linked list
while (current != nullptr && found != true) {
if (i == current->y && j == current->x) {
cout << 'o';
found = true;
}
current = current->next;
}
//If snake's body is not found check to see if there is anything else in the location
if (!found) {
if (i == fruit.y && j == fruit.x)
cout << 'F';
else
cout << ' ';
}
//Draw right side of playing field
if (j == width-1)
cout << '#';
}
//Add new lines where needed
if (i != height-1)
cout << '\n';
}
cout << '\n';
//Draw bottom of playing field
for (int i = 0; i < width+2; i++)
cout << '#';
cout << endl;
//Score screen
cout << "Score: " << score << " || Tail len: " << tailLen << '\n';
}
void input() {
//If a button is pushed
if (kbhit()) {
//Controls
switch(getch()) {
case 'W' : case 'w' :
if (dir == STOP)
dir = UP;
break;
case 'A' : case 'a' :
if (dir == STOP)
dir = LEFT;
else if (dir == UP)
dir = LEFT;
else if (dir == DOWN)
dir = RIGHT;
else if (dir == LEFT)
dir = DOWN;
else if (dir == RIGHT)
dir = UP;
break;
case 'S' : case 's' :
if (dir == STOP)
dir = DOWN;
break;
case 'D' : case 'd' :
if (dir == STOP)
dir = RIGHT;
else if (dir == UP)
dir = RIGHT;
else if (dir == DOWN)
dir = LEFT;
else if (dir == LEFT)
dir = UP;
else if (dir == RIGHT)
dir = DOWN;
break;
case 'x' : case 'X' :
gameState = false;
break;
}
}
}
void logic() {
//Movement and snake tail logic
switch (dir) {
case UP :
//Rotate list rotates the nodes for the snake tail
rotateList(head, posX, posY);
posY--;
break;
case DOWN :
//Rotate list rotates the nodes for the snake tail
rotateList(head, posX, posY);
posY++;
break;
case LEFT :
//Rotate list rotates the nodes for the snake tail
rotateList(head, posX, posY);
posX--;
break;
case RIGHT :
//Rotate list rotates the nodes for the snake tail
rotateList(head, posX, posY);
posX++;
break;
}
//Fruit logic (If fruit is picked up)
if (posX == fruit.x && posY == fruit.y) {
//Create new snake tail node
Node *link = newNode(posX, posY);
append(head, link);
//Add score
score += 100;
//Respawn fruit
fruit.x = rand() % width;
fruit.y = rand() % height;
//Play noise
cout << '\a' << endl;
//Update tail length score
++tailLen;
}
//Game over logic
//If snake goes out of bounds end game
if (posX > width-1 || posX < 0 || posY > height-1 || posY < 0)
gameState = false;
//If snake touches tail end game (Cycles through linked list nodes to check)
Node *current = head->next;
while (current != nullptr) {
if (posX == current->x && posY == current->y)
gameState = false;
current = current->next;
}
}
};
int main () {
//Run all the code
snakeClass snake;
snake.init();
while (gameState) {
snake.draw();
snake.input();
snake.logic();
}
return 0;
}