426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
479 lines
14 KiB
HTML
479 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Tetris Game</title>
|
|
<style>
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
background: linear-gradient(135deg, #1e3c72, #2a5298);
|
|
font-family: 'Arial', sans-serif;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 100vh;
|
|
color: white;
|
|
}
|
|
|
|
.game-container {
|
|
display: flex;
|
|
gap: 20px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.game-board {
|
|
border: 3px solid #fff;
|
|
background: #000;
|
|
box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.info-panel {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
backdrop-filter: blur(10px);
|
|
min-width: 200px;
|
|
}
|
|
|
|
.score {
|
|
font-size: 18px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.next-piece {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.next-piece canvas {
|
|
border: 2px solid #fff;
|
|
background: #000;
|
|
}
|
|
|
|
.controls {
|
|
margin-top: 20px;
|
|
font-size: 14px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.game-over {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: rgba(0, 0, 0, 0.9);
|
|
padding: 30px;
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
display: none;
|
|
}
|
|
|
|
.game-over button {
|
|
background: #4CAF50;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
font-size: 16px;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
margin-top: 15px;
|
|
}
|
|
|
|
.game-over button:hover {
|
|
background: #45a049;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="game-container">
|
|
<canvas id="gameCanvas" class="game-board" width="300" height="600"></canvas>
|
|
<div class="info-panel">
|
|
<div class="score">Score: <span id="score">0</span></div>
|
|
<div class="score">Lines: <span id="lines">0</span></div>
|
|
<div class="score">Level: <span id="level">1</span></div>
|
|
<div class="next-piece">
|
|
<h3>Next:</h3>
|
|
<canvas id="nextCanvas" width="120" height="120"></canvas>
|
|
</div>
|
|
<div class="controls">
|
|
<h3>Controls:</h3>
|
|
<div>← → Move</div>
|
|
<div>↓ Soft Drop</div>
|
|
<div>↑ Rotate</div>
|
|
<div>Space Hard Drop</div>
|
|
<div>P Pause</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="gameOver" class="game-over">
|
|
<h2>Game Over!</h2>
|
|
<div>Final Score: <span id="finalScore">0</span></div>
|
|
<button onclick="restartGame()" type="button">Play Again</button>
|
|
</div>
|
|
|
|
<script>
|
|
// Game constants
|
|
const BOARD_WIDTH = 10;
|
|
const BOARD_HEIGHT = 20;
|
|
const BLOCK_SIZE = 30;
|
|
|
|
// Game state
|
|
let board = [];
|
|
let currentPiece = null;
|
|
let nextPiece = null;
|
|
let score = 0;
|
|
let lines = 0;
|
|
let level = 1;
|
|
let dropTime = 0;
|
|
let dropInterval = 1000;
|
|
let gameRunning = true;
|
|
let paused = false;
|
|
|
|
// Canvas setup
|
|
const canvas = document.getElementById('gameCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const nextCanvas = document.getElementById('nextCanvas');
|
|
const nextCtx = nextCanvas.getContext('2d');
|
|
|
|
// Tetris pieces (tetrominoes)
|
|
const PIECES = [
|
|
// I-piece
|
|
{
|
|
shape: [
|
|
[1, 1, 1, 1]
|
|
],
|
|
color: '#00f5ff'
|
|
},
|
|
// O-piece
|
|
{
|
|
shape: [
|
|
[1, 1],
|
|
[1, 1]
|
|
],
|
|
color: '#ffff00'
|
|
},
|
|
// T-piece
|
|
{
|
|
shape: [
|
|
[0, 1, 0],
|
|
[1, 1, 1]
|
|
],
|
|
color: '#a000f0'
|
|
},
|
|
// S-piece
|
|
{
|
|
shape: [
|
|
[0, 1, 1],
|
|
[1, 1, 0]
|
|
],
|
|
color: '#00f000'
|
|
},
|
|
// Z-piece
|
|
{
|
|
shape: [
|
|
[1, 1, 0],
|
|
[0, 1, 1]
|
|
],
|
|
color: '#f00000'
|
|
},
|
|
// J-piece
|
|
{
|
|
shape: [
|
|
[1, 0, 0],
|
|
[1, 1, 1]
|
|
],
|
|
color: '#0000f0'
|
|
},
|
|
// L-piece
|
|
{
|
|
shape: [
|
|
[0, 0, 1],
|
|
[1, 1, 1]
|
|
],
|
|
color: '#f0a000'
|
|
}
|
|
];
|
|
|
|
// Initialize game board
|
|
function initBoard() {
|
|
board = Array(BOARD_HEIGHT).fill().map(() => Array(BOARD_WIDTH).fill(0));
|
|
}
|
|
|
|
// Create a new piece
|
|
function createPiece() {
|
|
const pieceType = Math.floor(Math.random() * PIECES.length);
|
|
const piece = JSON.parse(JSON.stringify(PIECES[pieceType]));
|
|
piece.x = Math.floor(BOARD_WIDTH / 2) - Math.floor(piece.shape[0].length / 2);
|
|
piece.y = 0;
|
|
return piece;
|
|
}
|
|
|
|
// Check if piece can be placed at position
|
|
function isValidPosition(piece, dx = 0, dy = 0, rotation = null) {
|
|
const shape = rotation || piece.shape;
|
|
const newX = piece.x + dx;
|
|
const newY = piece.y + dy;
|
|
|
|
for (let y = 0; y < shape.length; y++) {
|
|
for (let x = 0; x < shape[y].length; x++) {
|
|
if (shape[y][x]) {
|
|
const boardX = newX + x;
|
|
const boardY = newY + y;
|
|
|
|
if (boardX < 0 || boardX >= BOARD_WIDTH ||
|
|
boardY >= BOARD_HEIGHT ||
|
|
(boardY >= 0 && board[boardY][boardX])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Rotate piece 90 degrees clockwise
|
|
function rotatePiece(piece) {
|
|
const rotated = [];
|
|
const rows = piece.shape.length;
|
|
const cols = piece.shape[0].length;
|
|
|
|
for (let i = 0; i < cols; i++) {
|
|
rotated[i] = [];
|
|
for (let j = 0; j < rows; j++) {
|
|
rotated[i][j] = piece.shape[rows - 1 - j][i];
|
|
}
|
|
}
|
|
|
|
return rotated;
|
|
}
|
|
|
|
// Place piece on board
|
|
function placePiece(piece) {
|
|
for (let y = 0; y < piece.shape.length; y++) {
|
|
for (let x = 0; x < piece.shape[y].length; x++) {
|
|
if (piece.shape[y][x]) {
|
|
const boardY = piece.y + y;
|
|
const boardX = piece.x + x;
|
|
if (boardY >= 0) {
|
|
board[boardY][boardX] = piece.color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear completed lines
|
|
function clearLines() {
|
|
let linesCleared = 0;
|
|
|
|
for (let y = BOARD_HEIGHT - 1; y >= 0; y--) {
|
|
if (board[y].every(cell => cell !== 0)) {
|
|
board.splice(y, 1);
|
|
board.unshift(Array(BOARD_WIDTH).fill(0));
|
|
linesCleared++;
|
|
y++; // Check the same line again
|
|
}
|
|
}
|
|
|
|
if (linesCleared > 0) {
|
|
lines += linesCleared;
|
|
score += linesCleared * 100 * level;
|
|
level = Math.floor(lines / 10) + 1;
|
|
dropInterval = Math.max(50, 1000 - (level - 1) * 50);
|
|
updateUI();
|
|
}
|
|
}
|
|
|
|
// Move piece down
|
|
function dropPiece() {
|
|
if (isValidPosition(currentPiece, 0, 1)) {
|
|
currentPiece.y++;
|
|
} else {
|
|
placePiece(currentPiece);
|
|
clearLines();
|
|
currentPiece = nextPiece;
|
|
nextPiece = createPiece();
|
|
|
|
if (!isValidPosition(currentPiece)) {
|
|
gameOver();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Hard drop piece
|
|
function hardDrop() {
|
|
while (isValidPosition(currentPiece, 0, 1)) {
|
|
currentPiece.y++;
|
|
score += 2;
|
|
}
|
|
dropPiece();
|
|
updateUI();
|
|
}
|
|
|
|
// Draw a block
|
|
function drawBlock(ctx, x, y, color) {
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
|
|
ctx.strokeStyle = '#333';
|
|
ctx.lineWidth = 1;
|
|
ctx.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
|
|
}
|
|
|
|
// Draw the game board
|
|
function drawBoard() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
// Draw placed pieces
|
|
for (let y = 0; y < BOARD_HEIGHT; y++) {
|
|
for (let x = 0; x < BOARD_WIDTH; x++) {
|
|
if (board[y][x]) {
|
|
drawBlock(ctx, x, y, board[y][x]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw current piece
|
|
if (currentPiece) {
|
|
for (let y = 0; y < currentPiece.shape.length; y++) {
|
|
for (let x = 0; x < currentPiece.shape[y].length; x++) {
|
|
if (currentPiece.shape[y][x]) {
|
|
drawBlock(ctx, currentPiece.x + x, currentPiece.y + y, currentPiece.color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw next piece
|
|
function drawNextPiece() {
|
|
nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
|
|
|
|
if (nextPiece) {
|
|
const offsetX = (4 - nextPiece.shape[0].length) / 2;
|
|
const offsetY = (4 - nextPiece.shape.length) / 2;
|
|
|
|
for (let y = 0; y < nextPiece.shape.length; y++) {
|
|
for (let x = 0; x < nextPiece.shape[y].length; x++) {
|
|
if (nextPiece.shape[y][x]) {
|
|
nextCtx.fillStyle = nextPiece.color;
|
|
nextCtx.fillRect(
|
|
(offsetX + x) * 30,
|
|
(offsetY + y) * 30,
|
|
30, 30
|
|
);
|
|
nextCtx.strokeStyle = '#333';
|
|
nextCtx.strokeRect(
|
|
(offsetX + x) * 30,
|
|
(offsetY + y) * 30,
|
|
30, 30
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update UI elements
|
|
function updateUI() {
|
|
document.getElementById('score').textContent = score;
|
|
document.getElementById('lines').textContent = lines;
|
|
document.getElementById('level').textContent = level;
|
|
}
|
|
|
|
// Game over
|
|
function gameOver() {
|
|
gameRunning = false;
|
|
document.getElementById('finalScore').textContent = score;
|
|
document.getElementById('gameOver').style.display = 'block';
|
|
}
|
|
|
|
// Restart game
|
|
function _restartGame() {
|
|
initBoard();
|
|
currentPiece = createPiece();
|
|
nextPiece = createPiece();
|
|
score = 0;
|
|
lines = 0;
|
|
level = 1;
|
|
dropTime = 0;
|
|
dropInterval = 1000;
|
|
gameRunning = true;
|
|
paused = false;
|
|
document.getElementById('gameOver').style.display = 'none';
|
|
updateUI();
|
|
}
|
|
|
|
// Handle keyboard input
|
|
document.addEventListener('keydown', (e) => {
|
|
if (!gameRunning || paused) {
|
|
if (e.code === 'KeyP') {
|
|
paused = !paused;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (e.code) {
|
|
case 'ArrowLeft':
|
|
if (isValidPosition(currentPiece, -1, 0)) {
|
|
currentPiece.x--;
|
|
}
|
|
break;
|
|
case 'ArrowRight':
|
|
if (isValidPosition(currentPiece, 1, 0)) {
|
|
currentPiece.x++;
|
|
}
|
|
break;
|
|
case 'ArrowDown':
|
|
dropPiece();
|
|
score += 1;
|
|
updateUI();
|
|
break;
|
|
case 'ArrowUp': {
|
|
const rotated = rotatePiece(currentPiece);
|
|
if (isValidPosition(currentPiece, 0, 0, rotated)) {
|
|
currentPiece.shape = rotated;
|
|
}
|
|
break;
|
|
}
|
|
case 'Space':
|
|
e.preventDefault();
|
|
hardDrop();
|
|
break;
|
|
case 'KeyP':
|
|
paused = !paused;
|
|
break;
|
|
}
|
|
});
|
|
|
|
// Game loop
|
|
function gameLoop(timestamp) {
|
|
if (gameRunning && !paused) {
|
|
if (timestamp - dropTime > dropInterval) {
|
|
dropPiece();
|
|
dropTime = timestamp;
|
|
}
|
|
|
|
drawBoard();
|
|
drawNextPiece();
|
|
}
|
|
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
|
|
// Initialize game
|
|
function init() {
|
|
initBoard();
|
|
currentPiece = createPiece();
|
|
nextPiece = createPiece();
|
|
updateUI();
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
|
|
// Start the game
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html>
|