编写一个"人机对弈"的下棋程序,四个棋子连成一线即为赢。人走棋的合法输入为0-6。棋盘是垂直放置,棋子落到底部。固定为人先走,计算机后走.
首先,这里有用到枚举类型----增加程序的可读性.用来描述状态量
/*enum who { HUMAN, NEUTRAL, COMPUTER };
HUMAN=0, NEUTRAL=1,COMPUTER=2
枚举常量代表该枚举类型的变量可能取的值,编译系统为每个枚举常量指定一个整数值。
默认状态下,这个整数就是所列举元素的序号,序号从0开始。
可以在定义枚举类型时为部分或全部枚举常量指定整数值,在指定值之前的枚举常量仍按默认方式取值,
而指定值之后的枚举常量按依次加1的原则取值。 各枚举常量的值可以重复。
//枚举常量只能以标识符形式表示,而不能是整型、字符型等文字常量。
//直接输出,输出的是变量的整数值*/
定义一个who data[6][7];枚举变量数组,用于存储棋盘各位置上棋子的类型(人机无)
主要结构
class game { //这是个抽象类,抽象的是所有人走一步,计算机走一步的对弈游戏
private:
int move_number; //当前已走的步数
void make_human_move( ); //调用 get_user_move( ),并用is_legal(move)判断是否合法输入,若是则调用make_move(move)
public:
enum who { HUMAN, NEUTRAL, COMPUTER };//枚举类型变量who(同int float样的数据类型) :有枚举元素HUMAN, NEUTRAL, COMPUTER
game( ) {//构造函数,move_number初始值为0
move_number = 0;
}
virtual ~game( ) { }//析构函数
who play( ); //人先走,计算机后走,返回值是游戏的赢者,后面给出了此函数的定义
protected:
//以下虚函数可以被覆盖,也可以不被覆盖
//virtual void display_message(const string &message)const;//在屏幕显示信息message
virtual int get_user_move( )const; // 输出"Your move,please:",然后接受用户输入,并将输入返回
virtual who last_mover( ) const {
if (move_number == 0)
return NEUTRAL;
else
return (move_number % 2 == 1 ? HUMAN : COMPUTER);//人先走 必然结果
}
virtual int moves_completed( ) const {
return move_number;//返回已走步数//用于将私有变量显示在棋盘上
}
virtual who next_mover( ) const {//表示现在的下棋者 在make_move中调用
return (move_number % 2 == 0 ? HUMAN : COMPUTER);
}
//以下虚函数必须被覆盖
virtual void make_move(const int &move) { //将棋子真正下到棋盘上,此函数被子类覆盖
++move_number;
}
virtual void restart( ) {
move_number = 0; //开始新的棋局
}
//以下为纯虚函数
virtual who winning( )const = 0; //返回胜利者
virtual void display_status( ) = 0; //显示游戏棋盘的当前状态
virtual bool is_game_over( ) = 0; //如果游戏结束,返回真
virtual bool is_legal(const int &move) = 0; //如果给定走法为合法,就返回真
virtual void make_computer_move( ) = 0; //计算计算机所有的可能的走法,选出一种最好的,并调用make_move(best_move)
};
class c4 : public game { //这个类是我们具体要实现的“四子棋”
public:
c4( ) {//构造函数
restart( );//调用的是子类c4的restart
}
protected:
virtual void make_move(const int &move);
// 修改data[][],many_used[]和most_recent_column的值,并调用game::make_move(move)
virtual void restart();
//将data[6][7]各元素置为NEUTRAL,将many_used[7]各元素置为0 ,再调用game::restart();//清空棋盘以及步数
virtual game::who winning( )const ;
virtual void display_status( ) ; //显示棋盘的状态
virtual bool is_game_over( ) ;
virtual bool is_legal(const int &move) ;
virtual void make_computer_move( );
private:
who data[6][7]; //6行7列,表示棋盘
int many_used[7]; //表示各列中已有多少个棋子
int most_recent_column; //最近下的棋子在哪列
int chess_notation[30] = {0}; //定义数组存储 每步most_recent_column的位置
int *p = chess_notation;//指针指向存储全部most_recent_column
};
game为基类,c4表示四子棋为game的派生类。
game中的纯虚函数需要在c4中再次声明,并定义新的内容
主函数
int main() {
cout << "Game on!" << endl;
c4 instance;
game::who winner;//定义一个who枚举类型的变量 winner
winner = instance.play();//对winner进行赋元素操作,返回赢的人
switch (winner) {
case c4::HUMAN:
cout << "You win!" << endl;
break;
case c4::COMPUTER:
cout << "I win!" << endl;
break;
case c4::NEUTRAL:
cout << "A draw" << endl;
break;
}
return 0;
}
定义一个c4类结构体变量instance 定义一个枚举变量winner赢者
调用核心函数 game::play();
选择赢者——输出结果
核心函数
game::who game::play() {
restart();//清空棋盘 开始游戏
while (!is_game_over()) {//当游戏未结束时,显示棋盘的状态
display_status();
if (last_mover() == COMPUTER || last_mover() == NEUTRAL)
make_human_move();//上一步机走,下一步人走
else
make_computer_move();//上一步人走,下一步机走
}
display_status();//走一步,显示一下棋盘状态 或游戏结束显示棋盘状态
return winning();//返回赢者
}
以下按照核心函数的顺序 给出各个函数的定义
restart():
game中走的步数清零
c4中将棋盘上每一个都写为“无”,每一列的棋子数为0,再调用父类restart();
c4中的restart()函数覆盖了父类中函数,所以在使用restart()时调用的是子类的函数,如需要调用父类中的函数,则需要使用::
/game/
virtual void restart( ) {
move_number = 0; //开始新的棋局
}
void c4::restart( ) {//覆盖game类
//将data[6][7]各元素置为NEUTRAL,将many_used[7]各元素置为0 ,再调用game::restart();
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
data[i][j] = NEUTRAL;
}
}
for (int k = 0; k < 7; k++) {
many_used[k] = 0;
}
game::restart( );//开始新的棋局 即move_number = 0;
}
is_game_over():
判断是否有四子连成,如有返回真
因为使用了嵌套循环,所以只有break;只是跳出了内嵌循环。需要有一个变量来限制循环条件。
根据棋盘大小,确定ij的范围
bool c4::is_game_over( ) {
int win = 1;//用变量作为跳出循环的条件
if (last_mover() == NEUTRAL)//游戏开局 先手被定义为NEUTRAL 要排除这种情况
return false;
for (int j = 0; j < 7 && (win == 1); j++) {//win不等于1的时候 跳出循环
for (int i = 0; i < 6; i++) {
if (data[i][j] == HUMAN && data[i][j + 1] == HUMAN && data[i][j + 2] == HUMAN && data[i][j + 3] == HUMAN
|| data[i][j] == HUMAN && data[i + 1][j] == HUMAN && data[i + 2][j] == HUMAN
&& data[i + 3][j] == HUMAN) { //纵横方向上HUMAN
win = 0;
break;
}
if (data[i][j] == COMPUTER && data[i][j + 1] == COMPUTER && data[i][j + 2] == COMPUTER
&& data[i][j + 3] == COMPUTER
|| data[i][j] == COMPUTER && data[i + 1][j] == COMPUTER && data[i + 2][j] == COMPUTER
&& data[i + 3][j] == COMPUTER) { //纵横方向上COMPUTER
win = 2;
break;
}
}
}
for (int j = 0; j < 4 && (win == 1); j++) {
for (int i = 3; i < 6; i++) {
if (data[i][j] == HUMAN && data[i - 1][j + 1] == HUMAN && data[i - 2][j + 2] == HUMAN
&& data[i - 3][j + 3] == HUMAN) { //向右倾斜方向上HUMAN
win = 0;
break;
}
if (data[i][j] == COMPUTER && data[i - 1][j + 1] == COMPUTER && data[i - 2][j + 2] == COMPUTER
&& data[i - 3][j + 3] == COMPUTER) { //向右倾斜方向上COMPUTER
win = 2;
break;
}
}
}
for (int j = 3; j < 7 && (win == 1); j++) {
for (int i = 0; i < 3; i++) {
if (data[i][j] == HUMAN && data[i + 1][j - 1] == HUMAN && data[i + 2][j - 2] == HUMAN
&& data[i + 3][j - 3] == HUMAN) { //向左倾斜方向上HUMAN
win = 0;
break;
}
if (data[i][j] == COMPUTER && data[i + 1][j - 1] == COMPUTER && data[i + 2][j - 2] == COMPUTER
&& data[i + 3][j - 3] == COMPUTER) { //向左倾斜方向上COMPUTER
win = 2;
break;
}
}
}
if (win == 0 || win == 2)
return true;
else
return false;
}
i和j 的范围:由棋盘的大小决定,比如在纵横方向上,最后的一个棋在棋盘上的每一个格都可能使连成4子。而向右斜上方向,有些位置上不可能连成4子,所以,不用考虑,化简程序。
display_status():
Chess notation[30]:每步棋落在哪里,用一个数组存储。每次输出一个数组的全部元素
void c4::display_status( ) {
//显示棋盘的状态
for (int i = 0; i < 6; i++) {
cout << "\t\t";
for (int j = 0; j < 7; j++) {
if (data[i][j] == COMPUTER)
cout << "@" << " " ;
else if (data[i][j] == HUMAN)
cout << "$" << " " ;
else
cout << "." << " " ;
}
cout << endl;
}
cout << "\t\t" << "-------------------" << endl << "\t\t" << "0 1 2 3 4 5 6 " << endl << "\t" <<
"@:computer $:human humanfirst" << endl;
cout << "Chess notation:";
*p = most_recent_column;
for (p = chess_notation; p < moves_completed( ) + chess_notation; p++) {
cout << *p << " ";
}
cout << endl;
cout << "used[i]:";
for (int i = 0; i < 7; i++) {
cout << many_used[i] << " ";
}
cout << endl;
cout << "The number of moves have played:" << moves_completed( ) << endl << endl;
}
make_human_move( )
从键盘输入人走的列数,调用is_legal_move()判断正误,合法即调用make_move()
int game:: get_user_move( )const {
int move;
cout << "Your move,please:";
cin >> move;
return move;
}
void game::make_human_move( ) { //调用 get_user_move( ),并用is_legal(move)判断是否合法输入,若是则调用make_move(move)
int move = get_user_move( );
while (1) {
if (is_legal(move)) {
make_move(move);
break;
} else {
cout << "!retry!" << endl;
move = get_user_move( );
}
}
}
make_move()
virtual void make_move(const int &move) { //将棋子真正下到棋盘上,此函数被子类覆盖
++move_number;
}
void c4::make_move(const int &move) {//覆盖game类
// 修改data[][],many_used[]和most_recent_column的值,并调用game::make_move(move)
int temp = many_used[move]; //用中间变量存储 此时该列中 已有的棋子数
if (next_mover( ) == HUMAN)
data[5 - temp][move] = HUMAN; //人把棋子下到该列
else
data[5 - temp][move] = COMPUTER;//机把棋子下到该列
many_used[move]++;//该列已有棋子+1
most_recent_column = move;//保存最近下的棋在哪一列————————决定下一步机下在哪里
game::make_move(move);//即 ++move_number;已走步数加1
}