五子棋小游戏基于C++语言

开发环境:Linux 操作系统

项目描述:采用 C++语言的抽象、封装、继承、多态的思想对游戏中的棋子、棋盘、裁判等

类进行设计,由于在 Linux 下无图形界面、所以通过坐标绘制棋盘文件,从而实现

在 Linux 下的对战的功能。

运用技术点:

1.运用 C++语言的纯虚函数进行多态的设计。

2.txt棋盘 文件导入 Linux(15×15)

3.自动切换对象、并记录棋子在棋盘的坐标位置做防重复处理。

4.通过裁判的类判断棋盘上黑棋与白棋的获胜(依据坐标判断)。

面向对象编程,基类分为:棋类,玩家类,光标类,裁判类

棋类含有的属性为横纵坐标,颜色。

class Chess
{
public:
	Chess(int x, int y, char color) : x(x), 
		y(y),
		color(color)
	{

	}

	virtual ~Chess() {}

	virtual void show() = 0;

	int getX() const
	{
		return x;
	}

	int getY() const
	{
		return y;
	}

	char getColor() const
	{
		return color;
	}

private:
	int x;
	int y;
	char color;
};

通过颜色可以衍生出黑棋类和白棋类

//宏定义,采用了VT100控制码
#define BLACK 0x1
#define WHITE 0x0

#define BLACK_COLOR "\033[31;40m[✿]"
#define WHITE_COLOR "\033[36;47m[❀]"

//黑棋类
class BlackChess : public Chess
{
public:
	BlackChess(int x, int y) : Chess(x, y, BLACK)
	{

	}

	void show()
	{
		printf("\033[%d;%dH%s", getY(), getX() - 1, BLACK_COLOR);
		printf("\033[%d;%dH", getY(), getX()); //光标归位
	}
};

//白棋类
class WhiteChess : public Chess
{
public:
	WhiteChess(int x, int y) : Chess(x, y, WHITE)
	{

	}

	void show()
	{
		printf("\033[%d;%dH%s", getY(), getX() - 1, WHITE_COLOR);
		printf("\033[%d;%dH", getY(), getX()); //光标归位
	}
};

由于棋盘类只有一个,于是我就采用单例模式,棋盘大小是由.txt文件传入Linux系统,横纵为15×15,两点之间横坐标之间间隔四个字符,纵坐标间隔俩个字符


#define CHESS_BOARD_FILE "ChessBoard.txt"
#define CHESS_BOARD_COLOR "\033[43;37m"
//中心点
#define X_CENTER 29       
#define Y_CENTER 15

#define LINE 15
#define COLUMN 15

class ChessBoard                           //单例模式(饿汉版)
{
public:
	static ChessBoard* getInstance()        
	{
		return cb;
	}

	void show()
	{
		char buf[1024] = {0};
		FILE *fp = fopen(CHESS_BOARD_FILE, "r");
		if (fp == NULL) {
			fprintf(stderr, "Fail to open ChessBoard.txt : %s\n", strerror(errno));
			return ;
		}
		cout << "\033[1;1H" << CHESS_BOARD_COLOR; 
		while(fgets(buf, sizeof(buf), fp) != NULL) {
			fputs(buf, stdout);
		}

		fclose(fp);
		cout << "\033[0m";
	}

	bool isValidPostion(int line, int column)
	{
		if (line >= LINE || line < 0 || column >= COLUMN || column < 0) {
			return false;
		}
		return chess[line][column] == NULL ? true : false;
	}

	bool placeChess(Chess *chess)      //下棋的坐标
	{
		int line = (chess->getY() - 1) / LINE_INTERVAL;
		int column = (chess->getX() - 1) / COLUMN_INTERVAL;

		if (isValidPostion(line, column)) {
			this->chess[line][column] = chess;
			chess->show();
			curLine = line;
			curColumn = column;
			return true;
		}
		return false;
	}

	bool isValidColorChess(int line, int column, char color)   //判断下棋是否成功
	{
		if (line < 0 || line >= LINE || column < 0 || column >= COLUMN) {
			return false;
		}

		Chess *ch = chess[line][column];
		if (ch == NULL) {
			return false;
		}

		if (ch->getColor() != color) {
			return false;
		}
		return true;
	}

	int getCurLine() const
	{
		return curLine;
	}

	int getCurColumn() const
	{
		return curColumn;
	}

private:
	ChessBoard() : curLine(-1), curColumn(-1)      
	{
		for (int i = 0; i < LINE; ++i) {          //每个点赋空
			for (int j = 0; j < COLUMN; ++j) {
				chess[i][j] = NULL;
			}
		}
	}

	~ChessBoard()
	{

	}

	static ChessBoard *cb;
	Chess *chess[LINE][COLUMN];        
	int curLine;
	int curColumn;
};

ChessBoard *ChessBoard::cb = new ChessBoard;

玩家类含有的属性为名字和下棋的颜色

class Player
{
public:
	Player(const string &name, char color) : name(name),
		color(color)
	{

	}
	
	virtual ~Player(){}

	char getColor() const
	{
		return color;
	}

	string getName() const
	{
		return name;
	}

	virtual bool placeChess(int x, int y) = 0;

private:
	string name;
	char color;
};

通过颜色和棋盘下棋方式的判断可以衍生出黑棋手和白棋手:

class BlackPlayer : public Player
{
public:
	BlackPlayer(const string &name) : Player(name, BLACK)
	{

	}

	bool placeChess(int x, int y)
	{
		BlackChess *bc = new BlackChess(x, y);
		if (!ChessBoard::getInstance()->placeChess(bc)) {
			delete bc;
			return false;
		}

		return true;
	}
};

class WhitePlayer : public Player
{
public:
	WhitePlayer(const string &name) : Player(name, WHITE)
	{

	}

	bool placeChess(int x, int y)
	{
		WhiteChess *bc = new WhiteChess(x, y);
		if (!ChessBoard::getInstance()->placeChess(bc)) {
			delete bc;
			return false;
		}

		return true;
	}
};

光标类:将实际坐标装换成棋盘坐标

//相邻两点横纵坐标间隔
#define LINE_INTERVAL 2
#define COLUMN_INTERVAL 4
//横纵坐标最大值
#define X_MAX  57
#define Y_MAX  29

//中间点
#define X_CENTER 29
#define Y_CENTER 15

class Cursor
{
public:
	Cursor() : x(X_CENTER), y(Y_CENTER)
	{
		show();
	}

	void show() const
	{
		printf("\033[%d;%dH", y, x);
	}

	void moveUp()
	{
		if (y - LINE_INTERVAL <= 0) {
			return ;
		}
		y -= LINE_INTERVAL;
		show();
	}

	void moveDown()
	{
		if (y + LINE_INTERVAL > Y_MAX) {
			return ;
		}
		y += LINE_INTERVAL;
		show();
	}

	void moveLeft()
	{
		if (x - COLUMN_INTERVAL <= 0) {
			return ;
		}
		x -= COLUMN_INTERVAL;
		show();
	}

	void moveRight()
	{
		if (x + COLUMN_INTERVAL > X_MAX) {
			return ;
		}
		x += COLUMN_INTERVAL;
		show();
	}

	void moveCenter()
	{
		x = X_CENTER;
		y = Y_CENTER;
		show();
	}

	int getX() const 
	{
		return x;
	}

	int getY() const
	{
		return y;
	}

private:
	int x;
	int y;
};

通过wsad四个按键移动光标位置

class KeyHandle
{
public:
	KeyHandle()          //关闭回显
	{
#if 0
		struct termios attr;
		tcgetattr(0, &attr);
		attr.c_lflag &= ~(ICANON | ECHO);
		tcsetattr(0, TCSANOW, &attr);    //立即生效
#else
		system("stty -icanon");
		system("stty -echo");
#endif
	}

	~KeyHandle()                //打开回显
	{
#if 0
		struct termios attr;
		tcgetattr(0, &attr);
		attr.c_lflag |= (ICANON | ECHO);
		tcsetattr(0, TCSANOW, &attr);
#else
		system("stty icanon");
		system("stty echo");
#endif
	}

	bool waitPlayerPlaceChess(Player *player)
	{
		char key = '\0';
		bool ret = false;

		while (1) {
			key = getchar();

			switch (key) {
				case 'w':
				case 'W':
					cursor.moveUp();
					break;
				case 's':
				case 'S':
					cursor.moveDown();
					break;
				case 'a':
				case 'A':
					cursor.moveLeft();
					break;
				case 'd':
				case 'D':
					cursor.moveRight();
					break;
				case ' ':
					ret = player->placeChess(cursor.getX(), cursor.getY());
					break;
			}
			if (ret) {
				break;
			}
		}
	}

private:
	Cursor cursor;
};

裁判类:判断输赢

class Arbitration
{
public:
	bool isWin(char color)
	{
		ChessBoard *cb = ChessBoard::getInstance();
		int curLine = cb->getCurLine();
		int curColumn = cb->getCurColumn();
		bool ret = false;
		ret = isDirectionWin(curLine, curColumn, color, 1, 0); //水平方向
		if (ret) {
			return true;
		}

		ret = isDirectionWin(curLine, curColumn, color, 0, 1); //垂直方向
		if (ret) {
			return true;
		}

		ret = isDirectionWin(curLine, curColumn, color, 1, 1); //下坡方向
		if (ret) {
			return true;
		}

		ret = isDirectionWin(curLine, curColumn, color, 1, -1); //上坡方向
		if (ret) {
			return true;
		}
		return false;
	}

	bool isDirectionWin(int line, int column, char color, int x, int y)
	{
		ChessBoard *cb = ChessBoard::getInstance();
		int count = 1;

		for (int i = 1; i < 5; ++i) {
			bool ret = cb->isValidColorChess(line + (i * y), column + (i * x), color);
			if (!ret) {
				break ;
			}
			++count;
		}

		for (int i = 1; i < 5; ++i) {
			bool ret = cb->isValidColorChess(line - (i * y), column - (i * x), color);
			if (!ret) {
				break ;
			}
			++count;
		}

		if (count >= 5) {
			return true;
		}
		return false;
	}
};

主函数:

int main(int argc, const char *argv[])
{
	cout << "\033[2J";   // 清屏

	ChessBoard::getInstance()->show();   //显示棋盘
	KeyHandle keyHandle;                 //下棋操作
	Arbitration arb;                     //判断输赢
	
	Player *player[2];
	player[0] = new BlackPlayer("张三");
	player[1] = new WhitePlayer("李四");

	bool isWin = false;
	while (1) {
		for (int i = 0; i < 2; ++i) {
			keyHandle.waitPlayerPlaceChess(player[i]);
			isWin = arb.isWin(player[i]->getColor());
			if (isWin) {
				cout << "\033[32;0H" << player[i]->getName() << " 获得胜利!";
				break;
			}
		}
		if (isWin) {
			break;
		}
	}

	delete player[0];
	delete player[1];

	cout << "\033[0m\033[35;0H";    //关闭属性,移动光标到35行

	return 0;
}


版权声明:本文为m0_65612104原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。