一、五子棋项目介绍
1.游戏规则
五子棋是我国古代传统的黑白棋种之一,黑白双方依次落子,任意一方在棋盘上形成横向、纵向、斜向的连续相同颜色的五颗棋子的一方为胜。
2.完成界面显示
此次项目将不使用任何图形库进行开发,所有显示的内容都是以字符的形式进行显示。
二、棋子对象
1.棋子类
关于棋子类我们可以做如下几点思考:
由于黑棋和白棋的显示是不同的,棋子类的显示方法是不知道如何实现的,所以它是一个抽象类。每棵棋子在棋盘的不同位置存放,所以它应该包含棋子在屏幕上面的具体坐标。
每颗棋子都有自己的颜色,所以它应该包含颜色。
棋子类代码如下:
#ifndef CHESS_HPP
#define CHESS_HPP#include <iostream>
#include <cstdio>using namespace std;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;
};#endif
2.黑棋类
黑棋类应该继承棋子类,然后实现棋子类的void show(void) const函数,在这个函数中显示黑色棋子。
黑棋类代码如下:
#ifndef BLACKCHESS_HPP
#define BLACKCHESS_HPP#include "Chess.hpp"
#include "Config.hpp"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());//光标归位}
};#endif
3.白棋类
白棋类应该继承棋子类,然后实现棋子类的void show(void) const函数,在这个函数中显示白色棋子。
白棋类代码如下:
#ifndef WHITE_HPP
#define WHITE_HPP#include "Chess.hpp"
#include "Config.hpp"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());//光标归位}
};#endif
三、棋盘对象
棋盘对象只有—个,我们可以使用单例模式来设计
1.显示棋盘
棋盘已经在chessBoard.txt文件中存放,只需要将这个文件中的棋盘读取出来然后在终端上面显示就可以了。
链接:https://pan.baidu.com/s/113G9XCGdn1F0SKX_z6h1IQ 提取码:eoks
2.存放棋子
整个棋盘有15行和15列构成,我们把每个棋子都看成是一个对象,我们只需要记录这些对象的首地址就可以了,可以用一个二维的指针数组存放。
3.落子位置是否有效
如果落子位置已经有棋子了则返回false ,如果落子位置没有棋子则返回true 。
棋盘类代码如下:
#ifndef CHESSBOARD_HPP
#define CHESSBOARD_HPP#include "Chess.hpp"
#include "Config.hpp"
#include <errno.h>
#include <cstring>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;#endif
四、棋手对象
五子棋游戏中有两个玩家,一个玩家下黑旗,一个玩家下白旗。每个玩家至少包含以下成员:
1.玩家的名字 2.玩家下棋的棋子颜色 3.在棋盘落子的方法
1.玩家类
#ifndef PLAYER_HPP
#define PLAYER_HPP#include<iostream>using namespace std;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;
};#endif
2.黑棋玩家类
#ifndef BLACKPLAYER_HPP
#define BLACKPLAYER_HPP#include "Player.hpp"
#include "Config.hpp"
#include "ChessBoard.hpp"
#include "BlackChess.hpp"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;}
};#endif
3.白棋玩家类
#ifndef WHITEPLAYER_HPP
#define WHITEPLAYER_HPP#include "Player.hpp"
#include "Config.hpp"
#include "ChessBoard.hpp"
#include "WhiteChess.hpp"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;}
};#endif
五、按键方向控制类
在五子棋项目中,我们通过W/A/S/D/Space五个按键来做控制,这五个按键的功能如下;
W将光标向前移动,即将当前光标y轴坐标-2
S将光标向后移动,即将当前光标y轴坐标+2
A将光标向左移动,即将当前光标x轴坐标 - 4
D将光标向右移动,即将当前光标x轴坐标+4
Space空格键,用来控制落子
终端属性设置:
方向类代码如下:
#ifndef KEYHANDLE_HPP
#define KEYHANDLE_HPP#include "Cursor.hpp"
#include "Player.hpp"
#include <termios.h>
#include <unistd.h>
#include <cstdlib>class KeyHandle
{
public:KeyHandle(){
#if 0struct termios attr;tcgetattr(0,&attr);attr.c_lflag &= ~(ICANON | ECHO);tcsetattr(0,TCSANOW,&attr);
#elsesystem("stty -icanon");system("stty -echo");
#endif}~KeyHandle(){
#if 0struct termios attr;tcgetattr(0,&attr);attr.c_lflag |= (ICANON | ECHO);tcsetattr(0,TCSANOW,&attr);
#elsesystem("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;};#endif
六、裁判类
给出当前可以落子的棋手是谁
判断当前在棋盘上落子的棋子是否构成了五颗相同的棋子(赢棋)显示获得胜利的棋手
裁判类代码如下:
#ifndef ARBITRATION_HPP
#define ARBITRATION_HPP#include "ChessBoard.hpp"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;}
};#endif
主函数代码如下:
#include "BlackPlayer.hpp"
#include "WhitePlayer.hpp"
#include "ChessBoard.hpp"
#include "KeyHandle.hpp"
#include "Arbitration.hpp"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";return 0;
}
宏定义类代码如下:
#ifndef CONFIG_HPP
#define CONFIG_HPP#define BLACK 0x1
#define WHITE 0x0#define BLACK_COLOR "\033[31;40m[♚]"
#define WHITE_COLOR "\033[36;47m[♛]"#define LINE 15
#define COLUMN 15#define CHESS_BOARD_FILE "ChessBoard.txt"
#define CHESS_BOARD_COLOR "\033[45;37m"#define LINE_INTERVAL 2
#define COLUMN_INTERVAL 4#define X_MAX 57
#define Y_MAX 29#define X_CENTER 29
#define Y_CENTER 15#endif
运行效果如下: