Last active
April 16, 2017 21:39
-
-
Save MighteeCactus/fe763dd8450eea275b905fa254615fde to your computer and use it in GitHub Desktop.
Simple race game using Omega2 + LCD display + 2 buttons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# -= 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