HomeLập trìnhJavaScriptCách tạo bản...

Cách tạo bản sao Wordle trong JavaScript


Trong bài viết này, bạn sẽ tạo lại trò chơi đoán từ Wordle. Bài viết này bao gồm logic trò chơi cốt lõi nhưng không triển khai chia sẻ kết quả của bạn. Bài viết cũng không đề cập đến chức năng tạo số liệu thống kê trò chơi.

Hướng dẫn này dành cho các nhà phát triển front-end mới bắt đầu muốn xây dựng một dự án JavaScript tiêu chuẩn, thú vị.

Bạn có thể xem bản demo của dự án đã hoàn thành tại đây.

điều kiện tiên quyết

Hướng dẫn này giả định một sự hiểu biết cơ bản về:

Cách xây dựng bản sao Wordle

Đây là các bước bạn sẽ thực hiện để tạo bản sao Wordle:

  • Thiết lập dự án
  • Tạo bảng trò chơi
  • Tạo bàn phím trên màn hình
  • Chấp nhận đầu vào của người dùng
  • Thêm thông báo
  • Làm cho bàn phím trên màn hình tạo đầu vào
  • Thêm hoạt ảnh

Thiết lập dự án

Trước khi xây dựng trò chơi, bạn cần chuẩn bị sẵn một số thành phần. Trước tiên, bạn cần tạo một thư mục cho tất cả mã nguồn của bản sao của chúng tôi. Gọi thư mục này là build.

Sau khi bạn hoàn thành việc đó, hãy thiết lập máy chủ phát triển của mình.

máy chủ trực tiếp

Bạn sẽ sử dụng một máy chủ phát triển được gọi là máy chủ trực tiếp. Bước này là tùy chọn, nhưng giúp bạn không phải tải lại trang sau mỗi lần thay đổi mã nguồn.

Cài đặt máy chủ trực tiếp bằng cách nhập nội dung sau vào thiết bị đầu cuối của bạn:

npm install live-server

Thiết lập HTML

Bên trong bản dựng, tạo một tệp HTML và đặt tên là index.html. Đặt đoạn mã sau vào nó:

<!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">
    <title>Wordle</title>
</head>
<body>
    <h1> Wordle Clone </h1>
    
    <div id="game-board">

    </div>
</body>
</html>

Mã HTML tạo tiêu đề cho trò chơi của chúng ta và tạo vùng chứa cho bảng trò chơi.

Bạn sẽ sử dụng thư viện JavaScript có tên là Toastr cho các thông báo trong trò chơi và thư viện CSS có tên là Animate.css cho hoạt ảnh của bảng.

Để đưa chúng vào dự án của bạn, hãy thêm các liên kết sau vào đầu tệp index.html của bạn.

 <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet"/>
 
<link
    rel="stylesheet"
		href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
  />

Các liên kết đó sẽ tìm nạp CSS cho cả Animate.css và Toastr. Đặt đoạn mã sau vào index.html, ngay trước thẻ đóng body:

<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>

Mã đó sẽ tìm nạp JavaScript cho Toastr và jQuery (vì Toastr phụ thuộc vào nó).

Cài đặt JavaScript

JavaScript của bạn sẽ nằm trong một tệp có tên script.js. Tạo script.js và đặt nó bên trong build.

Đặt mã này ở đầu script.js:

import { WORDS } from "./words.js";

const NUMBER_OF_GUESSES = 6;
let guessesRemaining = NUMBER_OF_GUESSES;
let currentGuess = [];
let nextLetter = 0;
let rightGuessString = WORDS[Math.floor(Math.random() * WORDS.length)]
console.log(rightGuessString)

Đoạn mã này khởi tạo các biến toàn cục mà chúng ta sẽ sử dụng cho trò chơi của mình và chọn một từ ngẫu nhiên từ mảng WORDS như dự đoán đúng cho vòng này. Chúng tôi cũng ghi kết quả đoán đúng vào bảng điều khiển để gỡ lỗi mã nếu cần.

Danh sách các từ được phép sử dụng sẽ được mã hóa cứng và lưu trữ dưới dạng một mảng trong tệp word.js. Tạo word.js, bên trong bản dựng và sao chép JavaScript từ liên kết này vào đó.

Words.js sẽ trông như thế này:

wordjs_screenshot-1
word.js sẽ trông như thế nào

Thiết lập CSS

Đặt tên cho tệp CSS của bạn là style.css. Style.css cũng nên được đặt trong build.

h1 {
 text-align: center;
}

Thiết lập CSS duy nhất mà chúng tôi cần là một chút mã để căn giữa văn bản của tiêu đề

Đọc thêm  如何在 JavaScript 中遍历对象

Để tất cả chúng cùng nhau

Cuối cùng, liên kết script.js dưới dạng một mô-đun trong index.html của bạn, sau đó liên kết style.css.

Tại thời điểm này, index.html của bạn sẽ trông như thế này:

<!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">
    <title>Wordle</title>
    <link rel="stylesheet" href="https://www.freecodecamp.org/news/build-a-wordle-clone-in-javascript/style.css">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet"/>
    <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
  />
</head>
<body>
    <h1> Wordle Clone </h1>
    
    <div id="game-board">

    </div>
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script src="script.js" type="module"></script>
</body>
</html>

và cấu trúc tệp của bạn sẽ trông như thế này:

Ảnh chụp màn hình-từ-2022-02-28-13-49-21
ảnh chụp màn hình cây tập tin

Bắt đầu máy chủ trực tiếp bằng cách nhập mã này vào bảng điều khiển của bạn:

live-server build

Đó là nó để thiết lập.

Cách tạo bảng trò chơi

Bạn sẽ tạo bảng trò chơi bằng cách viết một hàm JavaScript. Hãy gọi hàm initBoard. Thêm mã này vào tệp script.js của bạn:

function initBoard() {
    let board = document.getElementById("game-board");

    for (let i = 0; i < NUMBER_OF_GUESSES; i++) {
        let row = document.createElement("div")
        row.className = "letter-row"
        
        for (let j = 0; j < 5; j++) {
            let box = document.createElement("div")
            box.className = "letter-box"
            row.appendChild(box)
        }

        board.appendChild(row)
    }
}

initBoard()

Vậy mã này làm gì? initBoard tạo một hàng cho mỗi dự đoán mà chúng tôi cung cấp cho người dùng và tạo 5 hộp cho mỗi hàng. Có một hộp cho mỗi chữ cái đoán và chức năng làm cho tất cả chúng trở thành con của hàng.

initBoard sau đó thêm từng hàng vào vùng chứa bảng. Mỗi hàng được đưa ra lớp letter-rowvà mỗi hộp được chỉ định letter-box.

Tiếp theo, bạn sẽ tạo kiểu cho bảng bằng một số CSS. Đặt đoạn mã sau vào tệp style.css của bạn:

#game-board {
  display: flex;
  align-items: center;
  flex-direction: column;
}

.letter-box {
  border: 2px solid gray;
  border-radius: 3px;
  margin: 2px;
  font-size: 2.5rem;
  font-weight: 700;
  height: 3rem;
  width: 3rem;
  display: flex;
  justify-content: center;
  align-items: center;
  text-transform: uppercase;
}

.filled-box {
  border: 2px solid black;
}

.letter-row {
  display: flex;
}

CSS này thực hiện một số điều:

  • căn giữa các hàng của bảng theo chiều ngang và chiều dọc
  • đặt chiều cao, chiều rộng và đường viền cho mỗi hộp trên bảng
  • tạo ra một cái nhìn khác biệt cho một hộp chứa đầy một chữ cái

Tại thời điểm này, khi bạn tải index.html trong trình duyệt của mình, nó sẽ giống như sau:

ảnh chụp màn hình1
ảnh chụp màn hình trò chơi

Cách tạo Bàn phím ảo

Cách đơn giản nhất để tạo bàn phím là với HTML. Thêm mã này vào index.html của bạn, sau div bảng trò chơi:

   <div id="keyboard-cont">
        <div class="first-row">
            <button class="keyboard-button">q</button>
            <button class="keyboard-button">w</button>
            <button class="keyboard-button">e</button>
            <button class="keyboard-button">r</button>
            <button class="keyboard-button">t</button>
            <button class="keyboard-button">y</button>
            <button class="keyboard-button">u</button>
            <button class="keyboard-button">i</button>
            <button class="keyboard-button">o</button>
            <button class="keyboard-button">p</button>
        </div>
        <div class="second-row">
            <button class="keyboard-button">a</button>
            <button class="keyboard-button">s</button>
            <button class="keyboard-button">d</button>
            <button class="keyboard-button">f</button>
            <button class="keyboard-button">g</button>
            <button class="keyboard-button">h</button>
            <button class="keyboard-button">j</button>
            <button class="keyboard-button">k</button>
            <button class="keyboard-button">l</button>
        </div>
        <div class="third-row">
            <button class="keyboard-button">Del</button>
            <button class="keyboard-button">z</button>
            <button class="keyboard-button">x</button>
            <button class="keyboard-button">c</button>
            <button class="keyboard-button">v</button>
            <button class="keyboard-button">b</button>
            <button class="keyboard-button">n</button>
            <button class="keyboard-button">m</button>
            <button class="keyboard-button">Enter</button>
        </div>
    </div>

Bây giờ, tạo kiểu cho đánh dấu bằng cách thêm CSS này vào cuối style.css:

#keyboard-cont {
  margin: 1rem 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

#keyboard-cont div {
  display: flex;
}

.second-row {
  margin: 0.5rem 0;
}

.keyboard-button {
  font-size: 1rem;
  font-weight: 700;
  padding: 0.5rem;
  margin: 0 2px;
  cursor: pointer;
  text-transform: uppercase;
}

Đây là giao diện của index.html của bạn trong trình duyệt bây giờ:

ảnh chụp màn hình2
ảnh chụp màn hình của bàn phím trên màn hình

Cách chấp nhận đầu vào của người dùng

Chiến lược cho đầu vào của người dùng rất đơn giản: khi người chơi nhấn một phím trên bàn phím, chúng tôi muốn đặt phím đó vào đúng vị trí trên bảng. Bạn sẽ thực hiện điều này bằng cách lắng nghe sự kiện keyup.

Khi người chơi nhấn một phím, bạn muốn biết phím đó là gì. Nếu phím là một chữ cái, bạn muốn đặt nó vào đúng vị trí trên bảng.

Bạn tìm ra vị trí đúng trên bảng bằng cách kiểm tra số lần đoán mà người chơi còn lại và số lượng chữ cái mà người chơi đã nhập cho đến nay.

Nếu phím được nhấn là Enter hoặc Backspace, bạn kiểm tra dự đoán hoặc xóa một chữ cái khỏi dự đoán hiện tại. Bất kỳ khóa nào khác chúng tôi bỏ qua.

Thêm mã này vào script.js:


document.addEventListener("keyup", (e) => {

    if (guessesRemaining === 0) {
        return
    }

    let pressedKey = String(e.key)
    if (pressedKey === "Backspace" && nextLetter !== 0) {
        deleteLetter()
        return
    }

    if (pressedKey === "Enter") {
        checkGuess()
        return
    }

    let found = pressedKey.match(/[a-z]/gi)
    if (!found || found.length > 1) {
        return
    } else {
        insertLetter(pressedKey)
    }
})

Đoạn mã này sử dụng một biểu thức chính quy để kiểm tra xem phím chúng ta đã nhấn có phải là một phím chữ cái đại diện cho một chữ cái hay không. Nếu tên của khóa không có bất kỳ chữ cái nào (đó là một số) hoặc có nhiều chữ cái (Shift, Tab), chúng tôi sẽ bỏ qua sự kiện. Nếu không, chúng tôi chèn chữ cái vào bảng.

Đọc thêm  Chức năng gọi lại JavaScript –Giải thích bằng tiếng Anh đơn giản

chènThư

Hãy xác định insertLetter chức năng. Nó trông như thế này:

function insertLetter (pressedKey) {
    if (nextLetter === 5) {
        return
    }
    pressedKey = pressedKey.toLowerCase()

    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    let box = row.children[nextLetter]
    box.textContent = pressedKey
    box.classList.add("filled-box")
    currentGuess.push(pressedKey)
    nextLetter += 1
}

insertLetter kiểm tra xem còn chỗ trống trong phần đoán cho chữ cái này không, tìm hàng thích hợp và đặt chữ cái vào hộp.

xóaThư

deleteLetter trông như thế này:

function deleteLetter () {
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    let box = row.children[nextLetter - 1]
    box.textContent = ""
    box.classList.remove("filled-box")
    currentGuess.pop()
    nextLetter -= 1
}

deleteLetter lấy đúng hàng, tìm hộp cuối cùng và làm trống nó, sau đó đặt lại bộ đếm nextLetter.

kiểm tra đoán

Các checkGuess chức năng trông như thế này:

function checkGuess () {
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    let guessString = ''
    let rightGuess = Array.from(rightGuessString)

    for (const val of currentGuess) {
        guessString += val
    }

    if (guessString.length != 5) {
        alert("Not enough letters!")
        return
    }

    if (!WORDS.includes(guessString)) {
        alert("Word not in list!")
        return
    }

    
    for (let i = 0; i < 5; i++) {
        let letterColor=""
        let box = row.children[i]
        let letter = currentGuess[i]
        
        let letterPosition = rightGuess.indexOf(currentGuess[i])
        // is letter in the correct guess
        if (letterPosition === -1) {
            letterColor="grey"
        } else {
            // now, letter is definitely in word
            // if letter index and right guess index are the same
            // letter is in the right position 
            if (currentGuess[i] === rightGuess[i]) {
                // shade green 
                letterColor="green"
            } else {
                // shade box yellow
                letterColor="yellow"
            }

            rightGuess[letterPosition] = "#"
        }

        let delay = 250 * i
        setTimeout(()=> {
            //shade box
            box.style.backgroundColor = letterColor
            shadeKeyBoard(letter, letterColor)
        }, delay)
    }

    if (guessString === rightGuessString) {
        alert("You guessed right! Game over!")
        guessesRemaining = 0
        return
    } else {
        guessesRemaining -= 1;
        currentGuess = [];
        nextLetter = 0;

        if (guessesRemaining === 0) {
            alert("You've run out of guesses! Game over!")
            alert(`The right word was: "${rightGuessString}"`)
        }
    }
}

checkGuess là khá dài, vì vậy hãy phá vỡ nó. Nó làm một vài điều:

  • Đảm bảo đoán là 5 chữ cái
  • Đảm bảo dự đoán là một danh sách hợp lệ
  • Kiểm tra từng chữ cái của từ và tô màu chúng
  • Cho người dùng biết về sự kết thúc của trò chơi

checkGuess sử dụng một thuật toán đơn giản để quyết định màu nào sẽ tô bóng cho mỗi chữ cái:

  1. Kiểm tra xem chữ cái có trong từ đúng không
  2. Nếu chữ cái không có trong từ, chữ cái sẽ có màu xám
  3. Nếu chữ cái nằm trong từ, hãy kiểm tra xem nó có ở đúng vị trí không
  4. Nếu chữ cái ở đúng vị trí, màu xanh lá cây
  5. Khác, màu vàng

checkGuess sử dụng một chức năng shadeKeyboard để tô màu các phím của bàn phím ảo, nhưng nó chưa được xác định. Hãy làm điều đó tiếp theo.

bóng bàn phím

function shadeKeyBoard(letter, color) {
    for (const elem of document.getElementsByClassName("keyboard-button")) {
        if (elem.textContent === letter) {
            let oldColor = elem.style.backgroundColor
            if (oldColor === 'green') {
                return
            } 

            if (oldColor === 'yellow' && color !== 'green') {
                return
            }

            elem.style.backgroundColor = color
            break
        }
    }
}

shadeKeyBoard nhận chữ cái trên bàn phím ảo mà chúng tôi muốn tô bóng và màu chúng tôi muốn tô bóng. Đây là thuật toán:

  1. Tìm chìa khóa khớp với chữ cái đã cho
  2. Nếu phím đã có màu xanh, không làm gì cả
  3. Nếu phím hiện có màu vàng, chỉ cho phép phím chuyển sang màu xanh lục
  4. Khác, tô màu phím được truyền cho chức năng

Cách thêm thông báo

Tiếp theo, bạn sẽ thay thế các cảnh báo JavaScript trong checkGuess với bánh mì nướng, sử dụng Toastr.

Đi qua checkGuessvà thay thế tất cả các cảnh báo thông báo cho người dùng về lỗi bằng các cuộc gọi đến toastr.error().

Cảnh báo thông báo cho người dùng đoán đúng nên được thay thế bằng toastr.success()và cảnh báo cho người dùng biết dự đoán đúng nên được thay thế bằng toastr.info().

Đây là giao diện của checkGuess sau khi bạn hoàn thành:

function checkGuess () {
    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    let guessString = ''
    let rightGuess = Array.from(rightGuessString)

    for (const val of currentGuess) {
        guessString += val
    }

    if (guessString.length != 5) {
        toastr.error("Not enough letters!")
        return
    }

    if (!WORDS.includes(guessString)) {
        toastr.error("Word not in list!")
        return
    }

    
    for (let i = 0; i < 5; i++) {
        let letterColor=""
        let box = row.children[i]
        let letter = currentGuess[i]
        
        let letterPosition = rightGuess.indexOf(currentGuess[i])
        // is letter in the correct guess
        if (letterPosition === -1) {
            letterColor="grey"
        } else {
            // now, letter is definitely in word
            // if letter index and right guess index are the same
            // letter is in the right position 
            if (currentGuess[i] === rightGuess[i]) {
                // shade green 
                letterColor="green"
            } else {
                // shade box yellow
                letterColor="yellow"
            }

            rightGuess[letterPosition] = "#"
        }

        let delay = 250 * i
        setTimeout(()=> {
            //shade box
            box.style.backgroundColor = letterColor
            shadeKeyBoard(letter, letterColor)
        }, delay)
    }

    if (guessString === rightGuessString) {
        toastr.success("You guessed right! Game over!")
        guessesRemaining = 0
        return
    } else {
        guessesRemaining -= 1;
        currentGuess = [];
        nextLetter = 0;

        if (guessesRemaining === 0) {
            toastr.error("You've run out of guesses! Game over!")
            toastr.info(`The right word was: "${rightGuessString}"`)
        }
    }
}

Cách để Bàn phím ảo tạo đầu vào

Để bàn phím ảo của bạn hoạt động, tất cả những gì bạn phải làm là gửi một sự kiện nhấn phím bất cứ khi nào bấm vào bất kỳ phím nào trên bàn phím ảo của bạn. Để làm điều đó, hãy thêm mã này vào script.js:

document.getElementById("keyboard-cont").addEventListener("click", (e) => {
    const target = e.target
    
    if (!target.classList.contains("keyboard-button")) {
        return
    }
    let key = target.textContent

    if (key === "Del") {
        key = "Backspace"
    } 

    document.dispatchEvent(new KeyboardEvent("keyup", {'key': key}))
})

Chức năng này lắng nghe một cú nhấp chuột vào vùng chứa bàn phím hoặc bất kỳ phần tử con nào của nó (các nút). Nếu phần tử được nhấp không phải là nút, chúng tôi sẽ thoát khỏi chức năng. Nếu không, chúng tôi gửi một sự kiện mở khóa tương ứng với phím được nhấp.

Đọc thêm  Cách kiểm tra xem một đối tượng có trống không trong JavaScript – JS Java isEmpty Equivalent

Cách thêm hoạt ảnh

Chúng ta đã cài đặt animate.css, vì vậy bây giờ hãy viết một hàm JavaScript để sử dụng nó.

const animateCSS = (element, animation, prefix = 'animate__') =>
  // We create a Promise and return it
  new Promise((resolve, reject) => {
    const animationName = `${prefix}${animation}`;
    // const node = document.querySelector(element);
    const node = element
    node.style.setProperty('--animate-duration', '0.3s');
    
    node.classList.add(`${prefix}animated`, animationName);

    // When the animation ends, we clean the classes and resolve the Promise
    function handleAnimationEnd(event) {
      event.stopPropagation();
      node.classList.remove(`${prefix}animated`, animationName);
      resolve('Animation ended');
    }

    node.addEventListener('animationend', handleAnimationEnd, {once: true});
});

Chức năng này đến từ trang chủ Animate.css. Nó áp dụng các lớp cho mục tiêu hoạt ảnh để kích hoạt hoạt ảnh và khi hoạt ảnh kết thúc, nó sẽ xóa các lớp mà nó đã thêm vào.

Hàm trả về một lời hứa cho phép bạn thực hiện các hành động chỉ cần chạy sau khi hoạt ảnh kết thúc, nhưng bạn sẽ không cần thực hiện điều đó trong hướng dẫn này.

Bây giờ chúng ta có một chức năng để tạo hoạt ảnh cho bất kỳ phần tử nào, hãy áp dụng nó. quay trở lại của chúng tôi insertLetter chức năng và thêm dòng sau trước khi chúng tôi thay thế textContent của box:

    animateCSS(box, "pulse")

đây là cái gì insertLetter sẽ giống như bây giờ:

function insertLetter (pressedKey) {
    if (nextLetter === 5) {
        return
    }
    pressedKey = pressedKey.toLowerCase()

    let row = document.getElementsByClassName("letter-row")[6 - guessesRemaining]
    let box = row.children[nextLetter]
    animateCSS(box, "pulse")
    box.textContent = pressedKey
    box.classList.add("filled-box")
    currentGuess.push(pressedKey)
    nextLetter += 1
}

Mã cho biết insertLetter để nhanh chóng đập từng hộp, ngay trước khi chúng tôi điền vào đó một chữ cái.

Tiếp theo, bạn muốn tạo hiệu ứng động cho từng chữ cái của một lần đoán trong khi bạn đang kiểm tra nó.

Quay lại và sửa đổi checkGuessnhư vậy:

let delay = 250 * i
setTimeout(()=> {
    //flip box
    animateCSS(box, 'flipInX')
    //shade box
    box.style.backgroundColor = letterColor
    shadeKeyBoard(letter, letterColor)
}, delay)

Mã này thêm hoạt ảnh để lật từng hộp theo chiều dọc, ngay trước khi chúng tôi thay đổi màu sắc.

Phần kết luận

Điều đó kết thúc hướng dẫn. Bạn vừa tạo một bản sao Wordle và tôi hy vọng bạn đã có niềm vui trong quá trình này. Bạn có thể tìm thấy mã hoàn chỉnh tại kho lưu trữ GitHub cho dự án này.

Nếu bạn thích bài viết này, bạn có thể tìm thêm bài viết của tôi tại đây hoặc theo dõi tôi trên Twitter.





Zik.vn – Biên dịch & Biên soạn Lại

spot_img

Create a website from scratch

Just drag and drop elements in a page to get started with Newspaper Theme.

Buy Now ⟶

Bài viết liên quang

DMCA.com Protection Status