Sign in to follow this  

Snake Game (console) - snake display method issue

This topic is 394 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

I'm working on a C++ console Snake game. I have the following code:

 

main.cpp

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

using namespace std;

int main()
{
	map Map;
	snake Snake;

	Map.initMap();
	Snake.initSnake();
	Map.updateMap();

	system("pause");

    return 0;
}

map.h

#pragma once

const int row = 20;
const int column = 80;

class map
{
protected:
	char area[row][column];
public:
	void initMap();
	void updateMap();
};

map.cpp

#include "map.h"
#include <iostream>

using namespace std;

void map::initMap()
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < column; j++)
		{
			if (i == 0 || i == row - 1 || j == 0 || j == column - 1)
			{
				area[i][j] = '#';
			} else {
				area[i][j] = ' ';
			}
		}
	}
}

void map::updateMap()
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < column; j++)
		{
			cout << area[i][j];
		}
	}
}

snake.h

#pragma once

#include "map.h"
#include <vector>

class snake : public map
{
protected:
	std::vector <char> snakeb;
public:
	void initSnake();
	void updateSnake();
};

snake.cpp

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

using namespace std;

void snake::initSnake()
{
	snakeb.push_back('@');  // head
	snakeb.push_back('o');
	snakeb.push_back('o');

	for (int i = 0; i < snakeb.size(); i++)
	{
		area[row / 2][column / 2 + i] = snakeb[i];  // the snake will spawn in the middle of the map
	}
}


The program compiles fine, but the actual snake body won't get printed in the console. My guess is that the issue is caused because of the inherited snake class.

 

I would appreciate if I get any piece of advice to better organise (best practice) my code, splitting the different parts of the game into different classes etc.

 

Thank you! :)

Share this post


Link to post
Share on other sites

It seems quite odd that your snake is-a map. This does not seem like a correct use of inheritance to me. I would rather you say that a map contains a snake, but that they are otherwise unrelated by inheritance.

 

Your direct, immediate problem is that you create both a map (called Map) and a snake (called Snake). That means you have two maps: Map and Snake (because snake is-a map). 

 

When you call initSnake on Snake, you fill Snake's area member variable with the graphics for the snake. But you then call Map's updateMap, which draws Map's area to the screen. Map's area is unrelated to Snake's area, and is in fact empty. You never draw the Snake's area by calling Snake.updateMap().

Share this post


Link to post
Share on other sites

Oh, I think I get it. I modified my main.cpp:

int main()
{
	snake Snake;

	Snake.initMap();
	Snake.initSnake();
	Snake.updateMap();

	system("pause");

    return 0;
}

Now seems to work fine.

 

However, is this the way you would recommend me to continue the program?

 

I want to split my program as this:

 

the map class to contain the methods for:

  • initial map drawing;
  • random fruit spawning on the map;

and the snake class to contain the methods for:

  • the initial snake body;
  • snake movement;
  • snake growing;
  • score update;
  • lose condition.

Share this post


Link to post
Share on other sites

I would suggest you drop the "using namespace std;" and used "std::" prefix as you do with std::vector.

("using namespace std;" basically pollutes your name space with everything inside "std::", which is a lot. In addition, it becomes harder to understand where you got things from, "std::vector" clearly says "I am coming from std name space", just "vector" might come from it, but maybe not.)

 

As for your questions, dropping the inheritance from the map sounds like a good idea, as suggested by Josh Petrie. You are also not a planet, even if you live on one.

Instead, let the snake live in the map. The map should know there can be a grid cell filled with a snake piece (just as it knows a grid cell can be filled with fruit).

The snake should track where all its pieces are, a vector of (x,y) coordinates could be useful, for example.

Share this post


Link to post
Share on other sites
You mean to define the initSnake() inside the map class and the rest of the methods concerning the snake inside the snake class?

Not that literal "live in the map", better keep snake initialization inside the snake file.

 

you might want to pass the starting position as parameter though, ie "snake::initSnake(int xstart, int ystart)"  or so.

Edited by Alberth

Share this post


Link to post
Share on other sites

Something like: 

snake::initSnake(int pos_x, int pos_y)
{
	snakeb.push_back('@'); //head
	snakeb.push_back('o');
	snakeb.push_back('o');

	for (int i = 0; i < snakeb.size(); i++)
	{
		snakeb[i][0] = pos_x;
		snakeb[i][1] = pos_y;
	}
}

That doesn't seem right.The snakeb is a single dimension array, how do I pass the starting position?

Share this post


Link to post
Share on other sites

"std::vector <char> snakeb;" says each element of the vector is a "char", ie a single character.

Sure enough, you cannot store a position in a single character.

 

So instead of a 'char', you need a struct or a class describing the fields in each element, like

struct snakeelement {
    char visual; // Visual displayed for this snake element.
    int xpos;    // X position of the element
    int ypos;    // Y position of the element.
};

In the snake you then make a vector of such elements:

std::vector<snakeelement> snakeb;

// And modify values with
snakeb[i].visual = ..
snakeb[i].xpos = ...

Instead of a "struct" you can use a "class", if you like.

I am not sure "snakeelement" is right, it seems somewhat unreadable to me. I'd use initial caps for type names, like "SnakeElement", but that doesn't fit your "snake" and "map" class names. Perhaps you have a better idea.

Share this post


Link to post
Share on other sites

Hmm, good question. You can often find the answers to such specific question at sites like stack overflow, in this case:

http://stackoverflow.com/questions/13812703/c11-emplace-back-on-vectorstruct

 

A little story to explain things is probably useful :)

The simple / old solution  (before C++11) is

SnakeElement se; // Make new element
se.visual = '@';
se.xpos = 123;
se.ypos = 109; // Fill it
snakeb.push_back(se); // Make a copy of 'se', and put it at the end of the 'snakeb' vector

You make a variable of the struct type, fill it, and then push the filled value at the end of the vector.

This works, but you're making a temporary variable, which is not always wanted.

 

The vector got fixed in C++11, by adding 'emplace_back'. While push_back can only append existing values (ie it forces you to have a copy of the new element around first), emplace_back can create a new element directly at the new position by using a constructor:

struct SnakeElement {
    // Constructor, initializes a new SnakeElement object.
    SnakeElement(char visual, int xpos, int ypos): visual(visual), xpos(xpos), ypos(ypos)
    {
    }

    char visual;
    int xpos;
    int ypos;
};

std::vector<SnakeElement> elements;
elements.emplace_back('@', 12, 10); // Create new space at the end of the vector, and call the SnakeElement constructor to initialize it.

There are also ways to do this for a struct without having the constructor. In your case it would look like

struct SnakeElement {  // No constructor here.
    char visual;
    int xpos;
    int ypos;
};

std::vector<SnakeElement> elements;
elements.emplace_back( SnakeElement{'@', 12, 10} );

This does the same as above in c++11. The "SnakeElement{'@', 12, 10}" make a new 'SnakeElement' for you, without having a SnakeElement::SnakeElement constructor.

Share this post


Link to post
Share on other sites

This topic is 394 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this