使用多线程(或者使用kbhit和查表得方法)用二维数组模拟打字母游戏。

【主要思路和说明】:

【附加说明 如果不想使用多线程可以指向看文章最后面的解决问题2的方法】

1,使用俩个线程,一个线程用来更新二维数组->把之前在第0行的数据下移一行,在现在的第0行的随机列更新随机字母,清屏后,打印当前二维数组的数据。一个线程用来接受用户的输入,从最后一行往第一行遍历遇到于用户属于相同的字母消除字母(把它的位置的值置为0).

2,我定义了11行10列的二维数组,第十一行的数据主要是用来判断游戏是否结束。

3,只要游戏不结束就一直创建线程来等待用户输入。

4,使用CRITICAL_SECTION 把可能由多个线程同时修改的变量的操作放在临界区,防止出现异常。

【代码】:


#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <windows.h>
#include <conio.h>

#define MAXSIZE 10 

CRITICAL_SECTION cs;

bool gameoverflag = false;
char GameLayout[MAXSIZE + 1][MAXSIZE];
int YourScore = 0;



DWORD WINAPI removeCh(LPVOID p)
{
	char ch = 0;
	ch = getch();
	
	for (int i = MAXSIZE - 1; i >= 0; i--)
	{
		for (int j = 0; j < MAXSIZE; j++)
		{
			if (GameLayout[i][j] == ch)
			{
				EnterCriticalSection(&cs);
				GameLayout[i][j] = '\0';
				YourScore += 1;
				LeaveCriticalSection(&cs);
				break;
			}
		}
	}
	
	return 0;
}

void PrintArr()
{
	system("cls");
	printf("your score:%d\n", YourScore);
	for (int i = 0; i < MAXSIZE; i++)
	{
		for (int j = 0; j < MAXSIZE; j++)
		{
			printf("%3c", GameLayout[i][j]);
		}
		printf("\n------------------------------------------\n");
	}
}
bool isGameOver()
{
	for (int j = 0; j < MAXSIZE; j++)
	{
		if (GameLayout[MAXSIZE][j] != '\0')
			return true;
	}
	return false;
}
void RandAppearACh(int i)//i是第i行的意思
{
	int j = 0;
	//1,设置随机种子
	//srand((unsigned int)time(NULL));
	//2,生成0~MAXSIZE - 1 的随机数字---获得要放置的随机位置
	do 
	{
		j = rand() % MAXSIZE;
	} while (GameLayout[i][j] != 0);//确保选择的位置上面没有放置元素
	
	//3,获得大小或者小写字母的选项 0->小写 1->大写
	int flag = rand() % 2;
	//4,根据第三步选项判断获取的是65	~90(大写)还是97~122(小写)的值。
	int chval;

	if (flag == 0)
	{
		chval = rand() % (122 - 97 + 1) + 97;
	}
	else
	{
		chval = rand() % (90 - 65 + 1) + 65;//取任意范围的随机数的方法:[min,max] = rand % (max - min + 1) + min
	}

	//5,给相应位置赋值
	EnterCriticalSection(&cs);
	GameLayout[i][j] = chval;
	LeaveCriticalSection(&cs);
}
void UpdateArr()
{
	for (int i = MAXSIZE; i > 0; i--)
	{
		for (int j = 0; j < MAXSIZE; j++)
		{
			GameLayout[i][j] = GameLayout[i - 1][j];
			if (i == 1)
			{
				EnterCriticalSection(&cs);
				GameLayout[i - 1][j] = 0;
				LeaveCriticalSection(&cs);
			}
		}
	}
}
DWORD WINAPI MainGaming()
{
	//1,设置随机种子
	srand((unsigned int)time(NULL));
	//rand srand
	//
	int length = 1;		//每行的字母的个数  最多是5个
	int level = 0;		//一共更新了多少行当更新十行的时候,下次更新的没行的字母量加1
	while (true)
	{
		if (isGameOver())
		{
			MessageBox(NULL,"GameOver!\n","提示!\n",MB_OK);
			break;
		}
		//1,向下移动一行
		UpdateArr();
		//2,根据length的值随机移动个数
		int times = rand() % length;
		for (int i = 0; i <= times; i++)
		{
			RandAppearACh(0);
		}
		//3,打印二维数组
		PrintArr();

		Sleep(1000);

		//4,当移动的次数超过9次的话,每行的出现的个数可能加1
		level++;
		if (level == 9 && length<5)
		{
			length++;
			level = 0;
		}
	}

	return 0;
}


void main()
{
	InitializeCriticalSection(&cs);
	HANDLE myhandle;
	CreateThread(NULL, 0, MainGaming, NULL, 0, NULL);
	while (!gameoverflag)
	{
		myhandle = CreateThread(NULL, 0, removeCh, NULL, 0, NULL);
		WaitForSingleObject(myhandle, INFINITE);
	}
	DeleteCriticalSection(&cs);
	printf("hello...\n");
	system("pause");
	return ;
}

【目前存在的问题】:

1,分数的输出有问题,没有按照预期的输出。

2,消除字母并不可以"实时"有稍微的延迟。

【解决问题1】:主要是英文getch的函数的问题,导致在循环内Yourscore增加了数次,导致了Yourscore的异常


#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <windows.h>
#include <conio.h>

#define MAXSIZE 10 

CRITICAL_SECTION cs;

bool gameoverflag = false;
char GameLayout[MAXSIZE + 1][MAXSIZE];
int YourScore = 0;



DWORD WINAPI removeCh(LPVOID p)
{
	char ch = 0;
	while (!gameoverflag)
	{
		ch = getch();
		if (ch != 0)
		{
			bool matched = false;
			for (int i = MAXSIZE - 1; i >= 0 && !matched; i--)
			{
				for (int j = 0; j < MAXSIZE; j++)
				{
					if (GameLayout[i][j] == ch)
					{
						EnterCriticalSection(&cs);
						GameLayout[i][j] = '\0';
						YourScore += 1;
						matched = true;
						LeaveCriticalSection(&cs);
						break;
					}
				}
			}
		}
	}
	
	return 0;
}

void PrintArr()
{
	system("cls");
	EnterCriticalSection(&cs);
	printf("your score:%d\n", YourScore);
	LeaveCriticalSection(&cs);
	for (int i = 0; i < MAXSIZE; i++)
	{
		for (int j = 0; j < MAXSIZE; j++)
		{
			printf("%3c", GameLayout[i][j]);
		}
		printf("\n------------------------------------------\n");
	}
}
bool isGameOver()
{
	for (int j = 0; j < MAXSIZE; j++)
	{
		if (GameLayout[MAXSIZE][j] != '\0')
			return true;
	}
	return false;
}
void RandAppearACh(int i)//i是第i行的意思
{
	int j = 0;
	//1,设置随机种子
	//srand((unsigned int)time(NULL));
	//2,生成0~MAXSIZE - 1 的随机数字---获得要放置的随机位置
	do 
	{
		j = rand() % MAXSIZE;
	} while (GameLayout[i][j] != 0);//确保选择的位置上面没有放置元素
	
	//3,获得大小或者小写字母的选项 0->小写 1->大写
	int flag = rand() % 2;
	//4,根据第三步选项判断获取的是65	~90(大写)还是97~122(小写)的值。
	int chval;

	if (flag == 0)
	{
		chval = rand() % (122 - 97 + 1) + 97;
	}
	else
	{
		chval = rand() % (90 - 65 + 1) + 65;//取任意范围的随机数的方法:[min,max] = rand % (max - min + 1) + min
	}

	//5,给相应位置赋值
	EnterCriticalSection(&cs);
	GameLayout[i][j] = chval;
	LeaveCriticalSection(&cs);
}
void UpdateArr()
{
	for (int i = MAXSIZE; i > 0; i--)
	{
		for (int j = 0; j < MAXSIZE; j++)
		{
			GameLayout[i][j] = GameLayout[i - 1][j];
			if (i == 1)
			{
				EnterCriticalSection(&cs);
				GameLayout[i - 1][j] = 0;
				LeaveCriticalSection(&cs);
			}
		}
	}
}
DWORD WINAPI MainGaming()
{
	//1,设置随机种子
	srand((unsigned int)time(NULL));
	//rand srand
	//
	int length = 1;		//每行的字母的个数  最多是5个
	int level = 0;		//一共更新了多少行当更新十行的时候,下次更新的没行的字母量加1
	while (true)
	{
		if (isGameOver())
		{
			MessageBox(NULL,"GameOver!\n","提示!\n",MB_OK);
			break;
		}
		//1,向下移动一行
		UpdateArr();
		//2,根据length的值随机移动个数
		int times = rand() % length;
		for (int i = 0; i <= times; i++)
		{
			RandAppearACh(0);
		}
		//3,打印二维数组
		PrintArr();

		Sleep(1000);

		//4,当移动的次数超过9次的话,每行的出现的个数可能加1
		level++;
		if (level == 9 && length<5)
		{
			length++;
			level = 0;
		}
	}

	return 0;
}


void main()
{
	InitializeCriticalSection(&cs);
	HANDLE myhandle1, myhandle2;
	myhandle1 = CreateThread(NULL, 0, MainGaming, NULL, 0, NULL);
	
	myhandle2 = CreateThread(NULL, 0, removeCh, NULL, 0, NULL);

	WaitForSingleObject(myhandle1, INFINITE);
	DeleteCriticalSection(&cs);
	CloseHandle(myhandle1);
	CloseHandle(myhandle2);
	printf("hello...\n");
	system("pause");
	return ;
}

【解决问题2:在解决问题2的基础上又去掉了多线程函数的使用使程序看起来更加简洁---------主要始利用函数kbhit达到了非阻塞的目的,才可以实现不用多线程就可以完成功能】

#define  _CRT_SECURE_NO_WARNINGS 
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#include "color.h"
static const char *name[] = { RED,GREEN,BLUE,YELLOW };

struct LetterType {
	int flag;
	int row;
	int col;
};

#define ROWSIZE		10
#define COLSIZE		20
#define LETTERSIZE	26

bool gameoverflag = false;
char GameLayout[ROWSIZE + 1][COLSIZE];
LetterType Letter[LETTERSIZE];

int YourScore	= 0;
int ErrorInput	= 0;


//void before_remove_dealLetter(int i,int j)
//{
//	if (isupper(GameLayout[i][j]))
//	{
//		Letter[GameLayout[i][j] - 'A'].flag = 0;
//		Letter[GameLayout[i][j] - 'A'].row  = 0;
//		Letter[GameLayout[i][j] - 'A'].col  = 0;
//	}
//	else if (islower(GameLayout[i][j]))
//	{
//		Letter[GameLayout[i][j] - 'a'].flag = 0;
//		Letter[GameLayout[i][j] - 'a'].row = 0;
//		Letter[GameLayout[i][j] - 'a'].col = 0;
//	}
//}
bool removeCh(char chp)
{
	if (isupper(chp) && Letter[chp - 'A'].flag == 2)
	{
		GameLayout[Letter[chp - 'A'].row][Letter[chp - 'A'].col] = '\0';
		Letter[chp - 'A'].flag = 0;
		Letter[chp - 'A'].row = 0;
		Letter[chp - 'A'].col = 0;
		return true;
	}
	else if (islower(chp) && Letter[chp - 'a'].flag == 1)
	{
		GameLayout[Letter[chp - 'a'].row][Letter[chp - 'a'].col] = '\0';
		Letter[chp - 'a'].flag = 0;
		Letter[chp - 'a'].row = 0;
		Letter[chp - 'a'].col = 0;
		return true;
	}
	else
	{
		ErrorInput++;
		return false;
	}
	//char ch = chp;
	//bool matched = false;
	//if (ch != 0)
	//{

	//	for (int i = ROWSIZE - 1; i >= 0 && !matched; i--)
	//	{
	//		for (int j = 0; j < COLSIZE; j++)
	//		{
	//			if (GameLayout[i][j] == ch)
	//			{
	//				//删除二维数组前先处理Letter结构体数组
	//				before_remove_dealLetter(i, j);
	//				GameLayout[i][j] = '\0';
	//				YourScore += 1;
	//				matched = true;
	//				return matched;
	//			}
	//		}
	//	}
	//}
	//ErrorInput++;
	//return matched;
}
bool isfirstrowempty()
{
	for (int i = 0; i < COLSIZE; i++)
	{
		if (GameLayout[0][i] != '\0')
			return false;
	}
	return true;
}

void PrintArr()
{
	system("cls");
	printf("your score:%d\tyour error input:%d\n", YourScore, ErrorInput);
	for (int i = 0; i < ROWSIZE; i++)
	{
		for (int j = 0; j < COLSIZE; j++)
		{
			printf("%3c", GameLayout[i][j]);
		}
		printf("\n-------------------------------------------------------------------\n");
	}
}
bool isGameOver()
{
	for (int j = 0; j < ROWSIZE; j++)
	{
		if (GameLayout[ROWSIZE][j] != '\0')
			return true;
	}
	return false;
}
void RandAppearACh(int i)//i是第i行的意思
{
	int j = 0;
	//1,生成0~MAXSIZE - 1 的随机数字---获得要放置的随机位置
	do
	{
		j = rand() % COLSIZE;
	} while (GameLayout[i][j] != 0);//确保选择的位置上面没有放置元素

	//2,获得大小或者小写字母的选项 0->小写 1->大写
	int flag = rand() % 2;
	//3,根据第三步选项判断获取的是65	~90(大写)还是97~122(小写)的值。
	int chval;

	if (flag == 0)
	{
		chval = rand() % (26) + 97;
	}
	else
	{
		chval = rand() % (26) + 65;//括号中数字就是26-----取任意范围的随机数的方法:[min,max] = rand % (max - min + 1) + min
	}

	//4,给相应位置赋值
	//赋值的前提需要判断是否当前的结构体数组的flag为0(代表当前二维数组中没有这个值)
	//保证不能出现相同的字母(不区分大小写)
	if (isupper(chval) && Letter[(chval - 'A')].flag == 0)
	{
		Letter[(chval - 'A')].flag = 2;
		Letter[(chval - 'A')].row = i;
		Letter[(chval - 'A')].col = j;
		GameLayout[i][j] = chval;
	}
	else if (islower(chval) && Letter[(chval - 'a')].flag == 0)
	{
		Letter[(chval - 'a')].flag = 1;
		Letter[(chval - 'a')].row = i;
		Letter[(chval - 'a')].col = j;
		GameLayout[i][j] = chval;
	}
	else//如果要插入的字母已经在Letter中出现那么我们再次插入
	{
		RandAppearACh(i);
	}
}

void update_letter_beforeupdateArr()
{
	for (int i = 0; i < LETTERSIZE; i++)
	{
		if (Letter[i].flag != 0)
			Letter[i].row++;
	}
}
void UpdateArr()
{
	update_letter_beforeupdateArr();
	for (int i = ROWSIZE; i > 0; i--)
	{
		for (int j = 0; j < COLSIZE; j++)
		{
			GameLayout[i][j] = GameLayout[i - 1][j];
			if (i == 1)
			{
				GameLayout[i - 1][j] = 0;
			}
		}
	}
}
void MainGaming()
{
	//1,设置随机种子
	srand((unsigned int)time(NULL));
	for (int i = 0; i <= 10; i++)
	{
		RandAppearACh(0);
	}
	
	//rand srand
	//
	//int length = 1;		//每行的字母的个数  最多是5个
	//int level = 0;		//一共更新了多少行当更新十行的时候,下次更新的没行的字母量加1
	while (true)
	{
		//1,向下移动一行
		UpdateArr();

		//判断游戏是否结束 游戏结束不打印
		if (isGameOver())
		{
			MessageBox(NULL, "GameOver!\n", "提示!\n", MB_OK);
			break;
		}
		

		//2,打印二维数组
		PrintArr();

		Sleep(1000);

		while (kbhit())
		{
			char ch = _getwch();
			if (removeCh(ch))
			{
				if(isfirstrowempty())//如果当前第一行没有任何字母,则在第一行更新一个字母
					RandAppearACh(0);
			}
			PrintArr();
			fflush(stdin);
		}
	}

}


void main()
{
	
	MainGaming();

	printf("hello...\n");
	system("pause");
	return;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是实现贪吃蛇游戏的C++代码,使用了多文件结构管理程序,不使用类: ### main.cpp ```c++ #include <iostream> #include <conio.h> #include "game.h" using namespace std; int main() { Game game; game.start(); return 0; } ``` ### game.h ```c++ #ifndef GAME_H #define GAME_H void gotoxy(int x, int y); class Game { public: void start(); private: enum Direction { UP, DOWN, LEFT, RIGHT }; const int WIDTH = 20; const int HEIGHT = 20; int score = 0; int snakeLength = 4; int snakeX[400]; int snakeY[400]; int foodX; int foodY; Direction direction = RIGHT; bool gameOver = false; void init(); void draw(); void input(); void logic(); }; #endif ``` ### game.cpp ```c++ #include <iostream> #include <conio.h> #include <windows.h> #include <ctime> #include "game.h" using namespace std; void gotoxy(int x, int y) { COORD pos = { x, y }; HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hOut, pos); } void Game::init() { srand(time(NULL)); int x = WIDTH / 2; int y = HEIGHT / 2; for (int i = 0; i < snakeLength; i++) { snakeX[i] = x - i; snakeY[i] = y; } foodX = rand() % (WIDTH - 2) + 1; foodY = rand() % (HEIGHT - 2) + 1; score = 0; gameOver = false; } void Game::draw() { system("cls"); for (int i = 0; i < WIDTH + 2; i++) { cout << "#"; } cout << endl; for (int i = 0; i < HEIGHT; i++) { for (int j = 0; j < WIDTH; j++) { if (j == 0) { cout << "#"; } if (i == snakeY[0] && j == snakeX[0]) { cout << "O"; } else if (i == foodY && j == foodX) { cout << "$"; } else { bool printed = false; for (int k = 1; k < snakeLength; k++) { if (i == snakeY[k] && j == snakeX[k]) { cout << "o"; printed = true; } } if (!printed) { cout << " "; } } if (j == WIDTH - 1) { cout << "#"; } } cout << endl; } for (int i = 0; i < WIDTH + 2; i++) { cout << "#"; } cout << endl; cout << "Score: " << score << endl; } void Game::input() { if (_kbhit()) { switch (_getch()) { case 'w': if (direction != DOWN) { direction = UP; } break; case 's': if (direction != UP) { direction = DOWN; } break; case 'a': if (direction != RIGHT) { direction = LEFT; } break; case 'd': if (direction != LEFT) { direction = RIGHT; } break; case 'x': gameOver = true; break; } } } void Game::logic() { int prevX = snakeX[0]; int prevY = snakeY[0]; int prev2X, prev2Y; snakeX[0] += (direction == RIGHT) ? 1 : (direction == LEFT) ? -1 : 0; snakeY[0] += (direction == DOWN) ? 1 : (direction == UP) ? -1 : 0; for (int i = 1; i < snakeLength; i++) { prev2X = snakeX[i]; prev2Y = snakeY[i]; snakeX[i] = prevX; snakeY[i] = prevY; prevX = prev2X; prevY = prev2Y; } if (snakeX[0] == 0 || snakeX[0] == WIDTH - 1 || snakeY[0] == 0 || snakeY[0] == HEIGHT - 1) { gameOver = true; } for (int i = 1; i < snakeLength; i++) { if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) { gameOver = true; } } if (snakeX[0] == foodX && snakeY[0] == foodY) { score += 10; foodX = rand() % (WIDTH - 2) + 1; foodY = rand() % (HEIGHT - 2) + 1; snakeLength++; } } void Game::start() { init(); while (!gameOver) { draw(); input(); logic(); Sleep(100); } gotoxy(WIDTH / 2 - 4, HEIGHT / 2); cout << "Game Over!"; gotoxy(WIDTH / 2 - 6, HEIGHT / 2 + 1); cout << "Your score: " << score; gotoxy(0, HEIGHT); } ``` 以上就是用C++实现贪吃蛇游戏的代码,使用了多文件结构管理程序,不使用类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值