In our last project, we created a basic Minecraft game controller and wrote the code to allow us to map the buttons and accelerometer to specific functionality related to Steve, our Minecraft character.

In this project, we will implement the same functionality as we did previously, but we will revise the code to use a Dictionary to map the button inputs to functionality, making the program much more simple and straightforward. This will be the foundation of our future Minecraft programs.

Our goal with this project is to greatly simplify the button controls we implemented in our Minecraft Controller 1 project. This will make future controller projects easier to write and will also make the code easier to read and debug.

As a reminder, here is the code from the previous project that we're going to focus on changing:

left = Button(18) right = Button(14) up = Button(15) down = Button(23) smash = Button(24) jump = Button(12) accel = Accel() control.show() while True: if left.is_pressed(): control.left() else: control.left(release=True) if right.is_pressed(): control.right() else: control.right(release=True) if up.is_pressed(): control.forward() else: control.forward(release=True) if down.is_pressed(): control.backward() else: control.backward(release=True) if smash.is_pressed(): control.smash() else: control.smash(release=True) if jump.is_pressed(): control.jump() else: control.jump(release=True)

Specifically, in this project, we're going to make two major changes to the code above:

1. We will use a Dictionary to define the button controls and use a `for` loop to reduce the large number of `if/else` loops

2. We will greatly simplify the remaining `if/else` statement in our code

## Implement a Dictionary and a `for` loop

The first thing we're going to do to simplify our code is to use a Python Dictionary to enumerate (create a list of) our button controls.

If you recall, a Dictionary maps a set of keys to their values. In this case, the set of keys will be the buttons and their respective GPIOs, and the values will be the specific `control` commands for each:

keymap = { Button(18) : control.left, Button(14) : control.right, Button(15) : control.forward, Button(23) : control.backward, Button(24) : control.smash, Button(12) : control.jump, }

Now that we have our button control definitions in a Dictionary, we can replace the large number of `if/else` statements (one set for each button) with a `for` loop and single set of `if/else` statements:

for button, action in keymap.items(): if button.is_pressed(): action() else: action(release=True)

## Simplify the `if/else` statement

In our code above, we use a four-line if-else statement to check each controller button (to see if it was being pressed) each time through the game loop:

if button.is_pressed(): action(release=False) else: action(release=True)

While this works just fine, using four lines of code to complete this task is more cumbersome than necessary.

NOTE:You may notice that in the Minecraft Controller 1 project and in the above step, we did not include `release=False` in the first `action` statement. That's because this is the default assumption of the function, and therefore it's not needed. We include it above, as it will make the next part of this discussion more understandable.

As you can see, the first line of the code above tests whether the button is being pressed. If so, the second line of code takes specific action; if not, the third and fourth lines of code stop the specific action.

Those four lines of code can actually be condensed into the following single line of code, which does the exact same thing:

action(release=(not button.is_pressed()))
WHY DOES IT WORK?

If it's not clear why the single line of code above is equivalent to the original four lines of code, let's break it down in a little more detail.

You'll notice that the original four lines of code essentially accomplish the following two things:

- If `button.is_pressed()=True`, then `action(release=False)`

- If `button.is_pressed()=False`, then `action(release=True)`

In other words, the result of `button.is_pressed()` is changed to the opposite and inserted into the `action(release=...)` statement.

The `not` keyword is referred to as a "boolean operator," and it is used to flip False to True (0 to 1) and True to False (1 to 0). The statement:

`action(release=(not button.is_pressed()))`

simply takes the result of `button.is_pressed()`, flips it and inserts the result into the `release=...` statement.

We can now replace the `if/else` statement in our `for` with the single statement:

for button, action in keymap.items(): action(release=(not button.is_pressed()))

## Full Code

Here is what the full code for this project should look like. The functionality is equivalent to the functionality in the Minecraft Controller 1 project, but the code is much more compact and readable:

from rstem.accel import Accel from rstem.button import Button from rstem.mcpi import minecraft, control import time control.show() keymap = { Button(18) : control.left, Button(14) : control.right, Button(15) : control.forward, Button(23) : control.backward, Button(24) : control.smash, Button(12) : control.jump, } accel = Accel() while True: for button, action in keymap.items(): action(release=(not button.is_pressed())) x, y, z = accel.forces() control.look(up=50*y, left=50*x) time.sleep(0.01)

1. Add another button (or remap an existing button) to allow Steve to crouch at the press of the button. Notice how easy this is to add now compared to in the project.

2. Invert your controller — make it look down when you tilt up and up when you tilt down.