요즘 가장 유행하는 게임인 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 해줍니다.
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과 비슷한 게임이 구현되었습니다! 실제로 플레이해본 모습입니다.