C/C++入门项目:五子棋

EasyX函数库介绍

EasyX 是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。
下载链接:点我
参考文档:https://docs.easyx.cn。

全局文件

  1. GlobalPara.h
	#pragma once
	
	/*
	*	BGIMAGE:背景图
	*	WIDTH:宽度
	*	HEIGHT:高度
	* 
	*	N:棋盘长度N*N
	*	OFFSET:棋盘起点距离窗口左上角距离
	*	SIZE:棋盘方格边长
	*/
	
	#define BGIMAGE ".\\resources\\bg.jpg"
	#define WIDTH 700
	#define HEIGHT 520
	
	#define N      18
	#define OFFSET  45
	#define SIZE 24

  1. 注意事项
  • 对于 VC2008 以及更高版本的编辑器,需要将字符串用 _T() 函数改变编码格式,不然会报错。

棋盘

  1. Board.h
  • 定义棋盘类。
	#pragma once
	#include <iostream>
	#include <easyx.h>
	#include "globalpara.h"
	#include <vector>
	#include <cmath>
	#include <string>
	using namespace std;
	
	class Board
	{
	public:
		Board();
		void initBoard(); //初始化变量
		void drawBoard() const; //绘制棋盘
		void drawMenu(const int type) const; //绘制工具栏
		void showRecord() const; //展示棋盘状态
		void drawChess() const; //绘制棋子
		void setAxis(const int row, const int col); //设置框选坐标
		boolean getFlag() const; //获取状态
		void setFlag(const boolean flag); //设置状态
		void startGame(ExMessage& msg); //点击开始
	
		vector<vector<int> > record; //棋盘状态
		int num_white, num_black; //棋子数量
	
	private:
		int row, col; //框选坐标
		boolean show; //框选展示标志
		boolean START_FLAG; //开始标志
	};

  1. Board.cpp
  • 实现棋盘类,包括初始化变量initBoard(),绘制棋盘布局drawBoard(),绘制棋盘标题drawMenu(),绘制棋子drawChess(),开始游戏startGame()等成员方法。
	
	#include "Board.h"
	
	Board::Board()
	{
		initgraph(WIDTH, HEIGHT);
		//initgraph(WIDTH, HEIGHT, EX_SHOWCONSOLE); //打开带有控制台的窗口
		this->initBoard();
		this->drawBoard();
	}
	
	void Board::initBoard()
	{
		this->START_FLAG = false;
		this->show = false;
		this->col = 0;
		this->row = 0;
		this->num_black = 0;
		this->num_white = 0;
	
		this->record.clear();
		for (int i = 0; i <= N; i++)
		{
			vector<int> res(N + 1);
			for (int j = 0; j <= N; j++)
			{
				res.push_back(0);
			}
			this->record.push_back(res);
		}
	}
	
	void Board::drawBoard() const
	{
		//载入背景
		IMAGE bg;
		loadimage(&bg, _T(BGIMAGE)); ///_T()可以将ASCII类型转成unicode类型
		putimage(0, 0, &bg);
	
		//绘制棋盘边框
		setlinecolor(RGB(0, 0, 0));
		setlinestyle(PS_SOLID, 2);
		rectangle(OFFSET, OFFSET, OFFSET + N * SIZE, OFFSET + N * SIZE);
	
		//绘制格子
		setlinestyle(PS_DASH, 1);
		for (int i = 0; i <= N; i++)
		{
			line(OFFSET, OFFSET + i * SIZE, OFFSET + N * SIZE, OFFSET + i * SIZE);
			line(OFFSET + i * SIZE, OFFSET, OFFSET + i * SIZE, OFFSET + N * SIZE);
		}
	
		//绘制黑点 
		setfillcolor(RGB(0, 0, 0));
		solidcircle(OFFSET + 3 * SIZE, OFFSET + 3 * SIZE, 6);
		solidcircle(OFFSET + 15 * SIZE, OFFSET + 3 * SIZE, 6);
		solidcircle(OFFSET + 9 * SIZE, OFFSET + 9 * SIZE, 6);
		solidcircle(OFFSET + 3 * SIZE, OFFSET + 15 * SIZE, 6);
		solidcircle(OFFSET + 15 * SIZE, OFFSET + 15 * SIZE, 6);
	
		//绘制选择框
		if (this->show)
		{
			setlinestyle(PS_SOLID, 2);
			setlinecolor(RGB(255, 0, 0));
			rectangle(OFFSET + this->col * SIZE - 12, OFFSET + this->row * SIZE - 12,
			OFFSET + this->col * SIZE + 12, OFFSET + this->row * SIZE + 12);
		}
	}
	
	void Board::drawMenu(const int type) const
	{
		//绘制标题
		TCHAR title[] = _T("经典五子棋");
		settextcolor(RGB(255, 255, 255));
		settextstyle(35, 0, _T("Consolas"));
		outtextxy(21 * SIZE, 50, title);
	
		//绘制开始按钮
		setlinestyle(PS_SOLID, 1);
		setlinecolor(RGB(93, 107, 153));
		setfillcolor(RGB(93, 107, 153));
		fillroundrect(21 * SIZE, 140, 28 * SIZE, 190, 15, 15);
		TCHAR button[] = _T("点击开始");
		settextcolor(RGB(198, 220, 184));
		settextstyle(30, 0, _T("Courier"));
		outtextxy(22 * SIZE, 150, button);
	
		//绘制棋子类型
		TCHAR name[] = _T("当前棋手");
		settextcolor(RGB(255, 255, 255));
		settextstyle(30, 0, _T("Consolas"));
		outtextxy(22 * SIZE, 250, name);
		if (type == 1) //白棋
		{
			setfillcolor(RGB(255, 255, 255));
			solidcircle(12 + 24 * SIZE, 330, 11);
		}
		else if (type == 2) //黑棋
		{
			setfillcolor(RGB(0, 0, 0));
			solidcircle(12 + 24 * SIZE, 330, 11);
		}
	
		//绘制棋子数量
		settextcolor(RGB(255, 255, 255));
		settextstyle(25, 0, _T("Courier"));
		TCHAR w[] = _T("白棋");
		TCHAR b[] = _T("黑棋");
		outtextxy(12 + 22 * SIZE, 405, w);
		outtextxy(12 + 22 * SIZE, 430, b);
		TCHAR num_w[4];
		TCHAR num_b[4];
		swprintf_s(num_w, _T("%3d"), this->num_white);
		swprintf_s(num_b, _T("%3d"), this->num_black);
		outtextxy(12 + 24 * SIZE, 405, num_w);
		outtextxy(12 + 24 * SIZE, 430, num_b);
	}
	
	void Board::showRecord() const
	{
		cout << "------------------------------" << endl;
		for (int i = 0; i <= N; i++)
		{
			for (int j = 0; j <= N; j++)
			{
				cout << this->record[i][j] << " ";
			}
			cout << endl;
		}
		cout << "------------------------------" << endl;
	}
	
	void Board::drawChess() const
	{
		for (int i = 0; i <= N; i++)
		{
			for (int j = 0; j <= N; j++)
			{
				if (this->record[i][j] == 1) //白棋
				{
					setfillcolor(RGB(255, 255, 255));
					solidcircle(OFFSET + j * SIZE, OFFSET + i * SIZE, 11);
				}
				else if (this->record[i][j] == 2) //黑棋
				{
					setfillcolor(RGB(0, 0, 0));
					solidcircle(OFFSET + j * SIZE, OFFSET + i * SIZE, 11);
				}
			}
		}
	}
	
	void Board::setAxis(const int row, const int col)
	{
		this->col = col;
		this->row = row;
	}
	
	boolean Board::getFlag() const
	{
		return this->START_FLAG;
	}
	
	void Board::setFlag(const boolean flag)
	{
		this->START_FLAG = flag;
	}
	
	void Board::startGame(ExMessage& msg)
	{
		if (!this->getFlag() && peekmessage(&msg, EX_MOUSE))
		{
			if (msg.message == WM_LBUTTONDOWN && msg.x > 21 * SIZE 
				&& msg.x < 28 * SIZE && msg.y > 140 && msg.y < 190)
			{
				this->START_FLAG = true;
				this->show = true;
				MessageBox(GetHWnd(), _T("Game Start!"), _T("五子棋"), MB_OK);
				//cout << "Game start!" << endl;
			}
		}
	}

棋手

  1. Player.h
  • 定义棋手类。
	#pragma once
	#include "Board.h"
	
	class Player
	{
	public:
		Player() : type(1), col(-1), row(-1) {};
		void moveChess(Board& b, ExMessage& msg); // 移动棋子
		void changeType(); //改变身份
		int getType() const; //获取类型
		boolean judgeGame(Board& b) const; //判断输赢
	
	private:
		int type; //棋子类型
		int col, row; //当前坐标
	};

  1. Player.cpp
  • 实现棋手类,包括移动棋子下棋操作moveChes(),判断游戏输赢judgeGmae()等成员函数。
#include "Player.h"

void Player::moveChess(Board& b, ExMessage& msg)
{
	if (b.getFlag() && peekmessage(&msg, EX_MOUSE))
	{
		if (msg.x >= OFFSET - 12 && msg.x <= OFFSET + N * SIZE + 12
			&& msg.y >= OFFSET - 12 && msg.y <= OFFSET + N * SIZE + 12)
		{
			//坐标校准
			int col = (msg.x - OFFSET) / SIZE;
			int row = (msg.y - OFFSET) / SIZE;
			if ((msg.x - OFFSET) % SIZE >= 12)
			{
				col += 1;
			}
			if ((msg.y - OFFSET) % SIZE >= 12)
			{
				row += 1;
			}

			b.setAxis(row, col);

			//点击下棋
			if (msg.message == WM_LBUTTONDOWN && !b.record[row][col])
			{
				//cout << "col:" << col << ", row:" << row << endl;
				b.record[row][col] = this->type;
				if (this->type == 1)
				{
					b.num_white++;
				}
				else if (this->type == 2)
				{
					b.num_black++;
				}
				this->col = col;
				this->row = row;
				//b.showRecord();
				if (this->judgeGame(b))
				{
					b.setFlag(false);
					MessageBox(GetHWnd(), this->type == 1 ? _T("White Win!") : _T("Black Win!"), _T("五子棋"), MB_OK);
					//cout << "Game End!" << endl;
				}
				this->changeType();
			}
		}
	}
}

void Player::changeType()
{
	this->type = (this->type == 1) ? 2 : 1;
}

int Player::getType() const
{
	return this->type;
}

boolean Player::judgeGame(Board& b) const
{
	//判断横向
	for (int i = col - 4; i <= col; i++)
	{
		int num = 0;
		for (int k = 0; k < 5; k++)
		{
			if (i >= 0 && i + k <= N && b.record[row][i + k] == b.record[row][col])
			{
				num++;
			}
		}
		if (num == 5)
		{
			return true;
		}
	}
	//判断竖向
	for (int j = row - 4; j <= row; j++)
	{
		int num = 0;
		for (int k = 0; k < 5; k++)
		{
			if (j >= 0 && j + k <= N && b.record[j + k][col] == b.record[row][col])
			{
				num++;
			}
		}
		if (num == 5)
		{
			return true;
		}
	}
	//判断左倾斜
	for (int i = col - 4, j = row - 4; i <= col; i++, j++)
	{
		int num = 0;
		for (int k = 0; k < 5; k++)
		{
			if (i >= 0 && j >= 0 && i + k <= N && j + k <= N 
				&& b.record[j + k][i + k] == b.record[row][col])
			{
				num++;
			}
		}
		if (num == 5)
		{
			return true;
		}
	}
	//判断右倾斜
	for (int i = col - 4, j = row + 4; i <= col; i++, j--)
	{
		int num = 0;
		for (int k = 0; k < 5; k++)
		{
			if (i >= 0 && j - k >= 0 && j - k <= N && i + k <= N 
				&& b.record[j - k][i + k] == b.record[row][col])
			{
				num++;
			}
		}
		if (num == 5)
		{
			return true;
		}
	}

	return false;
}

  1. judgeGmae()判断逻辑
  • 对于每一次落子,以其为原点,自4个方向循环判断5次是否有连续类型棋子。
  • 需要在落子后进行判断,并需判断4个方向。

主程序

  1. Chess.cpp
	#pragma once
	#include "player.h"
	
	int main()
	{
		Board board = Board();
		Player player = Player();
		board.drawMenu(player.getType());
		ExMessage msg;
	
		while (true)
		{
			board.initBoard();
			board.startGame(msg);
			BeginBatchDraw(); //批量绘制
			while (board.getFlag())
			{
				cleardevice();
				board.drawBoard();
				board.drawMenu(player.getType());
				player.moveChess(board, msg);
				board.drawChess();
				FlushBatchDraw();
			}
			EndBatchDraw();
		}
		
		system("pause");
		return 0;
	}

完整项目代码链接:点我
示例

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值