一、五子棋项目介绍
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 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;
};
#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
运行效果如下: