React - 连连看小游戏

news2024/11/16 18:49:50

简介

        小时候经常玩连连看小游戏。在游戏中,当找到2个相同的元素就可以消除元素。

        本文会借助react实现连连看小游戏。

实现效果

实现难点

 1.item 生成

   1. 每一个图片都是一个item,items数组的大小为size*size。

       item对象包括grid布局的位置,key。

       key是标识符,可以标识图片, 相等判断等。

  2. items 可以先顺序生成,最后再调用shuffle算法随机排序。

    const size = 8; // 大小为 8 * 8

    const itemImgSize = 20; // 圖片素材大小
    const [items, setItems] = useState([]);
    useEffect(() => { // 初始化元素
        const initItems = [];
        let idx = 0;
        while (initItems.length < size * size) {
            // 一次插入2個
            initItems.push({
                key: (idx % itemImgSize) + 1,
                x: parseInt(idx / size),
                y: parseInt(idx % size)
            });
            
            initItems.push({
                key: (( idx)% itemImgSize) + 1,
                x: parseInt(( idx + 1) / size),
                y: parseInt((idx + 1)% size)
            });
            idx = idx + 1;
        }
        const nArr = [...shuffleArray(initItems)];
        setItems(nArr);

    }, [])


    function shuffleArray(arr) {

        for (let i = arr.length - 1; i >= arr.length / 2; i--) {
            const j = Math.floor(Math.random() * (size - 1));
            [arr[i], arr[j]] = [arr[j], arr[i]];
            if (arr[i] instanceof Array) {
                shuffleArray(arr[i]);
                shuffleArray(arr[j])
            } else {
                // 交换key
                let key = arr[i].key;
                arr[i].key = arr[j].key;
                arr[j].key = key;
            }
        }
        return arr;
    }

1. 判断选择的2个item可以消除

      基于dfs算法实现,以其中一方为原点,另一方为终点。找到一条成功的路径。

tips: dfs 可以改成bfs, dfs  当item 消除大半后,会变慢。

    /**
     * 判断 (i,j) 与(x,y)是否可达
     */
    function dfs(i, j, visited, x, y) {
        if (res.current === true) {
            return;
        }
        if (i < 0 || i >= size + 2 || j < 0 || j >= size + 2) { // 边界
            return;
        }

        if (i === x && j === y) {
            res.current = true;
            return;
        }

        if (visited[i][j] === 1) {
            return;
        }
        if (board.current[i][j] === 1) { // 只能走空白
            return;
        }
        visited[i][j] = 1;
        dfs(i - 1, j, visited, x, y);
        dfs(i + 1, j, visited, x, y);
        dfs(i, j + 1, visited, x, y);
        dfs(i, j - 1, visited, x, y);
        visited[i][j] = 0;
    }

  boards标记数组是根据items数组生成,若item存在,则boards对应标记为1,反之为null。

  item 对应位置为(item.x+1,item.y+1)

 boards 的大小为(size + 2) * (size + 2) ,  + 2是为了解决边界上的2点相连处理。

    // 二维int数组,标记是否存在元素 (size + 2) * (size + 2), +1是为了边界可以连接
    const board = useRef([]);
    useEffect(() => {
        const nBoard = new Array(size + 2);
        // init
        for (let i = 0; i < size + 2; i++) {
            nBoard[i] = new Array(size + 2);
            for (let j = 0; j< size + 2;j++){
                nBoard[i][j] = 0;
            }
        }

        //根据items设置boards
        items.map((item) => {
            nBoard[item.x + 1][item.y + 1] = 1;
        })

        board.current = (nBoard)
    }, [items]);
整体代码
import bgImg from './imgs/bg.png'
import {useEffect, useRef, useState} from "react";

export const LinkGame = () => {

    const size = 8; // 大小为 8 * 8

    const itemImgSize = 20; // 圖片素材大小
    const [items, setItems] = useState([]);
    useEffect(() => { // 初始化元素
        const initItems = [];
        let idx = 0;
        while (initItems.length < size * size) {
            // 一次插入2個
            initItems.push({
                key: (idx % itemImgSize) + 1,
                x: parseInt(idx / size),
                y: parseInt(idx % size)
            });
     
            initItems.push({
                key: (idx % itemImgSize) + 1,
                x: parseInt((idx + 1)/ size),
                y: parseInt((idx + 1) % size)
            });
            idx = idx + 2;
        }
        const nArr = [...shuffleArray(initItems)];
        setItems(nArr);

    }, [])


    function shuffleArray(arr) {

        for (let i = arr.length - 1; i >= arr.length / 2; i--) {
            const j = Math.floor(Math.random() * (size - 1));
            [arr[i], arr[j]] = [arr[j], arr[i]];
            if (arr[i] instanceof Array) {
                shuffleArray(arr[i]);
                shuffleArray(arr[j])
            } else {
                // 交换key
                let key = arr[i].key;
                arr[i].key = arr[j].key;
                arr[j].key = key;
            }
        }
        return arr;
    }

    // 二维int数组,标记是否存在元素 (size + 2) * (size + 2), +1是为了边界可以连接
    const board = useRef([]);
    useEffect(() => {
        const nBoard = new Array(size + 2);
        // init
        for (let i = 0; i < size + 2; i++) {
            nBoard[i] = new Array(size + 2);
            for (let j = 0; j< size + 2;j++){
                nBoard[i][j] = 0;
            }
        }

        //根据items设置boards
        items.map((item) => {
            nBoard[item.x + 1][item.y + 1] = 1;
        })

        board.current = (nBoard)
    }, [items]);


    // 当选择2个时候,判断是否能消除,如果能消除,则消除,不能则复原。
    const res = useRef(false);
    useEffect(() => {
        const checkedList = [];
        items.map(item => {
            if (item.checked) {
                checkedList.push(item);
            }
            if (checkedList.length === 2) {
                const a = checkedList[0];
                const b = checkedList[1];

                if (a.key !== b.key) {
                    a.checked = false;
                    b.checked = false;
                    setItems([...items])
                } else {
                    // 判断 a 和 b 直接是否能连接
                    const visited = new Array(size + 2);
                    for (let i = 0; i < size + 2; i++) {
                        visited[i] = new Array(size + 2);
                    }
                    const i = a.x + 1;
                    const j = a.y + 1;
                    const x = b.x + 1;
                    const y = b.y + 1;
                    dfs(i + 1, j, visited, x, y)
                    dfs(i - 1, j, visited, x, y)
                    dfs(i, j + 1, visited, x, y)
                    dfs(i, j - 1, visited, x, y)
                    if (res.current === true) { // 存在线路相连
                        // 移除 a 和 b
                        const nItems = [];
                        items.map((item) => {
                            if (item !== a && item !== b) {
                                nItems.push(item);
                            }
                        })
                        setItems(nItems)
                        res.current = false; //init
                    } else {
                        a.checked = false;
                        b.checked = false;
                        setItems([...items])
                    }
                }
            }
        })
    }, [items]);

    /**
     * 判断 (i,j) 与(x,y)是否可达
     */
    function dfs(i, j, visited, x, y) {
        if (res.current === true) {
            return;
        }
        if (i < 0 || i >= size + 2 || j < 0 || j >= size + 2) { // 边界
            return;
        }

        if (i === x && j === y) {
            res.current = true;
            return;
        }

        if (visited[i][j] === 1) {
            return;
        }
        if (board.current[i][j] === 1) { // 只能走空白
            return;
        }
        visited[i][j] = 1;
        dfs(i - 1, j, visited, x, y);
        dfs(i + 1, j, visited, x, y);
        dfs(i, j + 1, visited, x, y);
        dfs(i, j - 1, visited, x, y);
        visited[i][j] = 0;
    }

    function onItemClick(item) {
        item.checked = !item.checked;
        setItems([...items]);
    }

    const gameBoardStyle = { // 游戏区域样式
        display: 'grid',
        gridTemplateColumns: `repeat(${size}, 1fr)`,
        gridTemplateRows: `repeat(${size}, 1fr)`,
        width: '60vw',
        height: '80vh',
        backgroundImage: 'url(' + bgImg + ')',
        backgroundSize: 'cover'
    };

    const gameBoardItemStyle = (item) => {
        if (item.checked) {
            return ({
                gridRowStart: item.x + 1,
                gridColumnStart: item.y + 1,
                opacity: 0.4
            })
        }
        return ({
            gridRowStart: item.x + 1,
            gridColumnStart: item.y + 1,

        });
    }

    return <>
        <div id={'link-game'}>
            <div style={gameBoardStyle}>
                {
                    items.map((item, idx) => (
                        <div style={gameBoardItemStyle(item)}
                             onClick={() => onItemClick(item)} key={'item-' + idx}>
                            <img src={require(`./imgs/${item.key}.png`)}/>
                        </div>
                    ))
                }
            </div>
        </div>
    </>
}

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

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

相关文章

UE小:UE5.3无法创建C++工程

当您在使用Unreal Engine (UE) 构建项目时&#xff0c;如果遇到以下问题&#xff1a; Running C:/Program Files/Epic Games/UE\_5.3/Engine/Build/BatchFiles/Build.bat -projectfiles -project"C:/UEProject/Shp\_1/Shp\_1.uproject" -game -rocket -progress Usi…

网络基础三——初识IP协议

网络基础三 ​ 数据通过应用层、传输层将数据传输到了网络层&#xff1b; ​ 传输层协议&#xff0c;如&#xff1a;TCP协议提供可靠性策略或者高效性策略&#xff0c;UDP提供实时性策略&#xff0c;保证向下层交付的数据是符合要求的的&#xff1b;而网络层&#xff0c;如&a…

websokcet服务端实现

一/websokcet服务端实现 步骤一&#xff1a; springboot底层帮我们自动配置了websokcet&#xff0c;引入maven依赖 1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</arti…

分享10个免费高可用的GPT3.5和4.0网站并做功能测试【第一个】

1.介绍 网址&#xff1a;直接点&#xff1a;aicnn 或者 www.aicnn.cn 基于ChatGPT可以实现智能聊天、绘画生成、高清文本转语音、论文润色等多种功能&#xff0c;基于sd和mj实现的绘画功能&#xff0c;下面是功能测试&#xff1a; 博主从 1.GPT3.5是否完全免费/是否限制频率、…

基于SSM的基于个人需求和地域特色的外卖推荐系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的基于个人需求和地域特色的外卖推荐系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

作为一个前端,在入职新公司如何快速安装好开发环境

由于电脑运行内存才16G有点卡&#xff0c;今天公司给我们换了32G内存&#xff0c;是直接整个主机都换了&#xff0c;环境自然得重新安装&#xff0c;在装的过程中&#xff0c;自己会有些心得体会&#xff0c;就是想着一个新人如何快速安装环境。 个人说一下我的思路&#xff1a…

SPI接口原理以及读写接口用例的详细介绍

一&#xff0c;spi接口原理 SPI接口&#xff0c;即串行外设接口&#xff08;Serial Peripheral Interface&#xff09;&#xff0c;是一种同步串行数据传输协议。它主要用于连接微处理器和各种外设&#xff0c;如存储器、传感器、ADC&#xff08;模数转换器&#xff09;和DAC&…

LLM 构建Data Multi-Agents 赋能数据分析平台的实践之②:数据治理之二(自动处理)

前述 在前文的multi Agents for Data Analysis的设计说起&#xff0c;本文将继续探索和测试借助llm实现基于私有知识库的数据治理全自动化及智能化。整体设计如下&#xff1a; 整个体系设计了3个Agent以及一个Planer&Execute Agent&#xff0c;第一个Agent用于从企业数据…

蓝桥杯真题代码记录(数位排序

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 小蓝对一个数的数位之和很感兴趣, 今天他要按照数位之和给数排序。当 两个数各个数位之和不同时, 将数位和较小的排在前面, 当数位之和相等时, 将数值小的排在前面。 例如, 2022 排在 40…

这个开发板在线仿真网站你一定不能错过

大家好&#xff0c;我是知微&#xff01; 今天给大家推荐一个免费的在线的开发板仿真网站&#xff0c;你可以使用它来仿真Arduino、ESP32和许多其他流行的电路板、元器件以及传感器&#xff0c;免去初期需要购买开发才能学习的困扰。 它就是Wokwi&#xff0c;网址如下 https:…

10个程序员可以接私活的平台和一些建议

话不多说&#xff0c;直接进入正题。我把我压箱底的10个程序员接私活的平台都拿出来了&#xff0c;看之前记得先点赞收藏~ 码市 互联网网站外包服务平台&#xff0c;这个平台上还有产品原型可供参考。在码市上有一系列规范的接单和发单流程答疑过程&#xff0c;可以很好地帮助…

YouTube首席执行官指控OpenAI违反服务条款:AI训练数据伦理之争加剧

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

回溯法(一)——全排列 全组合 子集问题

全排列问题 数字序列 [ l , r ] [l,r] [l,r]​区间内元素的全排列问题 extern int ans[],l,r,num;//num&#xff1a;方案数 extern bool flag[]; void dfs(int cl){//cl:current left&#xff0c;即为当前递归轮的首元素if(cl r 1){//数组已越界&#xff0c;本轮递归结束for…

全国多年平均水汽压空间分布数据

引言 地理遥感生态网结合1971-2021年各地区地面气象监测站数据&#xff0c;应用气候数据空间插值软件Anusplin预测全国平均水汽压分布数据成果。得出全国各个省市自治区平均水汽压分布图&#xff0c;全国各省市自治区平均水汽压数据产品是地理遥感生态网推出的气象气候类数据产…

非关系型数据库——三万字Redis数据库详解

目录 前言 一、Redis概述 1.主要特点 2.Redis优缺点 3.Redis为什么这么快 4.Redis那么快&#xff0c;为什么不用它做主数据库&#xff0c;只用它做缓存 5.线程模型 5.1单线程架构 5.2多线程IO处理&#xff08;Redis 6及以上&#xff09; 5.3线程模型的优化 6.作用 …

基于Difussion图像、视频生成综述

2024年大年初七&#xff08;02.16&#xff09;OpenAI 发布视频生成模型 Sora 在各大平台转疯了&#xff0c;和2022年发布ChatGPT3.5时一样的疯狂。在开工第一天&#xff0c;我就去官网上看了 Sora 的技术报告&#xff0c;遗憾的是&#xff0c;在这份技术报告中只披露了一些模型…

文库配置异步转换(宝塔)| 魔众文库系统

执行以下操作前提前进入网站根目录&#xff0c;如 cd /www/wwwroot/example.com执行 artisan 命令前请参照 开发教程 → 开发使用常见问题 → 如何运行 /www/server/php/xxx/bin/php artisan xxx 命令 步骤1&#xff0c;生成数据库队列表迁移文件 在执行该步骤前&#xff0c;请…

记一次农业工程学报投稿流程与感悟

经过数段时间的实验与熬夜&#xff0c;终于得出一个比较满意的结果&#xff0c;本想着第一篇先随便发一个试试投稿流程&#xff0c;但是经过老师修改后非让投农业工程学报&#xff0c;然后在网上查了一些信息后有点害怕&#xff0c;大致都是在说周期长&#xff0c;审稿慢等等 …

GPT-4、PaLM-2等AI模型对黑人or女性存在偏见?丨AI偏见的案例和应对

生成式 AI&#xff08;Generative AI&#xff09;以其卓越的能力在模仿和理解人类智能方面不断突破界限&#xff0c;展现出令人瞩目的潜力。但与此同时&#xff0c;AI 系统在提供这些创新服务的过程中&#xff0c;有时也会暴露出一些问题&#xff0c;尤其是在文化和种族方面的偏…

基于java+SpringBoot+Vue的房屋租赁系统设计与实现

基于javaSpringBootVue的房屋租赁系统设计与实现 开发语言: Java 数据库: MySQL技术: Spring Boot JSP工具: IDEA/Eclipse、Navicat、Maven 系统展示 前台展示 房源浏览模块&#xff1a;展示可租赁的房源信息&#xff0c;用户可以根据条件筛选房源。 预约看房模块&#…