C + API 贪吃蛇程序



#include <windows.h>
#include <time.h>
#include <stdlib.h>

//定义4个方向
#define UP		1  //向上,蛇头y坐标不断减小
#define DOWN	2  //向下,蛇头y坐标不断增大
#define LEFT	3  //向左,蛇头x坐标不断减小
#define RIGHT	4  //向右,蛇头x坐标不断增大

#define SNAKEWIDTH	10  //单节蛇的大小
#define XWIDTH		20  //游戏区的宽度
#define YHEIGHT		20  //游戏区的高度
#define GETSCORE	10	//吃一个食物的得分
#define ID_TIMER	1   //定时器ID
#define TIMERSET	600 //定时间隔

static int			direct = RIGHT;  //方向(注:方向由蛇头决定)
static int			Score;  //吃到的食物数量

bool		IsOver = false, bEat = false;

struct Snake
{
	Snake():next(NULL), before(NULL) 
	{
		pt.x = 0; pt.y = 0;
	}
	Snake(const POINT &npt, Snake *, Snake *);
	POINT	pt;
	Snake * next, * before;
} snake;  //定义蛇头

Snake::Snake(const POINT &npt, Snake * nnext, Snake * nbefore):
pt(npt), next(nnext), before(nbefore){}

Snake * psLast = NULL, * food = NULL;

bool GameClient[XWIDTH][YHEIGHT]; //定义游戏区坐标布尔值,表示相应点的蛇身以及是否有食物。

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	TCHAR		szAppName[]		= TEXT("Snake"),
				szWindowName[]	= TEXT("贪吃蛇 by 阿卡(QQ:8596801)");
	WNDCLASS	wndcls;
	wndcls.cbClsExtra		= 0;
	wndcls.cbWndExtra		= 0;
	wndcls.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
	wndcls.hCursor			= LoadCursor(hInstance, IDC_ARROW);
	wndcls.hIcon			= LoadIcon(hInstance, IDI_APPLICATION);
	wndcls.hInstance		= hInstance;
	wndcls.lpfnWndProc		= WndProc;
	wndcls.lpszClassName	= szAppName;
	wndcls.lpszMenuName		= NULL;
	wndcls.style			= CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wndcls);

	HWND hwnd = CreateWindow(szAppName, szWindowName,
		WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU,
		CW_USEDEFAULT, CW_USEDEFAULT, (XWIDTH + 10) * SNAKEWIDTH, (YHEIGHT + 5) * SNAKEWIDTH,
		NULL, NULL, hInstance, NULL);
	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);

	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

//新食物刷新
void NewFood(HWND hwnd)
{
	food = new Snake();
	SYSTEMTIME st;
	GetLocalTime(&st);
	srand(st.wMilliseconds);
	food->pt.x = rand() % XWIDTH, food->pt.y = rand() % YHEIGHT;
	while(GameClient[food->pt.x][food->pt.y])
	{
		GetLocalTime(&st);
		srand(st.wMilliseconds);
		food->pt.x = rand() % XWIDTH, food->pt.y = rand() % YHEIGHT;
	}
	GameClient[food->pt.x][food->pt.y] = true;
	InvalidateRect(hwnd, NULL, TRUE);
}

//判断前方是否是食物
inline bool IsFood()
{
	if(snake.pt.x == food->pt.x && snake.pt.y == food->pt.y)
		return true;
	else
		return false;
}

//判断是否撞墙或者撞到自己身体
bool TouchWall()
{
	Snake * temp = psLast;
	//超越边界判断
	if(snake.pt.x >= XWIDTH ||
		snake.pt.x < 0 ||
		snake.pt.y < 0 ||
		snake.pt.y >= YHEIGHT)
		return true;
	//碰撞自身身体判断
	while(temp != &snake)
	{
		if(snake.pt.x == temp->pt.x &&
			snake.pt.y == temp->pt.y)
			return true;
		temp = temp->before;
	}
	return false;
}
		

void Move(HWND hwnd)
{
	int x, y;  //用于记录蛇头的当前位置(未前进时)
	//标记前方是否为食物,默认为不是食物,表示系通常的前进
	Snake * temp = psLast;
	
	//记录蛇头当前坐标
	x = snake.pt.x;
	y = snake.pt.y;
	//测试前进的地方是否是食物,同时将蛇头前进
	switch(direct)
	{
	case UP:
		--snake.pt.y;
		break;
	case DOWN:
		++snake.pt.y;
		break;
	case LEFT:
		--snake.pt.x;
		break;
	case RIGHT:
		++snake.pt.x;
		break;
	default:
		break;
	}
	//如果不是食物,作如下处理
	if(!IsFood())
	{
		//最后一节不是蛇头
		if(temp != &snake)
		{
			GameClient[temp->pt.x][temp->pt.y] = false;
			while(temp != snake.next)
			{
				temp->pt.x = temp->before->pt.x;
				temp->pt.y = temp->before->pt.y;
				temp = temp->before;
			}
			temp->pt.x = x;
			temp->pt.y = y;
			GameClient[temp->pt.x][temp->pt.y] = true;
		}
		else  //psLast == &snake,即蛇只有蛇头
			GameClient[x][y] = false;				
		if(TouchWall())
			IsOver = true;						//则标记游戏结束
		else
			GameClient[snake.pt.x][snake.pt.y] = true;	//不然移动后的蛇头位置标记为有方块了
		InvalidateRect(hwnd, NULL, TRUE);		//刷新游戏区
	}
	//如果是食物,作如下处理
	else
	{
		++Score;
		food->before = psLast;
		psLast->next = food;	
		
		if(temp != &snake)
		{	
			food->pt.x = psLast->pt.x;
			food->pt.y = psLast->pt.y;
			while(temp != snake.next)
			{
				temp->pt.x = temp->before->pt.x;
				temp->pt.y = temp->before->pt.y;
				temp = temp->before;
			}
			temp->pt.x = x;
			temp->pt.y = y;
		}
		else  //psLast == &snake,即蛇只有蛇头
		{
			food->pt.x = x; food->pt.y = y;
		}
		psLast = food;
		NewFood(hwnd);
	}
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC				hdc;
	PAINTSTRUCT		ps;
	TEXTMETRIC		tm;
	static int		cxChar, cyChar;
	TCHAR			szScore[]		= TEXT("得分:"),
					szGameOver[]	= TEXT("游戏结束!"),
					szPause[]		= TEXT("游戏暂停!"),
					szBuffer[20],
					*szText = NULL;
	int				x, y;
	static bool		pause = false;

	switch(message)
	{
	case WM_CREATE:
		direct			= RIGHT;   //游戏开局,默认方向向右
		snake.pt.x		= XWIDTH / 2;  //默认显示在游戏区中间位置
		snake.pt.y		= YHEIGHT / 2;
		psLast			= &snake;
		GameClient[snake.pt.x][snake.pt.y] = true;
		hdc = GetDC(hwnd);
		GetTextMetrics(hdc, &tm);
		cyChar = tm.tmExternalLeading + tm.tmHeight;
		ReleaseDC(hwnd, hdc);
		NewFood(hwnd);
		SetTimer(hwnd, ID_TIMER, TIMERSET, NULL);
		return 0;

	case WM_TIMER:
		if(pause) return 0;
		Move(hwnd);
		if(IsOver)
		{
			KillTimer(hwnd, ID_TIMER);
			InvalidateRect(hwnd, NULL, TRUE);
		}
		return 0;

	case WM_KEYDOWN:
		if(IsOver || pause) return 0;
		switch(wParam)
		{
		case VK_UP:
			if(direct != DOWN)
			{
				direct = UP;
				Move(hwnd);
			}
			break;
		case VK_DOWN:
			if(direct != UP)
			{
				direct = DOWN;
				Move(hwnd);
			}
			break;
		case VK_LEFT:
			if(direct != RIGHT)
			{
				direct = LEFT;
				Move(hwnd);
			}
			break;
		case VK_RIGHT:
			if(direct != LEFT)
			{
				direct = RIGHT;
				Move(hwnd);
			}
			break;
		default:
			break;
		}
		return 0;
			
	case WM_CHAR:
		if(IsOver && wParam != 'r') return 0;
		switch(wParam)
		{
		case 'p':
			{
				pause = !pause;
				InvalidateRect(hwnd, NULL, TRUE);
			}
			break;
		case 'r':
			{
				for(int x = 0; x < XWIDTH; ++x)
					for(int y = 0; y < YHEIGHT; ++y)
						GameClient[x][y] = false;
			}
			SetTimer(hwnd, ID_TIMER, TIMERSET, NULL);
			direct			= RIGHT;   //游戏开局,默认方向向右
			snake.pt.x		= XWIDTH / 2;  //默认显示在游戏区中间位置
			snake.pt.y		= YHEIGHT / 2;
			psLast			= &snake;
			IsOver			= false;
			pause			= false;
			GameClient[snake.pt.x][snake.pt.y] = true;
			NewFood(hwnd);
			break;
		default:
			break;
		}
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		SetViewportOrgEx(hdc, SNAKEWIDTH, SNAKEWIDTH, NULL);
		//画游戏区的边框
		MoveToEx(hdc, -1, -1, NULL);
		LineTo(hdc, XWIDTH * SNAKEWIDTH + 1, -1);
		LineTo(hdc, XWIDTH * SNAKEWIDTH + 1, YHEIGHT * SNAKEWIDTH + 1);
		LineTo(hdc, -1, YHEIGHT * SNAKEWIDTH + 1);
		LineTo(hdc, -1, -1);

		TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, 0, szScore, lstrlen(szScore));
		TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, cyChar,
			szBuffer, wsprintf(szBuffer, TEXT("%4d"), Score));
		if(pause)
			TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, 3 * cyChar, szPause, lstrlen(szPause));
		else if(IsOver)
			TextOut(hdc, (XWIDTH + 1) * SNAKEWIDTH, 3 * cyChar, szGameOver, lstrlen(szGameOver));
		SelectObject(hdc, GetStockObject(BLACK_BRUSH));
		for(x = 0; x < XWIDTH; ++x)
		{
			for(y = 0; y < YHEIGHT; ++y)
			{
				if(GameClient[x][y])
					Rectangle(hdc, x * SNAKEWIDTH, y * SNAKEWIDTH,
					(x + 1) * SNAKEWIDTH, (y + 1) * SNAKEWIDTH);
			}
		}
		return 0;

	case WM_DESTROY:
		KillTimer(hwnd, ID_TIMER);
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值