다음 그림과 같이 지뢰가 있는 지역과 지뢰에 인접한 위, 아래, 좌, 우 대각선 칸을 모두 위험지역으로 분류합니다.
지뢰는 2차원 배열 board에 1로 표시되어 있고 board에는 지뢰가 매설 된 지역 1과, 지뢰가 없는 지역 0만 존재합니다.
지뢰가 매설된 지역의 지도 board가 매개변수로 주어질 때, 안전한 지역의 칸 수를 return하도록 solution 함수를 완성해주세요.
제한사항
// 지뢰찾기
const findMine = (board) => {
let mine = [];
board.forEach((row, i) => {
row.forEach((ceil, j) => {
if (ceil === 1) mine.push([i, j]);
})
})
return mine
}
// 안전하지 않은 좌표를 0에서 1로 바꾸기 (단, 이미 1일 경우 그대로 1로 두기)
const changeBoard = (mines, board) => {
// 지뢰 주변의 안전하지 않은 좌표를 notSafe 배열에 추가
let notSafe = [];
mines.forEach(([x, y]) => {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
let newX = x + i;
let newY = y + j;
// 유효한 좌표인지 확인
if (newX >= 0 && newY >= 0 && newX < board.length && newY < board[0].length) {
notSafe.push([newX, newY]);
}
}
}
});
// notSafe 배열을 참고하여 해당하는 곳의 좌표를 1로 변경
notSafe.forEach(([x, y]) => {
board[x][y] = 1;
});
return board;
}
// 안전지역 숫자 세기
const countSafeSide = (board) => {
let safeArea = 0;
board.forEach((row, i) => {
row.forEach((ceil, j) => {
if (ceil === 0) safeArea++;
})
})
return safeArea;
}
function solution(board) {
// 지뢰 좌표 배열을 구한다
let mines = findMine(board);
// 지뢰에 안전한 칸을 위험지역으로 바꾸기
let remarkBoard = changeBoard(mines, board);
// 안전한 지역 칸 수 계산
return countSafeSide(remarkBoard)
}
하 안전지대 찾는거 정말 어렵다... 세상에 모든 지뢰는 없어져야 합니다.
문제를... 어떻게 풀어야 할지 정말 막막하다 생각을 했다. 그래서 일단 순서를 나눴다.
이 과정에서 어려웠던 건... 이중배열이라는 점... 어떻게 손 대야 할지 막막하다가, 손대면서도 '이게 맞나?' 하며 혼란스럽고 에러떴는데 어찌어찌... 일단 해결은 했습니다.
function solution(board) {
// 지뢰 좌표 배열을 구한다
let mines = findMine(board);
// 지뢰에 안전한 칸을 위험지역으로 바꾸기
let remarkBoard = changeBoard(mines, board);
// 안전한 지역 칸 수 계산
return countSafeSide(remarkBoard)
}
먼저 솔루션 함수 부분을 본다.
위에서 1, 2, 3으로 순서를 나눴던 부분을 각각 함수로 분리해서 따로 기능하도록 만들었다. 그리고 솔루션 함수에서 만들어둔 함수를 호출하며 최종 답을 구할 수 있게 했다.
findMine(board)
ceil === 1
이라는 조건을 만족시키면 mine 배열에 [i, j]
를 넣어준다. changeBoard(mines, board)
newX
와 newY
를 계산한다. (x + i)
, (y + j)
를 통해서 x좌표와 y좌표 기준 위, 아래, 현재 위치/왼쪽, 오른쪽, 현재 위치를 나타낸다. if (newX >= 0 && newY >= 0 && newX < board.length && newY < board[0].length) { ... }
➡️ newX와 newY가 board 배열의 범위를 벗어나지 않도록 조건을 추가한다.(newX, newY)
를 notSafe 배열에 추가한다. countSafeSide(board)
가독성 있게 만들고 싶어서 노력을 많이 했다. 갠적으로 이번주에 풀었던 문제 중에서 제일 빡쎘음...
function solution(board) {
let outside = [[-1,0], [-1,-1], [-1,1], [0,-1],[0,1],[1,0], [1,-1], [1,1]];
let safezone = 0;
board.forEach((row, y, self) => row.forEach((it, x) => {
if (it === 1) return false;
return outside.some(([oy, ox]) => !!self[oy + y]?.[ox + x])
? false : safezone++;
}));
return safezone;
}
true
를 반환한다. !!self[oy + y]?.[ox + x]
➡️ 인접한 셀이 1인지 검사하는 것으로 ?.
연산자는 인덱스가 배열 범위를 벗어날 때 발생할 수 있는 오류를 방지한다.false
를 반환하고, 그렇지 않으면 safezone을 증가시킨다.!!
는 논리 부정 연산자를 두 번 사용한 것이다. 원래 값이 참이면 참, 거짓이면 거짓으로 변환한다. 때문에 위 코드는 self[oy + y]?.[ox + x]
가 존재하고 그 값이 truthy한지 확인하는 표현이다. function solution(b) {
const directions = [[0,0],[0,1],[0,-1],[1,1],[1,0],[1,-1],[-1,-1],[-1,0],[-1,1]]
let bombSet = new Set();
for(let i = 0; i < b.length; i++) {
for(let j = 0; j < b[i].length; j++) {
if(b[i][j] == 1) {
directions.forEach(el => {
let [nextX, nextY] = el;
[nextX, nextY] = [i+nextX, j+nextY];
if(nextX >= 0 && nextX < b.length && nextY >= 0 && nextY < b[i].length) {
bombSet.add(nextX+' '+nextY);
}
})
}
}
}
return b.length * b[0].length - bombSet.size;
}
directions
배열은 2차원 평면에서의 8방향(상하좌우, 대각선)과 현재 위치를 나타낸다. [0,0]
은 현재 위치를 나타낸다. bombSet
은 폭탄이 있는 위치와 그 주변 위치를 저장하는 집합이다.b
)를 순회한다. b[i][j]
가 1이면 그 위치에 폭탄이 있다는 의미이다. directions
배열을 순회하며 폭탄이 있는 위치와 그 주변 위치를 계산한다.nextX
와 nextY
는 폭탄 위치 i, j와 방향 벡터 el을 더한 값이다.b.length * b[0].length
➡️ 2차원 배열 b의 총 요소 개수를 나타낸다.bombSet.size
는 폭탄이 있는 위치와 그 주변 위치의 개수를 나타낸다.아 ... 진짜 어려웠다. 이중 배열, 좌표 개념이 들어가니까 0단계지만 미친듯이 어려웠다... 파이팅...