Skip to content

Instantly share code, notes, and snippets.

@MighteeCactus
Last active April 16, 2017 21:39
Show Gist options
  • Save MighteeCactus/fe763dd8450eea275b905fa254615fde to your computer and use it in GitHub Desktop.
Save MighteeCactus/fe763dd8450eea275b905fa254615fde to your computer and use it in GitHub Desktop.
Simple race game using Omega2 + LCD display + 2 buttons
#
# -= TEXT RACE =-
#
# Rules are simple: you are racing through 16x2 LCD display
# and trying to reach the right border of it. That is all!
#
# Project is made for Omega2 platform.
#
# Made by Bykov Alexander <mightee.cactus@gmail.com>.
#
# Enjoy it! Because I did enjoy it very much! :D
# Here is the video where I play it https://youtu.be/5ktlY7JXeHc
#
import onionGpio # install by typing down "opkg install pyOnionGpio" in the terminal
import lcdDriver # take it here https://github.com/OnionIoT/Onion-Docs/blob/master/Omega2/Kit-Guides/Starter/experiments/09-lcd-screen.md
import time
# !!! WARNING !!!
# Make sure this i2c bus address is correct!!!
# It took me a while to learn how to retrieve it from the device
# Stumbled upon useful comments here: https://community.onion.io/topic/1326/i2c-can-t-find-device-address
#
# Default address of i2c backpack is 0x3f by default
lcdAddress = 0x3f
# setup LCD
lcd = lcdDriver.Lcd(lcdAddress)
lcd.backlightOn()
# Represents two buttons in the game
# Constructors accepts two GPIO numbers for right and left button inputs
class GamePad:
gpioRNum = 0
gpioLNum = 1
prevR = 0
prevL = 0
lastR = 0
lastL = 0
def __init__(self, RNum = 0, LNum = 1):
self.gpioRNum = RNum
self.gpioLNum = LNum
self.padR = onionGpio.OnionGpio(self.gpioRNum)
self.padL = onionGpio.OnionGpio(self.gpioLNum)
# set to input
statusR = self.padR.setInputDirection()
statusL = self.padL.setInputDirection()
print 'GamePad GPIO (%d, %d) status values: (%d, %d)'%(self.gpioRNum, self.gpioLNum, statusR, statusL)
def readInput(self):
self.lastR = int(self.padR.getValue())
self.lastL = int(self.padL.getValue())
# print 'GamePad inputs: (%d, %d)'%(self.lastR, self.lastL)
# This is the bunch of tracks which player is riding through
# Track is auto generated, tho it very seldom generates unpassable wall...
# I'm too lazy to fix it :)
class Track:
position = 0 # this thing allows track to be scrollable
tracks = ["",""]
visibleLength = 20
minOffset = 2
maxOffset = 7
prevRand = 0
obstacle = "|VWPUIM" # possible "obstacles" on the track
def __init__(self):
print "Generating track"
self.tracks = [" ", " "]
self.generateMore(self.visibleLength)
self.position = 0
self.prevRand = 0
# My lame implementation of random num
# Due to usage of python-lite, random library does not work for me
def randNum(self):
rand = int(time.time()*1000.0) % self.maxOffset
if abs(rand - self.prevRand) < self.minOffset:
rand = (rand + self.minOffset) % self.maxOffset
self.prevRand = rand
return rand
# Generate right part of the track while scrolling
def generateMore(self, length):
newLength = len(self.tracks[0]) + length
while len(self.tracks[0]) < newLength:
for i in range(len(self.tracks)):
track = self.tracks[i];
num = self.randNum()
obst = self.obstacle[num]
field = (" " * num) + obst + (" " * (self.maxOffset - num))
track = track + field
self.tracks[i] = track
def getTrack(self, num):
if self.position + self.visibleLength > len(self.tracks[0]):
self.generateMore(self.position + self.visibleLength - len(self.tracks[0]))
return self.tracks[num][ self.position : self.position + self.visibleLength]
def getSlice(self):
slice = []
for i in range(len(self.tracks)):
slice.append(self.getTrack(i))
return slice
# Yep, it is a player! You!
class Player:
avatar = ">"
avatarCrash = "*"
isAlive = True
# Position on the multi row track
# the 1 here is lame, but I' too lazy to change it :)
position = [1, 0]
def __init__(self):
self.position = [1, 0]
self.isAlive = True
# Collision means player is at the position which is not a space character
def checkCollision(self, tracks):
symb = tracks[self.position[1]][self.position[0]-1]
return False if symb == " " else True
# Update tracks to have player representation on it
def putOntoTrack(self, tracks):
curAvatar = self.avatar if self.isAlive else self.avatarCrash
track = tracks[self.position[1]]
track = track[ : self.position[0]-1 ] + curAvatar + track[ self.position[0] : len(track) ]
tracks[self.position[1]] = track
return tracks
# this is the interval for handling inputs from the player
inputPace = 0.1
# Before scrolling track further we read 3 player inputs every 0.1 second
inputReadsPerTurn = 3
gamePad = GamePad()
# main game cycle.
# It could be effectively wrapped into a class
# but again I'm too lazy to do it :)
while 1:
# New game title screen!
lcd.lcdClear()
lcd.lcdDisplayStringList([
"<= TEXT RACE =>",
"L: up/dwn R: fwd"
])
# Waiting for any input from the player to start the race
while 1:
gamePad.readInput()
if gamePad.lastR == 1 or gamePad.lastL == 1:
break
time.sleep(inputPace)
player = Player()
track = Track()
# Special case when player reach left side of the 16x2 screen without dying
isEpicWin = False
# Just for the sake of it, the score is here!
scores = 0
# While we're not hit any obstacle
while player.isAlive:
# Giving player 3 chances to avoid any disaster on the way
inputReads = 0
while inputReads < inputReadsPerTurn:
inputReads += 1
# Peice of the track currently on screen
trackSlice = track.getSlice()
# Checking collisions
if player.checkCollision(trackSlice):
print "---->>> Player collided with obstacle!!!"
player.isAlive = False
break
# No collisions so far, inserting player into the track, visually
trackSlice = player.putOntoTrack(trackSlice)
# Draw me baby!
lcd.lcdDisplayStringList(trackSlice)
# now the actual input check
gamePad.readInput()
# Advance
if gamePad.lastR == 1:
player.position[0] += 1
# Change track
if gamePad.lastL == 1:
player.position[1] = 1 if player.position[1] == 0 else 0
# Epic win! Reached right side of the screen
if player.position[0] > 15:
isEpicWin = True
break
# Is it time to end the race?
if not player.isAlive or isEpicWin:
break
time.sleep(inputPace)
scores += 1
print 'Scores %d'%(scores)
track.position += 1;
# This is a hack actually... for the case when player hit any wall
# for drawing the hit itself
trackSlice = track.getSlice()
trackSlice = player.putOntoTrack(trackSlice)
lcd.lcdDisplayStringList(trackSlice)
# Let the player to enjoy his crash of win
time.sleep(2)
lcd.lcdClear()
# Show game over message
if isEpicWin:
lcd.lcdDisplayStringList([
"** GAME OVER **",
" EpIc WiN Bro! " + str(scores)
])
else:
lcd.lcdDisplayStringList([
"-= GAME OVER =-",
" Scores: " + str(scores)
])
# Wait for player's input to proceed to the fabulous title screen
while 1:
gamePad.readInput()
if gamePad.lastR == 1 or gamePad.lastL == 1:
break
time.sleep(inputPace)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment