ttr1 / script.js
yoon2566's picture
Rename code.js to script.js
a57f6cc verified
// DOM ์š”์†Œ ๊ฐ€์ ธ์˜ค๊ธฐ
const canvas = document.getElementById('tetris-board');
const context = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const startButton = document.getElementById('start-button');
// ๊ฒŒ์ž„ ๋ณด๋“œ ์„ค์ •
const ROWS = 20;
const COLS = 10;
const BLOCK_SIZE = 30;
context.canvas.width = COLS * BLOCK_SIZE;
context.canvas.height = ROWS * BLOCK_SIZE;
// ๋ธ”๋ก ์ƒ‰์ƒ ๋ฐ ๋ชจ์–‘ ์ •์˜
const COLORS = [null, '#FF0D72', '#0DC2FF', '#0DFF72', '#F538FF', '#FF8E0D', '#FFE138', '#3877FF'];
const SHAPES = [
[], // 0๋ฒˆ ์ธ๋ฑ์Šค๋Š” ๋น„์›Œ๋‘ 
[[1, 1, 1, 1]], // I
[[1, 1, 1], [0, 1, 0]], // T
[[1, 1, 1], [1, 0, 0]], // L
[[1, 1, 1], [0, 0, 1]], // J
[[1, 1, 0], [0, 1, 1]], // S
[[0, 1, 1], [1, 1, 0]], // Z
[[1, 1], [1, 1]] // O
];
let board = Array.from({ length: ROWS }, () => Array(COLS).fill(0));
let score = 0;
let gameOver = false;
let currentPiece;
let gameInterval;
// ํ”Œ๋ ˆ์ด์–ด(ํ˜„์žฌ ๋ธ”๋ก) ๊ฐ์ฒด
class Piece {
constructor(shape, color) {
this.shape = shape;
this.color = color;
this.x = Math.floor(COLS / 2) - Math.floor(shape[0].length / 2);
this.y = 0;
}
draw() {
context.fillStyle = this.color;
this.shape.forEach((row, y) => {
row.forEach((value, x) => {
if (value > 0) {
context.fillRect((this.x + x) * BLOCK_SIZE, (this.y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
});
});
}
move(dx, dy) {
this.x += dx;
this.y += dy;
}
}
// ์ƒˆ๋กœ์šด ๋ธ”๋ก ์ƒ์„ฑ
function generateNewPiece() {
const rand = Math.floor(Math.random() * (SHAPES.length - 1)) + 1;
const shape = SHAPES[rand];
const color = COLORS[rand];
return new Piece(shape, color);
}
// ๊ฒŒ์ž„ ๋ณด๋“œ ๊ทธ๋ฆฌ๊ธฐ
function drawBoard() {
board.forEach((row, y) => {
row.forEach((value, x) => {
if (value > 0) {
context.fillStyle = COLORS[value];
context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
});
});
}
// ์ „์ฒด ํ™”๋ฉด ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
drawBoard();
if (currentPiece) {
currentPiece.draw();
}
}
// ์ถฉ๋Œ ๊ฐ์ง€
function isValidMove(piece, dx = 0, dy = 0) {
return piece.shape.every((row, y) => {
return row.every((value, x) => {
if (value === 0) {
return true;
}
const newX = piece.x + x + dx;
const newY = piece.y + y + dy;
return newX >= 0 && newX < COLS && newY < ROWS && (board[newY] && board[newY][newX] === 0);
});
});
}
// ๋ธ”๋ก ํšŒ์ „
function rotatePiece() {
const shape = currentPiece.shape;
const newShape = shape[0].map((_, colIndex) => shape.map(row => row[colIndex]).reverse());
const originalX = currentPiece.x;
let offsetX = 1;
const tempPiece = { ...currentPiece, shape: newShape };
if (isValidMove(tempPiece)) {
currentPiece.shape = newShape;
return;
}
while (true) {
if (isValidMove(tempPiece, offsetX)) {
currentPiece.x += offsetX;
currentPiece.shape = newShape;
return;
}
if (offsetX > 0) {
if (offsetX > tempPiece.shape[0].length) break;
offsetX = -(offsetX + 1);
} else {
if (Math.abs(offsetX) > tempPiece.shape[0].length) break;
offsetX = -(offsetX - 1);
}
}
currentPiece.x = originalX;
}
// ๋ธ”๋ก์„ ๋ณด๋“œ์— ๊ณ ์ •
function lockPiece() {
currentPiece.shape.forEach((row, y) => {
row.forEach((value, x) => {
if (value > 0) {
const boardY = currentPiece.y + y;
const boardX = currentPiece.x + x;
if (boardY < ROWS) {
const colorIndex = COLORS.indexOf(currentPiece.color);
board[boardY][boardX] = colorIndex > 0 ? colorIndex : 1;
}
}
});
});
}
// ์ค„ ์ œ๊ฑฐ
function clearLines() {
let linesCleared = 0;
for (let y = ROWS - 1; y >= 0; y--) {
if (board[y].every(value => value > 0)) {
linesCleared++;
board.splice(y, 1);
board.unshift(Array(COLS).fill(0));
y++;
}
}
if (linesCleared > 0) {
score += linesCleared * 100;
scoreElement.textContent = score;
}
}
// ๊ฒŒ์ž„ ์˜ค๋ฒ„ ํ™•์ธ
function checkGameOver() {
if (!isValidMove(currentPiece)) {
gameOver = true;
clearInterval(gameInterval);
alert(`Game Over! Your score: ${score}`);
startButton.textContent = "Restart Game";
}
}
// --- ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„: ํ•˜๋“œ ๋“œ๋กญ ํ•จ์ˆ˜ ---
function hardDrop() {
// ์ด๋™ ๊ฐ€๋Šฅํ•œ ๋™์•ˆ ๊ณ„์† ์•„๋ž˜๋กœ ์ด๋™์‹œํ‚ด
while (isValidMove(currentPiece, 0, 1)) {
currentPiece.move(0, 1);
score += 2; // ํ•˜๋“œ ๋“œ๋กญ ๋ณด๋„ˆ์Šค ์ ์ˆ˜
}
scoreElement.textContent = score;
// ๋ธ”๋ก์ด ๋ฉˆ์ถ˜ ํ›„ ๋ฐ”๋กœ ๋‹ค์Œ ๊ฒŒ์ž„ ๋ฃจํ”„ ์‹คํ–‰ (๊ณ ์ •, ์ค„ ์ œ๊ฑฐ, ์ƒˆ ๋ธ”๋ก ์ƒ์„ฑ)
gameLoop();
}
// ๊ฒŒ์ž„ ๋ฃจํ”„
function gameLoop() {
if (gameOver) return;
if (isValidMove(currentPiece, 0, 1)) {
currentPiece.move(0, 1);
} else {
lockPiece();
clearLines();
currentPiece = generateNewPiece();
checkGameOver();
}
draw();
}
// --- ์ˆ˜์ •๋œ ๋ถ€๋ถ„: ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ---
document.addEventListener('keydown', (event) => {
if (gameOver || !currentPiece) return;
switch (event.key) {
case 'ArrowLeft':
if (isValidMove(currentPiece, -1, 0)) currentPiece.move(-1, 0);
break;
case 'ArrowRight':
if (isValidMove(currentPiece, 1, 0)) currentPiece.move(1, 0);
break;
case 'ArrowDown':
if (isValidMove(currentPiece, 0, 1)) {
currentPiece.move(0, 1);
score += 1;
scoreElement.textContent = score;
} else {
gameLoop();
}
break;
case 'ArrowUp':
rotatePiece();
break;
case ' ': // ์ŠคํŽ˜์ด์Šค๋ฐ” ํ‚ค
event.preventDefault(); // ์ŠคํŽ˜์ด์Šค๋ฐ”์˜ ๊ธฐ๋ณธ ๋™์ž‘(ํŽ˜์ด์ง€ ์Šคํฌ๋กค) ๋ฐฉ์ง€
hardDrop();
break;
}
draw();
});
// ๊ฒŒ์ž„ ์‹œ์ž‘
function startGame() {
board = Array.from({ length: ROWS }, () => Array(COLS).fill(0));
score = 0;
scoreElement.textContent = score;
gameOver = false;
currentPiece = generateNewPiece();
if (gameInterval) clearInterval(gameInterval);
gameInterval = setInterval(gameLoop, 800);
draw();
startButton.textContent = "Start Game";
}
startButton.addEventListener('click', startGame);
// ์ดˆ๊ธฐ ์•ˆ๋‚ด
context.fillStyle = 'white';
context.font = '20px Arial';
context.textAlign = 'center';
context.fillText('Click "Start Game" to play!', canvas.width / 2, canvas.height / 2);