Spaces:
Running
Running
| <html lang="ko"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>์ค๋ชฉ ๊ฒ์</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| body { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| min-height: 100vh; | |
| background-color: #f5f5f5; | |
| font-family: 'Arial', sans-serif; | |
| } | |
| .board { | |
| display: grid; | |
| grid-template-columns: repeat(15, 40px); | |
| grid-template-rows: repeat(15, 40px); | |
| background-color: #e6c88c; | |
| border: 2px solid #8b4513; | |
| position: relative; | |
| box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2); | |
| } | |
| .cell { | |
| width: 40px; | |
| height: 40px; | |
| border: 1px solid #8b4513; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| cursor: pointer; | |
| } | |
| .cell:hover { | |
| background-color: rgba(255, 255, 255, 0.2); | |
| } | |
| .stone { | |
| width: 30px; | |
| height: 30px; | |
| border-radius: 50%; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .black { | |
| background: radial-gradient(circle at 30% 30%, #666, #000); | |
| box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); | |
| } | |
| .white { | |
| background: radial-gradient(circle at 30% 30%, #fff, #ccc); | |
| box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3); | |
| } | |
| .star-point { | |
| position: absolute; | |
| width: 4px; | |
| height: 4px; | |
| background-color: #000; | |
| border-radius: 50%; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| } | |
| .controls { | |
| margin-top: 20px; | |
| display: flex; | |
| gap: 15px; | |
| } | |
| .status { | |
| margin-top: 20px; | |
| font-size: 1.2rem; | |
| font-weight: bold; | |
| color: #333; | |
| padding: 10px 20px; | |
| background-color: #fff; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); | |
| } | |
| button { | |
| padding: 10px 20px; | |
| background-color: #4a7c59; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| font-weight: bold; | |
| transition: all 0.3s; | |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); | |
| } | |
| button:hover { | |
| background-color: #3a6a49; | |
| transform: translateY(-2px); | |
| } | |
| .win-line { | |
| position: absolute; | |
| background-color: red; | |
| z-index: 0; | |
| transform-origin: 0 0; | |
| } | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| z-index: 100; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .modal-content { | |
| background-color: white; | |
| padding: 30px; | |
| border-radius: 10px; | |
| text-align: center; | |
| box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); | |
| max-width: 400px; | |
| width: 80%; | |
| } | |
| .modal h2 { | |
| margin-top: 0; | |
| color: #4a7c59; | |
| } | |
| .modal button { | |
| margin-top: 20px; | |
| background-color: #4a7c59; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1 class="text-3xl font-bold mb-6 text-gray-800">์ค๋ชฉ ๊ฒ์</h1> | |
| <div class="status">ํ๋ ์ฐจ๋ก</div> | |
| <div class="board" id="board"></div> | |
| <div class="controls"> | |
| <button id="restart">๊ฒ์ ๋ค์ํ๊ธฐ</button> | |
| <button id="undo">๋ฌด๋ฅด๊ธฐ</button> | |
| </div> | |
| <div class="modal" id="modal"> | |
| <div class="modal-content"> | |
| <h2 id="modal-title">๊ฒ์ ์ข ๋ฃ</h2> | |
| <p id="modal-message">ํ๋์ด ์น๋ฆฌํ์ต๋๋ค!</p> | |
| <button id="modal-button">ํ์ธ</button> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const board = document.getElementById('board'); | |
| const statusDisplay = document.querySelector('.status'); | |
| const restartButton = document.getElementById('restart'); | |
| const undoButton = document.getElementById('undo'); | |
| const modal = document.getElementById('modal'); | |
| const modalTitle = document.getElementById('modal-title'); | |
| const modalMessage = document.getElementById('modal-message'); | |
| const modalButton = document.getElementById('modal-button'); | |
| // ๊ฒ์ ์ํ | |
| let gameState = Array(15).fill().map(() => Array(15).fill(null)); | |
| let currentPlayer = 'black'; | |
| let gameActive = true; | |
| let moveHistory = []; | |
| // ์น๋ฆฌ ๋ผ์ธ์ ์ ์ฅํ ๋ณ์ | |
| let winLine = null; | |
| // ๋ณด๋ ์ด๊ธฐํ | |
| function initializeBoard() { | |
| board.innerHTML = ''; | |
| // ๋ณด๋ ์์ฑ | |
| for (let i = 0; i < 15; i++) { | |
| for (let j = 0; j < 15; j++) { | |
| const cell = document.createElement('div'); | |
| cell.classList.add('cell'); | |
| cell.dataset.row = i; | |
| cell.dataset.col = j; | |
| // ๋ณ ํ์ (์ค๋ชฉํ์ ํน์ ์์น์) | |
| if ((i === 3 || i === 7 || i === 11) && (j === 3 || j === 7 || j === 11)) { | |
| const star = document.createElement('div'); | |
| star.classList.add('star-point'); | |
| cell.appendChild(star); | |
| } | |
| cell.addEventListener('click', () => handleCellClick(i, j)); | |
| board.appendChild(cell); | |
| } | |
| } | |
| // ๊ธฐ์กด ์น๋ฆฌ ๋ผ์ธ ์ ๊ฑฐ | |
| if (winLine) { | |
| winLine.remove(); | |
| winLine = null; | |
| } | |
| } | |
| // ์ ํด๋ฆญ ์ฒ๋ฆฌ | |
| function handleCellClick(row, col) { | |
| if (!gameActive || gameState[row][col] !== null) return; | |
| // ๋ ๋๊ธฐ | |
| placeStone(row, col, currentPlayer); | |
| // ๊ฒ์ ์ํ ์ ๋ฐ์ดํธ | |
| gameState[row][col] = currentPlayer; | |
| moveHistory.push({row, col, player: currentPlayer}); | |
| // ์น๋ฆฌ ํ์ธ | |
| if (checkWin(row, col)) { | |
| gameActive = false; | |
| showWinner(currentPlayer); | |
| return; | |
| } | |
| // ๋ฌด์น๋ถ ํ์ธ | |
| if (checkDraw()) { | |
| gameActive = false; | |
| showDraw(); | |
| return; | |
| } | |
| // ํ๋ ์ด์ด ๋ณ๊ฒฝ | |
| currentPlayer = currentPlayer === 'black' ? 'white' : 'black'; | |
| updateStatus(); | |
| } | |
| // ๋ ๋๊ธฐ | |
| function placeStone(row, col, player) { | |
| const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); | |
| const stone = document.createElement('div'); | |
| stone.classList.add('stone', player); | |
| cell.appendChild(stone); | |
| } | |
| // ์น๋ฆฌ ํ์ธ | |
| function checkWin(row, col) { | |
| const directions = [ | |
| [0, 1], // ๊ฐ๋ก | |
| [1, 0], // ์ธ๋ก | |
| [1, 1], // ๋๊ฐ์ โ | |
| [1, -1] // ๋๊ฐ์ โ | |
| ]; | |
| for (const [dx, dy] of directions) { | |
| let count = 1; | |
| // ํ ๋ฐฉํฅ์ผ๋ก ๊ฒ์ฌ | |
| count += countStones(row, col, dx, dy); | |
| // ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ๊ฒ์ฌ | |
| count += countStones(row, col, -dx, -dy); | |
| if (count >= 5) { | |
| drawWinLine(row, col, dx, dy); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // ์ฐ์๋ ๋ ๊ฐ์ ์ธ๊ธฐ | |
| function countStones(row, col, dx, dy) { | |
| const player = gameState[row][col]; | |
| let count = 0; | |
| let r = row + dx; | |
| let c = col + dy; | |
| while (r >= 0 && r < 15 && c >= 0 && c < 15 && gameState[r][c] === player) { | |
| count++; | |
| r += dx; | |
| c += dy; | |
| } | |
| return count; | |
| } | |
| // ์น๋ฆฌ ๋ผ์ธ ๊ทธ๋ฆฌ๊ธฐ | |
| function drawWinLine(row, col, dx, dy) { | |
| // ๋จผ์ ์น๋ฆฌํ ๋์ ์์น๋ฅผ ์ฐพ๊ธฐ ์ํด ์์ชฝ์ผ๋ก ๊ฒ์ฌ | |
| let startRow = row; | |
| let startCol = col; | |
| let endRow = row; | |
| let endCol = col; | |
| const player = gameState[row][col]; | |
| // ์์์ ์ฐพ๊ธฐ (๋ฐ๋ ๋ฐฉํฅ) | |
| while (startRow - dx >= 0 && startRow - dx < 15 && | |
| startCol - dy >= 0 && startCol - dy < 15 && | |
| gameState[startRow - dx][startCol - dy] === player) { | |
| startRow -= dx; | |
| startCol -= dy; | |
| } | |
| // ๋์ ์ฐพ๊ธฐ (์ฃผ์ด์ง ๋ฐฉํฅ) | |
| while (endRow + dx >= 0 && endRow + dx < 15 && | |
| endCol + dy >= 0 && endCol + dy < 15 && | |
| gameState[endRow + dx][endCol + dy] === player) { | |
| endRow += dx; | |
| endCol += dy; | |
| } | |
| // ์์์ ๊ณผ ๋์ ์ ์ค์ ์ขํ ๊ณ์ฐ | |
| const startCell = document.querySelector(`.cell[data-row="${startRow}"][data-col="${startCol}"]`); | |
| const endCell = document.querySelector(`.cell[data-row="${endRow}"][data-col="${endCol}"]`); | |
| const startRect = startCell.getBoundingClientRect(); | |
| const endRect = endCell.getBoundingClientRect(); | |
| const boardRect = board.getBoundingClientRect(); | |
| // ๋ณด๋ ์๋ ์ขํ ๊ณ์ฐ | |
| const startX = startRect.left + startRect.width / 2 - boardRect.left; | |
| const startY = startRect.top + startRect.height / 2 - boardRect.top; | |
| const endX = endRect.left + endRect.width / 2 - boardRect.left; | |
| const endY = endRect.top + endRect.height / 2 - boardRect.top; | |
| // ๋ผ์ธ ๊ธธ์ด ๊ณ์ฐ | |
| const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); | |
| // ๋ผ์ธ ๊ฐ๋ ๊ณ์ฐ | |
| const angle = Math.atan2(endY - startY, endX - startX); | |
| // ๋ผ์ธ ์์ฑ | |
| winLine = document.createElement('div'); | |
| winLine.classList.add('win-line'); | |
| winLine.style.width = `${length}px`; | |
| winLine.style.height = '3px'; | |
| winLine.style.left = `${startX}px`; | |
| winLine.style.top = `${startY}px`; | |
| winLine.style.transform = `rotate(${angle}rad)`; | |
| board.appendChild(winLine); | |
| } | |
| // ๋ฌด์น๋ถ ํ์ธ | |
| function checkDraw() { | |
| return gameState.every(row => row.every(cell => cell !== null)); | |
| } | |
| // ์น์ ํ์ | |
| function showWinner(player) { | |
| const winner = player === 'black' ? 'ํ๋' : '๋ฐฑ๋'; | |
| modalTitle.textContent = '๊ฒ์ ์ข ๋ฃ'; | |
| modalMessage.textContent = `${winner}์ด(๊ฐ) ์น๋ฆฌํ์ต๋๋ค!`; | |
| modal.style.display = 'flex'; | |
| } | |
| // ๋ฌด์น๋ถ ํ์ | |
| function showDraw() { | |
| modalTitle.textContent = '๊ฒ์ ์ข ๋ฃ'; | |
| modalMessage.textContent = '๋ฌด์น๋ถ์ ๋๋ค!'; | |
| modal.style.display = 'flex'; | |
| } | |
| // ์ํ ์ ๋ฐ์ดํธ | |
| function updateStatus() { | |
| const player = currentPlayer === 'black' ? 'ํ๋' : '๋ฐฑ๋'; | |
| statusDisplay.textContent = `${player} ์ฐจ๋ก`; | |
| } | |
| // ๊ฒ์ ๋ฆฌ์ | |
| function resetGame() { | |
| gameState = Array(15).fill().map(() => Array(15).fill(null)); | |
| currentPlayer = 'black'; | |
| gameActive = true; | |
| moveHistory = []; | |
| initializeBoard(); | |
| updateStatus(); | |
| modal.style.display = 'none'; | |
| } | |
| // ๋ฌด๋ฅด๊ธฐ ๊ธฐ๋ฅ | |
| function undoMove() { | |
| if (moveHistory.length === 0 || !gameActive) return; | |
| const lastMove = moveHistory.pop(); | |
| gameState[lastMove.row][lastMove.col] = null; | |
| const cell = document.querySelector(`.cell[data-row="${lastMove.row}"][data-col="${lastMove.col}"]`); | |
| cell.innerHTML = ''; | |
| if ((lastMove.row === 3 || lastMove.row === 7 || lastMove.row === 11) && | |
| (lastMove.col === 3 || lastMove.col === 7 || lastMove.col === 11)) { | |
| const star = document.createElement('div'); | |
| star.classList.add('star-point'); | |
| cell.appendChild(star); | |
| } | |
| currentPlayer = lastMove.player; | |
| updateStatus(); | |
| // ์น๋ฆฌ ๋ผ์ธ ์ ๊ฑฐ | |
| if (winLine) { | |
| winLine.remove(); | |
| winLine = null; | |
| } | |
| } | |
| // ์ด๋ฒคํธ ๋ฆฌ์ค๋ | |
| restartButton.addEventListener('click', resetGame); | |
| undoButton.addEventListener('click', undoMove); | |
| modalButton.addEventListener('click', () => { | |
| modal.style.display = 'none'; | |
| resetGame(); | |
| }); | |
| // ๊ฒ์ ์์ | |
| initializeBoard(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - ๐งฌ <a href="https://enzostvs-deepsite.hf.space?remix=aicoding101/g-g" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |