React 实现五子棋

简介

         本文将会基于React 实现五子棋小游戏,游戏规则为先让5颗棋子连成1线的一方获胜。

实现效果

技术实现

页面布局

<div><table style={{border: '1px solid #000', borderCollapse: 'collapse', backgroundColor: 'lightgray'}}><tbody>{squares.map((row, rowIndex) => {return <tr key={rowIndex}>{row.map((col, colIndex) => {return <td key={colIndex}style={{border: '1px solid #000', width: '30px', height: '30px'}}onClick={(event) => {const clickedElement = event.target as HTMLTableCellElement;const rect = clickedElement.getBoundingClientRect();const x = event.clientX - rect.left;const y = event.clientY - rect.top;const width = clickedElement.getBoundingClientRect().width;const height = clickedElement.getBoundingClientRect().height;const left = x / width;const top = y / height;if (left < 0.5 && top < 0.5){ // 左上if (rowIndex > 0){rowIndex--;}if (colIndex > 0){colIndex--;}}if (left < 0.5 && top > 0.5){ // 左下if (colIndex > 0){colIndex--;}}if (left > 0.5 && top < 0.5){ // 右上if (rowIndex > 0){rowIndex--;}}let item = squares[rowIndex][colIndex];if (item !== '') {return;}if (times % 2 === 0) {item = 'black';} else {item = 'white';}const newSquares = [...squares];newSquares[rowIndex][colIndex] = item;setTimes(times + 1);setSquares(newSquares);}}>{col === 'white' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'white',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div>: (col === 'black' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'black',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div> : col)}</td>})}</tr>})}</tbody></table></div>

        本文用table实现游戏布局,棋盘大小为15 * 15,背景色为浅灰,棋子为白色或黑色。

        由于棋子是位于交叉点上,需要将table cell 定位到右下角, right - 50%, bottom -50%,

下棋逻辑

onClick={(event) => {const clickedElement = event.target as HTMLTableCellElement;const rect = clickedElement.getBoundingClientRect();const x = event.clientX - rect.left;const y = event.clientY - rect.top;const width = clickedElement.getBoundingClientRect().width;const height = clickedElement.getBoundingClientRect().height;const left = x / width;const top = y / height;if (left < 0.5 && top < 0.5) { // 左上if (rowIndex > 0) {rowIndex--;}if (colIndex > 0) {colIndex--;}}if (left < 0.5 && top > 0.5) { // 左下if (colIndex > 0) {colIndex--;}}if (left > 0.5 && top < 0.5) { // 右上if (rowIndex > 0) {rowIndex--;}}let item = squares[rowIndex][colIndex];if (item !== '') {return;}if (times % 2 === 0) {item = 'black';} else {item = 'white';}const newSquares = [...squares];newSquares[rowIndex][colIndex] = item;setTimes(times + 1);setSquares(newSquares);
}
}

        基于times控制是白方还是黑方,然后更新squares状态,棋盘会根据squares状态进行显示。

        对于每个交叉点,棋子是以交叉点为右下角的table cell。

        计算点击位置位于table cell的哪个区域,然后计算交叉点对应的table cell。

        左上 -> r-- c--

        右上 -> r-- c

        左下 -> r c--

        右下 -> r ,c

获胜逻辑判断

    useEffect(()=>{// 计算dp的值for (let i = 0; i < 15; i++) {for (let j = 0; j < 15; j++) {if (squares[i][j] === '') {continue;}// 判断左边if (j >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断上边if (i >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断右边if (j <= 10){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断下边if (i <= 10){let num = 1;for (let k = 1; k < 5; k++){if (squares[i + k][j] === squares[i][j]) {num++;} else {break;}}console.log(i + ',' + j + ':'+ num)if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断左上if (i >= 5 && j >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断右上if (i <= 10 && j >= 5){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断左下if (i >= 5 && j <= 10){let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}// 判断右下if (i <= 10 && j <= 10){let num = 1;for (let k = 1; k < 5; k++){if (squares[i + k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] +'win');setSquares(initSquares)setTimes(0);return;}}}}}, [squares])

         判断每个棋子上下左右,左上,右上,左下,右下是否有连续5个棋子。

整体代码

const Board = () => {/*** 创建一个 15 * 15  的棋盘*/const initSquares = Array.from({length: 15},() => new Array(15).fill(''));const [squares, setSquares] = useState(initSquares);const [times, setTimes] = useState(0);useEffect(() => {// 计算dp的值for (let i = 0; i < 15; i++) {for (let j = 0; j < 15; j++) {if (squares[i][j] === '') {continue;}// 判断左边if (j >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断上边if (i >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断右边if (j <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断下边if (i <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断左上if (i >= 5 && j >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断右上if (i <= 10 && j >= 5) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j - k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断左下if (i >= 5 && j <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i - k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}// 判断右下if (i <= 10 && j <= 10) {let num = 1;for (let k = 1; k < 5; k++) {if (squares[i + k][j + k] === squares[i][j]) {num++;} else {break;}}if (num >= 5) {alert(squares[i][j] + '----' + 'win');setSquares(initSquares)setTimes(0);return;}}}}}, [squares])return <div><table style={{border: '1px solid #000', borderCollapse: 'collapse', backgroundColor: 'lightgray'}}><tbody>{squares.map((row, rowIndex) => {return <tr key={rowIndex}>{row.map((col, colIndex) => {return <td key={colIndex}style={{border: '1px solid #000', width: '30px', height: '30px'}}onClick={(event) => {const clickedElement = event.target as HTMLTableCellElement;const rect = clickedElement.getBoundingClientRect();const x = event.clientX - rect.left;const y = event.clientY - rect.top;const width = clickedElement.getBoundingClientRect().width;const height = clickedElement.getBoundingClientRect().height;const left = x / width;const top = y / height;if (left < 0.5 && top < 0.5) { // 左上if (rowIndex > 0) {rowIndex--;}if (colIndex > 0) {colIndex--;}}if (left < 0.5 && top > 0.5) { // 左下if (colIndex > 0) {colIndex--;}}if (left > 0.5 && top < 0.5) { // 右上if (rowIndex > 0) {rowIndex--;}}let item = squares[rowIndex][colIndex];if (item !== '') {return;}if (times % 2 === 0) {item = 'black';} else {item = 'white';}const newSquares = [...squares];newSquares[rowIndex][colIndex] = item;setTimes(times + 1);setSquares(newSquares);}}>{col === 'white' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'white',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div>: (col === 'black' ?<divstyle={{width: '100%',height: '100%',backgroundColor: 'black',borderRadius: '50%',position: "relative",right: "-50%",bottom: "-50%"}}></div> : col)}</td>})}</tr>})}</tbody></table></div>
}

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

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

相关文章

普中51单片机:定时器与计数器详解及应用(七)

文章目录 引言定时器工作原理TMOD定时器/计数器工作模式寄存器定时器工作模式模式0(13位定时器/计数器)模式1(16位定时器/计数器)模式2(8位自动重装模式)模式3(两个8位计数器) 定时器配置流程代码演示——LED1间隔1秒闪烁代码演示——按键1控制LED流水灯状态代码演示——LCD160…

GaussDB DWS 详解

文章目录 GaussDB DWS 详解一、简介二、DWS的分布式架构架构概述关键组件 三、分布式查询数据查询流程SQL执行的示例 批注&#xff1a;本文引鉴了Forlogen博主的一些内容&#xff0c;并加以补充&#xff0c;以供学习了解。 GaussDB DWS 详解 一、简介 DWS(Data Warehouse Ser…

免费分享一套SpringBoot+Vue农产品在线销售(在线商城)管理系统【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue农产品在线销售(在线商城)管理系统&#xff0c;分享下哈。 项目介绍 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发…

aidl的Android.bp脚本中java有哪些参数可以配置的?

在Android的Blueprint构建系统中&#xff08;使用Android.bp文件&#xff09;&#xff0c;当你定义一个AIDL&#xff08;Android Interface Definition Language&#xff09;服务或客户端时&#xff0c;你实际上并不是直接在Android.bp中配置AIDL文件本身的Java参数&#xff0c…

音频筑基:入门50问

音频筑基&#xff1a;入门50问 通用类编解码类 只问不答&#xff0c;意在启发。 通用类 为什么音频信号分析要从时域到频域&#xff1f;频域变换中&#xff0c;为啥要做TDAC时域混叠消除&#xff1f;人耳听觉频域敏感区是哪部分&#xff0c;为什么&#xff1f;人声发声频域重要…

在本科生中,发表SCI一区的情况如何?

在本科生中发表SCI一区论文是非常罕见且高度认可的成就。这表明该学生不仅在学术研究方面展现出超乎寻常的能力和潜力&#xff0c;而且在科研方法、创新思维以及专业领域的知识掌握上都达到了高水平。具体来说&#xff1a; 稀有性&#xff1a;本科生能够以第一作者身份发表SCI…

alike-cpp 编译

1. 源码链接&#xff1a; https://github.com/Shiaoming/ALIKE-cpp 2.已经安装好显卡驱动&#xff0c;cuda&#xff0c;cudnn,没安装的参考&#xff1a; 切记装cuda-11.x的版本&#xff0c;最好cuda11.3的版本 ubuntu重装系统后&#xff0c;安装cuda,cudnn-CSDN博客 3.安装…

HttpUtil工具

http工具 用到的依赖 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency><dependency><groupId>org.apache.httpcomponent…

C++客户端Qt开发——信号和槽

三、信号和槽 1.信号和槽概述 在Qt中&#xff0c;用户和控件的每次交互过程称为一个事件。比如"用户点击按钮”是一个事件&#xff0c;"用户关闭窗口”也是一个事件。每个事件都会发出一个信号&#xff0c;例如用户点击按钮会发出"按钮被点击"的信号&…

在互联网供应链系统可能是永远不会过时的系统

一、前言 在互联网在到人工智能&#xff0c;从基本的门户网站&#xff0c;社交网站&#xff0c;到移动互联网&#xff0c;视频网站&#xff0c;再到现在比较火爆短视频直播和人工智能AI&#xff0c;大模型。互联网的迭代&#xff0c;出现了无数的系统。但是有些系统一直久经不…

剪画小程序:做自媒体要做哪些准备!

在这个数字化的时代&#xff0c;自媒体成为了许多人展现自我、实现价值的舞台。如果你是一个自媒体小白&#xff0c;怀揣着梦想和热情准备踏上这条充满挑战与机遇的道路&#xff0c;那么在出发之前&#xff0c;有一些关键的准备工作可不能忽视。 一、明确自身定位 首先要思考的…

婚恋交友语音交友小程序APP系统开发

在数字化时代&#xff0c;婚恋交友的方式也日益多样化。传统的相亲、朋友介绍等方式已经无法满足现代人快节毒的生活需求&#xff0c;更多的人开始选择通过线上平台寻找自己的另-婚恋交友语音交友小程序APP应运而生&#xff0c;为单身男女提供了个便捷、高效的交友平台。本文将…

Windows图形界面(GUI)-DLG-C/C++ - 按钮控件(Button)

公开视频 -> 链接点击跳转公开课程博客首页 -> ​​​​​​链接点击跳转博客主页 目录 按钮控件(Button) 控件样式 消息处理 控件函数 示例代码 按钮控件(Button) 控件样式 普通按钮&#xff08;Push Button&#xff09;&#xff1a;当用户点击时执行一个命令或…

学习小记-RocketMQ的一些小知识

目录 如何保证消息不丢失 如何保证消息顺序消费 如何实现延时消息 如何保证消息不丢失 消息存储&#xff1a; RocketMQ 将消息存储在物理磁盘上&#xff0c;而不是仅仅缓存在内存中。每个消息都被序列化并存储在指定的存储路径中。 主从复制&#xff08;Master-Slave Replica…

指针!!C语言(第一篇)

指针1 指针变量和地址1.取地址操作符(&)2.指针变量和解引用操作符(*) 指针变量的大小和类型指针的运算特殊指针1.viod*指针2.const修饰指针3.野指针 assert断言指针的使用和传址调用1.strlen的模拟实现2.传值调用和传址调用 指针变量和地址 在认识指针之前&#xff0c;我们…

Iceberg概念和特性

1. 快照 Iceberg会随着时间的推进,跟踪表生命周期中的所有数据集变化,并使用快照(Snapshots)来表示每一次变化后的数据集合,每一次数据操作的事务提交均会产生一个快照,并将其记录在元数据文件(Metadata)中。 基于快照的概念,Iceberg有以下特性: 事务性:写入快照成…

使用 CSS 实现渐变效果

使用 CSS 实现渐变效果 使用 CSS 实现渐变效果非常简单且强大&#xff0c;CSS 提供了两种主要的渐变效果&#xff1a;线性渐变&#xff08;linear gradient&#xff09;和径向渐变&#xff08;radial gradient&#xff09;。下面是如何使用这些渐变效果的详细说明。 1. 线性渐…

Hive中的数据类型和存储格式总结

1.数据类型 Hive 支持多种数据类型&#xff0c;分为原始数据类型和复杂数据类型两类。以下是 Hive 支持的数据类型&#xff1a; 原始数据类型&#xff1a; 1.整数类型&#xff1a; tinyint: 1字节有符号整数 smallint: 2字节有符号整数 int:…

微服务负载均衡的艺术:Eureka中服务实例权重配置全解析

微服务负载均衡的艺术&#xff1a;Eureka中服务实例权重配置全解析 在微服务架构中&#xff0c;服务发现是实现服务间互连的基础&#xff0c;而负载均衡则是确保服务高可用性和响应性的关键。Eureka&#xff0c;作为Netflix开源的服务发现框架&#xff0c;提供了丰富的配置选项…

26.6 Django模型层

1. 模型层 1.1 模型层的作用 模型层(Model Layer)是MVC或MTV架构中的一个核心组成部分, 它主要负责定义和管理应用程序中的数据结构及其行为. 具体职责包括: * 1. 封装数据: 模型层封装了应用程序所需的所有数据, 这些数据以结构化的形式存在, 如数据库表, 对象等. * 2. 数据…