JavaScript 设计2048游戏

JavaScript 设计2048游戏

在这篇文章中,你将学习用JavaScript构建著名的2048游戏。这篇文章旨在培养问题解决和逻辑思维能力,因此将专注于JavaScript。我们将看到如何访问DOM元素并在用户与网页交互时多次更新它们,以创建一个交互式应用程序,即2048游戏。如果你还没玩过这个游戏,建议先玩一下,以熟悉一下。你可以很容易地在你的智能手机上安装它。

如何玩这个游戏

  • 你将有一个4×4的矩阵,其中两个随机选择的单元格将其值设为2。
  • 使用箭头键将所有单元格移动到相应的方向
  • 当单元格朝一个特定方向移动时,相同的值将相加合并为一个单元格,不同值的单元格将只是朝这个特定方向移动,如果有空的单元格。
  • 随着游戏的进行,分配的值的大小将增加。此外,空的单元格将随机分配随机值。所有的值都将是2的某个幂。
  • 你必须聪明地进行组合,使你达到2048。这是胜利条件。

    Demo:

JavaScript 设计2048游戏

编程方法

  • 首先,使用CSS网格创建一个4×4的矩阵。每个单元格都是一个带有文本的HTML div 标签,文本使用 p 标签包裹。
  • 在JavaScript中定义一个函数,随机选择一个单元格并给它赋值。
  • 在moveBlocks函数中使用JavaScript事件监听器来响应键盘按键事件,如箭头键,并根据箭头指示的方向移动单元格。
  • 为包含不同值的单元格以及空单元格分配不同的颜色。
  • 创建shift(direction)和move(direction)函数,分别用于移动值和合并相同的值。
  • 检查单元格中的最大值,并使用gameOver函数在最大值达到2048时显示获胜对话框,在没有空单元格且最大单元格值未达到2048时显示失败对话框。

项目结构

JavaScript 设计2048游戏

完整代码

Javascript

// Filename: script.js 
// Get the game grid 
const gridItems = [ 
    ...document.querySelectorAll(".grid-item"), 
]; 
const score_val = document.querySelector(".score-value"); 
const result = document.querySelector(".result"); 
let score = 0; 
let moves = 0; 
let moveFactor = 4; 
let options = [ 
    2, 4, 8, 2, 4, 8, 2, 2, 4, 4, 2, 8, 2, 2, 4, 4, 2, 
]; 
let matrix = []; 
let prevMatrix; 
  
let colors = [ 
    "#caf0f8", 
    "#90e0ef", 
    "#00b4d8", 
    "#0077b6", 
    "#03045e", 
    "#023047", 
    "#fca311", 
    "#14213d", 
    "#e63946", 
    "#ffc300", 
    "#6a040f", 
    "#000000", 
]; 
  
// Create the starting game grid. 
let row = []; 
for (let index = 1; index < gridItems.length + 1; index++) { 
    if (index % 4 === 0) { 
        let item = gridItems[index - 1]; 
        item.firstElementChild.innerText = ""; 
        row.push(item); 
        matrix.push(row); 
        row = []; 
    } else { 
        let item = gridItems[index - 1]; 
        item.firstElementChild.innerText = ""; 
        row.push(item); 
    } 
} 
  
// Assign any two grid blocks the value of 2 
const rowIdx = Math.floor(Math.random() * 4); 
const colIdx = Math.floor(Math.random() * 4); 
let rowIdx2 = Math.floor(Math.random() * 4); 
let colIdx2 = Math.floor(Math.random() * 4); 
  
if (rowIdx === rowIdx2 && colIdx === colIdx2) { 
    rowIdx2 = Math.floor(Math.random() * 4); 
    colIdx2 = Math.floor(Math.random() * 4); 
} 
  
matrix[rowIdx][colIdx].firstElementChild.textContent = 2; 
matrix[rowIdx2][colIdx2].firstElementChild.textContent = 2; 
  
let availIndexes = updateAvailIndexes(); 
  
updateColors(); 
  
// Make web page able to listen to keydown event 
document.addEventListener("keydown", moveBlocks); 
  
// Method to extract columns from an 2D array. 
const arrayColumn = (arr, n) => arr.map((x) => x[n]); 
  
function moveBlocks(e) { 
    if ( 
        e.key !== "ArrowLeft" && 
        e.key !== "ArrowRight" && 
        e.key !== "ArrowUp" && 
        e.key !== "ArrowDown"
    ) { 
        return; 
    } 
  
    moves++; 
    matrixVals = getCurrentMatrixValues(); 
    prevMatrix = matrixVals; 
  
    let col1 = arrayColumn(matrix, 0); 
    let col2 = arrayColumn(matrix, 1); 
    let col3 = arrayColumn(matrix, 2); 
    let col4 = arrayColumn(matrix, 3); 
    let row1 = matrix[0]; 
    let row2 = matrix[1]; 
    let row3 = matrix[2]; 
    let row4 = matrix[3]; 
  
    if (e.key === "ArrowLeft") { 
        moveLeft(row1); 
        moveLeft(row2); 
        moveLeft(row3); 
        moveLeft(row4); 
    } 
    if (e.key === "ArrowRight") { 
        moveRight(row1); 
        moveRight(row2); 
        moveRight(row3); 
        moveRight(row4); 
    } 
    if (e.key === "ArrowUp") { 
        moveLeft(col1); 
        moveLeft(col2); 
        moveLeft(col3); 
        moveLeft(col4); 
    } 
    if (e.key === "ArrowDown") { 
        moveRight(col1); 
        moveRight(col2); 
        moveRight(col3); 
        moveRight(col4); 
    } 
  
    matrixVals = getCurrentMatrixValues(); 
    availIndexes = updateAvailIndexes(); 
    updateColors(); 
  
    let check = checkMatrixEquality(prevMatrix, matrixVals); 
  
    if (availIndexes.length === 0 && check === true) { 
        gameOver("loose"); 
    } 
  
    if (moves % moveFactor === 0) { 
        generateNewBlock(); 
    } 
} 
  
setInterval(() => { 
    availIndexes = updateAvailIndexes(); 
    generateNewBlock(); 
}, 8000); 
  
setTimeout(() => { 
    options.push(16); 
    setTimeout(() => { 
        options.push(16); 
        options.push(32); 
        setTimeout(() => { 
            options.push(16); 
            options.push(32); 
            options.push(64); 
        }, 40000); 
    }, 18000); 
}, 120000); 
  
function getCurrentMatrixValues() { 
    let gridItems = [ 
        ...document.querySelectorAll(".grid-item"), 
    ]; 
    let matrix_grid = []; 
    let row = []; 
    for ( 
        let index = 1; 
        index < gridItems.length + 1; 
        index++ 
    ) { 
        if (index % 4 === 0) { 
            let item = gridItems[index - 1]; 
            row.push(item.firstElementChild.innerText); 
            matrix_grid.push(row); 
            row = []; 
        } else { 
            let item = gridItems[index - 1]; 
            row.push(item.firstElementChild.innerText); 
        } 
    } 
    return matrix_grid; 
} 
  
function shiftLeft(arr) { 
    for (let i = 0; i < 4; i++) { 
        for (let i = 1; i < 4; i++) { 
            let currElement = arr[i].firstElementChild; 
            let prevElement = arr[i - 1].firstElementChild; 
            if (prevElement.innerText == 0) { 
                prevElement.innerText = 
                    currElement.innerText; 
                currElement.innerText = ""; 
            } 
        } 
    } 
} 
  
function shiftRight(arr) { 
    for (let i = 0; i < 4; i++) { 
        for (let i = 2; i >= 0; i--) { 
            let currElement = arr[i].firstElementChild; 
            let nextElement = arr[i + 1].firstElementChild; 
            if (nextElement.innerText == 0) { 
                nextElement.innerText = 
                    currElement.innerText; 
                currElement.innerText = ""; 
            } 
        } 
    } 
} 
  
function moveRight(row) { 
    shiftRight(row); 
  
    for (let i = 2; i >= 0; i--) { 
        let currElement = row[i].firstElementChild; 
        let nextElement = row[i + 1].firstElementChild; 
        let val = parseInt(currElement.innerText); 
        let nextVal = parseInt(nextElement.innerText); 
        if (val === nextVal && val !== 0) { 
            let newVal = val + nextVal; 
            nextElement.innerText = newVal; 
            currElement.innerText = ""; 
            score = score + 2; 
            score_val.innerText = score; 
            if (newVal === 2048) { 
                gameOver("Win"); 
            } 
        } 
    } 
  
    shiftRight(row); 
} 
  
function moveLeft(row) { 
    shiftLeft(row); 
  
    for (let i = 1; i < 4; i++) { 
        let currElement = row[i].firstElementChild; 
        let prevElement = row[i - 1].firstElementChild; 
        let val = parseInt(currElement.innerText); 
        let prevVal = parseInt(prevElement.innerText); 
        if (val === prevVal && val !== 0) { 
            let newVal = val + prevVal; 
            prevElement.innerText = newVal; 
            currElement.innerText = ""; 
            score = score + 2; 
            score_val.innerText = score; 
            if (newVal === 2048) { 
                gameOver("Win"); 
            } 
        } 
    } 
  
    shiftLeft(row); 
} 
  
function updateAvailIndexes() { 
    matrixValues = getCurrentMatrixValues(); 
    let grid = []; 
    for (let i = 0; i < 4; i++) { 
        for (let j = 0; j < 4; j++) { 
            if (matrixValues[i][j] == "") { 
                grid.push([i, j]); 
            } 
        } 
    } 
    return grid; 
} 
  
function generateNewBlock() { 
    if (availIndexes.length !== 0) { 
        let randInt = Math.floor( 
            Math.random() * availIndexes.length 
        ); 
        let coords = availIndexes[randInt]; 
        let randInt3 = Math.floor( 
            Math.random() * options.length 
        ); 
        let ele = 
            matrix[coords[0]][coords[1]].firstElementChild; 
        ele.innerText = options[randInt3]; 
        updateColors(); 
    } 
} 
  
function checkMatrixEquality(mat1, mat2) { 
    for (let i = 0; i < 4; i++) { 
        for (let j = 0; j < 4; j++) { 
            if (mat1[i][j] !== mat2[i][j]) { 
                return false; 
            } 
        } 
    } 
    return true; 
} 
  
function gameOver(status) { 
    if (status === "Win") { 
        result.innerText = "You Won!!!"; 
        result.style.color = "rgb(78, 236, 144)"; 
    } else { 
        result.innerText = "You Loose!!!"; 
        result.style.color = "rgb(252, 51, 51)"; 
    } 
} 
  
function updateColors() { 
    for (let i = 0; i < 4; i++) { 
        for (let j = 0; j < 4; j++) { 
            let elem = matrix[i][j].firstElementChild; 
            if (elem.innerText == 0) { 
                elem.parentElement.style.backgroundColor = 
                    colors[0]; 
                elem.style.color = "black"; 
            } else if (elem.innerText == 2) { 
                elem.style.color = "black"; 
                elem.parentElement.style.backgroundColor = 
                    colors[1]; 
            } else if (elem.innerText == 4) { 
                elem.style.color = "black"; 
                elem.parentElement.style.backgroundColor = 
                    colors[2]; 
            } else if (elem.innerText == 8) { 
                elem.style.color = "black"; 
                elem.parentElement.style.backgroundColor = 
                    colors[3]; 
            } else if (elem.innerText == 16) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[4]; 
            } else if (elem.innerText == 32) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[5]; 
            } else if (elem.innerText == 64) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[6]; 
            } else if (elem.innerText == 128) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[7]; 
            } else if (elem.innerText == 256) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[8]; 
            } else if (elem.innerText == 512) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[9]; 
            } else if (elem.innerText == 1024) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[10]; 
            } else if (elem.innerText == 2048) { 
                elem.style.color = "white"; 
                elem.parentElement.style.backgroundColor = 
                    colors[11]; 
            } 
        } 
    } 
} 

HTML

<!-- filename: index.html -->
<!DOCTYPE html> 
<html lang="en"> 
  
<head> 
    <meta charset="UTF-8" /> 
    <meta http-equiv="X-UA-Compatible" 
        content="IE=edge" /> 
    <meta name="viewport" content= 
        "width=device-width, initial-scale=1.0" /> 
    <!-- Importing Google Fonts API Link -->
    <link rel="preconnect" 
        href="https://fonts.googleapis.com" /> 
    <link rel="preconnect" 
        href="https://fonts.gstatic.com" 
        crossorigin /> 
    <link href= 
"https://fonts.googleapis.com/css2?family=Montserrat:wght@600&family=Ubuntu:wght@700&display=swap"
        rel="stylesheet" /> 
  
    <!-- Importing our CSS File where we  
        write all our styles -->
    <link rel="stylesheet" href="./style.css" /> 
</head> 
  
<body> 
    <div class="container"> 
        <div class="score"> 
            <p class="score-title">Score:</p> 
            <p class="score-value">0</p> 
        </div> 
  
        <div class="result"></div> 
  
        <div class="grid"> 
            <div class="grid-item"> 
                <p id="1"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="2"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="3"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="4"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="5"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="6"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="7"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="8"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="9"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="10"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="11"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="12"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="13"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="14"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="15"></p> 
            </div> 
            <div class="grid-item"> 
                <p id="16"></p> 
            </div> 
        </div> 
    </div> 
    <script src="script.js"></script> 
</body> 
  
</html>

CSS

/* filename style.css */
:root { 
    --primary-text-color: #27374d; 
} 
  
* { 
    box-sizing: border-box; 
    padding: 0%; 
    margin: 0%; 
    font-family: "Montserrat", sans-serif; 
    font-family: "Ubuntu", sans-serif; 
} 
  
body { 
    background-color: #dda15e; 
    color: var(--primary-text-color); 
    min-height: 100vh; 
} 
  
.container { 
    width: 400px; 
    display: flex; 
    flex-direction: column; 
    align-items: center; 
    justify-content: center; 
    position: absolute; 
    top: 50%; 
    left: 50%; 
    transform: translate(-50%, -50%); 
} 
  
.score { 
    width: 100%; 
    display: flex; 
    align-items: center; 
    justify-content: space-evenly; 
    flex-wrap: wrap; 
    font-size: 2rem; 
    text-align: center; 
    padding: 10px; 
} 
  
.score-title { 
    width: 50%; 
} 
  
.score-value { 
    width: 50%; 
} 
  
.result { 
    font-size: 2rem; 
    text-align: center; 
    padding: 10px; 
} 
  
.grid { 
    display: grid; 
    grid-template-columns: 1fr 1fr 1fr 1fr; 
    grid-template-rows: 1fr 1fr 1fr 1fr; 
    width: 400px; 
    height: 400px; 
    background-color: rgb(209, 207, 207); 
    box-shadow: 5px 5px 20px rgba(0, 0, 0, 0.753); 
} 
  
.grid-item { 
    border: 1px solid var(--primary-text-color); 
    display: flex; 
    align-items: center; 
    justify-content: center; 
    font-size: 2rem; 
} 
  
.rules { 
    border: 2px solid white; 
    padding: 20px; 
} 
  
.rules>h1 { 
    width: 100%; 
    font-size: 2rem; 
    text-align: center; 
    padding: 10px; 
} 
  
.rules-para { 
    padding: 20px 10px; 
}

输出:

JavaScript 设计2048游戏

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程