当前位置: 首页 > news >正文

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>

相关文章:

  • C# Onnx yolov8 水表读数检测
  • java servlet勤工助学家教管系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
  • pytorch-metric-learning度量学习工具官方文档翻译
  • 数据库(SQL)
  • Qt知识点总结
  • JAVA线程执行中断方式和ElasticSearch未捕获异常的处理方式
  • Meta开源Code Llama 70B,缩小与GPT-4之间的技术鸿沟
  • leetcode-存在重复元素 II
  • 批量将csv文件转成excel
  • 2023年春秋杯网络安全联赛冬季赛_做题记录
  • Linux/ScriptKiddie
  • 18.通过telepresence调试部署在Kubernetes上的微服务
  • C++对象模型和this指针,const修饰成员函数详解
  • 【爬虫用户代理和ip自动生成】
  • 大中型企业DevSecOps建设
  • 《Javascript高级程序设计 (第三版)》第五章 引用类型
  • 3.7、@ResponseBody 和 @RestController
  • create-react-app做的留言板
  • ES6语法详解(一)
  • exports和module.exports
  • export和import的用法总结
  • log4j2输出到kafka
  • miaov-React 最佳入门
  • Otto开发初探——微服务依赖管理新利器
  • scala基础语法(二)
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Vue 重置组件到初始状态
  • Vue--数据传输
  • Vue学习第二天
  • windows-nginx-https-本地配置
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 和 || 运算
  • 一个项目push到多个远程Git仓库
  • 用Visual Studio开发以太坊智能合约
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • #android不同版本废弃api,新api。
  • #include
  • #Java第九次作业--输入输出流和文件操作
  • #NOIP 2014#Day.2 T3 解方程
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • #每日一题合集#牛客JZ23-JZ33
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (1) caustics\
  • (2024.6.23)最新版MAVEN的安装和配置教程(超详细)
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (附源码)springboot助农电商系统 毕业设计 081919
  • (附源码)ssm高校社团管理系统 毕业设计 234162
  • (算法)Travel Information Center
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)Android学习笔记 --- android任务栈和启动模式
  • .“空心村”成因分析及解决对策122344
  • .bashrc在哪里,alias妙用
  • .cfg\.dat\.mak(持续补充)