Jump to content

Wanting to make a Snake game...

- - - - -

This topic has been archived. This means that you cannot reply to this topic.
20 replies to this topic

#1
BlueMelon

BlueMelon

    Newbie

  • Members
  • PipPip
  • 28 posts
Heres my source:


#include <iostream>

#include <windows.h>




char Map[15][40] = {"#######################################", //40-1 = 39 : x

		            "#                                     #", //15-1 = 14 : y

		            "#                                     #",

		            "#                                     #",

		            "#                                     #",

      		        "#                                     #",

		            "#                                     #",

		            "#                                     #",

		            "#                                     #",

		            "#                                     #",

		            "#                                     #",

		            "#                                     #",

		            "#    x                                #",

		            "#######################################"};


unsigned short x; // Col's

unsigned short y; // Row's



bool left = false;

bool right = true;

bool up = false;

bool down = false;


void gotoxy(int x, int y)

{

static HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

static COORD WritePos;

WritePos.X = x; //-1 for borland compatibility??

WritePos.Y = y;

SetConsoleCursorPosition(hStdOut,WritePos);

}


void GetCoords()

{

	while(true)

	{


	Sleep(100);	

	for (unsigned short row(0);row<15; row++)

	{

		for (unsigned short col(0) ;col<40;col++)

		{

			if (Map[row][col] == 'x')

			{

				x = col;

				y = row;

				

				

			}

		}

	}

  }

}

void Input()

{

	while(true)

	{

	Sleep(100);

	if (GetAsyncKeyState(VK_RIGHT)!=0) // Right

				{

						right = true;

						up = false;

						down = false;

						left = false;

						

				}

				if (GetAsyncKeyState(VK_LEFT)!=0) // Left

				{

						right = false;

						up = false;

						down = false;

						left = true;

						

				}

				if (GetAsyncKeyState(VK_UP)!=0 ) // Up

				{

					right = false;

						up = true;

						down = false;

						left = false;

					

				}

				if (GetAsyncKeyState(VK_DOWN)!=0)// Down

				{

					right = false;

						up = false;

						down = true;

						left = false;

						

					

				}

	}

}

void snake()

{

	while(true)

	{

	if (down == true)

		{

			if (Map[y+1][x] == '#' || Map[y+1][x] == '*')

			{

			//gameover = true

			}else{

				Map[y][x] = '*';

				Map[y+1][x] = 'x'; 

				

			}

			

		}

		if (right == true)

		{

			if (Map[y][x+1] == '#' || Map[y][x+1] == '*')

			{

			//gameover = true

			}else{

				Map[y][x] = '*';

				Map[y][x+1] = 'x'; 

				

			}

			


			

		}

		if (left == true)

		{

			if (Map[y][x-1] == '#' || Map[y][x-1] == '*')

			{

			//gameover = true

			}else{

				Map[y][x] = '*';

				Map[y][x-1] = 'x'; 

				

			}

		}

		if (up == true)

		{

			if (Map[y-1][x] == '#' || Map[y-1][x] == '*')

			{

			//gameover = true

			}else{

				Map[y][x] = '*';

				Map[y-1][x] = 'x'; 

				

			}


		}

		Sleep(100);

	}		

}

int main()

{


	CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetCoords,0,0,NULL);

	CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Input,0,0,NULL);

	CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)snake,0,0,NULL);


	while(true)

	{

		gotoxy(0,0);

		for (unsigned short row(0); row<15; row++)

			{

				std::cout << Map[row] << "\n";

			}

	}



	return 0;


}


Its very messy.. I think I need to learn how to use structures..

I dont know how to delete the end of the tail.. Thats pretty much whats keeping me back.

I includes:
-Mouvement
-and tail

But I cant delete the tail..
will add the random fruit later.

Im stuck :crying:

#2
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
Before you even get started worrying about the tail, you have several more severe problems with your code. I highly suggest you fix these before going on with trying to get the tail problem solved, because otherwise you're going to get unpredictable results if you don't fix these.

1) You have a huge problem here with your multithreading. You can't share global variables like that among threads without some way of controlling which thread has access to the variable. Imagine if one thread decided to write to a variable at the same time that another read from it? Or what if two threads write to the same variable at the same time? What's going to happen? You're going to get unpredictable results. This is only necessary for variables that are written to. If your threads are only reading, then there's no need to protect it with a mutex. If, however, even one thread writes to it, you need a mutex.

2) You don't wait for any of your threads to finish. While this doesn't have any bad consequences with this current application, it's very bad practice not to wait for your threads to finish.

3) You're lucky your thread procedures even run. There's a specific template they're supposed to follow, and you violate that.

4) You don't save the handles to your threads. Again bad practice. You really should close the handles after you're done.

To solve the multiple access problem, you need what's called a mutex, which is short for MUTual EXclusion. I'll show you a brief example of how to use them with Windows API. Non-obvious changes are in red.
#define THREADCOUNT /*Something. In your case 3*/

/* This MUST be a global variable so all threads
can access it.*/
[COLOR="RED"]HANDLE ghMutex;[/COLOR]

int main()
{
    HANDLE aThreads[THREADCOUNT];
    DWORD aThreadIds[THREADCOUNT];
    unsigned int i;

    /* Use the default options for creating a mutex. look on
    MSDN if you want more information. I've provided the
    link below. Anyway, we need to create this mutex to
    protect the Map array.*/
    [COLOR="RED"]ghMutex = CreateMutex(NULL,FALSE,NULL);[/COLOR]

    /* Create your threads */
    aThreads[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetCoords,[COLOR="RED"]NULL[/COLOR],0,[COLOR="RED"]&aThreadIds[0][/COLOR]);
    aThreads[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)Input,[COLOR="RED"]NULL[/COLOR],0,[COLOR="RED"]&aThreadIds[1][/COLOR]);
    aThreads[2] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)snake,[COLOR="RED"]NULL[/COLOR],0,[COLOR="RED"]&aThreadIds[2][/COLOR]);

    /* Drawing loop - changes here are IMPORTANT. My change
    of the terminating condition to the escape key is just
    a suggestion. Feel free to ignore that bit if you want.
    */

    while(GetAsyncKeyState(VK_ESCAPE) == 0)
    {
        /* wait for read access to the map */
        [COLOR="RED"]WaitForSingleObject(ghMutex,INFINITE);[/COLOR]

        gotoxy(0,0);
        for (unsigned short row(0); row<15; row++)
            std::cout << Map[row] << "\n";

        /* relinquish control of the map */
        [COLOR="RED"]ReleaseMutex(ghMutex);[/COLOR]
    }

    /* Clean up - release threads and mutex*/
    [COLOR="RED"]WaitForSingleObject(ghMutex,INFINITE);[/COLOR]
    for(i = 0; i < THREADCOUNT; ++i)
    {
        TerminateThread(aThreads[i], 0);
        CloseHandle(aThreads[i]);
    }
    [COLOR="RED"]ReleaseMutex(ghMutex);
    CloseHandle(ghMutex);[/COLOR]

    return 0;
}
Basically, whenever a thread needs to read or write to Map, it has to acquire the mutex first, then do its thing, then unlock it. Do not forget to unlock it. Otherwise your threads will get stuck and hang, waiting for a resource that will never become available.

Now, as far as your procedures violating convention...your thread functions must be declared like so:
DWORD WINAPI MyThreadFunction(LPVOID myArgument);
You can only take one argument--a void pointer. Nothing else. The WINAPI declarator is especially important.

Mutex Objects

If you have any questions, just ask!

Edited by dargueta, 27 February 2010 - 01:42 AM.
Edited formatting for readability

sudo rm -rf /

#3
BlueMelon

BlueMelon

    Newbie

  • Members
  • PipPip
  • 28 posts
Wow thats great! thanks =] Never knew that.

#4
joyo

joyo

    Newbie

  • Members
  • PipPip
  • 17 posts
Though the code use the multithreading, and use the global variables , but the code modify the global variables only in one of them. So I think the problem is not serious.

#5
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts

joyo said:

Though the code use the multithreading, and use the global variables , but the code modify the global variables only in one of them. So I think the problem is not serious.

Yes, it is. snake() modifies it, and main() reads it. Thus you have a race condition.
@BlueMelon: Post your code again once you've fixed the race conditions so I can figure out your tail problem.

EDIT: I just realized you're either going to have to use one mutex for controlling access to Map, X, Y, left, right, up, and down, or you're going to have to use multiple mutexes. I suggest you weigh the cost of multiple mutexes (and possible unexpected--not unpredictable--results) against using one mutex (complete lockout and slower execution).
sudo rm -rf /

#6
BlueMelon

BlueMelon

    Newbie

  • Members
  • PipPip
  • 28 posts
I have decided to let down the Snake Project for now, I dont think im doing it correctly.

#7
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
Awww...come on, man. I'll help you out if you need it.
sudo rm -rf /

#8
BlueMelon

BlueMelon

    Newbie

  • Members
  • PipPip
  • 28 posts
I completely re-wrote it, this is what I got.
#include <iostream>
#include <windows.h>
using namespace std;



char Map[15][40] = {
			"***************************************", //40-1 = 39 : x
		    "*                                     *", //15-1 = 14 : y
		    "*                                     *",
		    "*                                     *",
		    "*                                     *",
      		"*                                     *",
		    "*                                     *",
		    "*                                     *",
		    "*                                     *",
		    "*                                     *",
		    "*                                     *",
		    "*                                     *",
		    "*                                     *",
		    "***************************************"};


struct Pos
{
	short x;
	short y;
}player,tail;
///
int size = 3;
bool gameover;
///
int stats(0);
unsigned short level(1);
///

void ClearScreen()
{
HANDLE hOut;
COORD Position;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
Position.X = 0;
Position.Y = 0;
SetConsoleCursorPosition(hOut, Position);
}
void CreatFood ()
{
	//Will do
}
void Input()
{
	
	if (GetAsyncKeyState(VK_RIGHT)!=0) // RIGHT
	{
		if(!(Map[player.y][player.x+1] == '*'|| Map[player.y][player.x+1] == 'o'))
		{
			player.x++;
		}else
		{
			gameover = true;
		}
		

	}
	else if (GetAsyncKeyState(VK_LEFT)!=0) //LEFT
	{
		if(!(Map[player.y][player.x-1] == '*'|| Map[player.y][player.x-1] == 'o'))
		{
			player.x--;
		}else
		{
			gameover = true;
		}
		
	}
	else if (GetAsyncKeyState(VK_UP)!=0) //UP
	{
		if(!(Map[player.y-1][player.x] == '*' || Map[player.y-1][player.x] == 'o'))
		{
			player.y--;
		}else
		{
			gameover = true;
		}
		
		
	}
	else if (GetAsyncKeyState(VK_DOWN)!=0) //DOWN
	{
		if(!(Map[player.y+1][player.x] == '*')|| Map[player.y+1][player.x] == 'o')
		{
			player.y++;
		}else
		{
			gameover = true;
		}
		
	}
	

}

int main()
{
	player.y = 12;
	player.x = 5;
	
	while(gameover == false)
	{
		ClearScreen();
		Input();

		Map[player.y][player.x] = 'o'; // Set Snake Head


		//Id like to delete tail here
		//tail.x = ?
		//tail.y = ?
		//Map[tail.y][tail.x] = ' ';
		
		for(int i(0);i<15;i++)
		{
			if (i != 14)
			{
			cout << Map[i] << "\n"; // Display Map
			}else
			{
			cout << Map[i];
			}
		}
		
		cout << "*                                     *\n" << "* Lvl: " << level << "                              *" << "\n***************************************";

	}
	while(gameover == true)
	{
		// will do
	}



	return 0;

}
I need a way to keep the snake moving at all times depending on the direction, I really dont think that the way I did it previously is good performance wise..

dargueta you got Msn?

#9
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
To be honest, I preferred your old version, as it was clear what was going on.
EDIT: It's not letting me post my version...this is getting really annoying.

EDIT: Ok, I attached it.

Attached Files


sudo rm -rf /

#10
BlueMelon

BlueMelon

    Newbie

  • Members
  • PipPip
  • 28 posts
The new one is less code and it uses a structure, I dont need the function to check X/Y, its stored in the structure
player.x
player.y

But now my problem is to find tail.x and tail.y to delete them..

#11
bobdark

bobdark

    Programmer

  • Members
  • PipPipPipPip
  • 164 posts
I think you might want to invest more time in design phase and consider maybe using already existing tools to implement what you want.
Just a thought (didn't really spent time thinking into it), but maybe its worth considering representing your snake with a help of a data structure. I had in mind linked list (or a queue), particularly one that has head and tail fields.
In my opinion it might be good because it provides you with access to head and tail of the snake in O(1) time (and it seems to me having access to those two is enough for moving the snake), and its structure is similar to that of the snake - each internal node will hold a position of a square in snake's body. Advancing the snake is basically removing the tail node and adding new head node..

I might be wrong and there might be some serious issues with list, as I said its just a quick thought, but right now I can't think of any..
In general try using already existing tools because it will make it easier in both thought and implementation phase, make the code more readable, etc..

#12
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,717 posts
EDIT: Sorry bobdark, copied the wrong part of your post. My bad. :D

Quote

Just a thought (didn't really spent time thinking into it), but maybe its worth considering representing your snake with a help of a data structure. I had in mind linked list (or a queue), particularly one that has head and tail fields.
Great idea! You can do something like the following:

typedef struct
{
    unsigned int x;
    unsigned int y;
    SNAKE_SEGMENT() : x(0), y(0) {}
}SNAKE_SEGMENT;

list<SNAKE_SEGMENT> mysnake;
And that'd be about it as far as moving the snake goes.
sudo rm -rf /