#!/usr/bin/env python # ----------------- # Modified 2018-09-30 to use Matt Hawkins' LCD library for I2C available # from https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/lcd_i2c.py # # A little test program to demo/test the ability to manage custom characters # on a 44780 display. # 2019-09-21 HME Initial entry # # Modified by Joshua Simard, 9/7/2023 # # The GPIO library. This program doesn't use it but most COMP430 code does import RPi.GPIO as GPIO # The Python time library import time # Random numbers are something you can count on from random import * # Game functions - these let you play sounds, read the keyboard and display # images and geometric graphics import pygame # Additional stuff for LCD import smbus #LCD pin assignments, constants, etc I2C_ADDR = 0x27 # I2C device address LCD_WIDTH = 16 # Maximum characters per line # Define some device constants LCD_CHR = 1 # Mode - Sending data LCD_CMD = 0 # Mode - Sending command LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line LCD_BACKLIGHT = 0x08 # On #LCD_BACKLIGHT = 0x00 # Off ENABLE = 0b00000100 # Enable it # Timing constants E_PULSE = 0.0005 E_DELAY = 0.0005 # LCD commands LCD_CMD_4BIT_MODE = 0x28 # 4 bit mode, 2 lines, 5x8 font LCD_CMD_CLEAR = 0x01 LCD_CMD_HOME = 0x02 # goes to position 0 in line 0 LCD_CMD_POSITION = 0x80 # Add this to DDRAM address GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) # Numbers GPIOs by standard marking #Open I2C interface #bus = smbus.SMBus(0) # Rev 1 Pi uses 0 bus = smbus.SMBus(1) # Rev 2 Pi uses 1 # Code for push button(s) is below # The two values below are what we use to determine what pin the buttons are connected to buttonPin = 17 buttonPin2 = 22 # Here, we will set up the buttons and set them to Pull Up GPIO.setup(buttonPin, GPIO.IN, pull_up_down = GPIO.PUD_UP) GPIO.setup(buttonPin2, GPIO.IN, pull_up_down = GPIO.PUD_UP) def lcd_init(): # Initialise display lcd_byte(0x33,LCD_CMD) # 110011 Initialise lcd_byte(0x32,LCD_CMD) # 110010 Initialise lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size lcd_byte(0x01,LCD_CMD) # 000001 Clear display time.sleep(E_DELAY) def lcd_byte(bits, mode): # Send byte to data pins # bits = the data # mode = 1 for data # 0 for command bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT # High bits bus.write_byte(I2C_ADDR, bits_high) lcd_toggle_enable(bits_high) # Low bits bus.write_byte(I2C_ADDR, bits_low) lcd_toggle_enable(bits_low) def lcd_toggle_enable(bits): # Toggle enable time.sleep(E_DELAY) bus.write_byte(I2C_ADDR, (bits | ENABLE)) time.sleep(E_PULSE) bus.write_byte(I2C_ADDR,(bits & ~ENABLE)) time.sleep(E_DELAY) # This takes a single byte (n) and rearranges the lower bits 0-4 # by swaping bits 0/4 and 1/3 def horz_flip_char(n): n2 = n & 0b00000100 # start with bit 2 n2 = n2 | ((n & 0b00010000) >> 4) # move bit 4 to bit 0 n2 = n2 | ((n & 0b00001000) >> 2) # move bit 3 to bit 1 n2 = n2 | ((n & 0b00000010) << 2) # move bit 1 to bit 3 n2 = n2 | ((n & 0b00000001) << 4) # move bit 0 to bit 4 return n2 def custom_chars(): lcd_byte(0x40, LCD_CMD) # set the memory address to 00 (start of CGRAM) # For this example, this is the only custom char we will use. lcd_byte(0b00001110, LCD_CHR) # ____XXX_ lcd_byte(0b00011111, LCD_CHR) # ___XXXXX lcd_byte(0b00011111, LCD_CHR) # ___XXXXX lcd_byte(0b00011111, LCD_CHR) # ___XXXXX lcd_byte(0b00011111, LCD_CHR) # ___XXXXX lcd_byte(0b00011111, LCD_CHR) # ___XXXXX lcd_byte(0b00001110, LCD_CHR) # ____XXX_ lcd_byte(0b00000000, LCD_CHR) # ________ def lcd_string(message,line): # Send string to display message = message.ljust(LCD_WIDTH," ") lcd_byte(line, LCD_CMD) for i in range(LCD_WIDTH): lcd_byte(ord(message[i]),LCD_CHR) # functions not in the original library # ------------------------------------- # Positions the cursor so that the next write to the LCD # appears at a specific row & column, 0-org'd def lcd_xy(col, row): lcd_byte(LCD_CMD_POSITION+col+(64*row), LCD_CMD) # Begins writing a string to the LCD at the current cursor # position. It doesn't concern itself with whether the cursor # is visible or not. Go off the screen? Your bad. def lcd_msg(msg_string): for i in range(0, len(msg_string)): lcd_byte(ord(msg_string[i]), LCD_CHR) pygame.init() # Sets up pygame lcd_init() # set up the LCD and clear the screen lcd_string("JSimard", LCD_LINE_1) lcd_string(" Fall 2023", LCD_LINE_2) custom_chars() # load in our custom char # This variable is used to determine the Y position, I.E. if the player is jumping # Note that we do not need to record the X value for the player, because it will always be 1 player_pos_y = 1 # A list to hold the barriers that are on screen # We use two lists, one of the X position and one of the Y # Note that the barriers will always be evenly spaced along the X axis barrier_poses_x = [4, 8, 12, 16] barrier_poses_y = [0, 1, 0, 1] # The following timers will be used to determine the game speed and final score. # Because the 'time' function simply returns an actual unit of time, # We cannot use it like as if the timer had started at the beginning of the program. # Instead, we record the last time, and then when we need to retrieve the time, we # subtract the current time from that. last_timer = 0 # This will be used to determine your final score, among other things game_start_time = int(time.time()*1000) # This variable determines the interval at which the sprites move game_speed = 500 # Define the gameover variable gameOver = False # Place the player character on the screen # This could be removed without any issues, but I find it helpful lcd_xy(1, player_pos_y) lcd_msg(chr(0)) # Give the player a bit of time to prepare for game start time.sleep(5) while not gameOver: # Adding a short sleep helps not burn through excessive CPU cycles, and also assists with Debounce. time.sleep(.1) # Update the timer... timer = int(time.time()*1000) # All controls for the character are below if(0 == GPIO.input(buttonPin) and player_pos_y == 1): print('Up Button Pressed') # Generate the new position we want the player to be # In this case, one position upwards. newPos = player_pos_y - 1 # Remove the old player lcd_xy(1, player_pos_y) lcd_msg(" ") # Find the coords for the new player lcd_xy(1, newPos) # Place the player from our custom chars lcd_msg(chr(0)) player_pos_y = newPos if (0 == GPIO.input(buttonPin2) and player_pos_y == 0): print('Down Button Pressed') # Generate the new position we want it at # Again, one position down this time newPos = player_pos_y + 1 # Remove the old player lcd_xy(1, player_pos_y) lcd_msg(" ") # Find the coords for the new player lcd_xy(1, newPos) #Place the player from our custom chars lcd_msg(chr(0)) player_pos_y = newPos # Code for the barriers is below # if we've elapsed the game update interval... if(timer - last_timer >= game_speed): # Step through numbers 0 through 4 (not including 4) for i in range(4): print(i) # Generate the new position we want it at newBarrierPos = barrier_poses_x[i] newBarrierPos = newBarrierPos - 1 # Remove the old barrier # This also checks if the player has hit a barrier, by checking to see # if the position of any barrier is exactly equal to the position of the player if(barrier_poses_x[i] == 1 and barrier_poses_y[i] == player_pos_y): gameOver = True else: last_timer = timer # Find the coords for the new barrier lcd_xy(newBarrierPos, barrier_poses_y[i]) # Place the barrier from our custom chars lcd_msg(chr(0)) lcd_xy(barrier_poses_x[i], barrier_poses_y[i]) lcd_msg(" ") # Update the list to reflect the new position barrier_poses_x[i] = newBarrierPos # Handle barriers hitting the end of the playing area if(barrier_poses_x[i] < 0): barrier_poses_x[i] = 16 # Reset the Y value of the barrier to random barrier_poses_y[i] = randint(0, 1) # If more than 2 seconds has elapsed, increase the game speed if(int(time.time()*1000) - game_start_time) > 2000: game_speed = 300 # The final score is equal to the total game time divided by 20. score = round((int(time.time()*1000) - game_start_time) / 20) print('Score: ', score) lcd_string("GAME ", LCD_LINE_1) # First, make a new string which is the score plus a lot of whitespace finalScore = str(score) + " " # Then, truncate it to 11 characters, and add 'over' to the end finalScore = finalScore[0:12] + "OVER" # Now print it to the screen lcd_string(finalScore, LCD_LINE_2)