현재 차례 : 흑돌
매일 일하느라 고생 많으시죠? 오늘은 오목 한 판 두면서 잠깐 쉬어가세요. 하루 종일 업무하고 회의하고 개발하다 보면 머리도 지치고 집중력도 떨어질 때가 있습니다. 잠깐 쉬어가는 것도 업무의 일부라고 생각합니다. 오목 게임은 왜 아직도 재미있을까요? ㅎㅎ별도 설치 없이 브라우저에서 바로 실행되는 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 게임 제작 방식도 경험해 보세요.
반응형
'개발 이야기 > 프로그램' 카테고리의 다른 글
| 온라인에서 이미지 크기 조절과 변환을 간편하게 하기(바로사용) (2) | 2026.06.13 |
|---|
블로그 추천 글