Bitwise in Practice: Flipping Bits
00:22 These were extremely common when memory was more expensive, but less so now. I don’t know it for a fact, but I’d be very surprised if each of the dots in the original Pac-Man game wasn’t a bit in a binary flag. As Pac-Man consumed a dot, the bit in the flag would get toggled.
01:13 The first is used to indicate that the top of the box should be drawn. The second is the bottom. I’ve written it here as one bit shifted a single position to underscore the fact that this is a bit flag. You’re just as likely to write the decimal number 2 in the real world.
01:30 The third and fourth flags are for the left and right sides. Each subsequent value being a bigger bit shift. The fifth constant is a bitmask used to determine if either the left or right sides are on.
01:45 It’s built using the left and right constants combined with a bitwise OR operation. The initializer on line nine takes an integer. A more robust one would check that only valid bits are used or mask out the value, but I was lazy when I wrote this code.
There are two methods of interest. The first, on lines 13 and 14, checks if any side is turned on. It does this by applying the bitmask called
.SIDES against the internal bit flag. If the box has either the left or right side turned on, the result of this AND operation will be a positive number.
You could just return that, but for completeness’s sake, I cast it to a Boolean value. The second method checks if both the left and right sides are on. This is a similar kind of calculation to the one before. The
.SIDES bitmask is supplied against the internal bit flag. But this time, rather than just checking truthiness, it is compared against the mask itself.
If both left and right are on, then the result of the AND will be equivalent to the mask. This time, I didn’t bother with the cast to Boolean. Let’s play with this in the REPL. First, I’ll import a
this time with everything but the bottom side.
True. Although you could achieve the same thing with four Booleans, checking if one or both sides is on would mean multiple
if ... else clauses.
Opening a door means setting the corresponding bit in the flag. To set a specific bit, first, you create a mask by shifting
1 by the specified position, then |= (OR equals) the mask with the flag.
All the bitwise operators support an op-equals variant, meaning you don’t have to write
self.door_state = self.door_state | <mask>. It saves a bit of typing. To close the door, you do something similar.
First, create a mask for the bit representing the door’s position. Then, invert it. The resulting mask will have 1s for everything but the door you’re closing.
&= (AND equals) this mask, and the door in question will be set to
05:10 You can open and close a door. How about querying the door’s state? I’ve implemented two different ways of accomplishing the same thing. Lines 12 and 13 are the first version. Once again, construct a position bitmask. With the mask, simply AND it with the flag.
05:48 Remember, this doesn’t actually change the flag. It returns a new value that is the shifted version of the original. This will cause the least significant bit of the result to be the bit representing the door in question.
If it is open, it gets closed. If it is closed, it gets opened. This might seem strange in real life, but in a game where there’s only one button to change the state of the door, toggling would be exactly what you’d want. To toggle a bit, you use the XOR operator. Like with the other methods, create the position bitmask, but this time,
^= (XOR equal) the value.
Remember that if both bits are on, XOR results in
0. If the door’s closed, the XOR results in
1, meaning open. If the door’s open,
1 ^ 1 is
0, closing the door. Let’s play with this object in the REPL.
10:36 To do the same for blue, you want to first isolate the blue portion using a mask and then you can bit shift the result. There’s the decimal. And the hex value showing you the correct blue digits.
10:52 Now for the green. Green is already the least significant thing, so no shifting needed this time. Just mask out the other values. Here’s the decimal and the hex. By using these operations, you can isolate the colors that make up the full RGB value.
11:14 You could use other bitwise operations or even additions and subtractions to change the intensities of these values. Turning up the red by 10% would be a simple multiplication on the red portion. Once you’ve made the changes to a part of the color, all you need to do to combine it is shift things back together.
11:55 And—as they say in the country that probably shouldn’t own this Italian painting—voilà! The next lesson is optional. It does a deeper dive into using the same techniques that you saw in this lesson. The lesson after that, if you decide to skip this one, is on big- and little-endian byte order.
Become a Member to join the conversation.