五子棋理解C++思想

news2024/11/14 19:49:54

双人五子棋项目目录:
在这里插入图片描述

class Game {
public:
	Game();
	void init();
	bool waitPlayerPutChess(Player* player, int& oldi, int& oldj);
	void draw();
	void play();
	bool isOver(int playerId);

public:
	int whoWin = -1;		
	// 谁赢了(0:白棋,1:黑棋,2:平局)
	//谁赢了(0:白棋,1:黑棋,2:red,3:平局)
	int total = 0;			// 一共下了多少个棋子
	Player* player[3];		// 两个玩家  3ge
	ChessBoard* chessBoard; // 棋盘
};

胜负判断:

// 判断游戏是否结束
bool Game::isOver(int playerId) {
    bool isInit = true; // 是否刚刚开局
    for (int i = 0; i < MAP_SIZE; i++) {
        for (int j = 0; j < MAP_SIZE; j++) {
            if (!this->chessBoard->chess[i][j].isEmpty()) {
                // 遍历每个可能的位置
                isInit = false; // 如果有棋子,那么就不是刚刚开局
                int nowcolor = this->chessBoard->chess[i][j].getValue(); // 当前棋子的颜色
                int length[4] = { 0, 0, 0, 0 }; // 四个方向的长度

                for (int k = 0; k < 4; k++) {
                    // 检查每个方向
                    int nowi = i;
                    int nowj = j;
                    // 正向检查当前颜色的连续棋子
                    while (nowi < MAP_SIZE && nowj < MAP_SIZE && nowi >= 0 && nowj >= 0 &&
                           this->chessBoard->chess[nowi][nowj].getValue() == nowcolor) {
                        length[k]++;
                        nowj += dx[k];
                        nowi += dy[k];
                    }
                    nowi = i;
                    nowj = j;
                    // 反向检查当前颜色的连续棋子
                    while (nowi < MAP_SIZE && nowj < MAP_SIZE && nowi >= 0 && nowj >= 0 &&
                           this->chessBoard->chess[nowi][nowj].getValue() == nowcolor) {
                        length[k]++;
                        nowj -= dx[k];
                        nowi -= dy[k];
                    }
                    length[k]--; // 去掉重复计算的当前棋子
                }

                for (int k = 0; k < 4; k++) {
                    // 如果某个方向有五个或更多连续棋子
                    if (length[k] >= 5) {
                        this->whoWin = playerId; // 设置当前玩家为获胜者
                    }
                }

                // 如果棋盘已满,则为平局
                if (this->total == MAP_SIZE * MAP_SIZE) {
                    this->whoWin = 2; // 平局
                }
            }
        }
    }

    if (this->whoWin == -1) {
        // 如果没有人胜利,继续游戏
        Sleep(500);
        return false;
    }
    return true; // 游戏结束,有人胜利或平局
}

game.cpp完整代码:

#include "Game.h"

int dx[4]{ 1, 0, 1, 1 }; // - | \ / 四个方向
int dy[4]{ 0, 1, 1, -1 };

TCHAR message[3][20] = { _T("白胜"), _T("黑胜"), _T("平局")};

Game::Game() {
	this->player[0] = new WhitePlayer("白棋");
	this->player[1] = new BlackPlayer("黑棋");
	this->chessBoard = new ChessBoard();
}

// 游戏初始化
void Game::init() {
	initgraph(WINDOW_WIDTH, WINDOW_HEIGHT, EX_NOMINIMIZE);	// 初始化窗口,NOMINIMIZE表示不允许最小化
	setbkcolor(WHITE);					// 设置背景颜色
	setbkmode(TRANSPARENT);				// 设置透明文字输出背景
}

// 等待玩家下棋
bool Game::waitPlayerPutChess(Player* player, int& oldi, int& oldj) {
	while (1) {
		ExMessage mouse = getmessage(EX_MOUSE); // 获取鼠标信息
		for (int i = 0; i < MAP_SIZE; i++) {
			for (int j = 0; j < MAP_SIZE; j++) {
				if (this->chessBoard->canPut(i, j, mouse)) {
					// 如果停在某一个空位置上面
					if (mouse.lbutton) {
						// 如果按下了
						this->total++;						// 下棋个数+1
						player->placeChess(chessBoard, i, j);
						oldi = -1;
						oldj = -1;
						return true;
					}
					// 更新选择框
					this->chessBoard->chess[oldi][oldj].setIsnew(false);
					this->chessBoard->chess[oldi][oldj].draw();
					this->chessBoard->chess[i][j].setIsnew(true);
					this->chessBoard->chess[i][j].draw();
					oldi = i;
					oldj = j;
				}
			}
		}
	}
}

void Game::draw() {
	this->whoWin = -1;// 谁赢了
	this->total = 0;
	for (int i = 0; i < MAP_SIZE; i++) {
		for (int j = 0; j < MAP_SIZE; j++) {
			this->chessBoard->chess[i][j].setValue(-1);	// 表示空位置
		}
	}
	cleardevice();
	// 绘制背景
	setfillcolor(RGB(255, 205, 150));
	solidrectangle(40, 25, 475, 460);
	// 设置字体样式
	settextstyle(30, 15, 0, 0, 0, 1000, false, false, false);
	settextcolor(BLACK);
	this->chessBoard->draw(); // 绘制
}

// 开始游戏
void Game::play() {
	// 上一个鼠标停的坐标
	int oldi = 0;
	int oldj = 0;
	// 0 白棋先下, 1 黑棋后下, 轮循
	int curPlayerId = 0;
	while (1) {
		// 等待玩家放棋子
		if (this->waitPlayerPutChess(this->player[curPlayerId], oldi, oldj)) {
			this->chessBoard->draw();
			if (this->isOver(curPlayerId)) {
				// 弹框提示
				MessageBox(GetHWnd(), message[this->whoWin], _T("游戏结束"), MB_ICONWARNING);
				break;
			}
			curPlayerId = !curPlayerId;  // 0 变 1, 1 变 0 
		}
	}
}

// 判断游戏是否结束
bool Game::isOver(int playerId) {
	bool isInit = true; // 是否刚刚开局
	for (int i = 0; i < MAP_SIZE; i++) {
		for (int j = 0; j < MAP_SIZE; j++) {
			if (!this->chessBoard->chess[i][j].isEmpty()) {
				// 遍历每个可能的位置
				isInit = false;                 // 如果有,那么就不是刚刚开局
				int nowcolor = this->chessBoard->chess[i][j].getValue(); // 现在遍历到的颜色
				int length[4] = { 0, 0, 0, 0 };    // 四个方向的长度
				for (int k = 0; k < 4; k++) {
					// 原理同寻找最佳位置
					int nowi = i;
					int nowj = j;
					while (nowi < MAP_SIZE && nowj < MAP_SIZE && nowi >= 0 && nowj >= 0 && this->chessBoard->chess[nowi][nowj].getValue() == nowcolor)
					{
						length[k]++;
						nowj += dx[k];
						nowi += dy[k];
					}
					nowi = i;
					nowj = j;
					while (nowi < MAP_SIZE && nowj < MAP_SIZE && nowi >= 0 && nowj >= 0 && this->chessBoard->chess[nowi][nowj].getValue() == 1 - nowcolor)
					{
						length[k]++;
						nowj -= dx[k];
						nowi -= dy[k];
					}
				}
				for (int k = 0; k < 4; k++) {
					// 如果满五子
					if (length[k] >= 5) {
						this->whoWin = playerId;
					}
				}
				// 全都下满了都没有位置了
				if (this->total == MAP_SIZE * MAP_SIZE) {
					this->whoWin = 2; // 平局
				}
			}
		}
	}
	if (this->whoWin == -1) {
		// 如果没有人胜利
		Sleep(500);
		return false;
	}
	return true;
}

在这里插入图片描述
定义四个方向

int dx[4]{ 1, 0, 1, 1 }; // -  |  \  /  四个方向
int dy[4]{ 0, 1, 1, -1 };

dx数组对应水平方向的偏移量,dy数组对应垂直方向的偏移量。
留作胜负判断时候正方向和反方向偏移判断是否有棋子
在判断游戏是否结束的函数isOver中被使用。通过遍历棋盘上的每个位置,对于非空位置,尝试在四个方向上进行延伸搜索,以判断是否有连续五个相同颜色的棋子。
在搜索过程中,通过不断更新当前位置的坐标nowi和nowj,使用nowi += dy[k]和nowj += dx[k]来沿着特定方向前进,从而检查该方向上的棋子颜色是否相同。
例如,当k = 0时,表示水平向右的方向,每次循环会将当前位置的横坐标增加 1(即nowj += dx[k]和nowi += dy[k]变为nowj += 1和nowi += 0),以检查水平方向上是否有连续相同颜色的棋子。
length[k]>= 5则返回玩家标识ID

/
加一个玩家,暂且定为红棋
因为有player类,在头文件加上玩家标识,添加RedPlayer.h .cpp,继承player

enum Color {
	White,	// 0
	Black,	// 1
	Red  //2
};

继承便新增一红棋玩家,胜负逻辑不变,二人轮流下棋原本直接取反便可切换角色
//curPlayerId = !curPlayerId; // 0 变 1, 1 变 0
当三人下棋时,便要用别的方式,取余数:

// 0 白棋先下, 1 黑棋后下, 轮循
int numPlayers = 3;
int curPlayerId = 0;
...
curPlayerId = (curPlayerId + 1) % numPlayers;

使得三个玩家切换

以此类推,N个玩家都行
当然,创建game对象时,其构造函数也要初始化一个新的玩家对象

Game::Game() {
    this->player[0] = new WhitePlayer("白棋");
    this->player[1] = new BlackPlayer("黑棋");
    this->player[2] = new RedPlayer("红棋");
}

消息数组也要新增一列:

TCHAR message[4][20] = { _T("白胜"), _T("黑胜"), _T("红胜"), _T("平局")};

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2071506.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【题解】【模拟】—— [CSP-J 2023] 一元二次方程

【题解】【模拟】—— [CSP-J 2023] 一元二次方程 [CSP-J 2023] 一元二次方程题目背景题目描述输入格式输出格式输入输出样例输入 #1输出 #1 提示 1.题意解析2.AC代码 [CSP-J 2023] 一元二次方程 戳我查看题目&#xff08;洛谷&#xff09; 题目背景 众所周知&#xff0c;对…

共享文件操作记录如何查看?这种方法帮你轻松实现

查看共享文件操作记录&#xff0c;可以通过以下几种方法实现&#xff1a; 一、利用操作系统的文件访问记录功能 在Windows操作系统中&#xff0c;可以利用“事件查看器”来查看共享文件的访问记录。事件查看器是Windows内置的一个工具&#xff0c;用于记录系统中发生的各种事…

50个必知的ChatGPT学术论文指令:提升研究效率,强烈推荐收藏!

随着AI技术的发展&#xff0c;AI已经成为学术创作的强大工具。如果你还不了解如何利用AI润色你的论文&#xff0c;那么这篇文章将为你提供极大的帮助。我们精心整理了50个顶级ChatGPT学术论文指令&#xff0c;强烈建议你加以利用&#xff01; 这些指令非常实用&#xff0c;能显…

线性表的顺序表示—插入操作

线性表的顺序表示—插入操作 插入代码 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define InitSize 15 // 初始化扩容长度 #define MaxSize 100typedef struct {int data[MaxSize]; // 申请空间&#xff08;静态&#xff09;int lengt…

探索ISP自动曝光技术:工作原理与应用(一)

在现代数码相机和智能手机中&#xff0c;图像信号处理器&#xff08;ISP&#xff09;是负责将传感器捕捉到的原始数据转换成高质量图像的重要组件。而在ISP的众多功能中&#xff0c;自动曝光&#xff08;Auto Exposure, AE&#xff09;是确保拍摄出清晰、明亮且细节丰富照片的关…

QT Quick QML 实例之定制 TableView

QT Quick QML 实例之定制 TableView 一、演示二、C关键步骤1. beginInsertRows()&#xff08;用户插入行&#xff09;2. roleNames() &#xff08;表格中列映射&#xff09;3. data() &#xff08;用户获取数据&#xff09;4. headerData() &#xff08;表头&#xff09;5. fla…

20240824给飞凌OK3588-C的核心板刷Ubuntu22.04后适配SONY索尼的HDMI OUT的机芯8530

20240824给飞凌OK3588-C的核心板刷Ubuntu22.04后适配SONY索尼的HDMI OUT的机芯8530 2024/8/24 16:33 echo 8 > /sys/class/gpio/export echo out > /sys/class/gpio/gpio8/direction echo 1 > /sys/class/gpio/gpio8/value 1、HDMI IN 4K 2024/7/25 18:01 v4l2-ctl -…

文件—python

一、文件编码 对于同一份文件&#xff0c;人的视角和计算机的视角是不相同的&#xff0c;人看到的是文字&#xff0c;计算机看到的0和1组成的编码。因为计算机只能识别0和1&#xff0c;无法直接识别文字&#xff0c;那我们是如何在电脑上看到文字的呢&#xff1f; 计算机按照一…

C++的动态数组vector番外之capacity

今日诗词&#xff1a; 爱他明月好&#xff0c;憔悴也相关。 西风多少恨&#xff0c;吹不散眉弯。 ——《临江仙寒柳》【清】纳兰容若 目录 引言 正文 string中的和vector中的capacity有什么区别 vector扩容时内存分配的策略是什么&#xff1f; capacity在vector中的表现如…

基于无人机边沿相关 ------- IBUS、SBUS协议和PPM信号

文章目录 一、IBUS协议二、SBUS协议三、PPM信号 一、IBUS协议 IBUS&#xff08;Intelligent Bus&#xff09;是一种用于电子设备之间通信的协议&#xff0c;采用串行通信方式&#xff0c;允许多设备通过单一数据线通信&#xff0c;较低延迟&#xff0c;支持多主机和从机结构&a…

redis | Django小项目之Mysql数据库和Redis缓存的应用

Django小项目 需求整体架构图技术细节环境配置各文件配置settings.pyurls.pyviews.pyuser_update.html 结果相关代码补充r.hgetall(cacahe_key)new_data {k.decode():v.decode() for k,v in data.items()} 需求 整体架构图 技术细节 环境配置 django-admin startprojrct rmysi…

WIFI 应用层代码

1.0 定义枚举类型 typedef enum {WIFI_COMM_WAIT, // AT 等待命令WIFI_COMM_OK, // AT 命令完成WIFI_COMM_FALL, // AT 命令失败 }WifiCommState_t; 注&#xff1a;该枚举类型的作用是&#xff0c;定义三个成员变量&#xff0c;分别表示AT指令等待发送&#xff0c;AT指令…

unity游戏开发——(细)深入解析 Unity 地形系统:从基础到高级应用

Unity游戏开发 “好读书&#xff0c;不求甚解&#xff1b;每有会意&#xff0c;便欣然忘食。” 本文目录&#xff1a; Unity游戏开发 Unity游戏开发前言深入解析 Unity 地形系统&#xff1a;从基础到高级应用一、初识 Unity 地形系统1. 地形尺寸与分辨率 二、地形编辑工具详解1…

下拉菜单 匹配搜索

操作版本&#xff1a;Excel 2010 下拉菜单 涉及到的函数&#xff1a; INDIRECT函数&#xff1a;返回由文本字符串指定的引用 原文链接 一级下拉菜单 方法一&#xff1a;手动输入 选中要制作下拉菜单的单元格区域&#xff0c;单击【数据】-【数据有效性】-【序列】&#…

15.CentOS7升级内核

升级内核 1.配置镜像源 vim /etc/yum.repos.d/elrepo.repo[elrepo] nameelrepo baseurlhttps://mirrors.aliyun.com/elrepo/archive/kernel/el7/x86_64 gpgcheck0 enabled12.备份 cd /etc/yum.repos.d/ mv local.repo repo.bak/ 3.清缓存 yum clean all 4.升级内核 yum ins…

背部筋膜炎最好的恢复办法

背部筋膜炎是由于寒冷、精神紧张、潮湿或慢性劳损等因素造成的背部肌筋膜和肌组织发生水肿、纤维变性和渗出&#xff0c;其主要症状包括&#xff1a; 1、疼痛&#xff1a;患者通常会出现背部疼痛&#xff0c;这种疼痛在着凉或劳累时可能会加重。晨起时疼痛可能尤为明显&#x…

将两对象(重复属性不替换)合并成一个对象

将这两个对象合并成一个对象 const obj1 {"configType": "all","config": {"a":1} };const obj2 {"target_cluster": "dev-0821","type": "import","config": {"connector…

攻防世界-web题型-4星难度汇总-个人wp

Confusion1 进入页面查看源代码&#xff0c;发现有两个提示 访问第一个源码里面有个 /opt/flag_1de36dff62a3a54ecfbc6e1fd2ef0ad1.txt /opt/salt_b420e8cfb8862548e68459ae1d37a1d5.txt 不过我发现好像是只要访问404页面都有这两个。。。 另外这两个admin和login页面都没用…

如何把照片转换成PDF格式?分享3种好用的PDF转换方法

在数字化的时代中&#xff0c;各种格式的文件已经是我们平时办公中不可或缺的一部分&#xff0c;其中尤其是以图片、PDF这两种文件使用的最多&#xff0c;图片能够更加直观的表现内容&#xff0c;而PDF文档因其稳定性、兼容性等特性&#xff0c;也在办公中有独特的优势&#xf…

Pandas DataFrame的创建方法(Create DataFrame)

pandas是一个第三方数据分析库&#xff0c;其集成了大量的数据模型和分析工具&#xff0c;可以方便的处理和分析各类数据。其中主要对象类型有Series&#xff0c;DataFrame和Index。本文介绍DataFrame对象的基本创建方法。 关于DataFrame的基础用法&#xff0c;可以查看下面的…