js实现动漫拼图2.0版

比较与1.0版,2.0版就更像与华容道类似的拼图游戏,从头到尾都只能控制白色块移动,而且打乱拼图和求助的实现与1.0都不相同

文章目录

  • 1 实现效果
  • 2 实现思路
    • 2.1 打乱拼图
    • 2.2 求助功能
    • 2.3 判赢
  • 3 代码实现

js实现动漫拼图1.0版
https://blog.csdn.net/m0_58730471/article/details/135891948?spm=1001.2014.3001.5501

1 实现效果

拼图2.0

2 实现思路

思路相同的就不在重复赘述,直说我感觉比较难的点或不同的点。

2.1 打乱拼图

这里的打乱不同于1.0版本的,1.0版本的打乱是随机产生x,y两个索引值,然后将该索引值对应元素与x0,y0对应的位置元素交换, 但是有可能产生无解的情况(就直接跨好几格,直接两者交换那种),一步一步就无法还原,而1.0版是可以手动选择原点的,所以就变成了有解。
而这里2.0版本,原点只有一个,无法手动更改,只能一步一步走,那么如果仍是随机产生x,y两个索引值,就可能产生无解的情况所以这里改为随机产生0-3,代表四个方向,让白块在随机的方向上走动n次(n<difficulty,因为有可能随机产生的某个方向走不了已经到边界了),这样一定是有解的,至少原路后退就是一种解。 通过这种交换n次实现打乱的效果。

/**/// 打乱图片// 随机产生0-3 代表四个方向 0-左,1-上 2-右 3-下 function shuffle() {for (let i = 0; i < difficulty; i++) {let x = parseInt(Math.random() * 4);if (x == 0) {direction = 'left';} else if (x == 1) {direction = 'up';} else if (x == 2) {direction = 'right';} else if (x == 3) {direction = 'down';}move(direction);}}

2.2 求助功能

这里通过定义trace,规定0-3代表的方向[0-左 1-上 2-右 3-下],在打乱图片和玩家移动的时候记录下所走的顺序,最后倒着走(从尾部取元素pop,根据值,做反向操作),就可以实现求助(这里的求助并不完美)
在这里插入图片描述
在move里面去记录顺序(trace里面添值)
在这里插入图片描述

 // 求助按钮// 通过trace的记录情况,进行后退,就可以还原最终的拼图function helpBake() {// 调用trace去重函数distinctTrace();// 从trace数组中取出最后一个元素let lastDir = trace.pop();// 判断方向,反方向移动(回退)if (lastDir == 0) {direction = 'right';} else if (lastDir == 1) {direction = 'down';} else if (lastDir == 2) {direction = 'left';} else if (lastDir == 3) {direction = 'up';}// 调用移动函数(移动函数中,每正确交换一次,都会trace.push()这次记录)move(direction);// trace.push()在这里是无用的,所以需要在pop一次trace.pop();}

问题:
trace里面记录的数据都是有用的吗?
会不会产生左右,左左右右,上下,上上下下等原地踏步的情况,答案是肯定的(至少在打乱图片时,随机产生的索引就会有这种情况),所以我们还需要对trace数组进行一个无效步骤去除的操作

// 但是,上面直接通过后退trace,里面存放的元素可能会导致多余步骤:/*  0 向左,1向上,2向右,3向下比如:trace[2 0 3 1 1 3 0 2]  模拟后退情况就先左走,又右走(原地),又向上再向下(原地),又向下再向上(原地),又右走再左走(原地),最终后退这么多步,最后就是呆在原地所以,需要对trace进行去重处理*/function distinctTrace() {let index = findDistinctIndex();// 有重复的,删除while (index != -1) {trace.splice(index, 2);index = findDistinctIndex();}}// 查找相邻的矛盾无用索引function findDistinctIndex() {let index = -1;// trace 是一维数组,相邻两个比较,比较次数就是数组长度-1// 例如: arr[1 2 3 4] 四个元素,相邻的元素比较 1和2比一次,2和3比一次,3和4比一次,所以比较次数就是3次for (let i = 0; i < trace.length - 1; i++) {let front = trace[i];let behind = trace[i + 1];// 情况:(左右)(右左)(上下)(下上)都是原地,需要去重if ((front == 0 && behind == 2) || (front == 2 && behind == 0) || (front == 1 && behind == 3) || (front == 3 && behind == 1)) {index = i;break;}}return index;}

2.3 判赢

这里有所不同的是,最后的一个值不在是16,而是0(因为0.png是白色背景块,在初始化时,将16换成了0,在ui里面动态拼接时,就是白色背景块了)

// 判断是否胜利,function judgeVictory() {let right = true;print.forEach((subArr, i) => {subArr.forEach((data, j) => {if (i == 3 && j == 3) {if (print[i][j] != 0) {right = false;}} else {let index = i * 4 + j;if (print[i][j] != index + 1) {right = false;}}})})return right;}

3 代码实现

代码下载:https://www.alipan.com/s/WrkusEaP8Uq

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>body {background-image: url('./images/background.png');}.first {text-align: center;margin-top: 20px;margin-bottom: 20px;}td {width: 100px;height: 100px;background-image: url('./images/1.png');background-size: 100% 100%;background-repeat: no-repeat;}.second {width: 60%;margin: 0 auto;display: flex;}.second_right {margin-left: 200px;}.third {margin-top: 20px;text-align: center;}#step {font-size: 30px;color: red;display: inline-block;width: 80px;box-sizing: border-box;text-align: center;}.change {width: 100px;height: 40px;font-size: 20px;background-color: #da3c24;border-radius: 10px;color: #fedcdc;}#look {width: 200px;height: 200px;background-repeat: no-repeat;background-size: 100% 100%;border: 4px solid white;}</style>
</head><body><audio src="./audio/bg.mp3" id="bgMusic"></audio><div class="first"><button class="change">更换图片</button><img src="./images/title.png" alt=""></div><div class="second"><div class="second_left"><table><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td></td><td></td><td></td><td></td></tr></table></div><div class="second_right"><div style="margin-bottom: 40px;"><img src="" id="look"></div><div style="margin-bottom: 30px; font-size: 25px; color:aliceblue">已经走了<span id="step">0</span></div><div><div style="text-align: center;"><img src="./images/shang.png" id="up"></div><div style="text-align: center;"><img src="./images/zuo.png" id="left"><img src="./images/xia.png" id="down"><img src="./images/you.png" id="right"></div></div></div></div><div class="third"><img src="./images/chongzhi.png" id="rest"><img src="./images/qiuzhu.png" id="help"><div style="color: aliceblue;">点击重置按钮(开始游戏或者重新打乱顺序)</div></div><script>let print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]];let tds = document.querySelectorAll('td');let isStart = false;let isRest = false;// 记录特殊格子坐标(白色格)let x0, y0;// 记录移动方向let direction = '';// 随机交换的次数let difficulty = 40;// 记录已经走的路径,方便后面实现帮助功能// 0-左 1-上 2-右 3-下let trace = [];// 移动步子let step = 0;// 更换背景拼图let photo = 'images_1';// 获取对应html元素let leftBtn = document.getElementById('left'); //左键let rightBtn = document.getElementById('right'); //右键let upBtn = document.getElementById('up'); //上键let downBtn = document.getElementById('down'); //下键let stepSpan = document.getElementById('step'); //移动步数let helpBtn = document.getElementById('help'); //求助按钮let changeBtn = document.querySelector('.change'); //更换背景拼图按钮let look = document.getElementById('look'); //显示背景拼图的图片let restBtn = document.getElementById('rest'); //重新开始按钮// 初始化背景拼图(默认显示)look.src = './images/images_1/canzhaotu.png';let bgMusic = document.getElementById('bgMusic');// 初始化游戏界面updateUI();// 监听按钮点击事件window.onkeyup = function (e) {if (isStart) {if (e.keyCode == 37) {direction = 'left';move(direction);} else if (e.keyCode == 38) {direction = 'up';move(direction);} else if (e.keyCode == 39) {direction = 'right';move(direction);} else if (e.keyCode == 40) {direction = 'down';move(direction);}}}// 开始或重置restBtn.onclick = function () {// 初始化step = 0;bgMusic.play();print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]//初始化白格print[3][3] = 0;trace = [];//记录原始位置x0 = 3;y0 = 3;// 点击重置不判断输赢isRest = true;// 打乱图片shuffle();// 更新uiupdateUI();// 游戏开始isStart = true;isRest = false;// 给求助按钮添加事件helpBtn.onclick = helpBake;}// 控制移动(游戏开始可以移动)leftBtn.onclick = function () {if (isStart) {direction = 'left';move(direction);}}rightBtn.onclick = function () {if (isStart) {direction = 'right';move(direction);}}upBtn.onclick = function () {if (isStart) {direction = 'up';move(direction);}}downBtn.onclick = function () {if (isStart) {direction = 'down';move(direction);}}// 更换拼图图片changeBtn.onclick = function () {isStart = false;print = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],[13, 14, 15, 16]]trace = [];step = 0;// 随机产生1-4的索引值let index = parseInt(Math.random() * 4) + 1;let photo_id = look.src.split('/')[look.src.split('/').length - 2].split('_')[1];// 确保新的索引值和原来的不是一个,确保图片更换while (index == photo_id) {index = parseInt(Math.random() * 4) + 1;}photo = 'images_' + index;look.src = "./images/" + photo + "/canzhaotu.png";updateUI();}// 更新UIfunction updateUI() {print.forEach((subArr, i) => {subArr.forEach((item, j) => {tds[i * 4 + j].style.backgroundImage = "url(./images/" + photo + "/" + item +".png)";})})stepSpan.innerHTML = step;if (isStart && judgeVictory() && !isRest) {alert("恭喜你,成功通关!");isStart = false;setTimeout(() => {print[3][3] = 16;updateUI();}, 100)}}/*这里的打乱不同于1.0版本的,1.0版本的打乱是随机产生x,y两个索引值,然后将该索引值对应元素与x0,y0对应的位置元素交换,但是有可能产生无解的情况(就直接跨好几格,直接两者交换那种),一步一步就无法还原,而1.0版是可以手动选择原点的,所以就变成了有解。而这里2.0版本,原点只有一个,无法手动更改,只能一步一步走,那么如果仍是随机产生x,y两个索引值,就可能产生无解的情况所以这里改为随机产生0-3,代表四个方向,让白块在随机的方向上走动n次(n<difficulty,因为有可能随机产生的某个方向走不了已经到边界了),这样一定是有解的,至少原路后退就是一种解。*/// 打乱图片// 随机产生0-3 代表四个方向 0-左,1-上 2-右 3-下 function shuffle() {for (let i = 0; i < difficulty; i++) {let x = parseInt(Math.random() * 4);if (x == 0) {direction = 'left';} else if (x == 1) {direction = 'up';} else if (x == 2) {direction = 'right';} else if (x == 3) {direction = 'down';}move(direction);}}// 移动交换图片function move(direction) {let x, y;let dir = -1;if (direction == 'left') {if (y0 - 1 < 0) {console.log("左边到边界了");return;} else {x = x0;y = y0 - 1;dir = 0;}} else if (direction == 'right') {if (y0 + 1 > 3) {console.log("右边到边界了");return;} else {x = x0;y = y0 + 1;dir = 2;}} else if (direction == 'up') {if (x0 - 1 < 0) {console.log("上边到边界了");return;} else {x = x0 - 1;y = y0;dir = 1;}} else if (direction == 'down') {if (x0 + 1 > 3) {console.log("下边到边界了");return;} else {x = x0 + 1;y = y0;dir = 3;}}// 这里就是记录走的情况(包括了打乱的顺序和玩家点击走的顺序)trace.push(dir);// 不是打乱顺序时,开始记录步数if (!isRest) {step++;}let temp = print[x][y];print[x][y] = print[x0][y0];print[x0][y0] = temp;// 更新坐标位置x0 = x;y0 = y;updateUI();}// 判断是否胜利,function judgeVictory() {let right = true;print.forEach((subArr, i) => {subArr.forEach((data, j) => {if (i == 3 && j == 3) {if (print[i][j] != 0) {right = false;}} else {let index = i * 4 + j;if (print[i][j] != index + 1) {right = false;}}})})return right;}// 求助按钮// 通过trace的记录情况,进行后退,就可以还原最终的拼图function helpBake() {// 调用trace去重函数distinctTrace();// 从trace数组中取出最后一个元素let lastDir = trace.pop();// 判断方向,反方向移动(回退)if (lastDir == 0) {direction = 'right';} else if (lastDir == 1) {direction = 'down';} else if (lastDir == 2) {direction = 'left';} else if (lastDir == 3) {direction = 'up';}// 调用移动函数(移动函数中,没正确交换一次,都会trace.push()这次记录)move(direction);// trace.push()在这里是无用的,所以需要在pop一次trace.pop();}// 但是,上面直接通过后退trace,里面存放的元素可能会导致多余步骤:/*  0 向左,1向上,2向右,3向下比如:trace[2 0 3 1 1 3 0 2]  模拟后退情况就先左走,又右走(原地),又向上再向下(原地),又向下再向上(原地),又右走再左走(原地),最终后退这么多步,最后就是呆在原地所以,需要对trace进行去重处理*/function distinctTrace() {let index = findDistinctIndex();// 有重复的,删除while (index != -1) {trace.splice(index, 2);index = findDistinctIndex();}}// 查找相邻的矛盾无用索引function findDistinctIndex() {let index = -1;// trace 是一维数组,相邻两个比较,比较次数就是数组长度-1// 例如: arr[1 2 3 4] 四个元素,相邻的元素比较 1和2比一次,2和3比一次,3和4比一次,所以比较次数就是3次for (let i = 0; i < trace.length - 1; i++) {let front = trace[i];let behind = trace[i + 1];// 情况:(左右)(右左)(上下)(下上)都是原地,需要去重if ((front == 0 && behind == 2) || (front == 2 && behind == 0) || (front == 1 && behind == 3) || (front == 3 && behind == 1)) {index = i;break;}}return index;}</script>
</body></html>

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

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

相关文章

python222网站实战(SpringBoot+SpringSecurity+MybatisPlus+thymeleaf+layui)-菜单管理实现

锋哥原创的SpringbootLayui python222网站实战&#xff1a; python222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火爆连载更新中... )_哔哩哔哩_bilibilipython222网站实战课程视频教程&#xff08;SpringBootPython爬虫实战&#xff09; ( 火…

cmake-find_package链接第三方库

文章目录 基本调用形式和模块模式使用方式 之前我们是使用了绝对路径来链接OpenCV第三方库&#xff0c;但是现在很多库一般会自己写一些cmake文件提供给用户&#xff0c;用户可以直接使用其中的内置变量即可。使用的命令就是find_package。 基本调用形式和模块模式 find_packa…

【RTP】webrtc 学习2: webrtc对h264的rtp打包

切片只是拷贝帧的split的各个部分到新的rtp 包的封装中。并没有在rtp包本身标记是否为关键帧FU-A 切片 输入的H.264 数据进行split :SplitNalu SplitNalu : 按照最大1200字节进行切分 切分后会返回一个数组 对于FU-A :split的数据总大小是 去掉一个字节的nalu header size …

实战 | OpenCV+OCR实现弧形文字识别实例(详细步骤 + 源码)

导 读 本文主要介绍基于OpenCV+OCR实现弧形文字识别实例,并给详细步骤和代码。源码在文末。 背景介绍 测试图如下,目标是正确识别图中的字符。图片来源: https://www.51halcon.com/forum.php?mod=viewthread&tid=6712 同样,论坛中已经给出了Halcon实现代码,…

1948-2022年金融许可信息明细数据

1948-2022年金融许可信息明细数据 1、时间&#xff1a;1948-2022年 2、来源&#xff1a;银监会&#xff08;银监会许可证发布系统&#xff09; 3、指标&#xff1a;来源表、机构编码、机构名称、所属银行、机构类型、业务范围、机构住所、地理坐标、行政区划代码、所属区县、…

【计算机网络】深入掌握计算机网络的核心要点(面试专用)

写在前面 前言四层模型网络地址管理Linux下设置ipARP请求包总结 前言 计算机网络是指将分散的计算机设备通过通信线路连接起来&#xff0c;形成一个统一的网络。为了使得各个计算机之间能够相互通信&#xff0c;需要遵循一定的协议和规范。OSI参考模型和TCP/IP参考模型是计算机…

(南京观海微电子)——OLED驱动与调试

一、OLED DDIC分类 OLED DDIC的技术方向可以分为3类&#xff1a;带Ram【内存】的IC、Ram-less IC和TDDI【显示&触控集成的IC】 1、带Ram的OLED DDIC OLED DDIC有两个Ram&#xff0c;分别是Demura Ram和Display Ram。 1、带Ram的OLED DDIC 1-1&#xff09;Demura Ram&a…

一张图文深入了解信息量概念

通信原理第10页最后一段&#xff1a; 概率论告诉我们&#xff0c;事件的不确定程度可以用其出现的概率来描述。因此&#xff0c;消息中包含的信息量与消息发生的概率密切相关。消息出现的概率越小&#xff0c;则消息中包含的信息量就越大。 这句话怎么理解呢&#xff1f; 比如…

安利6款免费又高清的视频转GIF方法,值得收藏

前言 平时我们在聊天的时候会发的很多有趣表情包&#xff0c;其实有些就是视频里面的画面&#xff0c;觉得好玩有趣就被网友转换成了GIF&#xff0c;聊天的时候就可以用这些表情包来代表当时的心情。 如何将视频转成GIF动图&#xff1f;对于还不知道怎么将视频转成GIF的朋友&a…

uniapp微信小程序-秋云u-charts层级过高

一、先说问题 想在这个每个圆环上面定位一些百分比或者定位一些东西,微信小程序端可以&#xff0c;真机调试不行&#xff0c;打算提高层级不行 二、解决 点击底下开启2d就可以&#xff0c; 也就是在你的图表上加上 :canvas2d"true"

Adobe ColdFusion 任意文件读取漏洞复现(CVE-2023-26361)

0x01 产品简介 Adobe ColdFusion是美国奥多比(Adobe)公司的一套快速应用程序开发平台。该平台包括集成开发环境和脚本语言。 0x02 漏洞概述 Adobe ColdFusion平台 filemanager.cfc接口存在任意文件读取漏洞,攻击者可通过该漏洞读取系统重要文件(如数据库配置文件、系统配…

流畅的Python(六)-使用一等函数实现设计模式

一、核心要义 《设计模式&#xff1a;可复用面向对象软件的基础》一书中有23个设计模式&#xff0c;其中有16个在动态语言中"不见了或者简化了"。作为动态语言之一的Python, 我们可以利用一等函数简化其中的某些设计模式&#xff0c;本章主要介绍如何使用一等函数重…

c++ QT 信号的个人理解 信号就是独立文件调用的一种“协议”

一. 简介 就我个人来理解&#xff0c;信号槽机制与Windows下消息机制类似&#xff0c;消息机制是基于回调函数&#xff0c;Qt中用信号与槽来代替函数指针&#xff0c;使程序更安全简洁。 信号和槽机制是 Qt 的核心机制&#xff0c;可以让编程人员将互不相关的对象绑定在一起&a…

如何用web界面打开华为防火墙

目录 1.创建一个虚拟网卡 2.cloud操作 3.防火墙上操作 4. 登录 1.创建一个虚拟网卡 2.cloud操作 3.防火墙上操作 4. 登录

Netty的解码器和编码器

链路图 一个完整的RPC请求中&#xff0c;netty对请求数据和响应数据的处理流程如下图所示 网络线路中传输的都是二进制数据&#xff0c;之后netty将二进制数据解码乘POJO对象&#xff0c;让客户端或者服务端程序处理。 解码的工具称为解码器&#xff0c;是一个入站处理器InBo…

物联网协议Coap之C#基于Mozi的CoapClient调用解析

目录 前言 一、CoapClient相关类介绍 1、CoapClient类图 2、CoapClient的设计与实现 3、SendMessage解析 二、Client调用分析 1、创建CoapClient对象 2、实际发送请求 3、Server端请求响应 4、控制器寻址 总结 前言 在之前的博客内容中&#xff0c;关于在ASP.Net Co…

node.js 分布式锁看这篇就够用了

Redis SETNX 命令背后的原理探究 当然&#xff0c;让我们通过一个简单的例子&#xff0c;使用 Redis CLI&#xff08;命令行界面&#xff09;来模拟获取锁和释放锁的过程。 在此示例中 获取锁: # 首先&#xff0c;设置锁密钥的唯一值和过期时间(秒) 127.0.0.1:6379> SET …

数字三角形(很经典的动态规划问题)

给定一个如下图所示的数字三角形&#xff0c;从顶部出发&#xff0c;在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点&#xff0c;一直走到底层&#xff0c;要求找出一条路径&#xff0c;使路径上的数字的和最大。 73 88 1 02 7 4 4 4 5 2 6 …

第2章-神经网络的数学基础——python深度学习

第2章 神经网络的数学基础 2.1 初识神经网络 我们来看一个具体的神经网络示例&#xff0c;使用 Python 的 Keras 库 来学习手写数字分类。 我们这里要解决的问题是&#xff0c; 将手写数字的灰度图像&#xff08;28 像素28 像素&#xff09;划分到 10 个类别 中&#xff08;0…

基于多种CNN模型在清华新闻语料分类效果上的对比

该实验项目目录如图&#xff1a; 1、 模型 1.1. TextCNN # coding: UTF-8 import torch import torch.nn as nn import torch.nn.functional as F import numpy as npclass Config(object):"""配置参数"""def __init__(self, dataset, embedd…