앞서 만들었던 부분에서는 뱀이 벽이나 스스로에게 끼여서 멈추는 현상이 있었다.
이를 해결하기위해 제자리에서 일정 횟수 이상 전진할 방향을 찾지 못하면 처음 위치로 돌아가도록 했다.
또 보석을 먹으면 뱀이 늘어나도록 해야했기 때문에 코드를 거의 다 새로 작성했다.
우선 작동 방식을 바꿨다.
화면에 보여질 필드와 뱀을 기록할 2차원 배열을 만든 뒤에 테스트용 버튼을 하나 만들어서 버튼을 클릭할 때 마다 뱀이 한 마리씩 추가되도록 했다.
만들고 사용한 함수들은 다음과 같다.
makeFiled() : 화면에 보여질 25*25 짜리 필드를 만든다.
addSnake() : 뱀을 한 마리 추가하는 함수다. 뱀이 추가될 위치에 다른 뱀이 지나고 있는지 확인하면서 아무것도 없을 때 들어가도록 한다.
moveSnake() : 아무것도 없어 뱀 추가가 가능해지면 실행된다.
forward() : 뱀이 앞으로 전진하는 함수이다.
turn() : 더 이상 앞으로 갈 수 없거나, 일정 거리 앞으로 전진했을 때 방향을 바꿔준다.
resetSnake() : 벽이나, 스스로 또는 다른 뱀에게 끼이는 일이 생기면 처음 위치로 되돌려준다.
먼저 html과 board, field 그리고 그외 필요한 변수들
<!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>Black Mamba</title>
</head>
<body>
<table border="1" width="500" height="500" id="game-board"></table>
<button id="add-snake">뱀 추가</button>
<span id="mamba"></span>
<script src="js/blmam.js"></script>
</body>
</html>
//board 생성
const board = new Array(25);
for (let i = 0; i<25; i++){
board[i] = new Array(25);
}
for (let i=0;i<25;i++){
for (let j=0; j<25; j++){
board[i][j] = 0;
}
}
//뱀 마리 수 표시용
const mamba = document.getElementById("mamba");
let mambaCount = 0;
//동서남북 체크에 쓰일 방향들
const dx = [0, -1, 0, 1]
const dy = [1, 0, -1, 0]
//뱀 추가 버튼
const button = document.getElementById("add-snake");
//field table생성
function makeField(){
const gameboard = document.getElementById("game-board");
for (let i = 0; i<25; i++){
const tr = document.createElement("tr");
for (let j = 0; j<25; j++){
const td = document.createElement("td");
td.id = `${i},${j}`;
tr.appendChild(td);
}
gameboard.appendChild(tr);
}
}
아래는 뱀을 추가해주는 함수
function addSnake(){
mambaCount += 1;
mamba.innerText = mambaCount;
const snake = [];
for (let i = 0; i < 9; i++){
snake.push([24, i])
} //왼쪽 하단에 뱀을 만들 것이다.
let checkEmpty = setInterval(function(){
let check = true;
for (let i = 0; i<9; i++){
if (board[24][i] === 1){
check = false;
}
}
if (check){
clearInterval(checkEmpty);
moveSnake(snake);
}
}, 100);
//왼쪽 하단 부분이 비어있는지를 확인하면서 새로운 뱀을 놓을 수 있을 때 까지 반복해준다. setInterval을 사용한 이유는 while문으로 했을 때 검사 속도가 너무 빨라서 아예 작동 자체가 멈춰버리는 것 같았다.
}
새로운 뱀을 놓을 수 있는 조건이 갖춰지면 moveSnake()를 호출
//방향 0:좌, 1:하, 2:우, 3:상
function moveSnake(snake){
let now_dir = 0;
for (let i = 0; i<9; i++){
document.getElementById(`24,${i}`).style.backgroundColor = "black";
board[24][i] = 1;
}
let forward_rnd = Math.floor(Math.random() * 10) % 3 + 3;
let count = 0;
let moving = setInterval(function() {
let move = forward(snake, now_dir, count, forward_rnd);
snake = move[0];
now_dir = move[1];
count = move[2];
forward_rnd = move[3];
if (snake === -1){
clearInterval(moving);
addSnake();
}
}, 100);
}
왼쪽 하단에 뱀을 그리고 앞으로 전진하는 forward함수를 만들고 호출해주었다. 이 함수는 뱀의 위치 정보와 현재 쳐다보고 있는 방향, 앞으로 몇 칸 째 움직이고 있는지, 앞으로 몇 칸을 갈 것인지를 받는다.
function forward(snake, now_dir, count, forward_rnd){
count = count + 1;
if (count === forward_rnd){
const nextDir = turn(snake);
forward_rnd = Math.floor(Math.random() * 10) % 3 + 3;
if (nextDir === -1){
return [resetSnake(snake), 0, 0, forward_rnd];
}
return [snake, nextDir, 0, forward_rnd];
}
let snakeHead = snake[snake.length - 1];
const nextX = snakeHead[0] + dx[now_dir];
const nextY = snakeHead[1] + dy[now_dir];
if ((nextX>=0 && nextX<25) && (nextY>=0 && nextY<25) && board[nextX][nextY] === 0){
snake.push([nextX,nextY]);
document.getElementById(`${nextX},${nextY}`).style.backgroundColor = "black";
board[nextX][nextY] = 1;
const snakeTail = snake.shift();
document.getElementById(`${snakeTail[0]},${snakeTail[1]}`).style.backgroundColor = "white";
board[snakeTail[0]][snakeTail[1]] = 0;
return [snake, now_dir, count, forward_rnd];
}else{
const nextDir = turn(snake);
forward_rnd = Math.floor(Math.random() * 10) % 3 + 3;
if (nextDir === -1){
return [resetSnake(snake), 0, 0, forward_rnd];
}
return [snake, nextDir, 0, forward_rnd];
}
}
앞으로 전진을 벽이 있지 않으면 최소 3칸은 하도록 했다. 매번 움직일 때 마다 방향을 랜덤으로 설정했더니 너무 쉽게 혼자 꼬여서 멈춰버려서 조금이라도 덜하게 하려고 이렇게 했다. 3~5칸을 랜덤으로 전진한 뒤에 새로 방향을 설정해주는 turn()함수를 호출한다.
function turn(snake){
const snakeHead = snake[snake.length - 1];
let count = 0;
while (true){
count+=1;
const next_dir = Math.floor(Math.random() * 10) % 4;
const nextX = snakeHead[0]+dx[next_dir];
const nextY = snakeHead[1]+dy[next_dir];
if (0<=nextX && nextX<25 && 0<=nextY && nextY<25){
if (board[nextX][nextY] === 0){
return next_dir;
}
}
if (count == 30){
return -1;
}
}
}
turn함수는 동서남북 네 방향 중 전진 가능한 방향을 찾아 반환해준다. 만약 갈 수 있는 곳을 일정 횟수 시도했을 때에도 찾지 못하면 -1을 반환하면서 뱀을 처음 위치에서 다시 소환하도록 했다.
function resetSnake(snake){
mambaCount -= 1;
mamba.innerText = mambaCount;
for (let i = 0; i < 9; i++){
document.getElementById(`${snake[i][0]},${snake[i][1]}`).style.backgroundColor = "white";
board[snake[i][0]][snake[i][1]] = 0;
}
return -1;
}
꼬였거나 막혀서 멈춘 뱀을 지워준다. 그리고 -1을 반환해서 addSnake()를 다시 호출할 수 있게 했다.
makeField();
button.addEventListener("click", addSnake);
마지막에는 필드 생성 함수를 호출해주고, 버튼을 클릭했을 때 뱀이 추가되도록 하는 코드를 작성했다.
나중에는 보석을 먹으면 뱀이 추가되도록 수정해야한다.
이제는 유저, 보석 추가, 타이머 추가를 하면서 관련 동작들을 추가해야한다.
그리고 오프닝과 엔딩을 앞뒤에 멋지게 넣어주면 된다.