【主要思路和说明】:
【附加说明 如果不想使用多线程可以指向看文章最后面的解决问题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;
}