EasyX函数库介绍
EasyX 是针对 C++ 的图形库,可以帮助 C/C++ 初学者快速上手图形和游戏编程。
下载链接:点我
参考文档:https://docs.easyx.cn。
全局文件
- 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
- 注意事项
- 对于 VC2008 以及更高版本的编辑器,需要将字符串用 _T() 函数改变编码格式,不然会报错。
棋盘
- 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; //开始标志
};
- 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;
}
}
}
棋手
- 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; //当前坐标
};
- 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;
}
- judgeGmae()判断逻辑
- 对于每一次落子,以其为原点,自4个方向循环判断5次是否有连续类型棋子。
- 需要在落子后进行判断,并需判断4个方向。
主程序
- 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;
}
完整项目代码链接:点我