#!/usr/bin/env qore # this is basically a direct port of the QT widget example # "tetrix" to Qore using Qore's "qt" module. # Note that Qore's "qt" module requires QT 4.3 or above # use the "qt-gui" module %requires qt-gui # this is an object-oriented program; the application class is "tetrix_example" %exec-class tetrix_example # require all variables to be explicitly declared %require-our # enable all parse warnings %enable-all-warnings namespace TetrixShape { const NoShape = 0; const ZShape = 1; const SShape = 2; const LineShape = 3; const TShape = 4; const SquareShape = 5; const LShape = 6; const MirroredLShape = 7; } namespace TetrixBoard { const BoardWidth = 10; const BoardHeight = 22; } namespace TetrixPiece { const coordsTable = ( ( ( 0, 0 ), ( 0, 0 ), ( 0, 0 ), ( 0, 0 ) ), ( ( 0, -1 ), ( 0, 0 ), ( -1, 0 ), ( -1, 1 ) ), ( ( 0, -1 ), ( 0, 0 ), ( 1, 0 ), ( 1, 1 ) ), ( ( 0, -1 ), ( 0, 0 ), ( 0, 1 ), ( 0, 2 ) ), ( ( -1, 0 ), ( 0, 0 ), ( 1, 0 ), ( 0, 1 ) ), ( ( 0, 0 ), ( 1, 0 ), ( 0, 1 ), ( 1, 1 ) ), ( ( -1, -1 ), ( 0, -1 ), ( 0, 0 ), ( 0, 1 ) ), ( ( 1, -1 ), ( 0, -1 ), ( 0, 0 ), ( 0, 1 ) ) ); } class TetrixBoard::TetrixBoard inherits QFrame { private $.timer, $.nextPieceLabel, $.isStarted, $.isPaused, $.isWaitingAfterLine, $.curPiece, $.nextPiece, $.curX, $.curY, $.numLinesRemoved, $.numPiecesDropped, $.score, $.level, $.board; constructor($parent) : QFrame($parent) { $.timer = new QBasicTimer(); $.nextPiece = new TetrixPiece(); $.curPiece = new TetrixPiece(); $.createSignal("scoreChanged(int)"); $.createSignal("levelChanged(int)"); $.createSignal("linesRemovedChanged(int)"); $.setFrameStyle(QFrame::Panel | QFrame::Sunken); $.setFocusPolicy(Qt::StrongFocus); $.isStarted = False; $.isPaused = False; $.clearBoard(); $.nextPiece.setRandomShape(); } setNextPieceLabel($label) { $.nextPieceLabel = $label; } sizeHint() { return new QSize(BoardWidth * 15 + $.frameWidth() * 2, BoardHeight * 15 + $.frameWidth() * 2); } minimumSizeHint() { return new QSize(BoardWidth * 5 + $.frameWidth() * 2, BoardHeight * 5 + $.frameWidth() * 2); } start() { if ($.isPaused) return; $.isStarted = True; $.isWaitingAfterLine = False; $.numLinesRemoved = 0; $.numPiecesDropped = 0; $.score = 0; $.level = 1; $.clearBoard(); $.emit("linesRemovedChanged(int)", $.numLinesRemoved); $.emit("scoreChanged(int)", $.score); $.emit("levelChanged(int)", $.level); $.newPiece(); $.timer.start($.timeoutTime(), $self); } pause() { if (!$.isStarted) return; $.isPaused = !$.isPaused; if ($.isPaused) { $.timer.stop(); } else { $.timer.start($.timeoutTime(), $self); } $.update(); } paintEvent($event) { QFrame::$.paintEvent($event); my $painter = new QPainter($self); my $rect = $.contentsRect(); if ($.isPaused) { $painter.drawText($rect, Qt::AlignCenter, TR("Pause")); return; } my $boardTop = $rect.bottom() - BoardHeight * $.squareHeight(); for (my $i = 0; $i < BoardHeight; ++$i) { for (my $j = 0; $j < BoardWidth; ++$j) { my $shape = $.shapeAt($j, BoardHeight - $i - 1); if ($shape != NoShape) $.drawSquare($painter, $rect.left() + $j * $.squareWidth(), $boardTop + $i * $.squareHeight(), $shape); } } if ($.curPiece.shape() != NoShape) { for (my $i = 0; $i < 4; ++$i) { my $x = $.curX + $.curPiece.x($i); my $y = $.curY - $.curPiece.y ($i); $.drawSquare($painter, $rect.left() + $x * $.squareWidth(), $boardTop + (BoardHeight - $y - 1) * $.squareHeight(), $.curPiece.shape()); } } } keyPressEvent($event) { if (!$.isStarted || $.isPaused || $.curPiece.shape() == NoShape) { QFrame::$.keyPressEvent($event); return; } switch ($event.key()) { case Qt::Key_Left: { $.tryMove($.curPiece, $.curX - 1, $.curY); break; } case Qt::Key_Right: { $.tryMove($.curPiece, $.curX + 1, $.curY); break; } case Qt::Key_Down: { $.tryMove($.curPiece.rotatedRight(), $.curX, $.curY); break; } case Qt::Key_Up: { $.tryMove($.curPiece.rotatedLeft(), $.curX, $.curY); break; } case Qt::Key_Space: { $.dropDown(); break; } case Qt::Key_D: { $.oneLineDown(); break; } default: { QFrame::$.keyPressEvent($event); } } } timerEvent($event) { if ($event.timerId() == $.timer.timerId()) { if ($.isWaitingAfterLine) { $.isWaitingAfterLine = False; $.newPiece(); $.timer.start($.timeoutTime(), $self); } else { $.oneLineDown(); } } else { QObject::$.timerEvent($event); } } clearBoard() { for (my $i = 0; $i < BoardHeight * BoardWidth; ++$i) $.board[$i] = NoShape; } dropDown() { my $dropHeight = 0; my $newY = $.curY; while ($newY > 0) { if (!$.tryMove($.curPiece, $.curX, $newY - 1)) break; --$newY; ++$dropHeight; } $.pieceDropped($dropHeight); } oneLineDown() { if (!$.tryMove($.curPiece, $.curX, $.curY - 1)) $.pieceDropped(0); } pieceDropped($dropHeight) { for (my $i = 0; $i < 4; ++$i) { my $x = $.curX + $.curPiece.x($i); my $y = $.curY - $.curPiece.y ($i); $.board[($y * BoardWidth) + $x] = $.curPiece.shape(); } ++$.numPiecesDropped; if ($.numPiecesDropped % 25 == 0) { ++$.level; $.timer.start($.timeoutTime(), $self); $.emit("levelChanged(int)", $.level); } $.score += $dropHeight + 7; $.emit("scoreChanged(int)", $.score); $.removeFullLines(); if (!$.isWaitingAfterLine) $.newPiece(); } removeFullLines() { my $numFullLines = 0; for (my $i = BoardHeight - 1; $i >= 0; --$i) { my $lineIsFull = True; for (my $j = 0; $j < BoardWidth; ++$j) { if ($.shapeAt($j, $i) == NoShape) { $lineIsFull = False; break; } } if ($lineIsFull) { ++$numFullLines; for (my $k = $i; $k < BoardHeight - 1; ++$k) { for (my $j = 0; $j < BoardWidth; ++$j) $.board[($k * BoardWidth) + $j] = $.board[(($k + 1) * BoardWidth) + $j]; } for (my $j = 0; $j < BoardWidth; ++$j) $.board[((BoardHeight - 1) * BoardWidth) + $j] = NoShape; } } if ($numFullLines > 0) { $.numLinesRemoved += $numFullLines; $.score += 10 * $numFullLines; $.emit("linesRemovedChanged(int)", $.numLinesRemoved); $.emit("scoreChanged(int)", $.score); $.timer.start(500, $self); $.isWaitingAfterLine = True; $.curPiece.setShape(NoShape); $.update(); } } newPiece() { $.curPiece = $.nextPiece.copy(); $.nextPiece.setRandomShape(); $.showNextPiece(); $.curX = BoardWidth / 2 + 1; $.curY = BoardHeight - 1 + $.curPiece.minY(); if (!$.tryMove($.curPiece, $.curX, $.curY)) { $.curPiece.setShape(NoShape); $.timer.stop(); $.isStarted = False; } } showNextPiece() { if (!exists $.nextPieceLabel) return; my $dx = $.nextPiece.maxX() - $.nextPiece.minX() + 1; my $dy = $.nextPiece.maxY() - $.nextPiece.minY() + 1; my $pixmap = new QPixmap($dx * $.squareWidth(), $dy * $.squareHeight()); my $painter = new QPainter($pixmap); $painter.fillRect($pixmap.rect(), $.nextPieceLabel.palette().background()); for (my $i = 0; $i < 4; ++$i) { my $x = $.nextPiece.x($i) - $.nextPiece.minX(); my $y = $.nextPiece.y ($i) - $.nextPiece.minY(); $.drawSquare($painter, $x * $.squareWidth(), $y * $.squareHeight(), $.nextPiece.shape()); } $.nextPieceLabel.setPixmap($pixmap); } tryMove($newPiece, $newX, $newY) { for (my $i = 0; $i < 4; ++$i) { my $x = $newX + $newPiece.x($i); my $y = $newY - $newPiece.y ($i); if ($x < 0 || $x >= BoardWidth || $y < 0 || $y >= BoardHeight) return False; if ($.shapeAt($x, $y) != NoShape) return False; } $.curPiece = $newPiece; $.curX = $newX; $.curY = $newY; $.update(); return True; } drawSquare($painter, $x, $y, $shape) { my $color = $colorTable[$shape]; $painter.fillRect($x + 1, $y + 1, $.squareWidth() - 2, $.squareHeight() - 2, $color); $painter.setPen($color.light()); $painter.drawLine($x, $y + $.squareHeight() - 1, $x, $y); $painter.drawLine($x, $y, $x + $.squareWidth() - 1, $y); $painter.setPen($color.dark()); $painter.drawLine($x + 1, $y + $.squareHeight() - 1, $x + $.squareWidth() - 1, $y + $.squareHeight() - 1); $painter.drawLine($x + $.squareWidth() - 1, $y + $.squareHeight() - 1, $x + $.squareWidth() - 1, $y + 1); } private shapeAt($x, $y) { return $.board[($y * BoardWidth) + $x]; } private timeoutTime() { return 1000 / (1 + $.level); } private squareWidth() { return $.contentsRect().width() / BoardWidth; } private squareHeight() { return $.contentsRect().height() / BoardHeight; } } class TetrixPiece::TetrixPiece { private $.pieceShape, $.coords; constructor() { $.setShape(NoShape); } shape() { return $.pieceShape; } x($index) { return $.coords[$index][0]; } y ($index) { return $.coords[$index][1]; } setRandomShape() { $.setShape(qrand() % 7 + 1); } setShape($shape) { for (my $i = 0; $i < 4 ; $i++) { for (my $j = 0; $j < 2; ++$j) $.coords[$i][$j] = coordsTable[$shape][$i][$j]; } $.pieceShape = $shape; } minX() { my $min = $.coords[0][0]; for (my $i = 1; $i < 4; ++$i) $min = min($min, $.coords[$i][0]); return $min; } maxX() { my $max = $.coords[0][0]; for (my $i = 1; $i < 4; ++$i) $max = max($max, $.coords[$i][0]); return $max; } minY() { my $min = $.coords[0][1]; for (my $i = 1; $i < 4; ++$i) $min = min($min, $.coords[$i][1]); return $min; } maxY() { my $max = $.coords[0][1]; for (my $i = 1; $i < 4; ++$i) $max = max($max, $.coords[$i][1]); return $max; } rotatedLeft() { if ($.pieceShape == SquareShape) return $self; my $result = new TetrixPiece(); $result.pieceShape = $.pieceShape; for (my $i = 0; $i < 4; ++$i) { $result.setX($i, $.y ($i)); $result.setY($i, -$.x($i)); } return $result; } rotatedRight() { if ($.pieceShape == SquareShape) return $self; my $result = new TetrixPiece(); $result.pieceShape = $.pieceShape; for (my $i = 0; $i < 4; ++$i) { $result.setX($i, -$.y ($i)); $result.setY($i, $.x($i)); } return $result; } private setX($index, $x) { $.coords[$index][0] = $x; } private setY($index, $y) { $.coords[$index][1] = $y; } } class TetrixWindow inherits QWidget { private $.board, $.nextPieceLabel, $.scoreLcd, $.levelLcd, $.linesLcd, $.startButton, $.quitButton, $.pauseButton; constructor() { $.board = new TetrixBoard(); $.nextPieceLabel = new QLabel(); $.nextPieceLabel.setFrameStyle(QFrame::Box | QFrame::Raised); $.nextPieceLabel.setAlignment(Qt::AlignCenter); $.board.setNextPieceLabel($.nextPieceLabel); $.scoreLcd = new QLCDNumber(5); $.scoreLcd.setSegmentStyle(QLCDNumber::Filled); $.levelLcd = new QLCDNumber(2); $.levelLcd.setSegmentStyle(QLCDNumber::Filled); $.linesLcd = new QLCDNumber(5); $.linesLcd.setSegmentStyle(QLCDNumber::Filled); $.startButton = new QPushButton(TR("&Start")); $.startButton.setFocusPolicy(Qt::NoFocus); $.quitButton = new QPushButton(TR("&Quit")); $.quitButton.setFocusPolicy(Qt::NoFocus); $.pauseButton = new QPushButton(TR("&Pause")); $.pauseButton.setFocusPolicy(Qt::NoFocus); $.board.connect($.startButton, SIGNAL("clicked()"), SLOT("start()")); QAPP().connect($.quitButton , SIGNAL("clicked()"), SLOT("quit()")); $.board.connect($.pauseButton, SIGNAL("clicked()"), SLOT("pause()")); $.scoreLcd.connect($.board, SIGNAL("scoreChanged(int)"), SLOT("display(int)")); $.levelLcd.connect($.board, SIGNAL("levelChanged(int)"), SLOT("display(int)")); $.linesLcd.connect($.board, SIGNAL("linesRemovedChanged(int)"), SLOT("display(int)")); my $layout = new QGridLayout(); $layout.addWidget($.createLabel(TR("NEXT")), 0, 0); $layout.addWidget($.nextPieceLabel, 1, 0); $layout.addWidget($.createLabel(TR("LEVEL")), 2, 0); $layout.addWidget($.levelLcd, 3, 0); $layout.addWidget($.startButton, 4, 0); $layout.addWidget($.board, 0, 1, 6, 1); $layout.addWidget($.createLabel(TR("SCORE")), 0, 2); $layout.addWidget($.scoreLcd, 1, 2); $layout.addWidget($.createLabel(TR("LINES REMOVED")), 2, 2); $layout.addWidget($.linesLcd, 3, 2); $layout.addWidget($.quitButton, 4, 2); $layout.addWidget($.pauseButton, 5, 2); $.setLayout($layout); $.setWindowTitle(TR("Tetrix")); $.resize(550, 370); } private createLabel($text) { my $lbl = new QLabel($text); $lbl.setAlignment(Qt::AlignHCenter | Qt::AlignBottom); return $lbl; } } class tetrix_example inherits QApplication { constructor() { our $colorTable = ( new QColor(0x000000), new QColor(0xCC6666), new QColor(0x66CC66), new QColor(0x6666CC), new QColor(0xCCCC66), new QColor(0xCC66CC), new QColor(0x66CCCC), new QColor(0xDAAA00) ); my $window = new TetrixWindow(); $window.show(); qsrand(int(now() - get_midnight(now()))); $.exec(); } }