네모로직에서 숫자로 보드 내 색칠해야할 칸을 알려주는 힌트 부분을 구현하려한다.
사용자의 문제풀이 화면에서는 힌트와 빈 테이블이 보여지고 힌트를 따라 셀을 채워가는 방식이지만 문제를 낼 때마다 힌트를 수기로 세어서 일일이 넣는 것이 굉장히 비효율적이기 때문에 보드판에 그림을 그린 후 배열 내 데이터를 바탕으로 힌트를 자동으로 만들어주도록 할 것이다. (이 후 기능이 확장된다면 사용자가 문제를 출제할 수도 있을 것이다.)

힌트(clue)의 레이아웃과 변수 명은 위 처럼 구성 된다.
//1) 힌트 컨테이너 만들기
const rowClueContainer = createClueContainer("row", ROWS);
const colClueContainer = createClueContainer("col", COLS);
//2) DOM요소에 선택자와 클래스로 활용 될 string type의 matrixName과 matrix 개수를 매개변수로 받는 함수 구현
function createClueContainer(matrixName, size) {
//3) clue container element 선택
const clueContainer = document.getElementById(`${matrixName}-clue`);
//4) row 혹은 col의 개수에 따라 clue container element 안에 들어갈 clue box 생성
for (let i = 0; i < size; i++) {
const clueArrBox = document.createElement("div");
clueArrBox.setAttribute("class", `${matrixName}${i} clue-container`);
clueContainer.appendChild(clueArrBox);
//5) clue box들이 속한 clueContainer 요소를 리턴
return clueContainer;
}
힌트 컨테이너 dom요소를 만들 때 로우/칼럼의 개수, 선택할 DOM 요소 외 기능은 동일하기 때문에 함수로 구현하였다.

만일 붉은 부분의 셀을 클릭했다고 가정한다면, 노란색 부분의 로우와 칼럼 배열을 순회하여 힌트를 업데이트 하는 방식으로 구현하였다.
//클릭 시 셀배열 체크
function checkCellArr(e) {
//클릭한 셀의 위치 값 찾기
const thisRow = parseInt(e.target.getAttribute("data-row"));
const thisCol = parseInt(e.target.getAttribute("data-col"));
...
//힌트 세팅 (문제출제용)
clueSetting(thisRow, thisCol);
}
체크 된 셀에 따라 힌트의 숫자가 세팅되어야하기 때문에 이전에 작성했었던 checkCellArr() 함수가 실행될 때 clueSetting() 함수를 실행시킨다.
위에서 말했던 것 처럼 클릭한 셀의 로우와 칼럼 배열을 순회하여야 하므로 thisRow, thisCol을 인자로 받는clueSetting()함수는 다음과 같이 구현된다.
//clue 세팅
function clueSetting(thisRow, thisCol) {
//1) 클릭한 셀이 속한 로우/칼럼을 순회하여 변경된 clueArr 받기
const rowClueArr = countTasks("row", COLS, thisRow);
const colClueArr = countTasks("col", ROWS, thisCol);
}
clueSetting이 실행되면 클릭한 로우 인덱스 값과 클릭한 칼럼 인덱스 값을 인자로 받아 clueArr를 리턴 받는 countTasks()함수가 실행된다.

네모로직 힌트는 연속으로 칠해진 셀의 숫자가 작성된다는 것이 특징이다. 칠해진 셀(true)과 셀 사이에 빈 셀(false)이 있다면 그 다음의 칠해진 셀의 숫자는 다음 칸에 작성된다.
clueArr를 만들 때 위의 특징을 유념하여 알고리즘을 구현해야 한다.

이미지 처럼 클릭한 셀의 로우를 인덱스 0 - 9를 순회하며 true를 만났을 때는 taskCount가 증가하고 false를 만났을 때는 이전까지 증가시켰던 taskCount를 clueArr에 넣는 방식으로 구현했다.
//1) 이 카운팅이 row를 기준으로 카운팅하는 것인지 col을 기준으로 카운팅하는 것인지 정하기 위해 matrixName, size, thisCell을 인자로 받는다.
function countTasks(matrixName, size, thisCell) {
let clueArr = [];
let taskCount = 0;
//2) row 혹은 col의 개수 만큼 셀 순회
for (let i = 0; i < size; i++) {
//3) row열을 순회하는거라면 cells[thisCell][i]형태로 순회
if (matrixName === "row" && cells[thisCell][i] === true){
//셀이 true인 경우 카운트가 1씩 증가
taskCount += 1;
// *만일 해당 셀이 마지막 인덱스일 경우 지금까지 카운트했던 task를 clueArr에 push한다.
if (i === size - 1) {
clueArr.push(taskCount);
}
} else {
//true가 아니라면 지금까지 카운트 된 셀은 clueArr에 담고 카운트 초기화
taskCount && clueArr.push(taskCount);
taskCount = 0;
}
}
//4) clueArr 리턴
return clueArr;
}

칼럼 힌트도 세팅하기 위해 matrixName이 "col"이라면 cell[i][thisCell] 형태로 순회하는 조건도 넣어주었다.
function countTasks(matrixName, size, thisCell) {
let clueArr = [];
let taskCount = 0;
for (let i = 0; i < size; i++) {
if (matrixName === "row" && cells[thisCell][i] === true) {
taskCount += 1;
if (i === size - 1) {
clueArr.push(taskCount);
}
} else if (matrixName === "col" && cells[i][thisCell] === true) {
taskCount += 1;
if (i === size - 1) {
clueArr.push(taskCount);
}
} else {
taskCount && clueArr.push(taskCount);
taskCount = 0;
}
}
return clueArr;
}
function clueSetting(thisRow, thisCol) {
const rowClueArr = countTasks("row", COLS, thisRow);
const colClueArr = countTasks("col", ROWS, thisCol);
//1) querySelector로 해당 로우/칼럼 컨테이너 dom요소를 선택해서 페인팅하기
const rowClueContainer = document.querySelector(`#row-clue .row${thisRow}`);
rowClueContainer.innerHTML = rowClueArr
.map((count) => `<p>${count}</p>`)
.join("");
const colClueContainer = document.querySelector(`#col-clue .col${thisCol}`);
colClueContainer.innerHTML = colClueArr
.map((count) => `<p>${count}</p>`)
.join("");
}
위에서 생성했던 clueSetting() 함수에 rowClueArr와 colClueArr를 활용하여 map매서드로 화면에 페인팅한다.
뒤에 .join("")을 넣은 이유는 배열 형태로 html요소가 추가되었기 대문에 ','가 표기되어 join으로 콤마를 없앴다.

=>
초반에는 막막했지만 이 로직을 나 혼자 구현했다니! 너무 뿌듯하다..
로직을 구현하는 것 자체는 오래걸리지 않았지만 설명하려고 하니 쉽지 않구만..