Aim of the game is to fill as many squares as possible within the time allowed. You score a point for every square that is filled and a bonus of 50 points for each full row of squares.
The code demonstrates many features of DXL that are not well documented.
// Tetris for DOORS /* Aim of the game is to fill as many squares as possible within the time allowed. You score a point for every square that is filled and a bonus of 50 points for each full row of squares. Use the cursor keys to control movement of the shapes as follows: LEFT Move shape to the left RIGHT Moves shape to the right UP Rotate shape DOWN Move shape down Limitation: There is no way in DXL to make the shapes fall automatically and capture keyboard input at the same time, so you have to move the shapes down yourself. To do: Store high scores in conf file. Allow shapes to be moved sideways underneath another shape. Make full rows disappear and move other rows down. Enjoy! smartDXL.com 17 May 2005 */ DB dbMain = null DBE dbeGrid = null /************************************ Grid ************************************/ const int SQUARE_SIZE = 25 const int SQUARE_EDGE = 2 const int BOARD_WIDTH = 12 const int BOARD_HEIGHT = 20 Array gridColours = create(BOARD_WIDTH, BOARD_HEIGHT) // Colour of each square /************************************ Shapes ************************************/ const int NUM_COORDS = 4 const int SHAPE_LINE[NUM_COORDS * 2] = { -2, 0, -1, 0, 0, 0, 1, 0 } const int SHAPE_SQUARE[NUM_COORDS * 2] = { -1, 1, 0, 1, 0, 0, -1, 0 } const int SHAPE_S[NUM_COORDS * 2] = { 1, 1, 0, 1, 0, 0, -1, 0 } const int SHAPE_Z[NUM_COORDS * 2] = { -1, 1, 0, 1, 0, 0, 1, 0 } const int SHAPE_RIGHT[NUM_COORDS * 2] = { -1, 0, 0, 0, 1, 0, 1, -1 } const int SHAPE_LEFT[NUM_COORDS * 2] = { -1, -1, -1, 0, 0, 0, 1, 0 } const int SHAPE_TRIANGLE[NUM_COORDS * 2] = { -1, 0, 0, 0, 1, 0, 0, 1 } const int LINE_SHAPE = 0 const int SQUARE_SHAPE = 1 const int S_SHAPE = 2 const int Z_SHAPE = 3 const int RIGHT_SHAPE = 4 const int LEFT_SHAPE = 5 const int TRIANGLE_SHAPE = 6 const int NUM_SHAPES = 7 /************************************ Current shape and its position and colour ************************************/ int shapeColour = 0 // Colour of current shape int shapeX = 0 // X-Coordinate of current shape int shapeY = 0 // Y-Coordinate of current shape int shapeCoords[NUM_COORDS * 2] = {} // Geometry of the current shape bool firstTime = true // Display welcome message if true /************************************ Scoring ************************************/ const int TIME_LIMIT = 120 // seconds int score = 0 int timeStart = 0 /************************************ startTimer ************************************/ void startTimer() { Date d = null d = today timeStart = intOf(d) } /************************************ setShape Update the coordinates of the current shape from the given array. ************************************/ void setShape(int coords[NUM_COORDS * 2]) { int i = 0 for (i = 0; i < NUM_COORDS * 2; i++) { shapeCoords[i] = coords[i] } } /************************************ getNextShape Get the next shape and its colour. ************************************/ bool getNextShape() { int shape = 0 int c = 0 // initial start position for new shape shapeX = BOARD_WIDTH / 2 shapeY = 0 // see if the square at the start position is full c = (int get(gridColours, shapeX, shapeY)) // empty squares are black if (c != realColor_Black) { // game over return(false) } // randomly select the next shape shape = random(NUM_SHAPES) if (shape == LINE_SHAPE) { setShape(SHAPE_LINE) shapeColour = realColor_Red } else if (shape == SQUARE_SHAPE) { setShape(SHAPE_SQUARE) shapeColour = realColor_Pink } else if (shape == S_SHAPE) { setShape(SHAPE_S) shapeColour = realColor_Blue } else if (shape == Z_SHAPE) { setShape(SHAPE_Z) shapeColour = realColor_Yellow } else if (shape == RIGHT_SHAPE) { setShape(SHAPE_RIGHT) shapeColour = realColor_Sea_Green } else if (shape == LEFT_SHAPE) { setShape(SHAPE_LEFT) shapeColour = realColor_Orange } else if (shape == TRIANGLE_SHAPE) { setShape(SHAPE_TRIANGLE) shapeColour = realColor_Thistle } return(true) } /************************************ paintSquare ************************************/ void paintSquare(int x, int y, int col) { int xCoord = 0 int yCoord = 0 xCoord = (x * SQUARE_SIZE) yCoord = (y * SQUARE_SIZE) realColour(dbeGrid, col) rectangle(dbeGrid, xCoord, yCoord, SQUARE_SIZE, SQUARE_SIZE) realColour(dbeGrid, realColor_Black) box(dbeGrid, xCoord, yCoord, SQUARE_SIZE, SQUARE_SIZE) } /************************************ paintShape ************************************/ void paintShape(int col) { int x = 0 int y = 0 int i = 0 // loop through shape coordinates and draw the squares for (i = 0; i < NUM_COORDS; i++) { x = shapeX + shapeCoords[i * 2] y = shapeY + shapeCoords[i * 2 + 1] paintSquare(x, y, col) put(gridColours, col, x, y) } } /************************************ inShape ************************************/ bool inShape(int x, int y) { int j = 0 int myX = 0 int myY = 0 // square isn't empty - see if it is part of current shape for (j = 0; j < NUM_COORDS; j++) { myX = shapeX + shapeCoords[j * 2] myY = shapeY + shapeCoords[j * 2 + 1] if (x == myX && y == myY) { return(true) } } return(false) } /************************************ getNewCoords Return the coordinates of the resulting shape, if the current shape was to be rotated by 90 degrees clockwise. ************************************/ void getNewCoords(int coords[NUM_COORDS * 2]) { int x = 0 int y = 0 int i = 0 for (i = 0; i < NUM_COORDS; i++) { x = shapeCoords[i * 2] y = shapeCoords[i * 2 + 1] coords[i * 2 + 1] = x coords[i * 2] = - y } } /************************************ canRotate Returns TRUE if the current shape can be rotated clockwise by 90 degrees without going off the grid or onto a square that is already full. ************************************/ bool canRotate() { int x = 0 int y = 0 int i = 0 int c = 0 int newCoords[NUM_COORDS * 2] = {} // find the proposed coordinates after the rotation getNewCoords(newCoords) // loop through shape coordinates and check the squares for (i = 0; i < NUM_COORDS; i++) { x = shapeX + newCoords[i * 2] y = shapeY + newCoords[i * 2 + 1] // see if the square is off the board if (x >= BOARD_WIDTH || x < 0 || y >= BOARD_HEIGHT) { return(false) } // see if the square is full c = (int get(gridColours, x, y)) // empty squares are black if (c != realColor_Black) { if (!inShape(x, y)) { // square if already full return(false) } } } return(true) } /************************************ canMoveTo Returns TRUE if the current shape can moved to the new position without going off the grid or onto a square that is already full. ************************************/ bool canMoveTo(int newX, int newY) { int x = 0 int y = 0 int i = 0 int c = 0 // loop through shape coordinates and check the squares for (i = 0; i < NUM_COORDS; i++) { x = newX + shapeCoords[i * 2] y = newY + shapeCoords[i * 2 + 1] // see if the square is off the board if (x >= BOARD_WIDTH || x < 0 || y >= BOARD_HEIGHT) { return(false) } // see if the square is full c = (int get(gridColours, x, y)) // empty squares are black if (c != realColor_Black) { if (!inShape(x, y)) { // square is already full return(false) } } } return(true) } /************************************ checkForFullRows When a complete row is full it is painted white. ************************************/ void checkForFullRows() { int x = 0 int y = 0 int c = 0 bool fullRow = true for (y = 0; y < BOARD_HEIGHT; y++) { fullRow = true for (x = 0; x < BOARD_WIDTH; x++) { c = (int get(gridColours, x, y)) // empty squares are black if (c == realColor_Black) { fullRow = false break } } if (fullRow) { // row is full for (x = 0; x < BOARD_WIDTH; x++) { paintSquare(x, y, realColor_White) put(gridColours, realColor_White, x, y) } } } } /************************************ drawWelcomeMessage ************************************/ void drawWelcomeMessage() { realColor(dbeGrid, realColor_White) font(dbeGrid, 4, HeadingsFont) draw(dbeGrid, 110, 40, "Welcome to") font(dbeGrid, 1, HeadingsFont) draw(dbeGrid, 73, 70, "Tetris for DOORS") paintSquare(4, 4, realColor_Red) paintSquare(5, 4, realColor_Red) paintSquare(6, 4, realColor_Red) paintSquare(7, 4, realColor_Red) realColor(dbeGrid, realColor_White) font(dbeGrid, 2, TextFont) draw(dbeGrid, 20, 150, "The aim of the game is to fill as many squares as") draw(dbeGrid, 20, 165, "possible within the time allowed. You score one") draw(dbeGrid, 20, 180, "point for each square that is filled and a bonus") draw(dbeGrid, 20, 195, "of 50 points for each full row.") draw(dbeGrid, 20, 220, "Unfortunately there is no way in DXL to make the") draw(dbeGrid, 20, 235, "shapes fall automatically and capture keyboard") draw(dbeGrid, 20, 250, "input at the same time so you have to control") draw(dbeGrid, 20, 265, "downward movement with the keyboard.") paintSquare(2, 11, realColor_Blue) paintSquare(2, 12, realColor_Blue) paintSquare(3, 12, realColor_Blue) paintSquare(3, 13, realColor_Blue) paintSquare(8, 12, realColor_Yellow) paintSquare(9, 12, realColor_Yellow) paintSquare(7, 13, realColor_Yellow) paintSquare(8, 13, realColor_Yellow) realColor(dbeGrid, realColor_White) draw(dbeGrid, 50, 380, "Use the cursor keys as follows:") draw(dbeGrid, 50, 400, "LEFT"); draw(dbeGrid, 110, 400, "Move shape to the left") draw(dbeGrid, 50, 420, "RIGHT"); draw(dbeGrid, 110, 420, "Moves shape to the right") draw(dbeGrid, 50, 440, "UP"); draw(dbeGrid, 110, 440, "Rotate shape") draw(dbeGrid, 50, 460, "DOWN"); draw(dbeGrid, 110, 460, "Move shape down") draw(dbeGrid, 90, 490, "HIT any key to begin ...") } /************************************ newGame ************************************/ void newGame() { int x = 0 int y = 0 // paint the grid background in black realBackground(dbeGrid, realColor_Black) // loop through and initialise all squares on the grid for (x = 0; x < BOARD_WIDTH; x++) { for (y = 0; y < BOARD_HEIGHT; y++) { // draw grid lines //realColor(dbeGrid, realColor_Grey66) //box(dbeGrid, x * SQUARE_SIZE, y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE) // all squares are black to start with put(gridColours, realColor_Black, x, y) } } // show instructions the first time we initialise the grid if (firstTime) { drawWelcomeMessage() } else { // get the first shape if (getNextShape()) { paintShape(shapeColour) } } // reset scoring score = 0 startTimer() } /************************************ gameOver ************************************/ void gameOver() { int y = 0 int c = 0 // count full rows for bonus points for (y = 0; y < BOARD_HEIGHT; y++) { c = (int get(gridColours, 0, y)) // all squares in a full row are white if (c == realColor_White) { score += 50 } } infoBox("GAME OVER\nYou scored " score " points") // start again newGame() } /************************************ checkTimer ************************************/ void checkTimer() { Date d = null int timeNow = null d = today timeNow = intOf(d) if (timeNow - timeStart > TIME_LIMIT) { gameOver() } } /************************************ moveRight ************************************/ void moveRight() { if (canMoveTo(shapeX + 1, shapeY)) { paintShape(realColor_Black) shapeX++ paintShape(shapeColour) } } /************************************ moveLeft ************************************/ void moveLeft() { if (canMoveTo(shapeX - 1, shapeY)) { paintShape(realColor_Black) shapeX-- paintShape(shapeColour) } } /************************************ moveDown ************************************/ void moveDown() { if (canMoveTo(shapeX, shapeY + 1)) { paintShape(realColor_Black) shapeY++ paintShape(shapeColour) // if the shape is resting on a full square then // it has reached the bottom and cannot be moved so get the next shape if (!canMoveTo(shapeX, shapeY + 1)) { score += 4 checkForFullRows() if (getNextShape()) { paintShape(shapeColour) } else { gameOver() } } } else { score += 4 checkForFullRows() if (getNextShape()) { paintShape(shapeColour) } else { gameOver() } } } /************************************ rotate ************************************/ void rotate() { int i = 0 int newCoords[NUM_COORDS * 2] = {} if (canRotate) { paintShape(realColor_Black) getNewCoords(newCoords) for (i = 0; i < NUM_COORDS * 2; i++) { shapeCoords[i] = newCoords[i] } paintShape(shapeColour) if (!canMoveTo(shapeX, shapeY + 1)) { score += 4 checkForFullRows() if (getNextShape()) { paintShape(shapeColour) } else { gameOver() } } } } /************************************ doDrawCanvas ************************************/ void doDrawCanvas(DBE dbe) { ; // do nothing } /************************************ doKeyboard Callback from keyboard. ************************************/ void doKeyboard(DBE cnv, char lastKey, bool ctrl, int x, int y) { if (firstTime) { firstTime = false startTimer() // hide welcome message by painting the grid background in black realBackground(dbeGrid, realColor_Black) // get the first shape if (getNextShape()) { paintShape(shapeColour) } } else { // check elapsed time since game started checkTimer() } if (lastKey == keyLeft) { moveLeft() } else if (lastKey == keyRight) { moveRight() } else if (lastKey == keyUp) { rotate() } else if (lastKey == keyDown) { moveDown() } } /************************************ doCloseAction Clean up data structures and destroy the dialog box. ************************************/ void doCloseAction(DB db) { delete(gridColours) hide(dbMain) destroy(dbMain) dbMain = null } /************************************ MAIN ************************************/ dbMain = create("Tetris", styleCentered | styleFixed) // magic number 12's make up for the pixels that are lost under the window frame dbeGrid = canvas(dbMain, (BOARD_WIDTH * SQUARE_SIZE + 12), (BOARD_HEIGHT * SQUARE_SIZE + 12), doDrawCanvas) close(dbMain, false, doCloseAction) realize dbMain set(dbeGrid, doKeyboard) newGame() show dbMain