Tetris/Wren

Revision as of 09:33, 19 May 2022 by PureFox (talk | contribs) (Added code.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Translation of: Go
Library: raylib-wren

See Go entry for notes on playing. <lang ecmascript>import "raylib" for Raylib as rl, Color, Key, Vector2 import "random" for Random import "./fmt" for Fmt

var SQUARE_SIZE = 20 var GRID_HORIZONTAL_SIZE = 12 var GRID_VERTICAL_SIZE = 20 var LATERAL_SPEED = 10 var TURNING_SPEED = 12 var FAST_FALL_AWAIT_COUNTER = 30 var FADING_TIME = 33

var EMPTY = 0 var MOVING = 1 var FULL = 2 var BLOCK = 3 var FADING = 4

var screenWidth = 800 var screenHeight = 450

var gameOver = false var pause = false

// These variables keep track of the active piece position var piecePositionX = 0 var piecePositionY = 0

// These variables record the active and incoming piece colors var pieceColor = Color.gray var incomingPieceColor = Color.gray

// Statistics var level = 1 var lines = 0

// Based on level var gravitySpeed = 30

// Matrices var grid = List.filled(GRID_HORIZONTAL_SIZE, null) for (i in 0...grid.count) grid[i] = List.filled(GRID_VERTICAL_SIZE, 0) var piece = List.filled(4, null) var incomingPiece = List.filled(4, null) for (i in 0..3) {

   piece[i] = List.filled(4, 0)
   incomingPiece[i] = List.filled(4, 0)

}

// Game parameters var fadingColor = Color.gray var beginPlay = true var pieceActive = false var detection = false var lineToDelete = false

// Counters var gravityMovementCounter = 0 var lateralMovementCounter = 0 var turnMovementCounter = 0 var fastFallMovementCounter = 0 var fadeLineCounter = 0

// Other variables var keys = Key.keyboard var rand = Random.new()

// Initialize game variables var InitGame = Fn.new {

   // Initialize game statistics
   level = 1 // Always stays at this level in the current program
   lines = 0
   fadingColor = Color.gray
   piecePositionX = 0
   piecePositionY = 0
   pieceColor = Color.gray
   incomingPieceColor = Color.gray
   pause = false
   beginPlay = true
   pieceActive = false
   detection = false
   lineToDelete = false
   // Counters
   gravityMovementCounter = 0
   lateralMovementCounter = 0
   turnMovementCounter = 0
   fastFallMovementCounter = 0
   fadeLineCounter = 0
   gravitySpeed = 30
   // Initialize grid matrices
   for (i in 0...GRID_HORIZONTAL_SIZE) {
       for (j in 0...GRID_VERTICAL_SIZE) {
           if (j == GRID_VERTICAL_SIZE-1 || i == 0 || i == GRID_HORIZONTAL_SIZE-1) {
               grid[i][j] = BLOCK
           } else {
               grid[i][j] = EMPTY
           }
       }
   }
   // Initialize incoming piece matrices
   for (i in 0..3) {
       for (j in 0..3) incomingPiece[i][j] = EMPTY
   }

}

// Update game (one frame) var UpdateGame = Fn.new {

   if (!gameOver) {
       if (rl.isKeyPressed(keys["p"])) pause = !pause
       if (!pause) {
           if (!lineToDelete) {
               if (!pieceActive) {
                   // Get another piece
                   pieceActive = CreatePiece.call()
                   // We leave a little time before starting the fast falling down
                   fastFallMovementCounter = 0
               } else { // Piece falling
                   // Counters update
                   fastFallMovementCounter = fastFallMovementCounter + 1
                   gravityMovementCounter  = gravityMovementCounter + 1
                   lateralMovementCounter  = lateralMovementCounter + 1
                   turnMovementCounter     = turnMovementCounter + 1
                   // We make sure to move if we've pressed the key this frame
                   if (rl.isKeyPressed(keys["left"]) || rl.isKeyPressed(keys["right"])) {
                       lateralMovementCounter = LATERAL_SPEED
                   }
                   if (rl.isKeyPressed(keys["up"])) {
                       turnMovementCounter = TURNING_SPEED
                   }
                   // Fall down
                   if (rl.isKeyDown(keys["down"]) &&
                       fastFallMovementCounter >= FAST_FALL_AWAIT_COUNTER) {
                       // We make sure the piece is going to fall this frame
                       gravityMovementCounter = gravityMovementCounter + gravitySpeed
                   }
                   if (gravityMovementCounter >= gravitySpeed) {
                       // Basic falling movement
                       CheckDetection.call()
                       // Check if the piece has collided with another piece or with the boundaries
                       ResolveFallingMovement.call()
                       // Check if we completed a line and, if so, erase the line
                       // and pull down the the lines above
                       CheckCompletion.call()
                       gravityMovementCounter = 0
                   }
                   // Move laterally at player's will
                   if (lateralMovementCounter >= LATERAL_SPEED) {
                       // Update the lateral movement and, if successful, reset the lateral counter
                       if (!ResolveLateralMovement.call()) lateralMovementCounter = 0
                   }
                   // Turn the piece at player's will
                   if (turnMovementCounter >= TURNING_SPEED) {
                       // Update the turning movement and reset the turning counter
                       if (ResolveTurnMovement.call()) turnMovementCounter = 0
                   }
               }
               // Game over logic
               for (j in 0..1) {
                   for (i in 1...GRID_HORIZONTAL_SIZE-1) {
                       if (grid[i][j] == FULL) gameOver = true
                   }
               }
           } else {
               // Animation when deleting lines
               fadeLineCounter = fadeLineCounter + 1
               if (fadeLineCounter%8 < 4) {
                   fadingColor = Color.maroon
               } else {
                   fadingColor = Color.gray
               }
               if (fadeLineCounter >= FADING_TIME) {
                   DeleteCompleteLines.call()
                   fadeLineCounter = 0
                   lineToDelete = false
                   lines = lines + 1
               }
           }
       }
   } else if (rl.isKeyPressed(keys["enter"])) {
       InitGame.call()
       gameOver = false
   }

}

// Draw game (one frame) var DrawGame = Fn.new {

   rl.beginDrawing()
   rl.clearBackground(Color.white)
   if (!gameOver) {
       // Draw gameplay area
       var offset = Vector2.new()
       offset.x = (screenWidth/2).floor - (GRID_HORIZONTAL_SIZE*SQUARE_SIZE/2).floor - 50
       offset.y = (screenHeight/2).floor - ((GRID_VERTICAL_SIZE-1)*SQUARE_SIZE/2).floor + SQUARE_SIZE*2
       offset.y = offset.y - 50 // NOTE: Hard-coded position!
       var controller = offset.x
       for (j in 0...GRID_VERTICAL_SIZE) {
           for (i in 0...GRID_HORIZONTAL_SIZE) {
               // Draw each square of the grid
               var ox = offset.x.truncate
               var oy = offset.y.truncate
               if (grid[i][j] == EMPTY) {
                   rl.drawLine(ox, oy, ox+SQUARE_SIZE, oy, Color.lightGray)
                   rl.drawLine(ox, oy, ox, oy+SQUARE_SIZE, Color.lightGray)
                   rl.drawLine(ox+SQUARE_SIZE, oy, ox+SQUARE_SIZE, oy+SQUARE_SIZE, Color.lightGray)
                   rl.drawLine(ox, oy+SQUARE_SIZE, ox+SQUARE_SIZE, oy+SQUARE_SIZE, Color.lightGray)
                   offset.x = offset.x + SQUARE_SIZE
               } else if (grid[i][j] == FULL) {
                   rl.drawRectangle(ox, oy, SQUARE_SIZE, SQUARE_SIZE, Color.gray)
                   offset.x = offset.x + SQUARE_SIZE
               } else if (grid[i][j] == MOVING) {
                   rl.drawRectangle(ox, oy, SQUARE_SIZE, SQUARE_SIZE, pieceColor)
                   offset.x = offset.x + SQUARE_SIZE
               } else if (grid[i][j] == BLOCK) {
                   rl.drawRectangle(ox, oy, SQUARE_SIZE, SQUARE_SIZE, Color.lightGray)
                   offset.x = offset.x + SQUARE_SIZE
               } else if (grid[i][j] == FADING) {
                   rl.drawRectangle(ox, oy, SQUARE_SIZE, SQUARE_SIZE, fadingColor)
                   offset.x = offset.x + SQUARE_SIZE
               }
           }
           offset.x = controller
           offset.y = offset.y + SQUARE_SIZE
       }
       // Draw incoming piece (hard-coded)
       offset.x = 500
       offset.y = 45
       var controler = offset.x
       for (j in 0..3) {
           for (i in 0..3) {
               var ox = offset.x.truncate
               var oy = offset.y.truncate
               if (incomingPiece[i][j] == EMPTY) {
                   rl.drawLine(ox, oy, ox+SQUARE_SIZE, oy, Color.lightGray)
                   rl.drawLine(ox, oy, ox, oy+SQUARE_SIZE, Color.lightGray)
                   rl.drawLine(ox+SQUARE_SIZE, oy, ox+SQUARE_SIZE, oy+SQUARE_SIZE, Color.lightGray)
                   rl.drawLine(ox, oy+SQUARE_SIZE, ox+SQUARE_SIZE, oy+SQUARE_SIZE, Color.lightGray)
                   offset.x = offset.x + SQUARE_SIZE
               } else if (incomingPiece[i][j] == MOVING) {
                   rl.drawRectangle(ox, oy, SQUARE_SIZE, SQUARE_SIZE, incomingPieceColor)
                   offset.x = offset.x + SQUARE_SIZE
               }
           }
           offset.x = controler
           offset.y = offset.y + SQUARE_SIZE
       }
       var ox = offset.x.truncate
       var oy = offset.y.truncate
       rl.drawText("INCOMING:", ox, oy-100, 10, Color.gray)
       rl.drawText(Fmt.swrite("LINES:      $04d", lines), ox, oy+20, 10, Color.gray)
       if (pause) {
           var text = "GAME PAUSED"
           rl.drawText(text,
               (screenWidth/2).floor - text.count * 8,
               (screenHeight/2).floor-40, 40, Color.gray)
       }
   } else {
       var text = "PRESS [ENTER] TO PLAY AGAIN"
       rl.drawText(text, 
           (rl.screenWidth/2).floor - text.count * 8,
           (rl.screenHeight/2).floor-50, 20, Color.gray)
   }
   rl.endDrawing()

}

// Unload game variables var UnloadGame = Fn.new {

   // Nothing to do here

}

// Update and Draw (one frame) var UpdateDrawFrame = Fn.new {

   UpdateGame.call()
   DrawGame.call()

}

var CreatePiece = Fn.new {

   piecePositionX = ((GRID_HORIZONTAL_SIZE - 4) / 2).floor
   piecePositionY = 0
   // If the game is starting and we are going to create the first piece,
   // we create an extra one
   if (beginPlay) {
       GetRandomPiece.call()
       beginPlay = false
   }
   // We assign the incoming piece to the actual piece
   for (i in 0..3) {
       for (j in 0..3) piece[i][j] = incomingPiece[i][j]
   }
   pieceColor = incomingPieceColor
   // We assign a random piece to the incoming one
   GetRandomPiece.call()
   // Assign the piece to the grid
   for (i in piecePositionX..piecePositionX+3) {
       for (j in 0..3) {
           if (piece[i-piecePositionX][j] == MOVING) grid[i][j] = MOVING
       }
   }
   return true

}

var GetRandomPiece = Fn.new {

   var random = rand.int(7) // 0 to 6 inclusive
   for (i in 0..3) {
       for (j in 0..3) incomingPiece[i][j] = EMPTY
   }
   if (random == 0) { // O (Square)
       incomingPiece[1][1] = MOVING
       incomingPiece[2][1] = MOVING
       incomingPiece[1][2] = MOVING
       incomingPiece[2][2] = MOVING
       incomingPieceColor  = Color.yellow
   } else if (random == 1) { // L
       incomingPiece[1][0] = MOVING
       incomingPiece[1][1] = MOVING
       incomingPiece[1][2] = MOVING
       incomingPiece[2][2] = MOVING
       incomingPieceColor  = Color.blue
   } else if (random == 2) { // J (Inverted L)
       incomingPiece[1][2] = MOVING
       incomingPiece[2][0] = MOVING
       incomingPiece[2][1] = MOVING
       incomingPiece[2][2] = MOVING
       incomingPieceColor  = Color.brown
   } else if (random == 3) { // I (Straight)
       incomingPiece[0][1] = MOVING
       incomingPiece[1][1] = MOVING
       incomingPiece[2][1] = MOVING
       incomingPiece[3][1] = MOVING
       incomingPieceColor  = Color.skyBlue
   } else if (random == 4) { // T (Cross cut)
       incomingPiece[1][0] = MOVING
       incomingPiece[1][1] = MOVING
       incomingPiece[1][2] = MOVING
       incomingPiece[2][1] = MOVING
       incomingPieceColor  = Color.purple
   } else if (random == 5) { // S
       incomingPiece[1][1] = MOVING
       incomingPiece[2][1] = MOVING
       incomingPiece[2][2] = MOVING
       incomingPiece[3][2] = MOVING
       incomingPieceColor  = Color.green
   } else if (random == 6) { // Z (Inverted S)
       incomingPiece[1][2] = MOVING
       incomingPiece[2][2] = MOVING
       incomingPiece[2][1] = MOVING
       incomingPiece[3][1] = MOVING
       incomingPieceColor  = Color.red
   }

}

var ResolveFallingMovement = Fn.new {

   // If we've finished moving this piece, we stop it
   if (detection) {
       for (j in GRID_VERTICAL_SIZE-2..0) {
           for (i in 1...GRID_HORIZONTAL_SIZE-1) {
               if (grid[i][j] == MOVING) {
                   grid[i][j] = FULL
                   detection = false
                   pieceActive = false
               }
           }
       }
   } else { // We move down the piece
       for (j in GRID_VERTICAL_SIZE-2..0) {
           for (i in 1...GRID_HORIZONTAL_SIZE-1) {
               if (grid[i][j] == MOVING) {
                   grid[i][j+1] = MOVING
                   grid[i][j] = EMPTY
               }
           }
       }
       piecePositionY = piecePositionY + 1
   }

}

var ResolveLateralMovement = Fn.new {

   var collision = false
   // Piece movement
   if (rl.isKeyDown(keys["left"])) { // Move left
       // Check if it's possible to move to left
       for (j in GRID_VERTICAL_SIZE-2..0) {
           for (i in 1...GRID_HORIZONTAL_SIZE-1) {
               if (grid[i][j] == MOVING) {
                   // Check if we are touching the left wall or
                   // we have a full square at the left
                   if (i-1 == 0 || grid[i-1][j] == FULL) collision = true
               }
           }
       }
       // If able, move left
       if (!collision) {
           for (j in GRID_VERTICAL_SIZE-2..0) {
               // We check the matrix from left to right
               for (i in 1...GRID_HORIZONTAL_SIZE-1) {
                   // Move everything to the left
                   if (grid[i][j] == MOVING) {
                       grid[i-1][j] = MOVING
                       grid[i][j] = EMPTY
                   }
               }
           }
           piecePositionX = piecePositionX - 1
       }
   } else if (rl.isKeyDown(keys["right"])) { // Move right
       // Check if it's possible to move to right
       for (j in GRID_VERTICAL_SIZE-2..0) {
           for (i in 1...GRID_HORIZONTAL_SIZE-1) {
               if (grid[i][j] == MOVING) {
                   // Check if we are touching the right wall or
                   // we have a full square at the right
                   if (i+1 == GRID_HORIZONTAL_SIZE-1 || grid[i+1][j] == FULL) {
                       collision = true
                   }
               }
           }
       }
       // If able, move right
       if (!collision) {
           for (j in GRID_VERTICAL_SIZE-2..0) {
               // We check the matrix from right to left
               for (i in GRID_HORIZONTAL_SIZE-1..1) {
                   // Move everything to the right
                   if (grid[i][j] == MOVING) {
                       grid[i+1][j] = MOVING
                       grid[i][j] = EMPTY
                   }
               }
           }
           piecePositionX = piecePositionX + 1
       }
   }
   return collision

}

var ResolveTurnMovement = Fn.new {

   // Input for turning the piece
   if (rl.isKeyDown(keys["up"])) {
       var aux = 0
       var next = false
       // Check all turning possibilities
       if (grid[piecePositionX+3][piecePositionY] == MOVING &&
           grid[piecePositionX][piecePositionY] != EMPTY &&
           grid[piecePositionX][piecePositionY] != MOVING) {
           next = true
       } else if (grid[piecePositionX+3][piecePositionY+3] == MOVING &&
           grid[piecePositionX+3][piecePositionY] != EMPTY &&
           grid[piecePositionX+3][piecePositionY] != MOVING) {
           next = true
       } else if (grid[piecePositionX][piecePositionY+3] == MOVING &&
           grid[piecePositionX+3][piecePositionY+3] != EMPTY &&
           grid[piecePositionX+3][piecePositionY+3] != MOVING) {
           next = true
       } else if (grid[piecePositionX][piecePositionY] == MOVING &&
           grid[piecePositionX][piecePositionY+3] != EMPTY &&
           grid[piecePositionX][piecePositionY+3] != MOVING) {
           next = true
       } else if (grid[piecePositionX+1][piecePositionY] == MOVING &&
           grid[piecePositionX][piecePositionY+2] != EMPTY &&
           grid[piecePositionX][piecePositionY+2] != MOVING) {
           next = true
       } else if (grid[piecePositionX+3][piecePositionY+1] == MOVING &&
           grid[piecePositionX+1][piecePositionY] != EMPTY &&
           grid[piecePositionX+1][piecePositionY] != MOVING) {
           next = true
       } else if (grid[piecePositionX+2][piecePositionY+3] == MOVING &&
           grid[piecePositionX+3][piecePositionY+1] != EMPTY &&
           grid[piecePositionX+3][piecePositionY+1] != MOVING) {
           next = true
       } else if (grid[piecePositionX][piecePositionY+2] == MOVING &&
           grid[piecePositionX+2][piecePositionY+3] != EMPTY &&
           grid[piecePositionX+2][piecePositionY+3] != MOVING) {
           next = true
       } else if (grid[piecePositionX+2][piecePositionY] == MOVING &&
           grid[piecePositionX][piecePositionY+1] != EMPTY &&
           grid[piecePositionX][piecePositionY+1] != MOVING) {
           next = true
       } else if (grid[piecePositionX+3][piecePositionY+2] == MOVING &&
           grid[piecePositionX+2][piecePositionY] != EMPTY &&
           grid[piecePositionX+2][piecePositionY] != MOVING) {
           next = true
       } else if (grid[piecePositionX+1][piecePositionY+3] == MOVING &&
           grid[piecePositionX+3][piecePositionY+2] != EMPTY &&
           grid[piecePositionX+3][piecePositionY+2] != MOVING) {
           next = true
       } else if (grid[piecePositionX][piecePositionY+1] == MOVING &&
           grid[piecePositionX+1][piecePositionY+3] != EMPTY &&
           grid[piecePositionX+1][piecePositionY+3] != MOVING) {
           next = true
       } else if (grid[piecePositionX+1][piecePositionY+1] == MOVING &&
           grid[piecePositionX+1][piecePositionY+2] != EMPTY &&
           grid[piecePositionX+1][piecePositionY+2] != MOVING) {
           next = true
       } else if (grid[piecePositionX+2][piecePositionY+1] == MOVING &&
           grid[piecePositionX+1][piecePositionY+1] != EMPTY &&
           grid[piecePositionX+1][piecePositionY+1] != MOVING) {
           next = true
       } else if (grid[piecePositionX+2][piecePositionY+2] == MOVING &&
           grid[piecePositionX+2][piecePositionY+1] != EMPTY &&
           grid[piecePositionX+2][piecePositionY+1] != MOVING) {
           next = true
       } else if (grid[piecePositionX+1][piecePositionY+2] == MOVING &&
           grid[piecePositionX+2][piecePositionY+2] != EMPTY &&
           grid[piecePositionX+2][piecePositionY+2] != MOVING) {
           next = true
       }
       if (!next) {
           aux = piece[0][0]
           piece[0][0] = piece[3][0]
           piece[3][0] = piece[3][3]
           piece[3][3] = piece[0][3]
           piece[0][3] = aux
           aux = piece[1][0]
           piece[1][0] = piece[3][1]
           piece[3][1] = piece[2][3]
           piece[2][3] = piece[0][2]
           piece[0][2] = aux
           aux = piece[2][0]
           piece[2][0] = piece[3][2]
           piece[3][2] = piece[1][3]
           piece[1][3] = piece[0][1]
           piece[0][1] = aux
           aux = piece[1][1]
           piece[1][1] = piece[2][1]
           piece[2][1] = piece[2][2]
           piece[2][2] = piece[1][2]
           piece[1][2] = aux
       }
       for (j in GRID_VERTICAL_SIZE-2..0) {
           for (i in 1...GRID_HORIZONTAL_SIZE-1) {
               if (grid[i][j] == MOVING) grid[i][j] = EMPTY
           }
       }
       for (i in piecePositionX..piecePositionX+3) {
           for (j in piecePositionY..piecePositionY+3) {
               if (piece[i-piecePositionX][j-piecePositionY] == MOVING) grid[i][j] = MOVING
           }
       }
       return true
   }
   return false

}

var CheckDetection = Fn.new {

   for (j in GRID_VERTICAL_SIZE-2..0) {
       for (i in 1...GRID_HORIZONTAL_SIZE-1) {
           if (grid[i][j] == MOVING && (grid[i][j+1] == FULL || grid[i][j+1] == BLOCK)) {
               detection = true
               return
           }
       }
   }

}

var CheckCompletion = Fn.new {

   for (j in GRID_VERTICAL_SIZE-2..0) {
       var calculator = 0
       for (i in 1...GRID_HORIZONTAL_SIZE-1) {
           // Count each square of the line
           if (grid[i][j] == FULL) calculator = calculator + 1
           // Check if we completed the whole line
           if (calculator == GRID_HORIZONTAL_SIZE-2) {
               lineToDelete = true
               calculator = 0
               // Mark the completed line
               for (z in 1...GRID_HORIZONTAL_SIZE-1) grid[z][j] = FADING
           }
       }
   }

}

var DeleteCompleteLines = Fn.new() {

   for (j in GRID_VERTICAL_SIZE-2..0) {
       while (grid[1][j] == FADING) {
           for (i in 1...GRID_HORIZONTAL_SIZE-1) {
               grid[i][j] = EMPTY
           }
           var j2 = j - 1
           while (j2 >= 0) {
               for (i2 in 1...GRID_HORIZONTAL_SIZE-1) {
                   if (grid[i2][j2] == FULL) {
                       grid[i2][j2+1] = FULL
                       grid[i2][j2] = EMPTY
                   } else if (grid[i2][j2] == FADING) {
                       grid[i2][j2+1] = FADING
                       grid[i2][j2] = EMPTY
                   }
               }
               j2 = j2 - 1
           }
       }
   }

}

rl.initWindow(screenWidth, screenHeight, "Tetris") InitGame.call() rl.targetFPS = 60 while (!rl.windowShouldClose) { // Detect window close button or ESC key

   UpdateDrawFrame.call()

} UnloadGame.call() rl.closeWindow()</lang>