mingw编译的windows命令行贪吃蛇示例
主线程:维护游戏逻辑,刷新画面。
后台线程:监听按键(getch)
暂时只支持Windows下的MinGW编译,本来用MinGW编译是想写成Linux下也能运行的。结果Linux下面没有直接提供getch()函数(Windows下的<conio.h>有)。
管他呢!纯属娱乐~
编译要加-lpthread
snake_cmd.cpp
#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; // 前进方向
// Point body[MAX_BODY_LEN]; // 身体位置
body_type body; // 身体位置
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; i<HEIGHT; i++) {
puts(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; i<HEIGHT; i++) {
for(int j=0; j<WIDTH; j++) {
log("%02X ", buffer[i][j]);
}
log("\n");
}
#endif
}
#endif
};
/////////////////////////////////////////////////////////////////////////////
volatile int keyPressed = INT_MAX; // 按键缓冲(只记录一次)
volatile bool isDirKey = false;
Game *pGame = NULL;
pthread_t keyLisener;
pthread_mutex_t keyBufferLock;
void* keyListenFun(void *args)
{
while(1) {
keyPressed = getch(); // getch will 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); // 创建按键侦听线程
}
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;
}
Makefile
debug: snake_cmd.cpp
g++ snake_cmd.cpp -lpthread -DDEBUG -DDELAY=1000 -g -Wall
release: snake_cmd.cpp
g++ snake_cmd.cpp -lpthread -o snake -DDELAY=1000
相关文章
- VPN可以虚拟出一个专用网络,让远处的计算机和你相当于处在同一个局域网中,而中间的数据也可以实现加密传输,用处很大,特别是在一些大公司,分公司处在不同的区域。...2016-01-27
- 这篇文章主要介绍了Windows批量搜索并复制/剪切文件的批处理程序实例,需要的朋友可以参考下...2020-06-30
- 这篇文章主要为大家详细介绍了C#创建一个简单windows窗体应用的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
- 这篇文章主要介绍了Powershell 查询 Windows 日志的方法,需要的朋友可以参考下...2020-06-30
- 这篇文章主要介绍了C#实现windows form限制文本框输入的方法,涉及C#限制文本框输入的技巧,非常具有实用价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了C#命令行编译器配置方法,本文讲解了配置C#命令行编译器、配置其它.NET命令行工具、通过csc命令行编译器来编译C#文件实例等内容,需要的朋友可以参考下...2020-06-25
- 本文实例讲述了JS实现不使用图片仿Windows右键菜单效果代码。分享给大家供大家参考,具体如下:这里演示JS不使用图片仿Windows右键菜单效果,这款代码灵活使用了文鼎字,配合CSS和JS做出了这个和系统右键菜单很相似的东东。...2015-10-23
C# Windows API应用之基于GetDesktopWindow获得桌面所有窗口句柄的方法
这篇文章主要介绍了C# Windows API应用之基于GetDesktopWindow获得桌面所有窗口句柄的方法,结合实例形式分析了GetDesktopWindow函数用于获取窗口句柄的具体使用方法与相关注意事项,需要的朋友可以参考下...2020-06-25- 作为一个新生事物,Linux吸引了不少眼球,但是它能否快捷、方便地与Windows资源共享,是一个很重要的问题。大家知道,Windows之间可以利用“网络邻居”来实现资源共享,而Linux...2016-11-25
- 这篇文章主要介绍了利用C#修改Windows操作系统时间,帮助大家更好的利用c#操作系统,感兴趣的朋友可以了解下...2020-12-08
- 本篇文章主要介绍了详解Windows下安装Nodejs步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...2017-05-22
C#3.0使用EventLog类写Windows事件日志的方法
这篇文章主要介绍了C#3.0使用EventLog类写Windows事件日志的方法,以简单实例形式分析了C#写windows事件日志的技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25- 这篇文章主要介绍了如何使用Swift来实现一个命令行工具,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
Windows Server 2012 R2或2016无法安装.NET Framework 3.5.1的解决方法
这篇文章主要为大家详细介绍了Windows Server 2012 R2或2016无法安装.NET Framework 3.5.1,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-07-06Windows Server 2016 上配置 APACHE+SSL+PHP+perl的教程详解
Windows Server 2016 上配置 APACHE+SSL+PHP+perl怎么配置?小编推荐了一篇介绍Windows Server 2016 上配置 APACHE+SSL+PHP+perl的教程,有需要的同学快来看看吧! ...2017-07-06- 这篇文章主要介绍了C#实现控制Windows系统关机、重启和注销的方法,涉及C#调用windows系统命令实现控制开机、关机等操作的技巧,非常简单实用,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了Jmeter如何基于命令行运行jmx脚本,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-22
- 这篇文章主要介绍了基于命令行执行带参数的php脚本并取得参数的方法,分析了命令行运行PHP代码的原理、用法与相关注意事项,需要的朋友可以参考下...2016-01-26
- 这篇文章主要介绍了Windows窗口消息,以实例形式详细罗列了Windows窗口消息,非常具有实用价值,需要的朋友可以参考下...2020-04-25
- rsync是linux下功能强大的同步软件。现在在windows下也可以使用rsync(cwrsync)来同步数据了...2016-01-27