从0开始写中国象棋-走一步棋(当门炮,马来跳)(C++)

2023-11-10 21:21

本文主要是介绍从0开始写中国象棋-走一步棋(当门炮,马来跳)(C++),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在文章 从0开始写中国象棋-创建棋盘与棋子

中,我们已经可以看到象棋游戏的界面了。

这是因为,我们创建了棋盘(棋盘数组),并在棋盘上放置一些数字来表示棋子。

让棋子动起来

现在,我们打算让棋子动起来。

棋子动起来,其实很简单,比如开局 炮二平五。

实际上就是棋子从棋盘上的一个位置移动到了棋盘的另一个位置。

实际上是棋盘在变化

对应棋盘的变化就是,棋盘从:

变成了:

由于棋盘上的棋子就是一个一个的不同的数字,所以,从计算机的角度来说,走一步棋就是给二维数组的一个位置设置一个新数字。

也就是,起点位置元素重新赋值为0(表示这里没有棋子了,棋子走到其他位置去了);

终点位置元素重新赋值为炮的ID(表示炮走棋来到了这里)。

程序实现 GoAhead(OneStep)

现在我们希望告诉程序,走一步棋,从一个位置走到另一个位置,程序就能够实现这个目标。

由于这种需求是在下棋双方走棋的时候反复出现的,所以我们可以很方便的用函数来实现。

这个函数大概像下面这样:

bool GoAhead(from_row, from_col, to_row, to_col);

其中,各个参数的意思是很明显的,我们就不解释了。

但是,函数返回值是bool,这个是干什么的呢?

因为用户可能会乱走棋,比如,蹩马腿了也要跳马,这个时候你就要告诉他不能走这步棋。这时候函数返回 false 。

现在让我们来实现这个函数:

bool GoAhead(int from_row, int from_col, int to_row, int to_col)
{//如果出发的地方没有旗子,就返回 falseif()
}

这个时候我们才发现我们需要访问棋盘。

而棋盘是定义在main函数内的变量。我们访问不到。

怎么办?

C++全局变量

不在函数内定义的变量就叫全局变量。

为了能够让任何代码都访问到棋盘,我们在程序的一开始就定义棋盘。这样我们的函数就可以访问棋盘了。

//棋盘数组,不同的数字表示不同的棋子,0表示没有棋子
vector<vector<int>> chess_board
{{ 1, 2, 3, 4, 5, 6, 7, 8, 9},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{ 0,10, 0, 0, 0, 0, 0,11, 0},{12, 0,13, 0,14, 0,15, 0,16},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{26, 0,27, 0,28, 0,29, 0,30},{ 0,31, 0, 0, 0, 0, 0,32, 0},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{17,18,19,20,21,22,23,24,25},
};bool GoAhead(int from_row, int from_col, int to_row, int to_col)
{//如果出发的地方没有旗子,就返回 falseif (chess_board[from_row][from_col] != 0){}else{return false;}
}

谁能告诉我能否走到哪里?

现在的问题是,当走棋的起点有棋子的时候,这个棋子能否到达目的地位置?

也就是走棋的合理性问题。

这里门道就多了:马走日,相走田,车走直路,炮翻山,小卒一去不会还。

如何实现这样的需求呢?

C++的方式是使用多态来实现。伪代码像下面这样:

//棋子类的基类
struct chess_base
{// 纯虚函数,表示 chess_base 是一个抽象类,专门用来做基类;//GoAhead 用来给各个派生类来重新实现 overridevirtual bool GoAhead(int from_row, int from_col, int to_row, int to_col) = 0;
};struct Horse : public chess_base
{// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){//ba la ba la ....return false;}
};struct King : public chess_base
{// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){//ba la ba la ....return false;}
};

每个棋子都知道自己的规则,所以自己是知道能否走到目的地的。

这也就要求我们要替各个棋子来实现它们各自的走法。

棋子数组包含对象

之前,我们的棋子数组是不同位置(数组下标)表示不同棋子,棋子只有一个内容,就是它们的名字。

现在,我们希望用类对象来表示。名字只是棋子的一个成员变量。走棋GoAhead才是棋子的主要能力。

#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;//棋盘数组,不同的数字表示不同的棋子,0表示没有棋子
vector<vector<int>> chess_board
{{ 1, 2, 3, 4, 5, 6, 7, 8, 9},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{ 0,10, 0, 0, 0, 0, 0,11, 0},{12, 0,13, 0,14, 0,15, 0,16},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{26, 0,27, 0,28, 0,29, 0,30},{ 0,31, 0, 0, 0, 0, 0,32, 0},{ 0, 0, 0, 0, 0, 0, 0, 0, 0},{17,18,19,20,21,22,23,24,25},
};//棋子类的基类
struct chess_base
{chess_base(const string& name1):name(name1){}// 纯虚函数,表示 chess_base 是一个抽象类,专门用来做基类;//GoAhead 用来给各个派生类来重新实现 overridevirtual bool GoAhead(int from_row, int from_col, int to_row, int to_col) = 0;string name;//棋子要显示的名字
};
//车 車
struct Chariot : public chess_base
{Chariot(const string& name1) :chess_base(name1) {}// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//ba la ba la ....return false;}
};
//马 馬
struct Horse : public chess_base
{Horse(const string& name1):chess_base(name1){}// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//ba la ba la ....return false;}
};
//炮 砲
struct Cannon : public chess_base
{Cannon(const string& name1) :chess_base(name1) {}// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//ba la ba la ....return false;}
};
//将 帅
struct King : public chess_base
{King(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){//ba la ba la ....cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;return false;}
};
//士 仕
struct Official : public chess_base
{Official(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){//ba la ba la ....cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;return false;}
};
//相 象
struct Minister : public chess_base
{Minister(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){//ba la ba la ....cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;return false;}
};
//兵 卒
struct Soldier : public chess_base
{Soldier(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){//ba la ba la ....cout << from_row << "," << from_col << "," << to_row << "," << to_col << endl;return false;}
};vector<chess_base*> chess_man
{nullptr,//下标为0不用,编号从1开始//上方黑方棋子new Chariot("车"),new Horse("马"), new Minister("相"), new Official("士"), new King("将"), new Official("士"), new Minister("相"), new Horse("马"), new Chariot("车"),new Cannon("炮"),new Cannon("炮"),new Soldier("兵"), new Soldier("兵"), new Soldier("兵"), new Soldier("兵"), new Soldier("兵"),//下方红方棋子new Chariot("車"), new Horse("馬"), new Minister("象"), new Official("仕"), new King("帥"), new Official("仕"), new Minister("象"), new Horse("馬"), new Chariot("車"),new Soldier("卒"), new Soldier("卒"), new Soldier("卒"), new Soldier("卒"), new Soldier("卒"),new Cannon("砲"), new Cannon("砲"),
};int main(void)
{for (int row = 0; row < 10; row++){for (int col = 0; col < 9; col++){auto id = chess_board[row][col];if (id != 0){cout <<setw(5)<< chess_man[id]->name ;//输出一个棋子}else{cout<<setw(5) << "_";//表示棋盘上的一个位置}}cout << endl;cout << endl;}return 0;
}

运行结果:

当门炮,马来跳

有了棋子对象之后,我们实现走棋就很好实现了。

    // 当门炮 : 炮二平五  ID 为 32 从(7,7) 移动到 (7,4)chess_man[32]->GoAhead(7, 7, 7, 4);// 马来跳 : 炮8进7  ID 为 8 从(7,7) 移动到 (7,4)chess_man[8]->GoAhead(0, 7, 2, 6);

此时,我们需要实现炮,马的 GoAhead 函数。

为了简单起见,我们不做合法性检查,让棋子直接在棋盘上从起点走到终点。

既然如此,其实所有棋子都是这样走棋的,我们把所有的棋子走棋都给实现了:

bool GoAhead(int from_row, int from_col, int to_row, int to_col)
{cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;
}

完整的代码

#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;//棋盘数组,不同的数字表示不同的棋子,0表示没有棋子
vector<vector<int>> chess_board
{/*      0  1  2  3  4  5  6  7  8*//* 0*/{ 1, 2, 3, 4, 5, 6, 7, 8, 9},/* 1*/{ 0, 0, 0, 0, 0, 0, 0, 0, 0},/* 2*/{ 0,10, 0, 0, 0, 0, 0,11, 0},/* 3*/{12, 0,13, 0,14, 0,15, 0,16},/* 4*/{ 0, 0, 0, 0, 0, 0, 0, 0, 0},/* 5*/{ 0, 0, 0, 0, 0, 0, 0, 0, 0},/* 6*/{26, 0,27, 0,28, 0,29, 0,30},/* 7*/{ 0,31, 0, 0, 0, 0, 0,32, 0},/* 8*/{ 0, 0, 0, 0, 0, 0, 0, 0, 0},/* 9*/{17,18,19,20,21,22,23,24,25},
};//棋子类的基类
struct chess_base
{chess_base(const string& name1):name(name1){}// 纯虚函数,表示 chess_base 是一个抽象类,专门用来做基类;//GoAhead 用来给各个派生类来重新实现 overridevirtual bool GoAhead(int from_row, int from_col, int to_row, int to_col) = 0;string name;//棋子要显示的名字
};
//车 車
struct Chariot : public chess_base
{Chariot(const string& name1) :chess_base(name1) {}// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};
//马 馬
struct Horse : public chess_base
{Horse(const string& name1):chess_base(name1){}// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};
//炮 砲
struct Cannon : public chess_base
{Cannon(const string& name1) :chess_base(name1) {}// 马实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};
//将 帅
struct King : public chess_base
{King(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout<<"GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};
//士 仕
struct Official : public chess_base
{Official(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};
//相 象
struct Minister : public chess_base
{Minister(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};
//兵 卒
struct Soldier : public chess_base
{Soldier(const string& name1) :chess_base(name1) {}// 帅实现自己的走棋bool GoAhead(int from_row, int from_col, int to_row, int to_col){cout << "GoAhead " << from_row << "," << from_col << "," << to_row << "," << to_col << endl;//先保存原来的 ID, 为着放到终点auto id = chess_board[from_row][from_col];//离开之后,留下 0chess_board[from_row][from_col] = 0;//走到了目的地chess_board[to_row][to_col] = id;return false;}
};vector<chess_base*> chess_man
{nullptr,//下标为0不用,编号从1开始//上方黑方棋子new Chariot("车"),new Horse("马"), new Minister("相"), new Official("士"), new King("将"), new Official("士"), new Minister("相"), new Horse("马"), new Chariot("车"),new Cannon("炮"),new Cannon("炮"),new Soldier("兵"), new Soldier("兵"), new Soldier("兵"), new Soldier("兵"), new Soldier("兵"),//下方红方棋子new Chariot("車"), new Horse("馬"), new Minister("象"), new Official("仕"), new King("帥"), new Official("仕"), new Minister("象"), new Horse("馬"), new Chariot("車"),new Soldier("卒"), new Soldier("卒"), new Soldier("卒"), new Soldier("卒"), new Soldier("卒"),new Cannon("砲"), new Cannon("砲"),
};int main(void)
{// 当门炮 : 炮二平五  ID 为 32 从(7,7) 移动到 (7,4)chess_man[32]->GoAhead(7, 7, 7, 4);// 马来跳 : 炮8进7  ID 为 8 从(7,7) 移动到 (7,4)chess_man[8]->GoAhead(0, 7, 2, 6);for (int row = 0; row < 10; row++){for (int col = 0; col < 9; col++){auto id = chess_board[row][col];if (id != 0){cout <<setw(5)<< chess_man[id]->name ;//输出一个棋子}else{cout<<setw(5) << "_";//表示棋盘上的一个位置}}cout << endl;cout << endl;}return 0;
}

运行效果

这篇关于从0开始写中国象棋-走一步棋(当门炮,马来跳)(C++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/385329

相关文章

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(