Raspberry Pi Projects for Dummies

Bonus

 

#!/usr/bin/env python

# NeoPixel Light Fantastic

# Neighbours

# Author: Mike Cook

#

import time, random

import wiringpi2 as io


from neopixel import *


print"if program quits here start IDLE with 'gksudo idle' from command line"

io.wiringPiSetupGpio()

print"OK no crash" ; print" "

pinList = [9,24,10,23,7,8,11,25] # pins for keyboard

gameOrder =[ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,14 ] # working order

lightC = [ Color(0,0,0) for i in range(0,16) ]

selected = False ; toggle = False ; target = 0

strip = Adafruit_NeoPixel(16,18,800000,5,False)


def main():

    global toggle

    initGPIO()

    strip.begin()

    print"Neighbours - get no identical colors adjacent"

print"press once to select and again to swap"

    wipe() ; key = 1

    timeToFlash = time.time()

    while True:

      setBoard() # set up colors to use

      while not finished():

        while keyPressed() == False :

          if selected and time.time() > timeToFlash :

              if toggle :

                 strip.setPixelColor(target, lightC[target])

              else:

                 strip.setPixelColor(target, Color(0,0,0))

              strip.show()      

              timeToFlash = time.time() + 0.3

              toggle = not(toggle)

          time.sleep(0.002) # de-bounce   

        newKey = getKey()

        if newKey != 16 :

            key = newKey

            #wipe()

        while keyPressed(): # wait for release

            pass

        makeMove(key)

      print"puzzle complete - any key for new game"

      time.sleep(0.6)

      while keyPressed() == False :

         showSet()

         time.sleep(0.3)

         wipe()

         time.sleep(0.3)

      while keyPressed(): # wait for release

         pass

def initGPIO():

    for pin in range (0,4):   

        io.pinMode(pinList[pin],0)

        io.pullUpDnControl(pinList[pin],1) # input enable pull down

    for pin in range(4,8):

        io.pinMode(pinList[pin],1) # output

        io.digitalWrite(pinList[pin],1) # all high


def keyPressed(): #is a key being pressed>?

    pressed = False

    for pin in range(0,4):

        if io.digitalRead(pinList[pin]):

          pressed = True

    return pressed


def getKey():

    key =16 # 16 = no key

    for outPin in range(4,8):

        io.digitalWrite(pinList[outPin],0) # all low 

    for outPin in range(4,8):

        io.digitalWrite(pinList[outPin],1)

        for read in range(0,4):

           if io.digitalRead(pinList[read]):

               key = ((outPin-4) * 4) + read

        io.digitalWrite(pinList[outPin],0) #remove active row    

    for outPin in range(4,8):

        io.digitalWrite(pinList[outPin],1) # leave all high  

    return key


def wipe():

    for i in range(0,strip.numPixels()):

        strip.setPixelColor(i, Color(0, 0, 0))

    strip.show()

def colorHV(angle,v): # color returned H=angle, V =v, S=1

    prop = v/255.0

    while angle <0 : # get angle in range 0 to 255

        angle += 256

    while angle > 255:

        angle -=256

    if angle < 85:

        return Color( int((255 - angle*3)*prop), int((angle*3)*prop), 0)

    elif angle < 170:

        angle -= 85

        return Color(0, int((255 - angle*3)*prop), int((angle*3)*prop))

    else:

        angle -= 170

        return Color(int((angle*3)*prop), 0, int((255 - angle*3)*prop))

   

def setBoard():

    global lightC

    nColors = random.randint(3,5)

    h = random.randint(0,255)

    hInc = random.randint(26,40)

    #print h, hInc

    for i in range(0,16,nColors):

       v = 64.0

       if (i & 0x4) == 0:

         v = 255.0

       for j in range (0,nColors):

           if i+j < 16:

               lightC[i+j] = colorHV(h,v)

       h += hInc      

    random.shuffle(lightC) # mix up the board

    while finished():

      random.shuffle(lightC)   

    showSet()  

   

def showSet():   

    for i in range(0,16):

        #print"game order ",gameOrder[i]       

        strip.setPixelColor(i, lightC[i])

    strip.show()

   

def finished():

    for i in range(0,15):

        if ((i+1) / 4 == i / 4) :

           if lightC[i] == lightC[i+1]:

               return False

        if (i+4) < 16:

           if lightC[i] == lightC[i+4]:

               return False          

    return True


def makeMove(move):

    global selected, target

    if move == target : # contact bounce

        return

    if selected:

        selected = False

        if checkAdjacent(target,move):

           temp = lightC[move]

           lightC[move] = lightC[target]

           lightC[target] = temp

           target = -1

        else:

           print"not adjacent move",move,"and target",target," move cancelled"

        showSet()

    else:

        target = move

        selected = True

       

def checkAdjacent(first,second):

    distanceX = abs((first % 4) - (second % 4))

    distanceY = abs((first / 4) - (second / 4))

    if (distanceX + distanceY) == 1 :

        return True

    else:

        return False

   

# Main program logic follows:

if __name__ == '__main__':

    main()

Good Neighbours Game

The Raspberry Pi Projects for Dummies book has an illuminated switch matrix project called:-

The Light Fantastic

There are also four games described in the book that you can play on it. Here is a bonus fifth game, called “Good Neighbours”.

Back to the Punnet

There is a famous theorem that says any collection of shapes can be coloured in using just four colours and no two shapes with a common border will have the same colour. For a square grid this is reduced to just two colours, think of a chess board. For this game you are presented with the Light Fantastic's key pad being a jumble of colours, your job is to swap adjacent coloured squares to end up with no two squares having the same colour neighbour.


The way this is played is that one position is selected (pressed) and it begins to flash. Then pressing one of the adjacent keys will cause that position to be swapped with the flashing one. If you press a key that is not adjacent to the flashing one then the move is canceled, a good way of taking back the initial selection, if you want to change your mind.

The four colour theorem

If you have been following the programs in Chapter 8 of the printed book you will be very familiar with the basic style of the code. After the function imports the gameOrder list is the one that determines the order of the displayed colours. These colours are stored in another list called lightC. Then the main function initialises the keyboard's GPIO pins and the LEDs. The instructions are printed out and the function enters an infinite loop.


The setBoard function generates the list of colours to use. However, unlike the games in the printed book this is done with the colorHV function. This takes in a Hue angle and a Value variable. The Value runs from 0 to 255 and applies a dimming to the colour generated by the H angle. This is in HSV colour space as discussed in the book. The function calculates a fractional proportion variable called prop that is multiplied with the colour derived from the H value to tone it all down a bit. Alternate colours generated in the board setup are therefore set to have a value (brightness) of either 255, that is full brightness, or a value of 64, a quarter brightness. Note due to the eye's non linear perception it will not look like exactly a quarter brightness.


The number of colours used in any game is set by the nColors variable set at random between 3 and 5. Finally the lightC list is shuffled up to ensure a random distribution. Just incase this produces by chance a finished board then the while loop keeps on shuffling the list until an unfinished board is produced.


After the board has been set up the main function enters a loop which continues until the board pattern has finished. This is determined by the finished function which checks each square in turn and returns a False if any square to the left or below is of the same colour as the square currently being checked. This will in effect check all squares for adjacent matching what ever the position because all the squares are considered.


There are two phases to a move, the first is to select a position and start it flashing. Unlike previous games where lights only flashed for a limited number of times, here it has to continue to flash until another key is pressed. Therefore there is a state machine variable, as it is known as, called selected. Only when this is true will the target position flash.


Once a key press has been identified the makeMove function is called. If the target key is the same as the move then the function returns as the most likely explanation is a contact bounce. Then the select variable decides what phase of the move is currently active, that is either waiting to select the target or waiting to select a candidate to swap with the target. If we have a target already selected the function checks to see if the current move is adjacent to the selected target and if it is then proceeds to swap the target and move colours, and display them. If they are not adjacent then an error message is printed on the console and the move is automatically canceled.


Once the loop in the main function has detected the board is finished then the whole board will flash until a key is pressed and then a new game will be setup.

The Python 2.7 code

Just like any program you can take things further by modifying this code. It is often the best way to start to learn how to program is to take a working code and modify it so it does what you want. Here there is lots of scope for changing the colours used, this can make it harder if the colours are quite close to each other.

You could impose a time limit on finishing the board or even one between moves. You could change the game where you have to get the colours in a certain order as well. This is a sort of simpler version of the sliding block puzzle in the printed book because you can swap any two colours not only just a blank one. This is a lot easier.


You could invert the logic and make adjacent colours disappear if they are the same and have the "bricks" above a blank space fall down into the space, generating a new colour if any position on the top edge is blank.


What ever you do then do try something, and make it your project.

The Please note that in order to play this game you will need the Light Fantastic hardware described in Chapter 7 of the Raspberry Pi Projects for Dummies book.

Download it from here Neighbours.py

Taking it further