使用EasyX实现简单的五子棋双人对战

            基于Visual Stdio 2017 IDE,利用EasyX图形函数库工具搭建可视化图形操作界面。通过鼠标在图形界面上点击选子,实现了五子棋的双人对战小游戏。

           本程序主体框架是从https://blog.csdn.net/ArchyLi/article/details/70446314学习来的,但其功能简单、界面简陋、程序功能还有漏洞,于是做出了一下修改。

           添加功能:1.增加随时退出功能、 2.增加重新开始功能、3.增加悔棋功能 4.增加总比分显示、5.增加落子提示

           美化界面:从开始界面,游戏界面、到结束界面使用一些网上搜集来的图片作为背景,不再是黑白背景

           修复漏洞及优化:1.修复了继续游戏时采用层层递归可能导致栈溢出的潜在问题、2.优化了判定胜利规则程序,不再是整个棋盘遍历。

2.效果展示

    开始界面

正式游戏界面

 

结束界面

3.程序代码

    采用的是C的面向过程的编程思想,以后会考虑改成C++面向对象的编程结构。关于字符集设置网上很多说设置成“多字节字符集”,但我的还是乱码。于是我将字符集设置成了“未设置”,字体就不乱码了。

#include<graphics.h>
#include<conio.h>
#include<windows.h>
#include<stdio.h>
#include<string>
#include <time.h>
#define GridSize 15
#define InternalSize 33
using namespace std;
#pragma warning(disable:4996)
void menu();
bool play();
void display();
bool playgame();
bool IsAgain(int index);
void DisplayExitGameMessage();
bool IsWin(int i, int j, int player);
int a[GridSize][GridSize] = { 0 };//棋盘标记场
IMAGE img_bk1, img_bk2;
int Player1WinCount = 0, Player2WinCount = 0;
int main()
{
	//initgraph(600, 600, SHOWCONSOLE);
	initgraph(600, 600);
	menu();
	DisplayExitGameMessage();
	closegraph();
	return 0;
}

void menu()
{
	IMAGE img;
	setaspectratio(1.1f, 1);
	//loadimage(&img, (LPCTSTR)"IMAGE", (LPCTSTR)"tur", 0, 0, true);
	loadimage(&img, _T(".\\五子棋1.jpg"));
	putimage(0, 0, &img, SRCPAINT);
	settextstyle(50, 20, (LPCTSTR)"楷体");
	settextcolor(YELLOW);
	setbkmode(WHITE);
	outtextxy(150, 100, (LPCTSTR)"五子棋双人对战");
	outtextxy(200, 400, (LPCTSTR)"开始游戏");
	outtextxy(200, 451, (LPCTSTR)"退出游戏");
	MOUSEMSG m;
	while (true)
	{
		m = GetMouseMsg();
		if (m.uMsg == WM_MOUSEMOVE && m.x >= 200 && m.x <= 360 && m.y >= 400 && m.y <= 450)//判断鼠标是否移动到了开始游戏,是则用黄色方框标记
		{
			setlinecolor(YELLOW);
			rectangle(200, 400, 360, 450);
		}
		else {
			setlinecolor(getbkcolor());
			rectangle(200, 400, 360, 450);
		}
		if (m.uMsg == WM_MOUSEMOVE && m.x >= 200 && m.x <= 360 && m.y >= 451 && m.y <= 500)//判断鼠标是否移动到了退出游戏,是则用黄色方框标记
		{
			setlinecolor(YELLOW);
			rectangle(200, 451, 360, 500);
		}
		else {
			setlinecolor(getbkcolor());
			rectangle(200, 451, 360, 500);
		}
		if (m.uMsg == WM_LBUTTONDOWN && m.x >= 200 && m.x <= 360 && m.y >= 400 && m.y <= 450)//判断是否点击开始游戏
		{
			cleardevice();
			Sleep(200);
			bool Continue_Game = true;
			while (Continue_Game) {
				Continue_Game = play();//开始游戏并循环判断是否继续游戏
			}
			break;
		}
		if (m.uMsg == WM_LBUTTONDOWN && m.x >= 200 && m.x <= 360 && m.y >= 451 && m.y <= 500)//判断是否点击退出游戏
		{
			break;
		}
	}
}
bool play()
{
	cleardevice();
	graphdefaults();
	display();//显示背景棋盘
	setlinecolor(WHITE);
	settextstyle(20, 10, (LPCTSTR)"楷体");
	settextcolor(BLACK);
	setbkmode(WHITE);
	outtextxy(5, 5, (LPCTSTR)"player1:黑子");
	outtextxy(5, 28, (LPCTSTR)"步数: ");
	outtextxy(475, 5, (LPCTSTR)"player2:白子");
	outtextxy(475, 28, (LPCTSTR) "步数: ");
	settextstyle(25, 15, (LPCTSTR)"楷体");//高25个像素,宽15个像素
	outtextxy(475, 560, (LPCTSTR) "退出游戏");
	outtextxy(10, 560, (LPCTSTR) "重新开始");
	outtextxy(270, 560, (LPCTSTR) "悔棋");
	return playgame();
}
void display()//显示棋盘网格
{
	loadimage(&img_bk1, _T(".\\水墨画.jpg"));
	putimage(0, 0, &img_bk1, SRCPAINT);
	clearrectangle(50, 50, 550, 550);
	loadimage(&img_bk2, _T(".\\五子棋棋盘.jpg"));
	putimage(50, 50, &img_bk2, SRCPAINT);
}
bool playgame()//正式开始游戏
{
	settextstyle(25, 15, (LPCTSTR)"楷体");//高25个像素,宽15个像素
	string str = to_string(Player1WinCount) + " : " + to_string(Player2WinCount);
	outtextxy(265, 15, (LPCTSTR)str.data());
	int StepNum1 = 0, StepNum2 = 0;	settextstyle(20, 10, (LPCTSTR)"楷体");
	outtextxy(60, 27, (LPCTSTR)to_string(StepNum1).data());
	outtextxy(530, 27, (LPCTSTR)to_string(StepNum2).data());
	settextstyle(25, 15, (LPCTSTR)"楷体", 2700, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
	outtextxy(25, 50, (LPCTSTR)"黑方落子");
	memset(a, 0, sizeof(a));
	int Pre_i = -1, Pre_j = -1, Pre_C = -1, Pre_R = -1;
	int player = 1; bool UndoAble = true;
	MOUSEMSG msg;
	while (true)
	{
		msg = GetMouseMsg();
		//“退出游戏”区域相关操作
		if (msg.uMsg == WM_MOUSEMOVE && msg.x >= 475 && msg.x <= 595 && msg.y >= 560 && msg.y <= 585)//判断鼠标是否移动到了退出游戏,是则用黄色方框标记
		{
			setlinecolor(YELLOW);
			rectangle(475, 560, 595, 585);
		}
		else {
			setlinecolor(BLACK);
			rectangle(475, 560, 595, 585);
		}
		if (msg.uMsg == WM_LBUTTONDOWN && msg.x >= 460 && msg.x <= 580 && msg.y >= 550 && msg.y <= 575)//判断鼠标是否点击了退出游戏
		{
			return false;
		}
		//“重新开始”区域相关操作
		if (msg.uMsg == WM_MOUSEMOVE && msg.x >= 10 && msg.x <= 130 && msg.y >= 560 && msg.y <= 585)//判断鼠标是否移动到了退出游戏,是则用黄色方框标记
		{
			setlinecolor(YELLOW);
			rectangle(10, 560, 130, 585);
		}
		else {
			setlinecolor(BLACK);
			rectangle(10, 560, 130, 585);
		}
		if (msg.uMsg == WM_LBUTTONDOWN && msg.x >= 10 && msg.x <= 130 && msg.y >= 560 && msg.y <= 585)//判断鼠标是否点击了退出游戏
		{
			Player1WinCount = 0, Player2WinCount = 0;
			return true;
		}
		//“悔棋”区域相关操作
		if (msg.uMsg == WM_MOUSEMOVE && msg.x >= 270 && msg.x <= 330 && msg.y >= 560 && msg.y <= 585)//判断鼠标是否移动到了悔棋,是则用黄色方框标记
		{
			setlinecolor(YELLOW);
			rectangle(270, 560, 330, 585);
		}
		else {
			setlinecolor(BLACK);
			rectangle(270, 560, 330, 585);
		}
		if (msg.uMsg == WM_LBUTTONDOWN && msg.x >= 270 && msg.x <= 330 && msg.y >= 560 && msg.y <= 585)//判断鼠标是否点击了悔棋
		{
			HWND hq = FindWindow(NULL, "五子棋双人对战");//获取父窗口句柄
			if (Pre_i == -1) {
				MessageBox(hq, (LPCTSTR) "目前还不能悔棋", "提示", MB_OK);
			}
			else if (UndoAble) {
				UndoAble = false;
				a[Pre_i][Pre_j] = 0;
				clearcircle(Pre_C, Pre_R, 12);
				putimage(Pre_C - 12, Pre_R - 12, 25, 25, &img_bk2, Pre_C - 12 - 50, Pre_R - 12 - 50, SRCCOPY);
				if (player == 1) {
					settextstyle(20, 10, (LPCTSTR)"楷体");
					putimage(530, 27, 20, 20, &img_bk1, 530, 27, SRCCOPY);
					outtextxy(530, 27, (LPCTSTR)to_string(--StepNum2).data());
					settextstyle(25, 15, (LPCTSTR)"楷体", 2700, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
					outtextxy(575, 50, (LPCTSTR)"白方落子");
					settextstyle(25, 15, (LPCTSTR)"楷体", 0, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
					clearrectangle(10, 70, 40, 150);
					putimage(10, 70, 30 + 1, 100 + 1, &img_bk1, 10, 70, SRCCOPY);
					player = 2;
				}
				else {
					settextstyle(20, 10, (LPCTSTR)"楷体");
					putimage(60, 27, 20, 20, &img_bk1, 60, 27, SRCCOPY);
					outtextxy(60, 27, (LPCTSTR)to_string(--StepNum1).data());
					settextstyle(25, 15, (LPCTSTR)"楷体", 2700, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
					outtextxy(25, 50, (LPCTSTR)"黑方落子");
					settextstyle(25, 15, (LPCTSTR)"楷体", 0, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
					clearrectangle(560, 70, 590, 150);
					putimage(560, 70, 30 + 1, 100 + 1, &img_bk1, 560, 70, SRCCOPY);
					player = 1;
				}
			}
			else {
				MessageBox(hq, (LPCTSTR) "最多连续悔一次", "已经悔过棋了", MB_OK);
			}
		}
		//棋盘区域点击操作
		if (msg.uMsg == WM_LBUTTONDOWN)
		{
			int i = 0, j = 0;
			for (int column = 71; j < GridSize; j++, j % 2 == 0 ? column += InternalSize : column += InternalSize - 1)
			{
				if (msg.x <= column + 10 && msg.x >= column - 10)//检测鼠标点击所在列
				{
					for (int row = 70; i < GridSize; i++, i % 2 == 0 ? row += InternalSize : row += InternalSize - 1)
					{
						if (msg.y <= row + 10 && msg.y >= row - 10)//检测鼠标点击所在行
						{
							Pre_i = i; Pre_j = j; UndoAble = true;
							Pre_C = column; Pre_R = row;
							if (player == 1 && a[i][j] == 0)
							{
								setfillcolor(BLACK);
								solidcircle(column, row, 12);
								a[i][j] = 1;
								settextstyle(20, 10, (LPCTSTR)"楷体" ,0, 0, 0, 0, 0, 0);
								putimage(60, 27, 20, 20, &img_bk1, 60, 27, SRCCOPY);
								outtextxy(60, 27, (LPCTSTR)to_string(++StepNum1).data());
								settextstyle(25, 15, (LPCTSTR)"楷体", 2700, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
								outtextxy(575, 50, (LPCTSTR)"白方落子");
								settextstyle(25, 15, (LPCTSTR)"楷体", 0, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
								clearrectangle(10, 70, 40, 150);
								putimage(10, 70, 30+1, 100+1, &img_bk1, 10, 70, SRCCOPY);
								if (IsWin(i, j, 1))  {
									settextstyle(25, 15, (LPCTSTR)"楷体");//高25个像素,宽15个像素
									putimage(265, 15, 75, 25, &img_bk1, 265, 15, SRCCOPY);
									str = to_string(++Player1WinCount) + " : " + to_string(Player2WinCount);
									outtextxy(265, 15, (LPCTSTR)str.data());
									return IsAgain(1);//判断是否赢及是否继续玩
								}
								player = 2;
								break;
							}
							else if (player == 2 && a[i][j] == 0)
							{
								setfillcolor(WHITE);
								solidcircle(column, row, 12);
								a[i][j] = 2;
								settextstyle(20, 10, (LPCTSTR)"楷体", 0, 0, 0, 0, 0, 0);
								putimage(530, 27, 20, 20, &img_bk1, 530, 27, SRCCOPY);
								outtextxy(530, 27, (LPCTSTR)to_string(++StepNum2).data());
								settextstyle(25, 15, (LPCTSTR)"楷体", 2700, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
								outtextxy(25, 50, (LPCTSTR)"黑方落子");
								settextstyle(25, 15, (LPCTSTR)"楷体", 0, 0, 0, 0, 0, 0);//高25个像素,宽15个像素
								clearrectangle(560, 70, 590, 150);
								putimage(560, 70, 30+1, 100+1, &img_bk1, 560, 70, SRCCOPY);
								if (IsWin(i, j, 2)) {
									settextstyle(25, 15, (LPCTSTR)"楷体");//高25个像素,宽15个像素
									putimage(265, 15, 75, 25, &img_bk1, 265, 15, SRCCOPY);
									str = to_string(Player1WinCount) + " : " + to_string(++Player2WinCount);
									outtextxy(265, 15, (LPCTSTR)str.data());
									return IsAgain(2);//判断是否赢及是否继续玩
								}
								player = 1;
								break;
							}
						}
					}
					break;
				}
			}
		}
	}
}
bool IsWin(int i, int j, int player) {
	int count = 1;
	//判断纵向是否有五子连珠
	for (int index_i = i - 1, index_j = j; index_i >= 0 && a[index_i][index_j] == player; index_i--) count++;
	for (int index_i = i + 1, index_j = j; index_i < GridSize && a[index_i][index_j] == player; index_i++) count++;
	if (count >= 5)  return true;
	else count = 1;
	//判断横向是否有五子连珠
	for (int index_i = i, index_j = j - 1; index_j >= 0 && a[index_i][index_j] == player; index_j--) count++;
	for (int index_i = i, index_j = j + 1; index_j < GridSize && a[index_i][index_j] == player; index_j++) count++;
	if (count >= 5)  return true;
	else count = 1;
	//判断135度斜轴是否有五子连珠
	for (int index_i = i - 1, index_j = j - 1; index_i >= 0 && index_j >= 0 && a[index_i][index_j] == player; index_i--, index_j--) count++;
	for (int index_i = i + 1, index_j = j + 1; index_i < GridSize && index_j < GridSize&& a[index_i][index_j] == player; index_i++, index_j++) count++;
	if (count >= 5)  return true;
	else count = 1;
	//判断45度斜轴是否有五子连珠
	for (int index_i = i - 1, index_j = j + 1; index_i >= 0 && index_j < GridSize && a[index_i][index_j] == player; index_i--, index_j++) count++;
	for (int index_i = i + 1, index_j = j - 1; index_i < GridSize && index_j >= 0 && a[index_i][index_j] == player; index_i++, index_j--) count++;
	if (count >= 5)  return true;
	else count = 1;
	return false;
}
bool IsAgain(int index)
{
	settextstyle(20, 10, (LPCTSTR)"楷体");
	settextcolor(YELLOW);
	if (index == 1) {
		outtextxy(250, 50, (LPCTSTR) "player1 win!");
	}
	else {
		outtextxy(250, 50, (LPCTSTR) "player2 win!");
	}
	HWND hq = FindWindow(NULL, "五子棋双人对战");//获取父窗口句柄
	string str = "Player" + to_string(index) + "  Win";
	int quit = MessageBox(hq, (LPCTSTR) "是否继续游戏", (LPCTSTR)str.data(), MB_YESNO);
	if (IDYES == quit) return true;
	else return false;
}

void DisplayExitGameMessage() {
	cleardevice();
	IMAGE img;
	loadimage(&img, _T(".\\水墨画2.jpg"));
	putimage(0, 0, &img, SRCPAINT);
	settextcolor(BLACK);
	settextstyle(50, 20, (LPCTSTR)"楷体");
	outtextxy(160, 80, (LPCTSTR)"欢迎下次再来!");
	Sleep(1500);
}

4.问题与不足

        待解决的问题:1.没有实现人机对战功能、2.不能两台电脑之间通信对战。

        不足:1.程序比较冗余(需优化) 、2.变量管理复杂,经常遇到改一个变量要多处手动修改

5.附录图片

         程序中用到的背景图图片直接跟源文件放到同一目录下即可

         水墨画

水墨画2

五子棋1

五子棋棋盘

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值