【趣味项目】2048 简单实现

【趣味项目】2048 简单实现

请添加图片描述

算法原理

假设用一个二维矩阵表示 2048 页面,操作是左滑

const matrix = [[2, 2, 4, 0],[0, 2, 4, 0],[0, 2, 2, 0],[2, 4, 4, 8]
];
  • 将所有非空的数字向左移动

    matrix = [[2, 2, 4, 0],[2, 4, 0, 0],[2, 2, 0, 0],[2, 4, 4, 8]
    ]
    
  • 将相邻的相同数字合并

    matrix = [[4, 0, 4, 0],[2, 4, 0, 0],[4, 0, 0, 0],[2, 8, 0, 8]
    ]
    
  • 将所有非空的数字向左移动

    matrix = [[4, 4, 0, 0],[2, 4, 0, 0],[4, 0, 0, 0],[2, 8, 8, 0]
    ]
    
  • 将相邻的相同数字合并

    matrix = [[8, 0, 0, 0],[2, 4, 0, 0],[4, 0, 0, 0],[2, 8, 8, 0]
    ]
    
  • 将所有非空的数字向左移动

     matrix = [[8, 0, 0, 0],[2, 4, 0, 0],[4, 0, 0, 0],[2, 8, 8, 0]]
    
  • 将相邻的相同数字合并

    matrix = [[8, 0, 0, 0],[2, 4, 0, 0],[4, 0, 0, 0],[2, 16, 0, 0]
    ]
    

用 js 实现上述操作

const moveLeft = (matrix) => {const moveLeftOnce = (matrix) => {const size = matrix.length;// 第一步:将所有非零的数字向左移动for (let i = 0; i < size; i++) {let currentRow = matrix[i].filter((item) => item !== 0);let zeroCount = size - currentRow.length;matrix[i] = currentRow.concat(Array(zeroCount).fill(0));}// 第二步:将相邻的相同数字合并for (let i = 0; i < size; i++) {for (let j = 0; j < size - 1; j++) {if (matrix[i][j] === matrix[i][j + 1]) {matrix[i][j] *= 2;matrix[i][j + 1] = 0;}}}}// 记录上一步结束的矩阵let lastSMatrix = JSON.stringify(matrix);while (true) {moveLeftOnce(matrix);// 没有变化则操作完毕if (JSON.stringify(matrix) === lastSMatrix) {break;}lastSMatrix = JSON.stringify(matrix);}
}
  • 左滑
  • 右滑: 翻转 -> 左滑 -> 翻转
  • 上滑: 对角线翻折 -> 左滑 -> 对角线翻折
  • 下滑: 对角线翻折 -> 右滑 -> 对角线翻折

实现思路

  1. render 函数用于渲染外部容器和内部单元格
  2. 通过 requestAnimationFrame 循环调用 render 函数
  3. 添加键盘事件,借助复杂类型传参直接修改 matrix
  4. render 函数根据 matrix 遍历渲染单元格
  5. 键盘事件触发后检测是否结束

实现源码

仅为 JS 文件部分,其他文件及目录结构可在该仓库查看 GitCode

// main.js
const moveLeft = (matrix) => {const moveLeftOnce = (matrix) => {const size = matrix.length;// 第一步:将所有非零的数字向左移动for (let i = 0; i < size; i++) {let currentRow = matrix[i].filter((item) => item !== 0);let zeroCount = size - currentRow.length;matrix[i] = currentRow.concat(Array(zeroCount).fill(0));}// 第二步:将相邻的相同数字合并for (let i = 0; i < size; i++) {for (let j = 0; j < size - 1; j++) {if (matrix[i][j] === matrix[i][j + 1]) {matrix[i][j] *= 2;matrix[i][j + 1] = 0;}}}}let lastSMatrix = JSON.stringify(matrix);while (true) {moveLeftOnce(matrix);if (JSON.stringify(matrix) === lastSMatrix) {break;}lastSMatrix = JSON.stringify(matrix);}
}const moveRight = (matrix) => {const reverseMatrix = (matrix) => {const size = matrix.length;for (let i = 0; i < size; i++) {matrix[i] = matrix[i].reverse();}}reverseMatrix(matrix);moveLeft(matrix);reverseMatrix(matrix);
}const moveUp = (matrix) => {const transposeMatrix = (matrix) => {const size = matrix.length;const newMatrix = Array.from({ length: size }, () => Array(size).fill(0));for (let i = 0; i < size; i++) {for (let j = 0; j < size; j++) {newMatrix[i][j] = matrix[j][i];}}for (let i = 0; i < size; i++) {for (let j = 0; j < size; j++) {matrix[i][j] = newMatrix[i][j];}}}transposeMatrix(matrix);moveLeft(matrix);transposeMatrix(matrix);
}const moveDown = (matrix) => {const transposeMatrix = (matrix) => {const size = matrix.length;const newMatrix = Array.from({ length: size }, () => Array(size).fill(0));for (let i = 0; i < size; i++) {for (let j = 0; j < size; j++) {newMatrix[i][j] = matrix[j][i];}}for (let i = 0; i < size; i++) {for (let j = 0; j < size; j++) {matrix[i][j] = newMatrix[i][j];}}}transposeMatrix(matrix);moveRight(matrix);transposeMatrix(matrix);
}const main = () => {const color = {0: '#CCC0B3',2: '#EEE4DA',4: '#EDE0C8',8: '#F2B179',16: '#F49563',32: '#F5794D',64: '#F55D37',128: '#EEE863',256: '#EDB04D',512: '#ECB04D',1024: '#EB9437',2048: '#EA7821',};const matrix = [[2, 2, 4, 0, 4],[0, 2, 4, 0, 0],[0, 2, 2, 0, 16],[2, 4, 4, 8, 32],[2, 4, 4, 8, 32],];/*** 绘制一个单元格* @param {string} color 颜色* @param {number} num 数字 */const renderPixel = (color, num) => {const div = document.createElement('div');div.style.width = '100px';div.style.height = '100px';div.style.backgroundColor = color;div.style.fontSize = '40px';div.style.textAlign = 'center';div.style.lineHeight = '100px';div.innerText = num;return div;}const size = matrix.length;const app = document.getElementById('app');const render = (matrix) => {const container = document.createElement('div');container.style.display = 'grid';container.style.gridTemplateColumns = `repeat(${size}, 100px)`;container.style.gridTemplateRows = `repeat(${size}, 100px)`;container.style.width = `${size * 100}px`;container.style.height = `${size * 100}px`;container.style.border = '1px solid #000';container.style.borderRadius = '5px';container.style.margin = '100px auto';container.style.boxShadow = '0 0 10px #000';container.style.position = 'relative';container.style.overflow = 'hidden';container.style.backgroundColor = '#BBADA0';container.style.borderRadius = '5px';container.style.padding = '5px';container.style.boxSizing = 'border-box';container.style.transition = 'all 0.3s';container.style.userSelect = 'none';container.style.touchAction = 'none';container.style.cursor = 'pointer';for (let i = 0; i < size; i++) {for (let j = 0; j < size; j++) {const pixel = renderPixel(color[matrix[i][j]], matrix[i][j]);container.appendChild(pixel);}}app.appendChild(container);};const update = (matrix) => {app.innerHTML = '';render(matrix);requestAnimationFrame(() => {update(matrix);});}requestAnimationFrame(() => {update(matrix);});document.addEventListener('keydown', (e) => {switch (e.key) {case 'ArrowLeft':moveLeft(matrix);break;case 'ArrowRight':moveRight(matrix);break;case 'ArrowUp':moveUp(matrix);break;case 'ArrowDown':moveDown(matrix);break;}const finishend = matrix.every((row) => row.every((item) => item !== 0));if (finishend) {alert('Game Over!');} else {const empty = [];for (let i = 0; i < size; i++) {for (let j = 0; j < size; j++) {if (matrix[i][j] === 0) {empty.push([i, j]);}}}const random = empty[Math.floor(Math.random() * empty.length)];matrix[random[0]][random[1]] = Math.random() > 0.5 ? 2 : 4;}});
};window.onload = main;

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

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

相关文章

Jmeter事务控制器实战

在性能测试工作中&#xff0c;我们往往只测试业务功能相关主要接口的数据请求和返回。然而实际上用户在使用web应用时&#xff0c;可能会加载诸多资源&#xff1a;htmldom、cssdom、javaScript、ajax请求、图片等。 从打开一个页面到界面渲染完成需要一定的加载时间&#xff0…

2024 AI 辅助研发的新纪年

随着人工智能技术的持续发展与突破&#xff0c;2024年AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI正逐渐渗透到研发的各个环节&#xff0c;变革着传统的研发模式。在这一背景下&#xff0c;AI辅助研发不仅…

Humanoid-Gym 开源人形机器人端到端强化学习训练框架!星动纪元联合清华大学、上海期智研究院发布!

系列文章目录 前言 Humanoid-Gym: Reinforcement Learning for Humanoid Robot with Zero-Shot Sim2Real Transfer GitHub Repository: GitHub - roboterax/humanoid-gym: Humanoid-Gym: Reinforcement Learning for Humanoid Robot with Zero-Shot Sim2Real Transfer 一、介…

操作系统体系结构(不是很重点)

目录 一. 大内核与微内核二. 分层结构, 模块化和外核2.1 分层结构2.2 模块化2.3 宏内核与微内核 三. 外核 \quad 一. 大内核与微内核 \quad 由于对系统资源进行管理的功能不会直接涉及硬件, 所以有的就没有把这个功能放在内核里面 注意: 考试的时候不要写变态, 要写CPU状态的转…

【前端】-初始前端以及html的学习

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

听 GPT 讲 client-go 源代码 (24)

分享更多精彩内容&#xff0c;欢迎关注&#xff01; File: client-go/applyconfigurations/batch/v1/jobstatus.go 在client-go的applyconfigurations/batch/v1/jobstatus.go文件中&#xff0c;定义了与Job的状态相关的配置和操作。 文件中定义了以下几个结构体&#xff1a; Jo…

关于 typeof 与 instanceof 区别引出的原型对象问题

一、关于 typeof 与 instanceof 区别&#xff1a; typeof 和 instanceof 是 JavaScript 中用于检查变量类型的两个不同操作符&#xff0c;它们在使用上有着明显的区别和不同的适用场景。 typeof typeof 是一个一元操作符&#xff0c;用于返回一个变量或表达式的数据类型的字符…

【C++】十大排序算法之 归并排序 快速排序

本次介绍内容参考自&#xff1a;十大经典排序算法&#xff08;C实现&#xff09; - fengMisaka - 博客园 (cnblogs.com) 排序算法是《数据结构与算法》中最基本的算法之一。 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a;通过比较来决定元素间的相对次序…

MySQL面试题-锁(答案版)

锁 1、MySQL 有哪些锁&#xff1f; &#xff08;1&#xff09;全局锁 加了全局锁之后&#xff0c;整个数据库就处于只读状态了&#xff0c;这时其他线程执行以下操作&#xff0c;都会被阻塞&#xff1a; 对数据的增删改操作&#xff0c;比如 insert、delete、update等语句&…

UDP实现文件的发送、UDP实现全双工的聊天、TCP通信协议

我要成为嵌入式高手之3月7日Linux高编第十七天&#xff01;&#xff01; ———————————————————————————— 回顾 重要程序 1、UDP实现文件的发送 发端&#xff1a; #include "head.h"int main(void) {int sockfd 0;struct sockaddr_i…

每日一练 | 华为认证真题练习Day195

1、下面是路由器RTD的部分输出信息&#xff0c;关于输出信息描述正确的是&#xff1a; A. 该接口使能的IGMP版本是版本1。 B. 该接口的IGMP状态是DOWN的。 C. 该接口上配置发送IGMP查询消息的时间间隔60S D. 该接口上IGMP查询报文中包含的最大响应时间0s 2、下面是一台路由器…

Leetcode : 1137. 高度检查器

学校打算为全体学生拍一张年度纪念照。根据要求&#xff0c;学生需要按照 非递减 的高度顺序排成一行。 排序后的高度情况用整数数组 expected 表示&#xff0c;其中 expected[i] 是预计排在这一行中第 i 位的学生的高度&#xff08;下标从 0 开始&#xff09;。 给你一个整数…