현재 차례 : 흑돌
 

매일 일하느라 고생 많으시죠?  오늘은 오목 한 판 두면서 잠깐 쉬어가세요. 하루 종일 업무하고 회의하고 개발하다 보면 머리도 지치고 집중력도 떨어질 때가 있습니다. 잠깐 쉬어가는 것도 업무의 일부라고 생각합니다. 오목 게임은 왜 아직도 재미있을까요? ㅎㅎ별도 설치 없이 브라우저에서 바로 실행되는 HTML 오목 게임입니다. 동료와 번갈아 즐겨도 좋고, 혼자 연습하면서 시간을 보내기에도 충분히 재미있습니다.


<style> #omok-info{ font-weight:bold; margin:10px 0; } #omok-board{ display:grid; grid-template-columns:repeat(15,34px); grid-template-rows:repeat(15,34px); width:max-content; margin:20px auto; background:#D8A94F; padding:10px; border:3px solid #8A5A22; } .omok-cell{ width:34px; height:34px; border-right:1px solid #6b4718; border-bottom:1px solid #6b4718; position:relative; } .omok-cell.omok-black::after, .omok-cell.omok-white::after{ content:""; position:absolute; left:4px; top:4px; width:25px; height:25px; border-radius:50%; } .omok-cell.omok-black::after{ background:#111; } .omok-cell.omok-white::after{ background:#fff; border:1px solid #aaa; } .omok-cell.omok-win::after{ outline:3px solid gold; } </style> <div style="text-align:center;"> <div id="omok-info"> 현재 차례 : 흑돌 </div> <button id="omok-undo"> 한 수 무르기 </button> <button id="omok-reset"> 새 게임 </button> </div> <div id="omok-board"></div> <script> const OMOK_SIZE = 15; const OMOK_STORAGE_KEY = "gomoku_blog_version"; let omokBoard; let omokCurrentPlayer; let omokGameOver; let omokHistory; let omokWinCells; /* 게임 초기화 */ function omokInitGame(){ omokBoard = Array.from( { length: OMOK_SIZE }, () => Array(OMOK_SIZE).fill("") ); omokCurrentPlayer = "black"; omokGameOver = false; omokHistory = []; omokWinCells = []; omokSaveGame(); } /* 화면 출력 */ function omokRender(){ const boardDiv = document.getElementById("omok-board"); boardDiv.innerHTML = ""; document.getElementById("omok-info").textContent = omokGameOver ? (omokCurrentPlayer === "black" ? "흑돌 승리" : "백돌 승리") : ("현재 차례 : " + (omokCurrentPlayer === "black" ? "흑돌" : "백돌")); for(let y=0; y<OMOK_SIZE; y++){ for(let x=0; x<OMOK_SIZE; x++){ const c = document.createElement("div"); c.className = "omok-cell"; if(omokBoard[y][x]){ c.classList.add("omok-" + omokBoard[y][x]); } if(omokIsWinCell(x,y)){ c.classList.add("omok-win"); } c.onclick = () => omokPlaceStone(x,y); boardDiv.appendChild(c); } } } /* 돌 놓기 */ function omokPlaceStone(x,y){ if(omokGameOver) return; if(omokBoard[y][x] !== "") return; omokBoard[y][x] = omokCurrentPlayer; omokHistory.push({x, y, p: omokCurrentPlayer}); if(omokCheckWin(x,y,omokCurrentPlayer)){ omokGameOver = true; omokSaveGame(); omokRender(); return; } omokCurrentPlayer = omokCurrentPlayer === "black" ? "white" : "black"; omokSaveGame(); omokRender(); } /* 승리 판정 */ function omokCheckWin(x,y,p){ const dirs = [ [1,0], [0,1], [1,1], [1,-1] ]; for(const [dx,dy] of dirs){ let cells = [{x,y}]; cells = cells.concat(omokScan(x,y,dx,dy,p)); cells = cells.concat(omokScan(x,y,-dx,-dy,p)); if(cells.length >= 5){ omokWinCells = cells.slice(0,5); return true; } } return false; } /* 연속 돌 탐색 */ function omokScan(x,y,dx,dy,p){ const arr = []; let nx = x + dx; let ny = y + dy; while( nx >= 0 && ny >= 0 && nx < OMOK_SIZE && ny < OMOK_SIZE && omokBoard[ny][nx] === p ){ arr.push({x:nx,y:ny}); nx += dx; ny += dy; } return arr; } /* 승리 돌 확인 */ function omokIsWinCell(x,y){ return omokWinCells.some(c=>c.x===x && c.y===y); } /* 게임 저장 */ function omokSaveGame(){ localStorage.setItem( OMOK_STORAGE_KEY, JSON.stringify({ board:omokBoard, currentPlayer:omokCurrentPlayer, gameOver:omokGameOver, history:omokHistory, winCells:omokWinCells }) ); } /* 게임 복원 */ function omokLoadGame(){ const s = localStorage.getItem(OMOK_STORAGE_KEY); if(!s){ omokInitGame(); return; } const d = JSON.parse(s); omokBoard = d.board; omokCurrentPlayer = d.currentPlayer; omokGameOver = d.gameOver; omokHistory = d.history; omokWinCells = d.winCells || []; } /* 한 수 무르기 */ document.getElementById("omok-undo").onclick = function(){ if(omokHistory.length === 0) return; const last = omokHistory.pop(); omokBoard[last.y][last.x] = ""; omokGameOver = false; omokWinCells = []; omokCurrentPlayer = last.p; omokSaveGame(); omokRender(); }; /* 새 게임 */ document.getElementById("omok-reset").onclick = function(){ localStorage.removeItem(OMOK_STORAGE_KEY); omokInitGame(); omokRender(); }; /* 프로그램 시작 */ omokLoadGame(); omokRender(); </script>

 

오목 게임의 변화


1990년대

간단한 PC 게임으로 많이 즐겼습니다. 혼자 플레이하거나 친구와 번갈아 두는 방식이 대부분이었습니다.

 

2000년대

인터넷 대국이 활성화되면서 전국의 사용자들과 실시간으로 대결할 수 있게 되었습니다.

 

2010년대

스마트폰 보급 이후 모바일 오목 게임이 빠르게 늘었고, 짧은 시간에도 부담 없이 즐기는 게임이 되었습니다.

 

2020년대 이후

HTML5와 JavaScript 기반 웹 게임이 많아지면서 설치 없이 바로 실행되는 방식이 일반적인 형태가 되었습니다.


주요코딩 포인트 설명

  • initGame()
    게임을 처음 시작하거나 새 게임을 눌렀을 때 실행됩니다. 바둑판 배열과 플레이어 정보, 착수 기록 등을 모두 초기화
  • render()
    현재 게임 상태를 화면에 다시 그립니다. 바둑판과 돌의 위치, 현재 차례, 승리 표시까지 한 번에 갱신
  • placeStone(x, y)
    사용자가 클릭한 위치에 돌을 놓는 핵심 함수입니다. 착수 기록 저장, 승리 검사, 플레이어 교체까지 함께 처리
  • checkWin(x, y, player)
    가로, 세로, 대각선 네 방향을 검사하여 같은 색 돌이 다섯 개 이상 연결되었는지 확인
  • scan(x, y, dx, dy, player)
    특정 방향으로 같은 색 돌이 얼마나 연속되는지 탐색하는 함수입니다. 승리 판정의 핵심 로직으로 사용
  • isWinCell(x, y)
    현재 좌표가 승리한 다섯 개의 돌인지 확인하여 화면에서 강조 표시할 때 사용
  • saveGame()
    현재 게임 상태를 LocalStorage에 저장합니다. 새로고침 후에도 이어서 플레이할 수 있습니다.
  • loadGame()
    저장된 게임 데이터가 있는지 확인한 뒤 이전 진행 상황을 자동으로 복원
  • Undo(한 수 무르기)
    history 배열을 이용하여 마지막 착수를 취소하고 직전 상태로 게임을 되돌림
  • Reset(새 게임)
    저장된 데이터를 삭제한 뒤 게임을 완전히 새로 시작

 

마무리

잠깐 쉬는 시간에 부담 없이 즐기기 좋은 게임이 바로 오목입니다. 규칙은 간단하지만 한 수 차이로 승부가 갈리는 만큼 생각보다 몰입감도 높습니다. 직접 플레이도 해보시고, 코드도 함께 살펴보면서 HTML과 JavaScript 게임 제작 방식도 경험해 보세요.

반응형
블로그 추천 글
블로그 운영자 프로필 이미지

촌놈 개발자 이야기

개발을 업으로 삼아 살아가며 관심 있는 기술 정보를 정리하고 공유. 프로그래밍과 IT 분야의 다양한 이야기부터 일상 속 소소한 생각까지, 꾸준히 기록하며 함께 성장해 나가는 개발자 일기