The Bitwise Challenge Post-Mortem, Part 2

Hello again.

This is the second post in a series about the Bitwise Challenge, wherein I wrote a Tetris clone as an Addon in the upcoming MMO WildStar. Part 1 is here.

When I ended the last post, I had just written the code to create a new block, randomly selected from the 7 different Tetris blocks that we had defined. The next code I wrote was the code to rotate one of these blocks as it was falling rising.

function RotateBlockLeft(tOldBlock)
 local tBlock = {}
 for i = 1,4 do
   tBlock[i] = {}
 end

 -- 1 2 3 4     4 8 C G
 -- 5 6 7 8 --> 3 7 B F
 -- 9 A B C     2 6 A E
 -- D E F G     1 5 9 D

 tBlock[4][1] = tOldBlock[1][1]
 tBlock[3][1] = tOldBlock[1][2]
 tBlock[2][1] = tOldBlock[1][3]
 tBlock[1][1] = tOldBlock[1][4]
 tBlock[4][2] = tOldBlock[2][1]
 tBlock[3][2] = tOldBlock[2][2]
 tBlock[2][2] = tOldBlock[2][3]
 tBlock[1][2] = tOldBlock[2][4]
 tBlock[4][3] = tOldBlock[3][1]
 tBlock[3][3] = tOldBlock[3][2]
 tBlock[2][3] = tOldBlock[3][3]
 tBlock[1][3] = tOldBlock[3][4]
 tBlock[4][4] = tOldBlock[4][1]
 tBlock[3][4] = tOldBlock[4][2]
 tBlock[2][4] = tOldBlock[4][3]
 tBlock[1][4] = tOldBlock[4][4]

 return tBlock
end
function RotateBlockRight(tOldBlock)

 local tBlock = {}
 for i = 1,4 do
   tBlock[i] = {}
 end
 -- 1 2 3 4     D 9 5 1
 -- 5 6 7 8 --> E A 6 2
 -- 9 A B C     F B 7 3
 -- D E F G     G C 7 4

 tBlock[1][4] = tOldBlock[1][1]
 tBlock[2][4] = tOldBlock[1][2]
 tBlock[3][4] = tOldBlock[1][3]
 tBlock[4][4] = tOldBlock[1][4]
 tBlock[1][3] = tOldBlock[2][1]
 tBlock[2][3] = tOldBlock[2][2]
 tBlock[3][3] = tOldBlock[2][3]
 tBlock[4][3] = tOldBlock[2][4]
 tBlock[1][2] = tOldBlock[3][1]
 tBlock[2][2] = tOldBlock[3][2]
 tBlock[3][2] = tOldBlock[3][3]
 tBlock[4][2] = tOldBlock[3][4]
 tBlock[1][1] = tOldBlock[4][1]
 tBlock[2][1] = tOldBlock[4][2]
 tBlock[3][1] = tOldBlock[4][3]
 tBlock[4][1] = tOldBlock[4][4]

 return tBlock
end

This code evolved during the challenge a bit. When I first wrote it, it received a block as a parameter and did the transformation on the block passed in. (All Lua tables are passed by reference.) I changed it so that instead I create a new block from the passed block, transform it, and then return it as a new object. I’ll explain why when I show the code that calls this.

The code for rotation is pretty straightforward. You can see that I included some comments (in green) to show exactly what a rotated 4×4 block should look like before and after the transformation. I wrote those comments before I coded the logic so that I could refer to them as I was doing the assignments.

After writing those utility functions, it was time to think about the actual game object. I created a table called tGame and filled it with the information needed to keep track of a game.

local tGame = {}
 tGame.arField = {}
 tGame.arPixies = {}
 for y = 1,28 do
   tGame.arField[y] = {}
   tGame.arPixies[y] = {}
   for x = 1,10 do
     tGame.arField[y][x] = (y <= 3)
     tGame.arPixies[y][x] = {
       id = 0,
       strSprite = "WhiteFill",
       loc = {fPoints = {0, 0, 0, 0}, nOffsets = {0, 0, 32, 32}},
       cr = ApolloColor.new("xkcdAquaMarine")
     }
   end
 end
 tGame.nScore = 0
 tGame.fProgress = 0
 tGame.iColumn = 5
 tGame.iRow = 21
 tGame.tBlock = nil
 tGame.bGameOver = false
 tGame.arBlockPixies = {}
 for i = 1,16 do
   local tBlockPixie = {id = 0}
   tBlockPixie.strSprite = "WhiteFill"
   tBlockPixie.cr = ApolloColor.new("blue")
   tBlockPixie.loc = {}
   tBlockPixie.loc.fPoints = {0, 0, 0, 0}
   tBlockPixie.loc.nOffsets = {0, 0, 32, 32}
   tGame.arBlockPixies[i] = tBlockPixie
 end

Some of these, like nScore and bGameOver, are self-explanatory. The arField member is a 2D-array of boolean variables that represent where blocks have been placed in the game. If you can follow the logic there you’ll notice that I made it 28×10. This is also the first of the (way too many) magic variables I would add to the code. Even as I was typing them out, I knew it was wrong, but I was under a time constraint so I did it anyway. Sigh.

You may also wonder why the array has 28 rows, instead of just 21 as in a standard Tetris board. This is another thing that evolved as I was writing the code. Because of the way I would eventually write the logic to check whether a block could be placed, it was a lot easier to have 3 “dummy” rows of filled in blocks at the top of the board, and 4 “dummy” rows of empty blocks at the bottom. This explains the assignment of:

tGame.arField[y][x] = (y <= 3)

The arPixies member holds the actual pixies (an Apollo object for drawing sprites) that will draw the placed blocks. tBlock is set to nil but this will eventually hold the block that is rising. iColumn and iRow represent the position of the upper-left corner of the rising block. Finally fProgress represents the “progress” of the rising block, from 0 to 1. Whenever fProgress reaches 1, we will check to see if the block can be moved up; if it can, we do so and reset progress to 0. If it cannot, we will place the block and generate a new one.

I’ll talk about the logic for checking whether a block can be moved in the next post, hopefully tomorrow as sun and sand permit.

Bitwise out.

About Wiesman

Husband, father, video game developer, liberal, and perpetual Underdog.
This entry was posted in Videogames, WildStar and tagged , , , , . Bookmark the permalink.

2 Responses to The Bitwise Challenge Post-Mortem, Part 2

  1. Pingback: The Bitwise Challenge Post-Mortem, Part 3 | Some Disagree

  2. Pingback: The Bitwise Challenge Post-Mortem, Part 4 | Some Disagree

Disagree?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s