|  | Home · Examples

# Code for Tetrix Example

    /****************************************************************************
    **
    ** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
    **
    ** This file is part of Qt Jambi.
    **
    ** ** This file may be used under the terms of the GNU General Public
    ** License version 2.0 as published by the Free Software Foundation
    ** and appearing in the file LICENSE.GPL included in the packaging of
    ** this file.  Please review the following information to ensure GNU
    ** General Public Licensing requirements will be met:
    ** http://www.trolltech.com/products/qt/opensource.html
    **
    ** If you are unsure which license is appropriate for your use, please
    ** review the following information:
    ** http://www.trolltech.com/products/qt/licensing.html or contact the
    ** sales department at sales@trolltech.com.

    **
    ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
    ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
    **
    ****************************************************************************/

    package com.trolltech.examples;

    import com.trolltech.qt.core.*;
    import com.trolltech.qt.gui.*;

    import java.util.*;

    class TetrixBoard extends QFrame
    {
        static final int redTable[] = new int[8];
        static final int greenTable[] = new int[8];
        static final int blueTable[] = new int[8];
        static {
            redTable[0] = 0x00;
            redTable[1] = 0xCC;
            redTable[2] = 0x66;
            redTable[3] = 0x66;
            redTable[4] = 0xCC;
            redTable[5] = 0xCC;
            redTable[6] = 0x66;
            redTable[7] = 0xDA;

            greenTable[0] = 0x00;
            greenTable[1] = 0x66;
            greenTable[2] = 0xCC;
            greenTable[3] = 0x66;
            greenTable[4] = 0xCC;
            greenTable[5] = 0x66;
            greenTable[6] = 0xCC;
            greenTable[7] = 0xAA;

            blueTable[0] = 0x00;
            blueTable[1] = 0x66;
            blueTable[2] = 0x66;
            blueTable[3] = 0xCC;
            blueTable[4] = 0x66;
            blueTable[5] = 0xCC;
            blueTable[6] = 0xCC;
            blueTable[7] = 0x00;

        };

        enum TetrixShape { NoShape, ZShape, SShape, LineShape, TShape, SquareShape,
                       LShape, MirroredLShape }
        private static final int BoardWidth = 10;
        private static final int BoardHeight = 22;

        private boolean isStarted = false;
        private boolean isPaused = false;
        private boolean isWaitingAfterLine = false;
        private int numLinesRemoved = 0;
        private int numPiecesDropped = 0;
        private int score = 0;
        private int level = 0;
        private int curX = 0;
        private int curY = 0;
        private QLabel nextPieceLabel = null;
        private QBasicTimer timer = new QBasicTimer();
        private TetrixPiece curPiece = new TetrixPiece();
        private TetrixPiece nextPiece = new TetrixPiece();
        private TetrixShape board[] = new TetrixShape[BoardWidth * BoardHeight];

        public Signal1<Integer> scoreChanged = new Signal1<Integer>();
        public Signal1<Integer> levelChanged = new Signal1<Integer>();
        public Signal1<Integer> linesRemovedChanged = new Signal1<Integer>();

        public TetrixBoard(QWidget parent)
        {
            super(parent);

            setFrameStyle(QFrame.Shape.Panel.value() | QFrame.Shadow.Sunken.value());
            setFocusPolicy(Qt.FocusPolicy.StrongFocus);
            clearBoard();

            nextPiece.setRandomShape();
        }

        public void scoreChanged(int score)
        {
            scoreChanged.emit(score);
        }

        public void levelChanged(int level)
        {
            levelChanged.emit(level);
        }

        public void linesRemovedChanged(int numLines)
        {
            linesRemovedChanged.emit(numLines);
        }

        public void setNextPieceLabel(QLabel label)
        {
            nextPieceLabel = label;
        }

        TetrixShape shapeAt(int x, int y)
        {
            return board[x + y * BoardWidth];
        }

        void setShapeAt(int x, int y, TetrixShape shape)
        {
            board[x + y * BoardWidth] = shape;
        }

        int timeoutTime()
        {
            return 1000 / (1 + level);
        }

        int squareWidth() { return contentsRect().width() / BoardWidth; }

        int squareHeight() { return contentsRect().height() / BoardHeight; }

        @Override
        public QSize sizeHint()
        {
            return new QSize(BoardWidth * 15 + frameWidth() * 2, BoardHeight * 15 + frameWidth() * 2);
        }

        @Override
        public QSize minimumSizeHint()
        {
            return new QSize(BoardWidth * 5 + frameWidth() * 2, BoardHeight * 5 + frameWidth() * 2);
        }

        public void start()
        {
            if (isPaused)
                return ;

            isStarted = true;
            isWaitingAfterLine = false;
            numLinesRemoved = 0;
            numPiecesDropped = 0;
            score = 0;
            level = 1;
            clearBoard();

            linesRemovedChanged(numLinesRemoved);
            scoreChanged(score);
            levelChanged(level);

            newPiece();

            timer.start(timeoutTime(), this);
        }

        public void pause()
        {
            if (!isStarted)
                return ;

            isPaused = !isPaused;
            if (isPaused) {
                timer.stop();
            } else {
                timer.start(timeoutTime(), this);
            }

            update();
        }

        @Override
        protected void paintEvent(QPaintEvent e)
        {
            super.paintEvent(e);

            QPainter painter = new QPainter();
            painter.begin(this);
            QRect rect = contentsRect();

            if (isPaused) {
                painter.drawText(rect, Qt.AlignmentFlag.AlignCenter.value(), "Pause");
                painter.end();
                return ;
            }

            int boardTop = rect.bottom() - BoardHeight * squareHeight();

            for (int i=0; i<BoardHeight; ++i) {
                for (int j=0; j<BoardWidth; ++j) {
                    TetrixShape shape = shapeAt(j, BoardHeight - i - 1);
                    if (shape != TetrixShape.NoShape) {
                        drawSquare(painter, rect.left() + j * squareWidth(),
                            boardTop + i * squareHeight(), shape);
                    }
                }
            }

            if (curPiece.shape() != TetrixShape.NoShape) {
                for (int i=0; i<4; ++i) {
                    int x = curX + curPiece.x(i);
                    int y = curY - curPiece.y(i);

                    drawSquare(painter, rect.left() + x * squareWidth(),
                        boardTop + (BoardHeight - y - 1) * squareHeight(),
                        curPiece.shape());
                }
            }

            painter.end();
        }

        @Override
        protected void keyPressEvent(QKeyEvent event)
        {
            if (!isStarted || isPaused || curPiece.shape() == TetrixShape.NoShape) {
                super.keyPressEvent(event);
                return ;
            }

            if (event.key() == Qt.Key.Key_Left.value())
                tryMove(curPiece, curX - 1, curY);
            else if (event.key() == Qt.Key.Key_Right.value())
                tryMove(curPiece, curX + 1, curY);
            else if (event.key() == Qt.Key.Key_Down.value())
                tryMove(curPiece.rotatedRight(), curX, curY);
            else if (event.key() == Qt.Key.Key_Up.value())
                tryMove(curPiece.rotatedLeft(), curX, curY);
            else if (event.key() == Qt.Key.Key_Space.value())
                dropDown();
            else if (event.key() == Qt.Key.Key_D.value())
                oneLineDown();
            else
                super.keyPressEvent(event);
        }

        @Override
        protected void timerEvent(QTimerEvent event)
        {
            if (event.timerId() == timer.timerId()) {
                if (isWaitingAfterLine) {
                    isWaitingAfterLine = false;
                    newPiece();
                    timer.start(timeoutTime(), this);
                } else {
                    oneLineDown();
                }
            } else {
                super.timerEvent(event);
            }
        }

        void clearBoard()
        {
            for (int i=0; i<BoardHeight * BoardWidth; ++i)
                board[i] = TetrixShape.NoShape;
        }

        void dropDown()
        {
            int dropHeight = 0;
            int newY = curY;
            while (newY > 0) {
                if (!tryMove(curPiece, curX, newY - 1))
                    break ;
                --newY;
                ++dropHeight;
            }
            pieceDropped(dropHeight);
        }

        void oneLineDown()
        {
            if (!tryMove(new TetrixPiece(curPiece), curX, curY - 1))
                pieceDropped(0);
        }

        void pieceDropped(int dropHeight)
        {
            for (int i=0; i<4; ++i) {
                int x = curX + curPiece.x(i);
                int y = curY - curPiece.y(i);
                setShapeAt(x, y, curPiece.shape());
            }

            ++numPiecesDropped;
            if (numPiecesDropped % 25 == 0) {
                ++level;
                timer.start(timeoutTime(), this);
                levelChanged(level);
            }

            score += dropHeight + 7;
            scoreChanged(score);

            removeFullLines();

            if (!isWaitingAfterLine)
                newPiece();
        }

        void removeFullLines()
        {
            int numFullLines = 0;

            for (int i=BoardHeight - 1; i >= 0; --i) {
                boolean lineIsFull = true;

                for (int j=0; j<BoardWidth; ++j) {
                    if (shapeAt(j, i) == TetrixShape.NoShape) {
                        lineIsFull = false;
                        break ;
                    }
                }

                if (lineIsFull) {
                    ++numFullLines;
                    for (int k=i; k<BoardHeight - 1; ++k) {
                        for (int j=0; j<BoardWidth; ++j)
                            setShapeAt(j, k, shapeAt(j, k + 1));
                    }
                    for (int j=0; j<BoardWidth; ++j)
                        setShapeAt(j, BoardHeight - 1, TetrixShape.NoShape);
                }
            }

            if (numFullLines > 0) {
                numLinesRemoved += numFullLines;
                score += 10 * numFullLines;
                linesRemovedChanged(numLinesRemoved);
                scoreChanged(score);

                timer.start(500, this);
                isWaitingAfterLine = true;
                curPiece.setShape(TetrixShape.NoShape);
                update();
            }
        }

        void newPiece()
        {
            curPiece = new TetrixPiece(nextPiece);

            nextPiece.setRandomShape();
            showNextPiece();
            curX = BoardWidth / 2 + 1;
            curY = BoardHeight - 1 + curPiece.minY();

            if (!tryMove(curPiece, curX, curY)) {
                curPiece.setShape(TetrixShape.NoShape);
                timer.stop();
                isStarted = false;
            }
        }

        void showNextPiece()
        {
            if (nextPieceLabel == null)
                return ;

            int dx = nextPiece.maxX() - nextPiece.minX() + 1;
            int dy = nextPiece.maxY() - nextPiece.minY() + 1;

            QPixmap pixmap = new QPixmap(dx * squareWidth(), dy * squareHeight());
            QPainter painter = new QPainter();
            painter.begin(pixmap);
            painter.fillRect(pixmap.rect(), nextPieceLabel.palette().window());

            for (int i=0; i<4; ++i) {
                int x = nextPiece.x(i) - nextPiece.minX();
                int y = nextPiece.y(i) - nextPiece.minY();
                drawSquare(painter, x * squareWidth(), y * squareHeight(), nextPiece.shape());
            }
            painter.end();

            nextPieceLabel.setPixmap(pixmap);
        }

        boolean tryMove(TetrixPiece newPiece, int newX, int newY)
        {
            for (int i = 0; i < 4; ++i) {
                int x = newX + newPiece.x(i);
                int y = newY - newPiece.y(i);
                if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)
                    return false;
                if (shapeAt(x, y) != TetrixShape.NoShape)
                    return false;
            }

            curPiece = new TetrixPiece(newPiece);
            curX = newX;
            curY = newY;
            update();
            return true;
        }

        void drawSquare(QPainter painter, int x, int y, TetrixShape shape)
        {
            QColor color = new QColor(redTable[shape.ordinal()], greenTable[shape.ordinal()], blueTable[shape.ordinal()]);
            painter.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2,
                new QBrush(color));

            painter.setPen(color.lighter());
            painter.drawLine(x, y + squareHeight() - 1, x, y);
            painter.drawLine(x, y, x + squareWidth() - 1, y);

            painter.setPen(color.darker());
            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);
        }
    }

    class TetrixPiece
    {
        static final int 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 } } };

        private TetrixBoard.TetrixShape pieceShape;
        private int coords[][] = new int[4][2];

        public TetrixPiece()
        {
            setShape(TetrixBoard.TetrixShape.NoShape);
        }

        public TetrixPiece(TetrixPiece copy)
        {
            pieceShape = copy.shape();
            for (int i=0; i<4; ++i) {
                setX(i, copy.x(i));
                setY(i, copy.y(i));
            }
        }

        public void setRandomShape()
        {
            Random rand = new Random();
            int shapeint = rand.nextInt(7) + 1;

            TetrixBoard.TetrixShape shape = TetrixBoard.TetrixShape.NoShape;
            switch (shapeint) {
            case 1: shape = TetrixBoard.TetrixShape.ZShape; break ;
            case 2: shape = TetrixBoard.TetrixShape.SShape; break ;
            case 3: shape = TetrixBoard.TetrixShape.LineShape; break ;
            case 4: shape = TetrixBoard.TetrixShape.TShape; break ;
            case 5: shape = TetrixBoard.TetrixShape.SquareShape; break ;
            case 6: shape = TetrixBoard.TetrixShape.LShape; break ;
            case 7: shape = TetrixBoard.TetrixShape.MirroredLShape; break ;
            }

            setShape(shape);
        }

        public TetrixBoard.TetrixShape shape()
        {
            return pieceShape;
        }

        public void setShape(TetrixBoard.TetrixShape shape)
        {
            for (int i=0; i<4; ++i) {
                for (int j=0; j<2; ++j)
                    coords[i][j] = coordsTable[shape.ordinal()][i][j];
            }

            pieceShape = shape;
        }

        public int minX()
        {
            int min = coords[0][0];
            for (int i=1; i<4; ++i)
                min = min < coords[i][0] ? min : coords[i][0];
            return min;
        }

        public int maxX()
        {
            int max = coords[0][0];
            for (int i=1; i<4; ++i)
                max = max > coords[i][0] ? max : coords[i][0];
            return max;
        }

        public int minY()
        {
            int min = coords[0][1];
            for (int i=1; i<4; ++i)
                min = min < coords[i][1] ? min : coords[i][1];
            return min;
        }

        public int maxY()
        {
            int max = coords[0][1];
            for (int i=1; i<4; ++i)
                max = max > coords[i][1] ? max : coords[i][1];
            return max;
        }

        public TetrixPiece rotatedLeft()
        {
            if (pieceShape == TetrixBoard.TetrixShape.SquareShape)
                return this;

            TetrixPiece result = new TetrixPiece();
            result.pieceShape = pieceShape;
            for (int i=0; i<4; ++i) {
                result.setX(i, y(i));
                result.setY(i, -x(i));
            }

            return result;
        }

        public TetrixPiece rotatedRight()
        {
            if (pieceShape == TetrixBoard.TetrixShape.SquareShape)
                return this;

            TetrixPiece result = new TetrixPiece();
            result.pieceShape = pieceShape;
            for (int i=0; i<4; ++i) {
                result.setX(i, -y(i));
                result.setY(i, x(i));
            }

            return result;
        }

        public int x(int index)
        {
            return coords[index][0];
        }

        public int y(int index)
        {
            return coords[index][1];
        }

        private void setX(int index, int x)
        {
            coords[index][0] = x;
        }

        private void setY(int index, int y)
        {
            coords[index][1] = y;
        }
    }

    @QtJambiExample(name = "Tetrix")
    public class Tetrix extends QWidget
    {
        public Tetrix() {
            this(null);
        }

        public Tetrix(QWidget parent)
        {
            super(parent);

            board = new TetrixBoard(null);

            nextPieceLabel = new QLabel();
            nextPieceLabel.setFrameStyle(QFrame.Shape.Box.value() | QFrame.Shadow.Raised.value());
            nextPieceLabel.setAlignment(new Qt.Alignment(Qt.AlignmentFlag.AlignCenter));
            board.setNextPieceLabel(nextPieceLabel);

            scoreLcd = new QLCDNumber(5);
            scoreLcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled);

            levelLcd = new QLCDNumber(2);
            levelLcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled);

            linesLcd = new QLCDNumber(5);
            linesLcd.setSegmentStyle(QLCDNumber.SegmentStyle.Filled);

            startButton = new QPushButton("&Start");
            startButton.setFocusPolicy(Qt.FocusPolicy.NoFocus);
            quitButton = new QPushButton("&Quit");
            quitButton.setFocusPolicy(Qt.FocusPolicy.NoFocus);
            pauseButton = new QPushButton("&Pause");
            pauseButton.setFocusPolicy(Qt.FocusPolicy.NoFocus);

            startButton.clicked.connect(board, "start()");
            quitButton.clicked.connect(this, "close()");
            pauseButton.clicked.connect(board, "pause()");
            board.scoreChanged.connect(scoreLcd, "display(int)");
            board.levelChanged.connect(levelLcd, "display(int)");
            board.linesRemovedChanged.connect(linesLcd, "display(int)");

            layout = new QGridLayout();
            layout.addWidget(createLabel("NEXT"), 0, 0);
            layout.addWidget(nextPieceLabel, 1, 0);
            layout.addWidget(createLabel("LEVEL"), 2, 0);
            layout.addWidget(levelLcd, 3, 0);
            layout.addWidget(startButton, 4, 0);
            layout.addWidget(board, 0, 1, 6, 1);
            layout.addWidget(createLabel("SCORE"), 0, 2);
            layout.addWidget(scoreLcd, 1, 2);
            layout.addWidget(createLabel("LINES REMOVED"), 2, 2);
            layout.addWidget(linesLcd, 3, 2);
            layout.addWidget(quitButton, 4, 2);
            layout.addWidget(pauseButton, 5, 2);

            setLayout(layout);

            setWindowTitle("Tetrix");
            setWindowIcon(new QIcon("classpath:com/trolltech/images/qt-logo.png"));
            resize(550, 370);
        }

        private QLabel createLabel(String text)
        {
            QLabel lbl = new QLabel(text);
            lbl.setAlignment(new Qt.Alignment(Qt.AlignmentFlag.AlignBottom));
            return lbl;
        }

        private TetrixBoard board = null;
        private QPushButton startButton = null;
        private QPushButton quitButton = null;
        private QPushButton pauseButton = null;
        private QLabel nextPieceLabel = null;
        private QLCDNumber scoreLcd = null;
        private QLCDNumber levelLcd = null;
        private QLCDNumber linesLcd = null;
        private QGridLayout layout = null;

        public static void main(String args[])
        {
            QApplication.initialize(args);

            Tetrix window = new Tetrix();
            window.show();

            QApplication.exec();
        }
    }