# IPython Blocks¶

Welcome to the IPython Blocks! This is a grid of colored blocks we can use to practice all sorts of programming concepts. There's a tutorial on IPython Blocks at http://nbviewer.ipython.org/urls/raw.github.com/jiffyclub/ipythonblocks/master/demos/ipythonblocks_demo.ipynb, but you don't need to read that now; just follow along here.

We start by creating our grid and giving it a name. We'll call it... hmm... let's see... how about "grid"?

In [ ]:
!pip install ipythonblocks

In [ ]:
from ipythonblocks import BlockGrid
grid = BlockGrid(8, 8, fill=(123, 234, 123))
grid


We can refer to each cell in the grid by its coordinates from the upper-left corner. The first index is the number of steps down, the second is the number of steps to the right.

In [ ]:
grid[0, 0]


In programming, colors are often referred to by an "RGB tuple". That's a series of three numbers, representing how much red, how much green, and how much blue, respectively. For the grid, those numbers go on a scale from 0 to 255. So pure red is (255, 0, 0), black is (0, 0, 0), white is (255, 255, 255), and so on.

If we assign an "RGB tuple" to a cell from the grid, that cell takes on that color.

In [ ]:
grid[0, 0] = (0, 0, 0)
grid[0, 2] = (255, 0, 0)
grid[0, 4] = (255, 255, 255)
grid[0, 6] = (0, 150, 150)
grid.show()


Let's not spend all day typing those tedious RGB tuples! This looks like a perfect place for a dictionary. Lucky us, we can get a dictionary with all sorts of colors defined from the ipythonblocks module, the same place we got the BlockGrid.

In [ ]:
from ipythonblocks import colors

In [ ]:
colors


Wow, looks like somebody used to work at a paint store! OK, let's use some of those in the next row down.

In [ ]:
grid[1, 1] = colors['Teal']
grid[1, 2] = colors['Thistle']
grid[1, 3] = colors['Peru']
grid.show()


Now suppose we want to color a bunch of blocks. Let's use a loop so we don't have to write a line for every single one.

In [ ]:
row_number = 3
for column_number in [0, 1, 2, 3, 4, 5, 6]:
grid[row_number, column_number] = colors['Chocolate']
grid.show()


The grid defines a "height" and a "width", these will be handy to work on cells all the way across it.

In [ ]:
grid.width

In [ ]:
row_number = 5
for column_number in range(grid.width):
grid[row_number, column_number] = colors['Violet']
grid.show()


How about columns? And how about painting three columns at once? Let's use nested loops.

In [ ]:
for column_number in [4, 5, 6]:
for row_number in range(grid.height):
grid[row_number, column_number] = colors['Crimson']
grid.show()


Our grid is looking cluttered. Let's define a function to start over by painting it all one color.

In [ ]:
def one_color(target_grid, color):
for row_number in range(target_grid.height):
for column_number in range(target_grid.width):
grid[row_number, column_number] = color

In [ ]:
one_color(grid, colors['LightGreen'])
grid.show()


## Animation¶

A couple of tricks will let our grid change over time. We'll need sleep from the time module, plus the clear_output function from IPython.

In [ ]:
import time
from IPython.display import clear_output
for color in [colors['Red'], colors['Green'], colors['Blue'], colors['White'], colors['Purple']]:
clear_output()
one_color(grid, color)
grid.show()
time.sleep(1)


What if we used grid.show() like before, instead of clear_output? Not telling. Feel free to try it yourself.

OK, how about a checkerboard? We could paint the whole grid black, then paint red onto every second square.

In [ ]:
one_color(grid, colors['Black'])
for row_number in range(grid.height):
for column_number in range(grid.width):
if is_even(column_number):
grid[row_number, column_number] = colors['Yellow']
grid.show()


Icky, an error message! Because there's no such thing as a function is_even; I just made that up. But we can fix that!

In [ ]:
def is_even(number):
if number % 2 == 0:
return True
else:
return False


Now go back up and re-run the earlier cell. And we get... hmm, good for bumblebees, bad for checkers. Let's make a modified version.

In [ ]:
one_color(grid, colors['Black'])
for row_number in range(grid.height):
for column_number in range(grid.width):
if is_even(column_number + row_number):
grid[row_number, column_number] = colors['Yellow']
grid.show()


How crazy can we get, using just what we've already learned? Let's make a diagonal color gradient, then pour some bleach on it.

In [ ]:
base_color = [50, 50, 50]
for i in range(200):
clear_output()
for row_number in range(grid.height):
for column_number in range(grid.width):
grid[row_number, column_number] = (base_color[0], base_color[1]+row_number*20, base_color[2]+column_number*20)
grid.show()
base_color[0] += 1
base_color[1] += 1
base_color[2] += 1
time.sleep(0.02)



Now it's time to try out some ideas of your own! Here are some suggestions:

• Diagonal stripes
• Draw random stripes
• A flag
• Put a dark square in the middle of the grid, then make a randomly wandering path from there!

Use a bigger grid if you need one. Remember at the very beginning when we created the grid?

grid = BlockGrid(8, 8, fill=(123, 234, 123))



You can create it again with some different numbers.

# Code snippets¶

Here are some more little pieces of code that might be fun elements to work into your projects!

## Random numbers¶

Each time you execute this cell, you'll see a different, random shade.

In [ ]:
from random import randint
random_color = (randint(0, 255), randint(0, 255), randint(0, 255))
one_color(grid, random_color)
print(random_color)
grid.show()


## Pythagorean theorem¶

Can you make the color depend on the distance from the upper-left corner? Can you draw an arc, for instance?

In [ ]:
for row_number in range(grid.height):
for column_number in range(grid.width):
distance_to_corner = (row_number**2 + column_number**2)**0.5
# now what can you do with that number?
grid.show()


## ASCII Art¶

There's a file called ascii8x8.py in this directory that we can use to do ASCII-style art with our blocks.

In [ ]:
from ascii8x8 import Font8x8
print(Font8x8['p'])


It's hard to tell, but that's a list of strings, and if those strings are stacked, they form a rough inversed "p". But we've also provided a function that, using that same data, and given the (row_number, column_number) of a place in the grid, tells us whether that spot should be on (True) or off (False).

In [ ]:
from ascii8x8 import screen
screen('p', 0, 0)


What good does that do? Well, here's an example of applying it to the grid:

In [ ]:
for row_number in range(8):
for column_number in range(8):
if screen('p', row_number, column_number):
grid[row_number, column_number] = colors['Black']
grid.show()


So, perhaps you could...

• Flash your name across the grid, one letter at a time
• Print a letter upside-down
• Print a letter whose color changes with time
• Make a very wide grid and print a whole word in it