요상한 오목을 만들고 2차원 배열도 써볼겸 기존 단점 보완도 할 겸 시작한 오목 프로젝트 씨즌 투,,. 결론만 먼저 말하면 사실상 실패에 가까웠는데, 시간부터가 배열을 쓰지 않고 구현한 버전은 2시간 만에 만든 반면 이놈은 거의 4시간 가까이 걸렸다. 게다가 세부 룰 구현을 해보겠다는 목표는 몇 번의 테스트 후 포기해버렸다...
여튼 이번 오목의 출발점은
const board = document.querySelector('#board');
let gameboard = [];
let turn = 0;
let count = 0;
function start_game(){
gameboard = [];
turn = 0;
for(let y = 19 ; y>=1 ; y--){
let board_cols = [];
for(let x = 1; x<=19; x++){
board_cols.push('');
}
gameboard.push(board_cols);
}
let storage = '';
for(let i = 0 ; i<19 ; i++){
storage += '<div class="line">';
for(let j = 0 ; j<19 ; j++){
storage += '<div class=cell onclick=click_btn('+i+','+j+')>'+gameboard[i][j]+'</div>';
}
storage += '</div>';
}
board.innerHTML = storage;
}
보드를 그린 방법부터 사뭇 다르다. 먼저 배열 하나를 선언한 뒤, 모든 엘리먼트를 공백으로 설정해서 cols배열에 넣어주고 그걸 다시 gameboard에 push해서 그대로 HTML에 값을 뿌렸다.
19*19 보드에서 내가 보는 값 = 해당 좌표의 배열 내부 값
으로 만들고 싶었기 때문이다.
function check_win(x,y){
let win_count = 0;
for(let i = 0 ; i<18 ; i++){
if(gameboard[i][y]!=''&&gameboard[i][y]==gameboard[i+1][y]){
win_count++;
if(win_count==4){
return true;
}
}else{
win_count = 0;
}
}
// 더
// 많은
// 코드
win_count = 0;
for(let i = -4 ; i < 4 ; i++){
if(x+i<0 || y+i<0 || x+i>17 || y+i>17){
continue;
}else if(gameboard[x+i][y+i]!='' && gameboard[x+i][y+i]==gameboard[x+i+1][y+i+1]){
win_count++
if(win_count==4){
return true;
}
}else{
win_count = 0;
}
}
// 또 다시
// 코드
}
승리 조건도 연산 횟수가 굉장히 줄어들었다. 다만 대각선 조건을 구현하는데 있어서 인덱스가 계속 배열 길이를 넘어가는 문제가 생겨서 해당 부분을 구현하는데 애를 좀 먹었다. 2차원 배열의 대각선 좌표 표현은 내가 지금까지도 다소 어려워하는 부분중 하나다.
if( x-y == data ){ ... }
if( x+y == data ){ ... }
지금은 이렇게 판단하기도 하는데 활용까지 가는 길은 여전히 요원하다.
여하튼 다시 오목으로 돌아가면 저 대각선 외에도 문제가 생기는데
function click_btn(x,y){
if(turn==2){ alert('게임을 다시 시작해주세요.'); return; }
if(gameboard[x][y]=='<img src="흑돌.png">' || gameboard[x][y]=='<img src="백돌.png">'){ alert('이미 둔 자리입니다.'); return; }
if(turn==0){ gameboard[x][y] = '<img src="흑돌.png">'; turn++; }
else if(turn==1){ gameboard[x][y] = '<img src="백돌.png">'; turn--; }
let storage = '';
for(let i = 0 ; i<19 ; i++){
storage += '<div class="line">';
for(let j = 0 ; j<19 ; j++){
storage += '<div class=cell onclick=click_btn('+i+','+j+')>'+gameboard[i][j]+'</div>';
}
storage += '</div>';
}
board.innerHTML = storage;
if(check_win(x,y)){
if(turn==1){ alert('흑돌 승리'); turn=2; return; }
else if(turn==0){ alert('백돌 승리'); turn=2; return; }
}
count++;
if(count==361){
turn=2;
alert('무승부');
}
}
중복검사 부분은 그냥 공백이 아니면 리턴 하는 식으로 훨씬 짧게 짤 수 있었을 것이고...
배열을 사용한 이유가 연산을 적게하기 위함이었는데, 돌을 하나 둘 때마다 361번씩 게임판을 다시 그려서 HTML에 입력하는, 또다시 배보다 배꼽이 큰 상황이 나와버린 것이다. 가볍게 만들자는 마음으로 시작했는데 클릭마다 화면 전체 렌더링을(요즘 리액트 공부한다고 이런 말도 다 쓴다...) 다시하는 상황이니 참 미숙했다고 하겠다. 배열값을 바꾸면서 해당 셀에 innerHTML 한 번만 했으면 되지 않았을까... 저때는 2차원 배열에 심취해서 배열값을 그대로 출력하는 데에만 정신이 팔려있던 것 같다.
그리고 한가지 문제가 더 있다면 렌주룰 구현에 대한 것이었는데, 생각보다 3,3이나 4,4에 대한 룰 구현이 쉽지 않았다. 한쪽이 막혀있는 상황이나 5개의 빈 칸을 양쪽에서 미리 막고있는 상황 등등 그냥 돌만 세면 끝이라고 생각했던 게 여러가지 케이스를 적용하면서 자꾸만 반례 케이스가 튀어나왔다. 결국 저때 당시엔 세부 룰 구현을 포기했었는데, 지금 하고있는 팀 프로젝트가 끝나면 개인프로젝트로 소켓을 이용해 서로 다른 PC에서 사람끼리 두는 오목을 구현해볼까 한다.
여하튼... 오늘 적는 코딩 왕초보의 오목 만들기는 여기까지!