Reading and Writing to ByteBoard

For this part of the work I’m incredibly thankful to Maksym for his article on bitwise operators in Solidity. It was actually the first time I understood what bit operators were and how to use them—I know I’m late to the game but better late than never. After going over the idea of masking I set about creating a method for reading from my ByteBoard.

Reading from ByteBoard

To read a selection of a binary string you need to first decide where that section is, create a mask of 1s, shift them over to that desired read point, and use an & operator to extract them. Finally shift them back the same amount and you’ve got your value. Before building the mask I needed to find out where my desired read point was.

I made a function that took column and row positions to help me determine the read point. I know that each tile consists of two bits to make up a Black tile 0x01 a White tile 0x10 or an Empty tile 0x11 so whatever my position is I’ll have to double it eventually. I also know that each Row has 8 tiles so my placement would be (8 * col) + row . All of that is a description of how far from the beginning of the binary string it would be, but I need to know how far it is from the end, so I subtract that value from the total length of the 8x8 board (64 — (8*col) + row) . This would bring me to the end of my desired read location so I add back 1 place for the span of the mask and multiply by 2 for each bit:

uint128 shiftAmount = posToPush(col, row); function posToPush (uint8 col, uint8 row) returns(uint128) {

return uint128( ( (64) - ( (8 * col) + row + 1) ) * 2);

}

Now I build a mask of two bits in length with both bits turned on ( 0x11 ). This will get moved on top of the read point of my target string in a new and otherwise empty string. The & bitwise operator will return a new string where the only bits on will be present in both previous strings. That way all of the tiles around my target will result in 0, and only the bits that were also present in my target will persist:

function returnTile (bytes16 board, uint8 col, uint8 row) returns (uint8){

uint128 push = posToPush(col, row); bytes16 mask = bytes16(3);

// 0b00000011 (mask) mask = shiftLeft(mask, push);

// 0b00110000 (mask shifted if push was equal to 4) bytes16 before = board & mask;

// (board)0b01010101

// & (mask)0b00110000

// = (tile)0b00010000 bytes16 tile = shiftRight(before, push);

// 0b00000001 = 0b01 = 1 = Black Tile



return uint8(tile);

// returns 1

} function shiftLeft (bytes16 a, uint128 n) returns (bytes16) {

uint128 shifted = uint128(a) * 2 ** uint128(n);

return bytes16(shifted);

}



function shiftRight (bytes16 a, uint128 n) returns (bytes16) {

uint128 shifted = uint128(a) / 2 ** uint128(n);

return bytes16(shifted);

}

And now we know whether the tile at column X and row Y is equal to Black, White or Empty. (If you’d like more info about the shifting functions check out Maksym’s Article)

Writing to ByteBoard

In order to write a new value to a specific point on our binary board we follow the same steps as reading, only using different bit operators. This time instead of extracting the value with a mask, we use it to clear out a space for our new tile using XOR, then write to it using OR.

function turnTile (bytes16 board, uint8 color, uint8 col, uint8 row) returns (bytes16){ uint128 push = posToPush(col, row);

bytes16 mask = bytes16(3);

// 0b00000011 (mask) mask = shiftLeft(mask, push);

// 0b00110000 (mask shifted if push was equal to 4) board = ((board ^ mask) & board);

// (board)0b01010101

// ^ (mask)0b00110000

// = 0b01100101

// & (board)0b01010101

// = 0b01000101 bytes16 tile = bytes16(color);

// 0b00000010 (if tile was White)



tile = shiftLeft(tile, push);

// 0b00100000 (tile shifted if push was equal to 4) return board | tile;

// (board)0b01000101

// | (tile) 0b00100000

// = 0b01100101 (original board was 0b01010101)

}

In this case we use XOR to make a copy of the board only inverted at the mask. After using AND on that copy with the original the modified mask section gets erased completely, leaving a space for the new tile. Next we use the OR operator on our shifted new tile value and return the whole board with our newly flipped tile 🎉