본문 바로가기
Javascript 프로젝트/기능별 페이지

[기능별 페이지] omok

by cogito21_js 2024. 6. 10.
반응형

물론입니다. 각 파일의 코드에 대한 상세 설명을 제공하겠습니다.

HTML (index.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="viewport" content="width=device-width, initial-scale=1.0">
    <title>Omok Game</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <header>
        <!-- 기존 header 내용 -->
    </header>
    <main>
        <div class="game-container">
            <h1>Omok Game</h1>
            <p id="turnIndicator"></p> <!-- 턴 표시 -->
            <div class="board" id="board"></div>
            <p id="message"></p>
            <button id="resetButton">Reset Game</button>
        </div>
    </main>
    <div id="modal" class="modal">
        <div class="modal-content">
            <span class="close-button" id="closeButton">&times;</span>
            <p id="modalMessage"></p>
        </div>
    </div>
    <footer>
        <!-- 기존 footer 내용 -->
    </footer>
    <script src="script.js"></script>
</body>
</html>

설명

  1. 기본 HTML 구조:

    • <!DOCTYPE html>: 문서가 HTML5로 작성되었음을 명시합니다.
    • <html lang="en">, <head>, <meta charset="viewport" content="width=device-width, initial-scale=1.0">: HTML 문서의 기본 설정을 정의합니다.
    • <title>: 페이지의 제목을 설정합니다.
    • <link rel="stylesheet" href="styles.css">: CSS 파일을 연결합니다.
  2. 메인 콘텐츠:

    • <header><footer>: 기존의 헤더와 푸터 내용을 유지합니다.
    • <main>: 페이지의 주요 콘텐츠를 감싸는 태그입니다.
    • <div class="game-container">: 게임 보드와 컨트롤 버튼을 감싸는 컨테이너입니다.
    • <h1>Omok Game</h1>: 게임 제목을 표시합니다.
    • <p id="turnIndicator"></p>: 현재 턴을 표시하는 요소입니다.
    • <div class="board" id="board"></div>: 오목 게임 보드를 표시하는 요소입니다.
    • <p id="message"></p>: 메시지를 표시하는 요소입니다.
    • <button id="resetButton">Reset Game</button>: 게임을 리셋하는 버튼입니다.
  3. 모달 창:

    • <div id="modal" class="modal">: 모달 창의 기본 컨테이너입니다.
    • <div class="modal-content">: 모달 창의 콘텐츠를 감싸는 요소입니다.
    • <span class="close-button" id="closeButton">&times;</span>: 모달 창을 닫는 버튼입니다.
    • <p id="modalMessage"></p>: 모달 창 내에 메시지를 표시하는 요소입니다.
  4. 스크립트 파일 연결:

    • <script src="script.js"></script>: JavaScript 파일을 연결합니다.

CSS (styles.css)

/* 기본 스타일 설정 */
body {
    font-family: 'Arial', sans-serif;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin: 0;
    background-color: #f4f4f9;
}

/* main 태그 스타일 설정 */
main {
    display: flex;
    justify-content: center;
    align-items: center;
    flex: 1;
    padding: 20px;
}

/* 게임 컨테이너 스타일 설정 */
.game-container {
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    padding: 20px;
    text-align: center;
}

/* 보드 스타일 설정 */
.board {
    display: grid;
    grid-template-columns: repeat(15, 30px);
    grid-template-rows: repeat(15, 30px);
    gap: 1px;
    margin-top: 20px;
    background-color: #d2b48c; /* 황토색 */
    border: 2px solid #000; /* 검정색 테두리 */
}

/* 셀 스타일 설정 */
.cell {
    width: 30px;
    height: 30px;
    background-color: #d2b48c; /* 황토색 */
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border: 1px solid #000; /* 셀의 선을 검정색으로 설정 */
}

/* 바둑알 스타일 설정 */
.stone {
    width: 80%;
    height: 80%;
    border-radius: 50%;
}

.black {
    background-color: black;
}

.white {
    background-color: white;
    border: 1px solid #fff; /* 백색 돌에 흰색 테두리를 추가 */
}

/* 턴 표시 스타일 설정 */
#turnIndicator {
    margin-top: 10px;
    font-size: 18px;
    font-weight: bold;
}

/* 버튼 스타일 설정 */
button {
    padding: 10px 20px;
    font-size: 16px;
    cursor: pointer;
    margin: 5px;
    border: none;
    border-radius: 5px;
    background-color: #6200ea;
    color: white;
    transition: background-color 0.3s ease;
}

button:hover {
    background-color: #3700b3;
}

/* 모달 스타일 설정 */
.modal {
    display: none; /* 기본적으로 모달을 숨깁니다 */
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgba(0, 0, 0, 0.5);
    justify-content: center;
    align-items: center;
}

.modal-content {
    background-color: #fff;
    margin: auto;
    padding: 20px;
    border: 1px solid #888;
    width: 80%;
    max-width: 400px;
    border-radius: 10px;
    text-align: center;
}

.close-button {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

.close-button:hover,
.close-button:focus {
    color: black;
    text-decoration: none;
    cursor: pointer;
}

설명

  1. body 스타일:

    • font-family: 기본 폰트를 'Arial'로 설정합니다.
    • display: flex: Flexbox 레이아웃을 사용합니다.
    • flex-direction: column: Flexbox 방향을 열 방향으로 설정합니다.
    • min-height: 100vh: 최소 높이를 뷰포트의 100%로 설정하여 전체 화면을 채우도록 합니다.
    • margin: 0: 기본 마진을 제거합니다.
    • background-color: #f4f4f9: 배경색을 연한 회색으로 설정합니다.
  2. main 스타일:

    • display: flex: Flexbox 레이아웃을 사용합니다.
    • justify-content: center: 가로 방향으로 중앙 정렬합니다.
    • align-items: center: 세로 방향으로 중앙 정렬합니다.
    • flex: 1: Flexbox의 남은 공간을 모두 차지하도록 설정합니다.
    • padding: 20px: 안쪽 여백을 20px로 설정합니다.
  3. game-container 스타일:

    • background: #fff: 배경색을 흰색으로 설정합니다.
    • border-radius: 10px: 모서리를 둥글게 만듭니다.
    • box-shadow: 그림자를 추가하여 입체감을 줍니다.
    • padding: 20px: 안쪽 여백을 20px로 설정합니다.
    • text-align: center: 텍스트를 중앙 정렬합니다.
  4. board 스타일:

    • display: grid: 그리드 레이아웃을 사용합니다.
    • grid-template-columns: repeat(15, 30px): 15개의 30px 너비의 열을 만듭니다.
    • grid-template-rows: repeat(15, 30px): 15개의 30px 높이의 행을 만듭니다.
    • gap: 1px: 셀 사이에 1px 간격을 추가합니다.
    • margin-top: 20px: 보드의 위쪽 여백을 20px로 설정합니다.
    • background-color: #d2b48c: 바둑판의 배경색을 황토색으로 설정합니다.
    • border: 2px solid #000: 바둑판의 테두리를 검정색으로 설정합니다.
  5. cell 스타일:

    • width: 30px: 셀의 너비를 30px로 설정합니다.
    • height: 30px: 셀의 높이를 30px로 설정합니다.
    • `background-color: #d2b48

c`: 셀의 배경색을 황토색으로 설정합니다.

  • display: flex: Flexbox 레이아웃을 사용합니다.
  • align-items: center: 세로 방향으로 중앙 정렬합니다.
  • justify-content: center: 가로 방향으로 중앙 정렬합니다.
  • cursor: pointer: 커서를 포인터로 변경하여 클릭 가능함을 나타냅니다.
  • border: 1px solid #000: 셀의 선을 검정색으로 설정합니다.
  1. stone 스타일:

    • width: 80%: 바둑알의 너비를 셀의 80%로 설정합니다.
    • height: 80%: 바둑알의 높이를 셀의 80%로 설정합니다.
    • border-radius: 50%: 바둑알을 원형으로 설정합니다.
  2. black:

    • background-color: black: 검정 바둑알의 배경색을 검정색으로 설정합니다.
  3. white:

    • background-color: white: 백색 바둑알의 배경색을 흰색으로 설정합니다.
    • border: 1px solid #fff: 백색 바둑알의 테두리를 흰색으로 설정합니다.
  4. turnIndicator:

    • margin-top: 10px: 위쪽 여백을 10px로 설정합니다.
    • font-size: 18px: 폰트 크기를 18px로 설정합니다.
    • font-weight: bold: 폰트를 굵게 설정합니다.
  5. button:

    • padding: 10px 20px: 안쪽 여백을 10px 20px로 설정합니다.
    • font-size: 16px: 폰트 크기를 16px로 설정합니다.
    • cursor: pointer: 커서를 포인터로 변경하여 클릭 가능함을 나타냅니다.
    • margin: 5px: 모든 방향에 5px의 여백을 설정합니다.
    • border: none: 테두리를 제거합니다.
    • border-radius: 5px: 모서리를 둥글게 만듭니다.
    • background-color: #6200ea: 배경색을 보라색으로 설정합니다.
    • color: white: 텍스트 색상을 흰색으로 설정합니다.
    • transition: background-color 0.3s ease: 배경색 전환을 부드럽게 만듭니다.
  6. button:hover:

    • background-color: #3700b3: 버튼에 마우스를 올렸을 때 배경색을 더 어두운 보라색으로 변경합니다.
  7. modal:

    • display: none: 기본적으로 모달을 숨깁니다.
    • position: fixed: 고정 위치를 사용합니다.
    • z-index: 1: 다른 요소들보다 앞에 위치하도록 설정합니다.
    • left: 0, top: 0, width: 100%, height: 100%: 모달 창이 화면 전체를 덮도록 설정합니다.
    • overflow: auto: 내용이 넘칠 경우 스크롤을 허용합니다.
    • background-color: rgba(0, 0, 0, 0.5): 배경색을 반투명 검정색으로 설정합니다.
    • justify-content: center: 가로 방향으로 중앙 정렬합니다.
    • align-items: center: 세로 방향으로 중앙 정렬합니다.
  8. modal-content:

    • background-color: #fff: 배경색을 흰색으로 설정합니다.
    • margin: auto: 모달 콘텐츠를 자동으로 정렬합니다.
    • padding: 20px: 안쪽 여백을 20px로 설정합니다.
    • border: 1px solid #888: 테두리를 회색으로 설정합니다.
    • width: 80%: 너비를 화면의 80%로 설정합니다.
    • max-width: 400px: 최대 너비를 400px로 설정합니다.
    • border-radius: 10px: 모서리를 둥글게 만듭니다.
    • text-align: center: 텍스트를 중앙 정렬합니다.
  9. close-button:

    • color: #aaa: 버튼의 색상을 회색으로 설정합니다.
    • float: right: 버튼을 오른쪽으로 정렬합니다.
    • font-size: 28px: 폰트 크기를 28px로 설정합니다.
    • font-weight: bold: 폰트를 굵게 설정합니다.
  10. close-button:hover, .close-button:focus:

    • color: black: 마우스를 올리거나 포커스를 맞췄을 때 버튼의 색상을 검정색으로 변경합니다.
    • text-decoration: none: 텍스트 장식을 제거합니다.
    • cursor: pointer: 커서를 포인터로 변경하여 클릭 가능함을 나타냅니다.

JavaScript (script.js)

// DOM 요소를 가져옴
const boardElement = document.getElementById('board');
const turnIndicator = document.getElementById('turnIndicator');
const messageElement = document.getElementById('message');
const resetButton = document.getElementById('resetButton');
const modal = document.getElementById('modal');
const modalMessage = document.getElementById('modalMessage');
const closeButton = document.getElementById('closeButton');

// 게임 상태 변수
let board;
let currentPlayer;
const boardSize = 15;

// 이벤트 리스너 설정
resetButton.addEventListener('click', initializeGame);
closeButton.addEventListener('click', closeModal);
window.addEventListener('click', outsideClick);

// 게임 초기화 함수
function initializeGame() {
    board = Array.from({ length: boardSize }, () => Array(boardSize).fill(null));
    currentPlayer = 'black';
    turnIndicator.textContent = `${currentPlayer.toUpperCase()}'s turn`;
    messageElement.textContent = '';
    renderBoard();
}

// 보드 렌더링 함수
function renderBoard() {
    boardElement.innerHTML = '';
    board.forEach((row, rowIndex) => {
        row.forEach((cell, colIndex) => {
            const cellElement = document.createElement('div');
            cellElement.classList.add('cell');
            cellElement.addEventListener('click', () => handleCellClick(rowIndex, colIndex));
            if (cell) {
                const stoneElement = document.createElement('div');
                stoneElement.classList.add('stone', cell);
                cellElement.appendChild(stoneElement);
            }
            boardElement.appendChild(cellElement);
        });
    });
}

// 셀 클릭 처리 함수
function handleCellClick(row, col) {
    if (board[row][col] || checkWinner()) return;
    board[row][col] = currentPlayer;
    renderBoard();
    if (checkWinner()) {
        showModal(`${currentPlayer.toUpperCase()} wins!`);
        messageElement.textContent = `${currentPlayer.toUpperCase()} wins!`;
    } else {
        currentPlayer = currentPlayer === 'black' ? 'white' : 'black';
        turnIndicator.textContent = `${currentPlayer.toUpperCase()}'s turn`;
    }
}

// 승리 조건 확인 함수
function checkWinner() {
    // 보드 전체를 순회하며 승리 조건을 확인합니다.
    for (let row = 0; row < boardSize; row++) {
        for (let col = 0; col < boardSize; col++) {
            const player = board[row][col];
            if (player && (
                checkDirection(row, col, 1, 0, player) || // 가로
                checkDirection(row, col, 0, 1, player) || // 세로
                checkDirection(row, col, 1, 1, player) || // 대각선 오른쪽 아래
                checkDirection(row, col, 1, -1, player)   // 대각선 왼쪽 아래
            )) {
                return true;
            }
        }
    }
    return false;
}

// 특정 방향으로 연속된 돌이 5개 있는지 확인하는 함수
function checkDirection(row, col, rowDir, colDir, player) {
    let count = 0;
    for (let i = 0; i < 5; i++) {
        const r = row + i * rowDir;
        const c = col + i * colDir;
        if (r >= 0 && r < boardSize && c >= 0 && c < boardSize && board[r][c] === player) {
            count++;
        } else {
            break;
        }
    }
    return count === 5;
}

// 모달 창을 여는 함수
function showModal(message) {
    modalMessage.textContent = message;
    modal.style.display = 'flex';
}

// 모달 창을 닫는 함수
function closeModal() {
    modal.style.display = 'none';
}

// 모달 창 바깥 클릭 시 닫기
function outsideClick(event) {
    if (event.target == modal) {
        closeModal();
    }
}

// 게임 초기화 실행
initializeGame();

설명

  1. DOM 요소를 가져옴:

    • boardElement, turnIndicator, messageElement,

    resetButton, modal, modalMessage, closeButton: HTML 요소를 가져와 변수에 저장합니다.

  2. 게임 상태 변수:

    • board: 바둑판 배열을 저장합니다.
    • currentPlayer: 현재 플레이어를 저장합니다.
    • boardSize: 바둑판의 크기를 설정합니다.
  3. 이벤트 리스너 설정:

    • resetButton.addEventListener('click', initializeGame): 리셋 버튼을 클릭하면 게임을 초기화합니다.
    • closeButton.addEventListener('click', closeModal): 모달 창의 닫기 버튼을 클릭하면 모달 창을 닫습니다.
    • window.addEventListener('click', outsideClick): 모달 창 바깥을 클릭하면 모달 창을 닫습니다.
  4. 게임 초기화 함수 (initializeGame):

    • board: 바둑판을 초기화합니다.
    • currentPlayer: 현재 플레이어를 'black'으로 설정합니다.
    • turnIndicator.textContent: 현재 턴을 표시합니다.
    • messageElement.textContent: 메시지를 초기화합니다.
    • renderBoard(): 바둑판을 렌더링합니다.
  5. 보드 렌더링 함수 (renderBoard):

    • boardElement.innerHTML: 바둑판을 비웁니다.
    • board.forEach: 바둑판 배열을 순회하며 각 셀을 생성합니다.
    • cellElement.addEventListener('click', () => handleCellClick(rowIndex, colIndex)): 셀을 클릭하면 handleCellClick 함수를 호출합니다.
    • cellElement.appendChild(stoneElement): 셀에 돌을 추가합니다.
    • boardElement.appendChild(cellElement): 바둑판에 셀을 추가합니다.
  6. 셀 클릭 처리 함수 (handleCellClick):

    • if (board[row][col] || checkWinner()) return: 셀이 이미 채워져 있거나 승자가 있으면 클릭을 무시합니다.
    • board[row][col] = currentPlayer: 현재 플레이어의 돌을 놓습니다.
    • renderBoard(): 바둑판을 다시 렌더링합니다.
    • if (checkWinner()): 승자가 있으면 모달 창을 열고 메시지를 설정합니다.
    • currentPlayer = currentPlayer === 'black' ? 'white' : 'black': 현재 플레이어를 전환합니다.
    • turnIndicator.textContent: 현재 턴을 표시합니다.
  7. 승리 조건 확인 함수 (checkWinner):

    • board.forEach: 바둑판 전체를 순회하며 승리 조건을 확인합니다.
    • checkDirection: 특정 방향으로 연속된 돌이 5개 있는지 확인합니다.
    • return true: 승자가 있으면 true를 반환합니다.
    • return false: 승자가 없으면 false를 반환합니다.
  8. 특정 방향으로 연속된 돌이 5개 있는지 확인하는 함수 (checkDirection):

    • for (let i = 0; i < 5; i++): 5개의 돌이 연속으로 있는지 확인합니다.
    • if (board[r][c] === player): 연속된 돌이 현재 플레이어의 돌인지 확인합니다.
    • count++: 연속된 돌의 개수를 증가시킵니다.
    • return count === 5: 연속된 돌의 개수가 5개이면 true를 반환합니다.
  9. 모달 창을 여는 함수 (showModal):

    • modalMessage.textContent: 모달 창의 메시지를 설정합니다.
    • modal.style.display = 'flex': 모달 창을 표시합니다.
  10. 모달 창을 닫는 함수 (closeModal):

    • modal.style.display = 'none': 모달 창을 숨깁니다.
  11. 모달 창 바깥 클릭 시 닫기 (outsideClick):

    • if (event.target == modal): 클릭한 대상이 모달 창이면
    • closeModal(): 모달 창을 닫습니다.
  12. 게임 초기화 실행:

    • initializeGame(): 게임을 초기화합니다.

이렇게 작성된 코드를 통해 오목 게임을 구현할 수 있으며, 승리 시 모달 창을 통해 승리 메시지를 표시할 수 있습니다.

반응형