Warning

You're browsing the old v2.x documentation. This version is no longer maintained. Click here to go the v3.x documentation.

Step 3.2.2: Testing if a piece overlaps

The next one is the overlaps() function that is to check if a piece overlaps the blocks on the playfield. This is a key function as it has to run every time we're about to move or rotate a piece, to check whether a move is legal.

If you remember the previous steps, we have defined both the shape and the playfield as integers where each bit corresponds to a block. For this reason we can use bitwise operations to check if the piece overlaps. The only difficulty is to extract the individual parts of the piece that's comparable with a single row of the playfield. I'll try to explain this with the help of the following figure:

The blocks in black are parts of the plyfield and the ones in orange are parts of the piece. We will compare 4 times 2 integers, the rows of the palyfield to the rows of the piece. The former is easy, we just take the corresponding row from the palyfield array. But for the latter we will need bitwise operations to extract a row from the piece. So here's the plan:

  • Since each row in the piece is a 4-bit nibble, we can extract them by masking out the rest of the nibbles: the first row equals shape & %1111000000000000, the second row equals shape & %0000111100000000, etc…
  • We need to align the rows that we have just extracted. So we need to shift the second row 4 bits to the left, the third row 8 bits and the last row 12 bits. This way they'll all be placed in the left end.
  • Now we have to shift all of them to right as much as the piece's X position, to be able to compare them with the palyfield.

The following figure might help you to understand the above steps:

It is worth to code this operation into a separate function, because we will use it for another purpose later! Here is how it can be coded:

REM -- Extract a single row from the shape
FUN extract_row(shape, row!, x!)
  REM -- Step 1: mask the nibble
  REM -- Step 2: move it to the left end
  REM -- Step 3: move it right by the piece's  X position    
  RETURN RSHIFT(LSHIFT(shape & mask[row!], bitpos![row!]), x!)
  
  REM -- Helper data for bitwise calculations
  DATA mask[] = %1111000000000000, %0000111100000000, ~
                %0000000011110000, %0000000000001111
  DATA bitpos![] = 0, 4, 8, 12
ENDFUN

Unlike in CBM BASIC, the bitwise AND operator in XC=BASIC is the ampersand (&) character. The keyword AND is different. It is a conditional operator that can be used in an IF ... THEN statement.

When we have extracted all rows from the shape, we can compare them with the palyfield easily using the bitwise AND operation. If the AND operation results in true for any bit, it means there was an overlap.

Here is the overlaps() function:

REM -- Check if the piece overlaps the playfield
REM -- at the given position
REM -- Returns 1 or 0 (true or false)
FUN overlaps!(shape_no!, rotation!, x!, y!)
  REM -- Get shape by number and rotation
  tmp_shape = get_shape(shape_no!, rotation!)
  REM -- Check row by row
  FOR i! = 0 TO 3
    playfield_row = \playfield[i! + y!]
    piece_row = extract_row(tmp_shape, i!, x!)
    IF piece_row & playfield_row <> 0 THEN RETURN 1
  NEXT
  RETURN 0
ENDFUN

Read the above code carefully and notice the following:

  • The function returns a byte, so we've appended an ! to its name
  • We need to access the global variable playfield from within the function so we must prepend it with a backslash: \playfield

Are you still following? Good, let's move on to the next routine.