This commit is contained in:
gpt-engineer-app[bot]
2026-01-06 23:55:01 +00:00
parent 49abc06046
commit d13fa4f454
9 changed files with 84 additions and 1486 deletions
+7 -2
View File
@@ -62,7 +62,12 @@ const Breakout = () => {
if (height > maxHeight) { height = maxHeight; width = height * aspectRatio; }
return { width: Math.floor(width), height: Math.floor(height) };
}
return isFullscreen ? { width: 600, height: 720 } : { width: 480, height: 580 };
// Desktop: maximize based on available height
const availableHeight = window.innerHeight - 100;
const aspectRatio = 480 / 580;
const height = Math.min(availableHeight, 700);
const width = Math.floor(height * aspectRatio);
return { width, height };
}, [isFullscreen]);
const [canvasSize, setCanvasSize] = useState(getCanvasSize);
@@ -261,7 +266,7 @@ const Breakout = () => {
</button>
</div>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4'} flex-1 ${isFullscreen ? 'justify-center items-center' : ''}`}>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4 items-start justify-center'} flex-1`}>
<div className="border-2 border-primary box-glow bg-background/80">
<canvas ref={canvasRef} width={canvasSize.width} height={canvasSize.height} onTouchMove={handleTouchMove} className="block" />
</div>
+6 -2
View File
@@ -101,7 +101,11 @@ const Pacman = () => {
const maxHeight = window.innerHeight - 420;
return Math.min(Math.floor(maxWidth / GRID_WIDTH), Math.floor(maxHeight / GRID_HEIGHT), 16);
}
return isFullscreen ? 26 : 20;
// Desktop: calculate based on available height (subtract header ~60px, margins ~40px)
const availableHeight = window.innerHeight - 100;
const maxCellFromHeight = Math.floor(availableHeight / GRID_HEIGHT);
// Clamp to reasonable max
return Math.min(maxCellFromHeight, 28);
}, [isFullscreen]);
const [cellSize, setCellSize] = useState(getCellSize);
@@ -554,7 +558,7 @@ const Pacman = () => {
</button>
</div>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4'} flex-1 ${isFullscreen ? 'justify-center items-center' : ''}`}>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4 items-start justify-center'} flex-1`}>
<div className="border-2 border-primary box-glow p-1 bg-background/80 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-primary/5 animate-pulse" style={{ animationDuration: '4s' }}></div>
<div className="grid relative z-10" style={{ gridTemplateColumns: `repeat(${GRID_WIDTH}, ${cellSize}px)` }}>{renderGrid()}</div>
+6 -5
View File
@@ -75,10 +75,11 @@ const Snake = () => {
const maxHeight = window.innerHeight - 420;
return Math.min(Math.floor(maxWidth / GRID_SIZE), Math.floor(maxHeight / GRID_SIZE), 18);
}
if (gameMode === '2p') {
return isFullscreen ? 20 : 16;
}
return isFullscreen ? 28 : 24;
// Desktop: calculate based on available height (subtract header ~60px, margins ~40px)
const availableHeight = window.innerHeight - 100;
const maxCellFromHeight = Math.floor(availableHeight / GRID_SIZE);
// Clamp to reasonable max
return Math.min(maxCellFromHeight, 30);
}, [isFullscreen, gameMode]);
const [cellSize, setCellSize] = useState(getCellSize);
@@ -538,7 +539,7 @@ const Snake = () => {
</button>
</div>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4'} flex-1 ${isFullscreen ? 'justify-center items-center' : ''}`}>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4 items-start justify-center'} flex-1`}>
<div className="border-2 border-primary box-glow p-1 bg-background/80">
<div className="grid" style={{ gridTemplateColumns: `repeat(${GRID_SIZE}, ${cellSize}px)` }}>
{gameMode === '1p' ? renderGrid1P() : renderGrid2P()}
+65 -55
View File
@@ -148,10 +148,17 @@ const Tetris = () => {
const maxHeight = window.innerHeight - 440;
return Math.min(Math.floor(maxWidth / BOARD_WIDTH), Math.floor(maxHeight / BOARD_HEIGHT), 22);
}
// Desktop: calculate based on available height (subtract header ~60px, margins ~40px)
const availableHeight = window.innerHeight - 100;
const maxCellFromHeight = Math.floor(availableHeight / BOARD_HEIGHT);
if (gameMode === '2p') {
return isFullscreen ? 22 : 18;
// 2P needs two boards side by side plus controls (~500px for both boards + gaps)
const availableWidth = window.innerWidth - 300; // space for controls
const maxCellFromWidth = Math.floor(availableWidth / (BOARD_WIDTH * 2 + 4));
return Math.min(maxCellFromHeight, maxCellFromWidth, 28);
}
return isFullscreen ? 30 : 24;
// 1P mode - maximize based on height
return Math.min(maxCellFromHeight, 32);
}, [isFullscreen, gameMode]);
const [cellSize, setCellSize] = useState(getCellSize);
@@ -598,7 +605,7 @@ const Tetris = () => {
</button>
</div>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-4'} flex-1 ${isFullscreen ? 'justify-center items-center' : ''}`}>
<div className={`flex ${isMobile ? 'flex-col items-center' : 'flex-row gap-3 items-start justify-center'} flex-1`}>
{gameMode === '1p' ? (
<>
<div className="border-2 border-primary box-glow p-1 bg-background/80">{renderBoard1P()}</div>
@@ -629,42 +636,44 @@ const Tetris = () => {
)}
</>
) : (
/* 2P Layout: P1 Board with Next | Controls | P2 Board with Next */
<>
{/* P1 Board */}
<div className="flex flex-col items-center gap-2">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-sm bg-primary" />
<span className="font-pixel text-xs text-primary">P1 (WASD/Q)</span>
{/* P1 Section */}
<div className="flex gap-2 items-start">
<div className="flex flex-col items-center gap-1">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-sm bg-primary" />
<span className="font-pixel text-xs text-primary">P1 (WASD/Q)</span>
</div>
<div className="border-2 border-primary box-glow p-1 bg-background/80">{renderBoard2P(player1, 'border-primary/20')}</div>
<div className="flex gap-2 text-center">
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-primary">{player1.score}</p></div>
<div><p className="font-pixel text-[8px] text-foreground/60">LINES</p><p className="font-minecraft text-sm text-primary">{player1.lines}</p></div>
</div>
{player1.gameOver && <span className="font-pixel text-xs text-destructive">GAME OVER</span>}
</div>
<div className="border-2 border-primary box-glow p-1 bg-background/80">{renderBoard2P(player1, 'border-primary/20')}</div>
<div className="flex gap-2 text-center">
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-primary">{player1.score}</p></div>
<div><p className="font-pixel text-[8px] text-foreground/60">LINES</p><p className="font-minecraft text-sm text-primary">{player1.lines}</p></div>
</div>
{player1.gameOver && <span className="font-pixel text-xs text-destructive">GAME OVER</span>}
</div>
{/* P1 Next Piece */}
<div className="border-2 border-primary/50 p-2 bg-background/50 flex flex-col items-center justify-center min-h-[60px]">
<p className="font-pixel text-[10px] text-foreground/60">P1 NEXT</p>
<div className="flex flex-col gap-1">
{player1.nextPiece.shape.map((row, y) => (
<div key={y} className="flex gap-1">
{row.map((val, x) => (
<div key={`${y}-${x}`} className={`w-2 h-2 ${val ? 'bg-primary box-glow' : 'bg-transparent'}`} />
))}
</div>
))}
{/* P1 Next Piece */}
<div className="border border-primary/50 p-2 bg-background/50 flex flex-col items-center justify-center">
<p className="font-pixel text-[8px] text-foreground/60 mb-1">NEXT</p>
<div className="flex flex-col gap-0.5">
{player1.nextPiece.shape.map((row, y) => (
<div key={y} className="flex gap-0.5">
{row.map((val, x) => (
<div key={`${y}-${x}`} className={`w-2 h-2 ${val ? 'bg-primary box-glow' : 'bg-transparent'}`} />
))}
</div>
))}
</div>
</div>
</div>
{/* Center controls */}
<div className="flex flex-col gap-2 min-w-[100px] items-center">
<div className="flex flex-col gap-2 min-w-[90px] items-center justify-center">
<div className="border border-primary/50 p-2 bg-background/50 text-center">
<p className="font-pixel text-[8px] text-foreground/60 mb-1">CONTROLS</p>
<p className="font-pixel text-[8px] text-primary">P1: WASD Q</p>
<p className="font-pixel text-[8px] text-purple-400">P2: /</p>
<p className="font-pixel text-[8px] text-foreground/60 mt-1">P: Pause</p>
<p className="font-pixel text-[7px] text-primary">P1: WASD Q</p>
<p className="font-pixel text-[7px] text-purple-400">P2: /</p>
<p className="font-pixel text-[7px] text-foreground/60 mt-1">P: Pause</p>
</div>
{!gameStarted || gameOver ? (
<button onClick={() => startGame('2p')} className="font-minecraft text-xs py-2 px-3 border border-primary bg-primary/20 text-primary hover:bg-primary/40 transition-all box-glow">
@@ -677,31 +686,32 @@ const Tetris = () => {
)}
</div>
{/* P2 Board */}
<div className="flex flex-col items-center gap-2">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-sm" style={{ backgroundColor: 'hsl(280 70% 50%)' }} />
<span className="font-pixel text-xs text-purple-400">P2 (/)</span>
{/* P2 Section */}
<div className="flex gap-2 items-start">
{/* P2 Next Piece */}
<div className="border border-purple-500/50 p-2 bg-background/50 flex flex-col items-center justify-center">
<p className="font-pixel text-[8px] text-purple-400 mb-1">NEXT</p>
<div className="flex flex-col gap-0.5">
{player2.nextPiece.shape.map((row, y) => (
<div key={y} className="flex gap-0.5">
{row.map((val, x) => (
<div key={`${y}-${x}`} className={`w-2 h-2 ${val ? 'bg-purple-400 box-glow' : 'bg-transparent'}`} />
))}
</div>
))}
</div>
</div>
<div className="border-2 border-purple-500 p-1 bg-background/80" style={{ boxShadow: '0 0 10px hsl(280 70% 50% / 0.5)' }}>{renderBoard2P(player2, 'border-purple-500/20')}</div>
<div className="flex gap-2 text-center">
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-purple-400">{player2.score}</p></div>
<div><p className="font-pixel text-[8px] text-foreground/60">LINES</p><p className="font-minecraft text-sm text-purple-400">{player2.lines}</p></div>
</div>
{player2.gameOver && <span className="font-pixel text-xs text-destructive">GAME OVER</span>}
</div>
{/* P2 Next Piece */}
<div className="border-2 border-purple-500/50 p-2 bg-background/50 flex flex-col items-center justify-center min-h-[60px]">
<p className="font-pixel text-[10px] text-purple-400">P2 NEXT</p>
<div className="flex flex-col gap-1">
{player2.nextPiece.shape.map((row, y) => (
<div key={y} className="flex gap-1">
{row.map((val, x) => (
<div key={`${y}-${x}`} className={`w-2 h-2 ${val ? 'bg-purple-400 box-glow' : 'bg-transparent'}`} />
))}
</div>
))}
<div className="flex flex-col items-center gap-1">
<div className="flex items-center gap-2">
<div className="w-3 h-3 rounded-sm" style={{ backgroundColor: 'hsl(280 70% 50%)' }} />
<span className="font-pixel text-xs text-purple-400">P2 (/)</span>
</div>
<div className="border-2 border-purple-500 p-1 bg-background/80" style={{ boxShadow: '0 0 10px hsl(280 70% 50% / 0.5)' }}>{renderBoard2P(player2, 'border-purple-500/20')}</div>
<div className="flex gap-2 text-center">
<div><p className="font-pixel text-[8px] text-foreground/60">SCORE</p><p className="font-minecraft text-sm text-purple-400">{player2.score}</p></div>
<div><p className="font-pixel text-[8px] text-foreground/60">LINES</p><p className="font-minecraft text-sm text-purple-400">{player2.lines}</p></div>
</div>
{player2.gameOver && <span className="font-pixel text-xs text-destructive">GAME OVER</span>}
</div>
</div>
</>
-15
View File
@@ -1,15 +0,0 @@
{/* P2 Next Piece */}
<div className="border-2 border-purple-500/50 p-3 bg-background/50 flex flex-col items-center justify-center min-h-[60px]">
<p className="font-pixel text-[10px] text-purple-400">P2 NEXT</p>
<div className="flex flex-col gap-1">
{player2.nextPiece.shape.map((row, y) => (
<div key={y} className="flex gap-1">
{row.map((val, x) => (
<div key={`${y}-${x}`} className={`w-2 h-2 ${val ? 'bg-purple-400 box-glow' : 'bg-transparent'}`} />
))}
</div>
))}
</div>
</div>
{/* Center controls */}