Advertisement

Snake game (console app) - move function

Started by January 21, 2017 04:46 PM
6 comments, last by dmatter 7 years, 11 months ago

Hello,

I have the following code and I need some help:

Snake.h


#pragma once

#include <vector>
#include <iostream>

enum direction { Up = 'w', Down = 's', Left = 'a', Right = 'd' };

class snake
{
private:	
	direction dir;
	struct SNAKE
	{
		int x;
		int y;
		char body;
	};
	std::vector<SNAKE> Snake;
public:
	snake();
	void startPos();
	//void checkValidMove(direction dir);
	void move(direction dir);
};

Snake.cpp


#include "snake.h"
#include <iostream>
#include <Windows.h>

snake::snake()
{
	Snake.emplace_back(SNAKE{ 10,12,'@' });
	Snake.emplace_back(SNAKE{ 10,13,'o' });
	Snake.emplace_back(SNAKE{ 10,14,'o' });
}

void snake::startPos()
{
	COORD coord;
	for (int i = 0; i < Snake.size(); i++)
	{
	coord.X = Snake[i].x;
	coord.Y = Snake[i].y;
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
	std::cout << Snake[i].body;
	}	
}


//continuos move of the snake in a direction

void snake::move(direction dir)
{
		COORD coord;
		switch(dir)
		{
		case(Up):
			for (int i = 0; i < Snake.size(); i++)
			{
				coord.X = Snake[i].x;
				coord.Y = Snake[i].y-1;
				SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
				std::cout << Snake[i].body;
			}
			break;
		case(Down):
			for (int i = 0; i < Snake.size(); i++)
			{
				coord.X = Snake[i].x;
				coord.Y = Snake[i].y + 1;
				SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
				std::cout << Snake[i].body;
			}
			break;
		case(Left):
			for (int i = 0; i < Snake.size(); i++)
			{
				coord.X = Snake[i].x - 1;
				coord.Y = Snake[i].y;
				SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
				std::cout << Snake[i].body;
			}
			break;
		case(Right):
			for (int i = 0; i < Snake.size(); i++)
			{
				coord.X = Snake[i].x + 1;
				coord.Y = Snake[i].y;
				SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
				std::cout << Snake[i].body;
			}
			break;
		}		
}


Main.cpp


// Snake.cpp : Defines the entry point for the console application.
//

#include "snake.h"
#include "map.h"
#include <stdlib.h>
#include <conio.h>
#include <iostream>

using namespace std;

int main()
{

	map MAP;
	MAP.draw();

	snake SNAKE;
	SNAKE.startPos();
	char input = _getch();

	direction dir = Up;

	SNAKE.move(dir);
		
	system("pause");
    return 0;
}


In Snake.cpp, the move(direction dir) method doesn't work as intended. I wanted it to continuously move the snake. (I know I will have to store the position of the snake's head and tail in order to properly change the direction, but for now, this is what I have). How do I exactly make it move when I press the 'w' key for example?

Also, is it ok to initialize in Main.cpp the dir with Up value?

How do I exactly make it move when I press the 'w' key for example?

The Snake vector contains all segments of the snake at a certain position. The normal procedure is to draw that data to the screen.

In that way, you can detect eg a collision by checking the positions of the head and each segment, and if their x/y coordinate matches, you know it's game over.

To show where exactly the snake is to the player, you should draw each segment at the position that is stored in the vector.

So far you have a fixed snake position, drawn at the point where the vector says it should be drawn.

Making the snake move is now a simple matter of changing the positions stored in the vector. The draw routine will then draw the snake at the new position all by itself.

How to change the positions is a bit different from what you try now. I would suggest to first draw a snake at some grid paper, and then draw it again after moving 1 position. After you did that compare the positions of the segments. Once you understand how segments move, coding is simple.

Also, is it ok to initialize in Main.cpp the dir with Up value?

If I remember correctly, the snake always moved, right? Well, if it should also move at the start of the game (ie the player has to take control immediately), then you must pick a direction. A fixed direction is one way to do that, and not a bad one, imho.

If the snake should wait until the player presses the first movement button, then likely you want a special 'stopped' value there.

Advertisement

Making the snake move is now a simple matter of changing the positions stored in the vector. The draw routine will then draw the snake at the new position all by itself.

Well, could you point out what exactly is wrong in my move method?

For example:


case(Up):
			for (int i = 0; i < Snake.size(); i++)
			{
				coord.X = Snake[i].x;
				coord.Y = Snake[i].y-1;
				SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
				std::cout << Snake[i].body;
			}
			break;

This should move the snake up, but it doesn't really work as intended. It actually moves the snake up once, than it won't move. In Main.cpp I changed the following:


while(true)
{
SNAKE.move(dir);
}

Also, how can I delete the tail in the move method?

This should move the snake up, but it doesn't really work as intended. It actually moves the snake up once, than it won't move.
Consider what you're doing here.

You query the position of a segment, displace that position by 1 in y position, and draw it at that location.

Next iteration:

You query the position of a segment, displace that position by 1 in y position, and draw it at that location.

Next iteration:

You query the position of a segment, displace that position by 1 in y position, and draw it at that location.

How would move 2 positions ever?

Since you never change the position of the segment itself, you repeatedly draw the snake one level above itself.

Well, could you point out what exactly is wrong in my move method?
You move all the segments up, now consider what that will do with a snake that has a bend in it.

Also, how can I delete the tail in the move method?
Huh? does the snake get shorter?

Maybe you need to clear the screen between drawing the snake. Another solution is to print a space at the old tail position.

Since you never change the position of the segment itself, you repeatedly draw the snake one level above itself.

I don't get it. Isn't that the purpose of the moveUp position? Just to redraw the whole snake one position upper, delete the old last position and repeat the whole process all over again?

ok, an example. Let's say the snake is 1 segment, at (10, 12).

You take it x&y position, and draw the segment at (10, 11).

Next iteration:

The snake is still at (10, 12), since you didn't change the snake segment position.

You take it x&y position, and draw the segment at (10, 11).

Next iteration:

...

For comparison:


y=12
print(y-1)
print(y-1)
print(y-1)
# What's the value of y now? Why is it not 9?

The problem with computers is that they do what you say. In particular they do not do what you mean to happen, but didn't write.

When a problem pops up, you must forget how it should behave, and read the code to check what you really told the computer to do.

Advertisement

Crystal clear. Thank you so much!

I would suggest rethinking your strategy a little. I get the impression that you are planning to update the state of each snake segment to make the snake move? At the very least you must update the state of the head element to set it to a body char.

I suggest modelling the movement simply as this: You add a new segment to the front of the list (in the direction of movement) and the back-most segment is removed from the list.

None of the mid-body elements need to be changed at all. The snake is still N segments long (you add 1 and remove 1 but it balances overall) and if you want to grow the snake by 1 then you simply do not remove the back-most element but you still add one to the front.

You wouldn't want (or even need) to store the body char in the segments themselves either. You simply draw the 1st element in the list with an '@' and the rest with an 'o'. The segments themselves don't care if they are head/body/tail elements.

This topic is closed to new replies.

Advertisement