오늘 틱택토 게임 만들기를 해봤다.
게임을 만드는 과정 중 어려웠던 부분을 기록하려한다.
내가 생각했던 로직
1. 9개의 숫자 중 3개로 빙고가 될 수 있을 만한 배열들을 추려놓는다.
2. 9개의 버튼 중에 철수(X)와 영희(O)가 차례대로 누른다.
3. 철수가 누르는 버튼의 숫자(X가 배정된 숫자 = xList)와 영희가 누른 버튼의 숫자(O가 배정된 숫자 = oList)를 각각 다른 배열에 따로 담아둔다.
4. xList와 oList 중에서 3개를 골라서 나올 수 있는 모든 경우의 수를 구한다.
5. 만약 xList(또는 oList)에서 무작위로 3개를 고른 배열들 중 1번의 빙고가 될 수 있는 배열을 포함하면 X(철수) 또는 O(영희)가 이긴다.
나의 코드
특히 내 코드에서 combilist.some((arr) => arr.every((num) => dapList[i].includes(num)))
이 부분을 쓸 때 매우 오래걸려버렸다ㅠㅠ
- 의도했던 바는 "xList나 oList 중에서 무작위로 3개를 뽑은 배열들 중 빙고가 될 수 있는 배열을 포함하면~" 이었고,
처음에는 combilist.includes(dapList[i]) 이렇게 썼었다. 그랬더니 절대 false가 되는 것이다.
이 글을 읽는 사람들은 이걸 보고 웃을지도 모른다. 아니 이런 기본적인 걸 몰랐다고~?ㅎ 몰랐다. 아니 깜빡해버렸다.
객체형 데이터는 각자 메모리의 주소값을 가지기 때문에 -> 두 배열이 같아보여도 사실은 다른 주소값을 가진다.
-> 두 배열은 같다고 할 수 없다 = includes로 찾을 수 없다....
따라서 두 배열이 같은지(?)를 따져보려면 객체형 데이터 안에 있는 단순 데이터들끼리를 비교해 봐야한다.(주소값을 가지지 않는..)
그래서 결국에는 x List에서 무작위로 세 개를 뽑은 배열들(= combilist) 중에서, 빙고가 될 수 있는 배열(= dapList[i])이
combilist의, 배열 하나의(arr), 각 요소들을 모두(every)포함(그래야 똑같은거니까)하는 combi가 하나라도 있으면(some) 승자를 알리는 alert가 뜨는 것이다.
즉, A배열(combilist) 안의 요소와 B배열(dapList) 안의 요소가 같은지를 비교할 때는 이중 반복문을 써야하는데,
그 안의 요소들이 또 배열이라면, 배열과 배열의 직접 비교는 불가능 => A배열 안의 각 요소들을 다시 돌리면서 다른 배열이 그 요소들을 다 포함하는지를 봐야한다.
휴우 매우... 복잡시럽다.
그래서 답 코드를 봤다.
진짜... 이럴 때마다 너무 벽이 느껴진다.
답 코드는 이거였다.
const WINNING_LINES = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
];
initialState: {
board: Array(9).fill(null),
currentPlayer: "X",
winner: null,
histories: [],
}
>>
...
state.board[index] = state.currentPlayer;
state.currentPlayer = state.currentPlayer === "X" ? "O" : "X";
...
>>
const winningLine = WINNING_LINES.find((line) => {
const [a, b, c] = line;
if (board[a] && board[a] === board[b] && board[a] === board[c]) {
return board[a];
}
이건 마치 애기한테 빙고라는 게임을 알려주는 것과 같다고 생각했다.
1. 자 우리는 X랑 O중에 누가 이기는지를 알아볼거야~
2. board(9개의 칸)에는 O 또는 X가 들어갈거야
=> state.board[index] = state.currentPlayer;
3. 그리고 이렇게, 이렇게, 이렇게 가 다 O이거나 X면 우린 그걸 "one 빙고"라고 하는거야
4. 여기서 이렇게, 이렇게, 이렇게는 board의 순서고, 그 경우는 WINNING_LINES에 다 들어있어.
5. 근데 WINNING_LINES 을 보니 될 수 있는 경우들이 많지?
매번 board의 0번째, 3번째, 6번째 그러고 또 board의 2번째, 5번째, 8번째 이렇게 모든 경우를 비교하기엔 너무 많으니
이 경우들을 통틀어서 우리는 [a, b, c]라고 할거야.
=> const [a, b, c] = line;
6. 그럼 board의 a번째랑, b번째랑, c번째가 모두 같으면 "one 빙고!"라는 말이랑 같은 말이지?
=> if (board[a] && board[a] === board[b] && board[a] === board[c])
7. 그때 board의 a번째에 들어가 있는 값이 O면 O가 이기는 거고 X면 X가 이기는거라는 거지
=> return board[a];
이 답을 봤을 때 충격적이었다.
왜 나는 이런 생각을 하지 못했을까?
이미 답이 될 수 있는 경우들은 정해져있고 결국 누가 이기는지가 중요했던 것인데,
철수와 영희가 매번 뭘 고르는지는 몰라도 됐는데
철수와 영희가 매번 뭘 고르는지가 중요한 게 아니라, 빙고를 성공하는 경우(WINNING_LINES) 그걸 성공한 사람이
철수인지 영희인지가 중요했던 것인데...
결국 분석을 하자면 나는 눌린 버튼 숫자 간의 직접비교를 했던 것이고,
답에서는 버튼의 숫자가 몇이든 어차피 그 숫자에는 O 또는 X가 들어갈 것이니,
세 개에 모두 같은 게 들어갈 경우 그게 O인지, X인지로 풀이를 한 듯하다.
ㅎㅎㅎㅎㅎ현업에 종사하는 분이 그랬다.
개발자의 세계로 들어가는 문턱은 낮지만 천장은 끝도 없다고.......
이런 간단한 알고리즘만으로도 실감이 됩니다^^
'TIL' 카테고리의 다른 글
2024.02.26 TIL #Map() #flex-grow, flex-shrink, flex-basis (0) | 2024.02.27 |
---|---|
2024.02.23 TIL #객체의 구조분해 할당 (0) | 2024.02.26 |
2024.02.21 TIL #spread연산자의 무서운 점 (0) | 2024.02.21 |
2024.02.16 TIL #exportVSexportDefault #inputType="number" #substring()VSslice() (0) | 2024.02.19 |
2024.02.06 TIL #useRef로 ref가 부여되는 시점 #payload의 데이터형 (0) | 2024.02.06 |