TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
10 KiB
356 lines
10 KiB
<?php
|
|
|
|
/**
|
|
* Tetris Game - Logic in PHP, SDL operations in C++
|
|
*/
|
|
|
|
// SDL Constants
|
|
const SDL_WINDOWPOS_UNDEFINED = 0x1FFF0000;
|
|
const SDL_WINDOW_SHOWN = 0x00000004;
|
|
const SDL_QUIT = 256;
|
|
const SDL_KEYDOWN = 768;
|
|
|
|
// Tetromino shapes (7 types)
|
|
const TETROMINOES = [
|
|
// I
|
|
[[0,0,0,0], [1,1,1,1], [0,0,0,0], [0,0,0,0]],
|
|
// O
|
|
[[0,0,0,0], [0,1,1,0], [0,1,1,0], [0,0,0,0]],
|
|
// T
|
|
[[0,0,0,0], [0,1,0,0], [1,1,1,0], [0,0,0,0]],
|
|
// S
|
|
[[0,0,0,0], [0,1,1,0], [1,1,0,0], [0,0,0,0]],
|
|
// Z
|
|
[[0,0,0,0], [1,1,0,0], [0,1,1,0], [0,0,0,0]],
|
|
// J
|
|
[[0,0,0,0], [1,0,0,0], [1,1,1,0], [0,0,0,0]],
|
|
// L
|
|
[[0,0,0,0], [0,0,1,0], [1,1,1,0], [0,0,0,0]],
|
|
];
|
|
|
|
class TetrisGame
|
|
{
|
|
private mixed $game;
|
|
private array $board;
|
|
private array $currentPiece;
|
|
private int $currentX;
|
|
private int $currentY;
|
|
private int $currentType;
|
|
private int $score;
|
|
private bool $gameOver;
|
|
private int $lastDropTime;
|
|
private int $dropInterval;
|
|
|
|
const BOARD_WIDTH = 10;
|
|
const BOARD_HEIGHT = 20;
|
|
|
|
public function __construct()
|
|
{
|
|
echo "正在创建游戏实例...\n";
|
|
$this->game = tetris_new();
|
|
echo "游戏实例已创建\n";
|
|
|
|
$this->initGameState();
|
|
|
|
// Initialize timing
|
|
$this->lastDropTime = SDL_GetTicks();
|
|
$this->dropInterval = 1000; // 1 second auto drop
|
|
|
|
echo "[PHP] Initial time: {$this->lastDropTime}, interval: {$this->dropInterval}ms\n";
|
|
}
|
|
|
|
private function initGameState(): void
|
|
{
|
|
// Initialize empty board
|
|
$this->board = [];
|
|
for ($i = 0; $i < self::BOARD_HEIGHT; $i++) {
|
|
$row = [];
|
|
for ($j = 0; $j < self::BOARD_WIDTH; $j++) {
|
|
$row[] = 0;
|
|
}
|
|
$this->board[] = $row;
|
|
}
|
|
|
|
$this->score = 0;
|
|
$this->gameOver = false;
|
|
$this->spawnNewPiece();
|
|
|
|
echo "[PHP] Game initialized\n";
|
|
}
|
|
|
|
private function spawnNewPiece(): void
|
|
{
|
|
$this->currentType = rand(0, 6);
|
|
$this->currentPiece = TETROMINOES[$this->currentType];
|
|
$this->currentX = intdiv(self::BOARD_WIDTH - 4, 2);
|
|
$this->currentY = 0;
|
|
|
|
// Check game over
|
|
if (!$this->isValidPosition($this->currentX, $this->currentY)) {
|
|
$this->gameOver = true;
|
|
echo "[PHP] Game Over!\n";
|
|
}
|
|
|
|
echo "[PHP] Spawned piece type: {$this->currentType}\n";
|
|
}
|
|
|
|
private function isValidPosition(int $x, int $y): bool
|
|
{
|
|
for ($i = 0; $i < 4; $i++) {
|
|
for ($j = 0; $j < 4; $j++) {
|
|
if ($this->currentPiece[$i][$j]) {
|
|
$newX = $x + $j;
|
|
$newY = $y + $i;
|
|
|
|
if ($newX < 0 || $newX >= self::BOARD_WIDTH || $newY >= self::BOARD_HEIGHT) {
|
|
return false;
|
|
}
|
|
|
|
if ($newY >= 0 && $this->board[$newY][$newX]) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function moveLeft(): bool
|
|
{
|
|
if ($this->gameOver) return false;
|
|
|
|
if ($this->isValidPosition($this->currentX - 1, $this->currentY)) {
|
|
$this->currentX--;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function moveRight(): bool
|
|
{
|
|
if ($this->gameOver) return false;
|
|
|
|
if ($this->isValidPosition($this->currentX + 1, $this->currentY)) {
|
|
$this->currentX++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function moveDown(): bool
|
|
{
|
|
if ($this->gameOver) return false;
|
|
|
|
if ($this->isValidPosition($this->currentX, $this->currentY + 1)) {
|
|
$this->currentY++;
|
|
return true;
|
|
} else {
|
|
$this->lockPiece();
|
|
$this->spawnNewPiece();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function rotate(): void
|
|
{
|
|
if ($this->gameOver) return;
|
|
|
|
// Rotate 90 degrees clockwise
|
|
$newPiece = [];
|
|
for ($i = 0; $i < 4; $i++) {
|
|
$newPiece[$i] = [];
|
|
for ($j = 0; $j < 4; $j++) {
|
|
$newPiece[$i][$j] = $this->currentPiece[3 - $j][$i];
|
|
}
|
|
}
|
|
|
|
// Save old piece
|
|
$oldPiece = $this->currentPiece;
|
|
$this->currentPiece = $newPiece;
|
|
|
|
// Check if rotation is valid
|
|
if (!$this->isValidPosition($this->currentX, $this->currentY)) {
|
|
// Revert
|
|
$this->currentPiece = $oldPiece;
|
|
}
|
|
}
|
|
|
|
private function lockPiece(): void
|
|
{
|
|
$color = $this->currentType + 1;
|
|
for ($i = 0; $i < 4; $i++) {
|
|
for ($j = 0; $j < 4; $j++) {
|
|
if ($this->currentPiece[$i][$j]) {
|
|
$boardY = $this->currentY + $i;
|
|
$boardX = $this->currentX + $j;
|
|
if ($boardY >= 0 && $boardY < self::BOARD_HEIGHT && $boardX >= 0 && $boardX < self::BOARD_WIDTH) {
|
|
$this->board[$boardY][$boardX] = $color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
echo "[PHP] Piece locked\n";
|
|
$this->clearLines();
|
|
}
|
|
|
|
private function clearLines(): void
|
|
{
|
|
$linesCleared = 0;
|
|
for ($y = self::BOARD_HEIGHT - 1; $y >= 0; $y--) {
|
|
$fullLine = true;
|
|
for ($x = 0; $x < self::BOARD_WIDTH; $x++) {
|
|
if ($this->board[$y][$x] == 0) {
|
|
$fullLine = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($fullLine) {
|
|
array_splice($this->board, $y, 1);
|
|
$newRow = [];
|
|
for ($x = 0; $x < self::BOARD_WIDTH; $x++) {
|
|
$newRow[] = 0;
|
|
}
|
|
array_unshift($this->board, $newRow);
|
|
$linesCleared++;
|
|
$y++;
|
|
}
|
|
}
|
|
|
|
if ($linesCleared > 0) {
|
|
$this->score += $linesCleared * 100;
|
|
echo "[PHP] Cleared {$linesCleared} lines, score: {$this->score}\n";
|
|
}
|
|
}
|
|
|
|
private function updateBoard(): void
|
|
{
|
|
// Create render board with current piece
|
|
$renderBoard = $this->board;
|
|
|
|
if (!$this->gameOver) {
|
|
$color = $this->currentType + 1;
|
|
for ($i = 0; $i < 4; $i++) {
|
|
for ($j = 0; $j < 4; $j++) {
|
|
if ($this->currentPiece[$i][$j]) {
|
|
$boardY = $this->currentY + $i;
|
|
$boardX = $this->currentX + $j;
|
|
if ($boardY >= 0 && $boardY < self::BOARD_HEIGHT && $boardX >= 0 && $boardX < self::BOARD_WIDTH) {
|
|
$renderBoard[$boardY][$boardX] = $color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sync to C++
|
|
tetris_set_board($this->game, $renderBoard);
|
|
tetris_set_score($this->game, $this->score);
|
|
}
|
|
|
|
private function handleKeyPress(int $keyCode): void
|
|
{
|
|
switch ($keyCode) {
|
|
case 1073741904: // Left arrow
|
|
case 97: // 'a'
|
|
$this->moveLeft();
|
|
break;
|
|
|
|
case 1073741903: // Right arrow
|
|
case 100: // 'd'
|
|
$this->moveRight();
|
|
break;
|
|
|
|
case 1073741906: // Up arrow
|
|
case 119: // 'w'
|
|
$this->rotate();
|
|
break;
|
|
|
|
case 1073741905: // Down arrow
|
|
case 115: // 's'
|
|
$this->moveDown();
|
|
break;
|
|
|
|
case 32: // Space
|
|
while ($this->moveDown()) {
|
|
// Hard drop
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public function run(): void
|
|
{
|
|
echo "游戏开始!\n";
|
|
$frameCount = 0;
|
|
|
|
while (true) {
|
|
// Handle events
|
|
$event = tetris_poll_event($this->game);
|
|
|
|
if (!empty($event)) {
|
|
$eventType = $event[0] ?? 0;
|
|
|
|
if ($eventType == 256) { // SDL_QUIT
|
|
echo "[PHP] Received QUIT event\n";
|
|
break;
|
|
}
|
|
|
|
if ($eventType == 768) { // SDL_KEYDOWN
|
|
$keyCode = $event[1] ?? 0;
|
|
$this->handleKeyPress($keyCode);
|
|
}
|
|
}
|
|
|
|
// Auto drop
|
|
$currentTime = SDL_GetTicks();
|
|
if ($currentTime - $this->lastDropTime > $this->dropInterval) {
|
|
if (!$this->gameOver) {
|
|
echo "[PHP] Auto drop triggered at time {$currentTime}\n";
|
|
$this->moveDown();
|
|
// Increase speed based on score (max speed 100ms)
|
|
$this->dropInterval = max(100, 1000 - intdiv($this->score, 500) * 50);
|
|
}
|
|
$this->lastDropTime = $currentTime;
|
|
}
|
|
|
|
// Update and render
|
|
$this->updateBoard();
|
|
tetris_render($this->game, 0);
|
|
|
|
$frameCount++;
|
|
if ($frameCount % 60 == 0) {
|
|
echo "[PHP] Frame {$frameCount}, Score: {$this->score}\n";
|
|
}
|
|
|
|
if ($this->gameOver) {
|
|
echo "游戏结束!最终得分: {$this->score}\n";
|
|
break;
|
|
}
|
|
|
|
usleep(16000); // ~60 FPS
|
|
}
|
|
}
|
|
}
|
|
|
|
function main(): void
|
|
{
|
|
date_default_timezone_set('Asia/Shanghai');
|
|
|
|
echo "========================================\n";
|
|
echo " 俄罗斯方块 - PHP 编译器演示\n";
|
|
echo "========================================\n\n";
|
|
|
|
$game = new TetrisGame();
|
|
|
|
echo "控制说明:\n";
|
|
echo " A/D 或 ← → : 左右移动\n";
|
|
echo " W 或 ↑ : 旋转方块\n";
|
|
echo " S 或 ↓ : 加速下落\n";
|
|
echo " 空格 : 直接落下\n\n";
|
|
|
|
$game->run();
|
|
|
|
echo "\n感谢游玩!\n";
|
|
}
|
|
|