```
public void gameOfLife(final int[][] board) {
if (board.length == 0 || board[0].length == 0) {
return;
}
int[][] directions = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
int rows = board.length, columns = board[0].length;
for (int row = 0; row < rows; row++) {
for (int column = 0; column < columns; column++) {
for (int[] direction : directions) {
int x = row + direction[0];
int y = column + direction[1];
// As we are using bits 1 to 4 to store count of live neighbors,
// we have to mod by 2 to get the live or dead bit i.e. bit 0 of the neighbor.
if (x >= 0 && x < rows && y >= 0 && y < columns && board[x][y] % 2 == 1) {
board[row][column] += 2; // using bits 1 to 4 to store count of live neighbors
}
}
}
}
for (int row = 0; row < rows; row++) {
for (int column = 0; column < columns; column++) {
// As we are using bits 1 to 4 to store count of live neighbors,
// we have to mod by 2 to get the live or dead bit i.e. bit 0
// and we have to right shift by one bit to get the live neighbors count.
int liveNeighbors = board[row][column] / 2;
boolean liveCell = board[row][column] % 2 == 1;
if (liveCell && (liveNeighbors < 2 || liveNeighbors > 3)) {
board[row][column] = 0;
} else if (liveNeighbors == 3) {
board[row][column] = 1;
} else {
board[row][column] %= 2;
}
}
}
}
```