Wordle 만들기

김지연·2022년 2월 28일
1

요즘 가장 유행하는 게임인 Wordle 을 직접 구현해보았습니다.

Wordle은 그날에 주어진 5글자의 단어를 알아내는 게임입니다.

보드판은 높이 6 x 넓이 5 총 30개의 타일로 이루어져 있고, 한 타일에 알파벳을 하나씩 입력할 수 있습니다. 5글자의 유효한 단어만 입력할 수 있으며 한 줄은 한번의 시도를 의미합니다. 단어를 입력하고 엔터를 누르면 타일의 색이 바뀌면서 정답에 대한 힌트를 알려주고 다음 줄로 이동합니다.

  • 타일이 노란색으로 변하면 해당 알파벳이 정답 단어에 포함되어 있지만 다른 위치에 있다는 뜻입니다.
  • 타일이 포록색으로 변하면 해당 알파벳이 정답 단어에 포함되어 있고, 해당 위치에 있다는 뜻입니다.
  • 타일이 회색으로 변하면 해당 알파벳이 정답 단어에 포함되어 있지 않다는 뜻입니다.

이런식으로 얻은 힌트를 이용해 6번의 시도만에 단어를 맞추면 게임이 끝납니다.

worlde.html 파일을 만들고 world.css, wordle.js 를 연결해줍니다.

<html>
    <head>
        <title>WORDLE</title>
        <meta charset="UTF-8">
        <meta name="viewpoint" content="width=device-width, initial-scale=0, user-scalable=no">

        <link rel="stylesheet" href="wordle.css">
        <script src="worldle.js"></script>
    </head>

타이틀과 네모 상자들이 들어갈 보드를 만들어줍니다. 자바스크립트로 DOM에 접근해 board 안에 개별 타일들을 넣어줄 예정입니다.

    <body>
        <h1 id="title">WORDLE</h1>
        <hr>
        <br>
        <div id="board">

        </div>

        <br>
        <h1 id="answer"></h1>
    </body>
</html>

css 스타일시트

기본적인 타이틀과 보드, 타일에 css 를 적용해주었습니다.

body {
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
}

hr {
    width: 500px; 
}

#title {
    font-size: 36px; 
    font-weight: bold;
    letter-spacing: 2px;
}

#board {
    width: 350px;
    height: 420 px;
    margin: 100px auto;
    margin-top: 3px;
    display: flex;
    flex-wrap: wrap;
}

.tile {
    /* box */
    border: 2px solid lightgrey;
    width: 60px;
    height: 60px;
    margin: 2.5px;

    /* text */
    color: black;
    font-size: 36px;
    font-weight: bold;
    display: flex;
    justify-content: center;
    align-items: center;
}

단어를 확인할 때마다 힌트 여부에 따라 타일 엘리먼트에 클래스를 추가해주어 색상이 변할 수 있도록 할 예정입니다.

.correct {
    background-color: #619978;
    color: white;
    border-color: white;
}

.present {
    background-color: #C9B458;
    color: white;
    border-color: white;
}

.absent {
    background-color: #8d8f91;
    color: white;
    border-color: white
}

js

let height = 6; // 줄 갯수 
let width = 5; // 단어 길이

let row = 0; // 현재 줄 (attempt #)
let col = 0; // 현재 알파벳 인덱스 

let gameOver = false;
let word = "SQUID";

페이지가 로드되면 initialize 함수를 불러올 수 있게 해줍니다.

window.onload = function() {
    initialize();
};

initialize 함수에서는 먼저 보드판을 채워줍니다.
6 x 5 의 개수만큼 스판 태그를 만들고 보드의 자식 노드에 append 해줍니다. 각각의 타일을 구분할 수 있도록 각 타일의 위치에 부합하는 인덱스(ex. 0-0, 0-1)를 고유한 아이디로 주었습니다.

function initialize() {

    // 보드 만들기
    for (let r = 0; r < height; r++) {
        for (let c = 0; c < width; c++) {
            //<span id="0-0" class="tile"></span>
            let tile = document.createElement("span"); 
            tile.id = r.toString() + "-" + c.toString(); 
            tile.classList.add("tile");
            tile.innerText = "";
            document.getElementById("board").appendChild(tile); 
        }
    }

다음은 키를 입력했을 때 작동하는 부분입니다.
게임이 끝났을 때 더이상 키를 입력해도 이벤트 리스너가 작동하지 않도록 합니다.

	// 키 입력 
	document.addEventListener("keyup", (e) => {
    	if(gameOver) return; 

게임에 필요한 키만 사용할 것이므로 조건을 A 에서 Z 사이의 키가 눌렸을 때로 지정해줍니다.
현재 위치한 타일에 입력 키의 알파벳이 나타나도록 변수 currTile을 선언해주고, 앞서 만든 타일 엘리먼트들 중 현재 위치한 타일을 고유 아이디값(인덱스)으로 조회하여 입력한 키를 innerText로 넣어줍니다. 다음 칸으로 넘어가도록 col 을 +1 해줍니다.

  • KeyboardEvent.code 는 "KeyA", "KeyB"의 형식으로 반환하므로 알파벳만 나타날 수 있도록 반환값의 세번째 인덱스(e.code[3])를 불러옵니다.
if ("KeyA" <= e.code && e.code <= "KeyZ") {
  if (col < width) {
    let currTile = document.getElementById(row.toString() + "-" + col.toString());
    if (currTile.innerText === "") {
      currTile.innerText = e.code[3];
      col += 1;
    }
  }
}

뒤로가기 버튼을 눌렀을 때 현재 위치를 col - 1 로 변경해주고 빈 타일을 만들어줍니다.

else if (e.code === "Backspace") {
  if (0 < col && col <= width) {
    col -= 1;
  }
  let currTile = document.getElementById(row.toString() + '-' + col.toString());
  currTile.innerText = "";
}

한 줄이 다 채워지고 나서 엔터를 눌렀을 때 타일의 색들을 변경해주는 업데이트 함수를 실행해주고, 다음칸으로 넘어가도록 row 를 +1 해주고 col 은 0으로 초기화 해줍니다.

 else if (e.code === "Enter" && col === width) {
   update();
   row += 1; // start new row
   col = 0;  // start at 0 for new row
 }

마지막 줄까지 다 채워졌을 때 게임이 끝나고 정답을 출력합니다.

  if (!gameOver && row === height) {
    gameOver = true;
    document.getElementById("answer").innerText = word;
  	}
  })

};

단어를 입력하고 엔터를 눌렀을 때 실행되는 업데이트 함수입니다.

단어의 각 글자를 확인해 정답과 비교해야 하기 때문에 for 문으로 조회하고 비교할 알파벳을 letter 변수에 할당해줍니다. 조건문으로 letter 가 정답과 비교해 맞는 자리에 있을때, 정답 안에 존재할때, 아예 존재하지 않을때로 나누어 위에서 지정한 css 속성에 따라 타일의 색이 변할 수 있도록 class 를 추가해줍니다.
마지막 타일까지 채우지 않아도 중간에 답을 맞추면 더이상 key press 이벤트가 작동하지 않아야 하므로 게임오버 상태를 true 로 바꿔줍니다.

function update() {
    let correct = 0;
    for (let c = 0; c < width; c++) {
        let currTile = document.getElementById(row.toString() + '-' + c.toString());
        let letter = currTile.innerText;

        // letter가 맞는 자리에 있는가?
        if (word[c] === letter) {
            currTile.classList.add("correct");
            correct += 1;
        } // letter가 정답에 존재하는가? 
        else if (word.includes(letter)) {
            currTile.classList.add("present");
        } // letter 정답에 없음? 
        else {
            currTile.classList.add("absent");
        }
		// 정답을 맞추면 게임오버 
        if (correct === width) {
            gameOver = true;
        }
    }
};

짜잔! 실제 Wordle과 비슷한 게임이 구현되었습니다! 실제로 플레이해본 모습입니다.

profile
Aspiring Front-end Developer

0개의 댓글