Main thread: Maintain the game logic, refresh the picture.
Background Thread: Monitor key (Getch)
Temporarily only supports the MinGW compilation under Windows, originally uses the MINGW compilation to want to write under the Linux also to be able to run. As a result, Linux does not directly provide the Getch () function (<conio.h> under Windows).
#include <limits.h>//For Int_max
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <pthread.h>
#include <list>
#include <stack>
#include <vector>
#include <algorithm>
#ifdef WIN32
#include <windows.h>
#include <conio.h>/For console I/O
# Define sleep (x) Sleep (1000 * (x))
#define MSLEEP (x) sleep (x)
#define CLEAR_TERM System ("CLS");
#else
#include <unistd.h>
#define MSLEEP (x) usleep (1000 * (x))
#define Clear_term System ("clear");
#endif
Namespace Game {
using namespace Std;
/////////////////////////////////////////////////////////////////////////////
For debug
#ifdef DEBUG
struct Logger {
File* out;
Logger (FILE *PF): Out (PF) {}
void operator () (const char *format, ...) {
Va_list args;
Va_start (args, format);
vfprintf (out, format, args);
Va_end (args);
}
};
Logger Outlogger (stdout);
Logger Errlogger (stderr);
#define LOG Outlogger
#define ERR Errlogger
void Logger (const char *format, ...)
// {
Va_list args;
Va_start (args, format);
vprintf (format, args);
Va_end (args);
// }
#else
#define LOGGER (FMT, ...) fmt
#define LOG Logger
#define ERR Logger
#endif
#define LOG Logger
#define LOG Outlogger
/////////////////////////////////////////////////////////////////////////////
Key values:
#define K_SPACE 32
#define K_ESC 27
#define K_W 119
#define K_S 115
#define K_A 97
#define K_D 100
Up, down, left, right pressed 1 key return 2 value
#define K_DIR 224//Ignore this
#define K_UP 72
#define K_DOWN 80
#define K_LEFT 75
#define K_right 77
Common constants:
#ifndef DELAY
#define GAME_CYCLE_MS 1000
#else
#define Game_cycle_ms DELAY
#endif
#define Max_body_len 128
#define MAX_FOOD_NUM 8
#define WIDTH 64
#define HEIGHT 24
Char constants:
#define Ch_border ' # '
#define Ch_blank '
#define Ch_snake ' * '
#define Ch_snakeh ' @ '
#define Ch_snaket ' + '
#define Ch_food ' $ '
#define Ch_mine ' # '
/////////////////////////////////////////////////////////////////////////////
Enum Direction
{
Unknow, up, down, left, right
};
struct POINT
{
int x;
int y;
Point (): X (0), y (0) {}
Point (int xx, int yy): X (xx), Y (yy) {}
BOOL operator== (const point &RHS) Const {
return x = = Rhs.x && y = = Rhs.y;
}
point& operator+= (const point &RHS) {
x + + rhs.x;
Y + + rhs.y;
return *this;
}
Point operator+ (const point &RHS) Const {
Point Res (RHS);
res.x = x;
Res.y = y;
return res;
}
#ifdef DEBUG
void Show () {
Log ("Point_%p: (%d,%d) \ n", this, x, y);
}
#endif
};
/////////////////////////////////////////////////////////////////////////////
/*
Dimension
0---x+
|
Y
+
*/
Point operator+ (const point &point, const Direction &dir)
{
Point pt (Point);
Switch (dir) {
Case up:
pt.y--; Break
Case down:
pt.y++; Break
Case left:
pt.x--; Break
Case right:
pt.x++; Break
Default
Err ("Error:point + Direction error!\n");
Break
}
Return pt;
}
/////////////////////////////////////////////////////////////////////////////
Class Snake
{
typedef std::vector<point> BODY_TYPE;
typedef body_type::iterator Body_iter;
typedef body_type::const_iterator Body_citer;
Direction dir; Forward direction
Point Body[max_body_len]; Body position
Body_type body; Body position
Public
Snake (): Dir (unknow) {}
Snake (Direction D): Dir (d) {}
void Setdir (Direction dir) {
This->dir = dir;
}
void Sethead (point P) {
Log ("sethead...\n");
P.show ();
if (body.size () = = 0) {
Body.insert (Body.begin (), p);
}
}
int length () const {return body.size ();}
Point getnode (int ino) const {return Body[ino];}
Point GetHead () const {
return *body.begin ();
}
Point Nexthead () const {
return GetHead () + dir;
}
BOOL Isonbody (Point pt) Const {
for (Body_citer it = Body.begin (); it!= body.end (); ++it) {
if (*it = = pt) return true;
}
return false;
}
BOOL Checkdir (Direction newdir) const {
if (dir = = up && Newdir = down
|| dir = down && newdir = up
|| Dir = left && Newdir = right
|| Dir = = Right && Newdir = left)
return false;
return true;
}
BOOL Selfcollision () const {
Point h = gethead (); Next time head position
if (length () > 1 && isonbody (h)) return true;
Body_citer it = Body.begin ();
for (++it; it!= body.end (); ++it) {
if (*it = = h) return true;
}
return false;
}
BOOL Changedir (Direction newdir) {
if (Checkdir (Newdir)) {
Setdir (Newdir);
return true;
}
else {
return false;
}
}
void Move () {
if (! selfcollision ()) {
Point NH = Nexthead ();
Body.insert (Body.begin (), NH);
Body.erase (Body.end ());
}
else {
Err ("Error:move failed! Direction incorrect!\n ");
}
}
void growth () {
Point NH = Nexthead ();
Body.insert (Body.begin (), NH);
}
void Putto (Point axis) {
For (Body_iter It=body.begin (); It!=body.end (); ++it) {
*it = axis;
// }
// }
#ifdef DEBUG
void Show () {
Log ("snake_%p:\n", this);
Log ("{\ n");
Log ("dir:%d,\n", dir);
Log ("Body: [");
for (Body_citer it = Body.begin (); it!= body.end (); ++it) {
Log ("(%d,%d),", It->x, It->y);
}
Log ("]\n}\n");
}
#endif
};
struct Playground
{
int width;
int height;
BOOL border;
Playground (): Width (0), height (0), border (true) {}
Playground (int w, int h, bool b): Width (w), Height (h), border (b) {}
X--width, y--height
BOOL Inarea (int x, int y) {
if (border) {
if (x < 1 | | | x >= width-1) return false;
if (Y < 1 | | | y >= height-1) return false;
}
else {//no Border
if (x < 0 | | | x >= width) return false;
if (Y < 0 | | | y >= height) return false;
}
return true;
}
BOOL Inarea (point P) {return Inarea (p.x, p.y);}
#ifdef DEBUG
void Show () {
Log ("playground_%p:\n", this);
Log ("{\ n");
Log ("width:%d,\n", width);
Log ("Height:%d,\n", height);
Log ("border:%d,\n", border);
Log ("}\n");
}
#endif
};
Enum GameState {
Gs_unknow,
Gs_start,
Gs_pause,
Gs_over,
Gs_exit
};
Class Game
{
Snake *snake;
Playground *ground;
Char buffer[height][width+2];
int foodcount;
Point *foodbuffer[max_food_num];
int time;
GameState State;
Std::stack<gamestate> Gsstack;
Public
Game (): Snake (New Snake (right)),
Ground (new Playground (WIDTH, HEIGHT, True)), Foodcount (0) {init ();}
~game () {
if (ground) delete ground;
if (snake) Delete snake;
}
void SetState (GameState gs) {state = GS;}
GameState getState () const {return state;}
void Pause () {
if (state!= gs_pause) {
state = Gs_pause;
Gsstack.push (state);
Log ("state:%d, statck.size ():%d\n", State, Gsstack.size ());
}
else {
State = Gsstack.top ();
Gsstack.pop ();
}
}
void Init () {
memset (buffer, 0, sizeof (buffer));
memset (foodbuffer, 0, sizeof (foodbuffer));
Point ph (2, GROUND->HEIGHT/3); Init head POS
Snake->sethead (ph);
Time = 0;
}
void Syncground () {//ground => Buffer
for (int x=0; x<ground->width; x + +) {
for (int y=0; y<ground->height; y++) {
if (Ground->border
&& (y = = 0 | | | y = = HEIGHT-1
|| x = = 0 | | x = = WIDTH-1)
){
BUFFER[Y][X] = Ch_border;
}
else buffer[y][x] = Ch_blank;
}
}
}
void Syncsnake () {//Snake => buffer
Point head = Snake->getnode (0);
Buffer[head.y][head.x] = Ch_snakeh;
for (int i=1; I<snake->length ()-1; i++) {
Point P = snake->getnode (i);
Buffer[p.y][p.x] = Ch_snake;
}
if (Snake->length () > 1) {
Point tail = Snake->getnode (Snake->length ()-1);
Buffer[tail.y][tail.x] = Ch_snaket;
}
}
void Syncfood () {//Foodbuffer => Buffer
for (int i=0; i<max_food_num; i++) {
Point *p = Foodbuffer[i];
if (NULL!= p) {
BUFFER[P->Y][P->X] = Ch_food;
}
}
}
void Draw () {//Buffer => Console
0. Clear last Buffer
memset (buffer, 0, sizeof (buffer));
1. Sync Playground
Syncground ();
2. Sync Snake
Syncsnake ();
3. Draw Food
Syncfood ();
4. Draw to console
for (int i=0; iPuts (Buffer[i]);
}
}
BOOL Checkpos (point P) Const {
Check for border
if (! Ground->inarea (p)) return false;
Check for snake
if (Snake->isonbody (p)) return false;
Check for foods
for (int i=0; i<max_food_num; i++) {
if (NULL!= foodbuffer[i] && p = = *foodbuffer[i]) {
return false;
}
}
return true;
}
void Genfood () {//Gen food => Foodbuffer
Log ("Food generate...\n");
if (Foodcount < Max_food_num) {
int x, y;
do {
x = rand ()% WIDTH;
y = rand ()% HEIGHT;
}while (! Checkpos (Point (x, y)));
for (int i=0; i<max_food_num; i++) {
if (NULL = = Foodbuffer[i]) {
Foodbuffer[i] = new Point (x, y);
Break
}
}
foodcount++;
Foodinfo ();
}
}
void Update () {//move snake once
++time;
Point NH = Snake->nexthead ();
Check for eating food
BOOL Willeat = false;
int foodidx =-1;
for (int i=0; i<max_food_num; i++) {
if (Foodbuffer[i] && *foodbuffer[i] = NH) {
Willeat = true;
Foodidx = i;
Break
}
}
if (willeat) {//Snake growth and food delete.
Snake->growth ();
Food Delete.
Delete Foodbuffer[foodidx];
FOODBUFFER[FOODIDX] = NULL;
foodcount--;
New food.
Genfood ();
}
else Snake->move ();
Check for wall collision
if (Ground->border) {
if (nh.x = 0 | | nh.x = = WIDTH-1
|| NH.Y = 0 | | Nh.y = = HEIGHT-1) {
state = Gs_over;
}
}
else {
Log ("Undefine ...");
Exit (-1);
}
Check for Slef colision
if (Snake->selfcollision ()) {
state = Gs_over;
Return
}
}
void Snaketrun (Direction d) {
Snake->changedir (d);
}
void info () {
printf ("Greedy snake! Length:%d time:%d\n ", Snake->length (), time);
}
#ifdef DEBUG
void foodinfo () {
log ("foodinfo: {\ n");
log ("Foodcount:%d,\n", Foodcount);
log ("Foodbuffer: [");
for (int i=0; i<max_food_num; i++) {
point *p = foodbuffer[i];
if (P) log ("(%d,%d)", p->x, P->y);
else log ("null");
log ("%s", I!= max_food_num-1?) ", " : "");
}
log ("]\n}\n");
}
void Show () {
Snake->show ();
Ground->show ();
Log ("buffer:\n");
#if 0
for (int i=0; ifor (int j=0; j<width; j) {
Log ("%02x", Buffer[i][j]);
}
Log ("\ n");
}
#endif
}
#endif
};
/////////////////////////////////////////////////////////////////////////////
volatile int keypressed = Int_max; Key buffer (only once)
volatile bool Isdirkey = false;
Game *pgame = NULL;
pthread_t Keylisener;
pthread_mutex_t Keybufferlock;
void* keylistenfun (void *args)
{
while (1) {
keypressed = Getch (); Getch'll bolck this thread.
Log ("Pressed:%d\n", keypressed);
}
return NULL;
}
void Init ()
{
Pgame = new Game ();
Srand (Time (NULL));
Pthread_mutex_init (&keybufferlock, NULL);
Pthread_create (&keylisener, NULL, keylistenfun, NULL); Creating a key listener thread
}
void Cleanup ()
{
Pthread_cancel (Keylisener);
Pthread_mutex_destroy (&keybufferlock);
Delete Pgame;
}
};
/////////////////////////////////////////////////////////////////////////////
int main (int argc, char *argv[])
{
using namespace game;
Init ();
Pgame->show ();
for (int i=0; i<max_food_num-1; i++)
Pgame->genfood ();
Pgame->draw ();
while (1) {
if (Gs_pause!= pgame->getstate ()) {
Clear_term
Switch (keypressed) {
Case K_space:pgame->setstate (Gs_pause); Break
Case K_UP:
Case K_w:pgame->snaketrun (UP); Break
Case K_down:
Case K_s:pgame->snaketrun (down); Break
Case K_left:
Case K_a:pgame->snaketrun (left); Break
Case K_right:
Case K_d:pgame->snaketrun (right); Break
Case K_esc:pgame->setstate (gs_exit); Break
}
Pgame->info ();
Pgame->update ();
Pgame->draw ();
}
else {
Switch (keypressed) {
Case K_space:pgame->setstate (Gs_start); Break
Case K_esc:pgame->setstate (gs_exit); Break
}
}
if (pgame->getstate () = = Gs_over) {
Puts ("Game over!");
Break
}
else if (pgame->getstate () = = Gs_exit) {
Puts ("exit!");
Break
}
keypressed = Int_max;
Msleep (Game_cycle_ms);
}
Cleanup ();
return 0;
}