g-g / index.html
aicoding101's picture
Add 3 files
2e007a8 verified
<!DOCTYPE html>
<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>