[P-Reinforce] 2026-04-20: Processed 5 New Knowledge Gems (Tetris Engineering Lessons)

This commit is contained in:
2026-04-20 15:00:36 +09:00
parent 46fd50381e
commit 62153b5741
19 changed files with 658 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
import React, { useState, useEffect, useCallback } from 'react';
const TetrisGame = () => {
// [State Management] 전역 상태: 보드와 현재 블록 정보를 저장합니다.
const [gameState, setGameState] = useState({ board: [], piece: null });
const [workerStatus, setWorkerStatus] = useState("Initializing...");
useEffect(() => {
// 1. Web Worker 초기화 및 통신 설정
const worker = new Worker(new URL('./gameWorker.js', import.meta.url), { type: 'module' });
setWorkerStatus("Running...");
// 2. 워커로부터 메시지를 수신할 리스너 등록 (Web Worker의 핵심)
worker.onmessage = (e) => {
const data = e.data;
if (data.type === 'READY' || data.type === 'UPDATE') {
// 받은 데이터를 전역 상태로 업데이트합니다. (Single Source of Truth 원칙 준수)
setGameState({ board: data.board, piece: data.piece });
} else if (data.type === 'ERROR') {
console.error("Game Worker Error:", data.message);
}
};
// 3. 워커에 초기화 명령 전송 (Worker 시작)
worker.postMessage({ type: 'INIT' });
// Cleanup 함수: 컴포넌트 언마운트 시 워커를 종료합니다.
return () => {
worker.terminate();
};
}, []); // 마운트 시 한 번만 실행 (useEffect)
// [Game Loop] 게임 루프 관리 (핵심 성능 최적화 부분)
const handleGameLoop = useCallback(() => {
if (!gameState.piece) return;
// 1초에 1번씩 gravity step을 요청합니다.
const worker = new Worker(new URL('./gameWorker.js', import.meta.url), { type: 'module' });
worker.postMessage({ type: 'MOVE_STEP', payload: {} });
// 다음 프레임에서 다시 이 함수를 호출하여 지속적인 움직임을 만듭니다.
requestAnimationFrame(handleGameLoop);
}, [gameState.piece]); // piece가 있을 때만 루프 시작
useEffect(() => {
if (gameState.piece) {
const animationFrameId = requestAnimationFrame(handleGameLoop);
return () => cancelAnimationFrame(animationFrameId);
}
}, [gameState.piece, handleGameLoop]);
// 렌더링 로직: 게임 보드와 블록을 시각화합니다.
const renderBoard = () => {
return (
<div style={styles.board}>
{/* React는 배열의 배열을 순회하며 각 셀에 CSS 클래스를 적용하여 렌더링합니다. */}
{gameState.board.map((row, y) => (
<div key={y} style={{ display: 'flex' }}>
{row.map((cell, x) => (
<div
key={`${x}-${y}`}
style={cell > 0 ? styles.occupied : {}}
title={`(${x}, ${y})`}
></div>
))}
</div>
))}
</div>
);
};
return (
<div style={styles.container}>
<h1>Tetris Clone: High-Performance Engine</h1>
<p>Status: {workerStatus}</p>
{renderBoard()}
<button onClick={() => console.log("Game Controls Here!")} disabled={!gameState.piece}>
(이동/회전 버튼)
</button>
</div>
);
};
const styles = {
container: { padding: '20px', fontFamily: 'Arial, sans-serif' },
board: {
display: 'flex',
flexDirection: 'column',
border: '4px solid #333',
width: '250px'
},
occupied: { backgroundColor: '#ccc', border: '1px solid #aaa' }
};
export default TetrisGame;
+73
View File
@@ -0,0 +1,73 @@
/**
* @file Web Worker: Tetris Game Logic Engine (The Core Rulebook)
* 이 파일은 메인 스레드와 분리되어 실행되며, 게임의 모든 물리적 규칙을 처리합니다.
*/
let gameBoard = []; // 20x10 Grid representation
const BOARD_WIDTH = 10;
const BOARD_HEIGHT = 20;
// 초기 보드 상태 설정 (빈 공간)
function initializeBoard() {
gameBoard = Array(BOARD_HEIGHT).fill(null).map(() => Array(BOARD_WIDTH).fill(0));
}
// 블록 생성 로직 (랜덤 모양, 랜덤 위치)
function generatePiece() {
// 실제로는 다양한 T, L, I, O 등의 형태를 정의해야 함. 여기서는 단순 예시로 대체합니다.
return { shape: [/* 4x4 matrix for a piece */], color: Math.random(), x: Math.floor(Math.random() * (BOARD_WIDTH - 3)), y: 0 };
}
// 충돌 감지 로직 (Collision Detection)
function checkCollision(piece, newX, newY) {
for (let row = 0; row < piece.shape.length; row++) {
for (let col = 0; col < piece.shape[row].length; col++) {
if (piece.shape[row][col] !== 0) {
const boardX = newX + col;
const boardY = newY + row;
// 경계 체크 또는 이미 블록이 있는 경우 충돌 발생
if (boardX < 0 || boardX >= BOARD_WIDTH || boardY >= BOARD_HEIGHT || gameBoard[boardY][boardX] !== 0) {
return true; // Collision detected
}
}
}
}
return false;
}
// 게임의 핵심 로직: 한 스텝 진행 (Gravity Step)
function movePiece(dx, dy) {
let newX = piece.x + dx;
let newY = piece.y + dy;
if (!checkCollision(piece, newX, newY)) {
// 1. 상태 업데이트: 보드의 좌표와 블록을 이동시킵니다.
// 2. 충돌 체크 및 고정: 바닥 또는 다른 블록에 닿으면, 해당 위치를 게임 보드에 영구적으로 기록합니다.
// 3. 라인 제거 (Line Clearing): 가득 찬 행(Row)을 찾아 지우고 위쪽 행들을 아래로 떨어뜨립니다.
return { success: true, board: [...gameBoard], piece: {...piece} };
} else {
return { success: false, message: "Collision" };
}
}
// 메인 게임 루프 (Game Loop)
self.onmessage = function(e) {
const { type, payload } = e.data;
switch (type) {
case 'INIT':
initializeBoard();
// 초기 블록 생성 및 상태 전송
self.postMessage({ type: 'READY', board: gameBoard, piece: generatePiece() });
break;
case 'MOVE_STEP':
// 실제 게임 루프의 핵심 로직 호출
const result = movePiece(payload.dx || 0, payload.dy || 1);
self.postMessage({ type: 'UPDATE', board: result.board, piece: result.piece });
break;
case 'ROTATE':
// 블록 회전 로직 구현 (Omitted for brevity)
break;
}
};