从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++中实现调试日志输出

《C++中实现调试日志输出》在C++编程中,调试日志对于定位问题和优化代码至关重要,本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳,希望对大家有所帮助... 目录1. 使用 #ifdef _DEBUG 宏2. 加入时间戳:精确到毫秒3.Windows 和 MFC 中的调试日志方法MFC

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)