Skip to content

Instantly share code, notes, and snippets.

@cwmyers
Created October 19, 2019 06:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cwmyers/59251ee295002ce962aeb5628223ba21 to your computer and use it in GitHub Desktop.
Save cwmyers/59251ee295002ce962aeb5628223ba21 to your computer and use it in GitHub Desktop.
A little game that can be run on arduino with an LCD sheild
#include <LiquidCrystal.h>
//LCD pin to Arduino
const int pin_RS = 8;
const int pin_EN = 9;
const int pin_d4 = 4;
const int pin_d5 = 5;
const int pin_d6 = 6;
const int pin_d7 = 7;
const int pin_BL = 10;
const int LEFT = 1;
const int RIGHT = 2;
const int UP = 3;
const int DOWN = 4;
const int SELECT = 5;
const int UNKNOWN_KEY = -1;
const int rows = 2;
const int columns = 16;
const int cellHeight = 8;
const int cellWidth = 5;
const int roadSize = 15;
const int roadLength = 33;
LiquidCrystal lcd( pin_RS, pin_EN, pin_d4, pin_d5, pin_d6, pin_d7);
byte heart[8] = {
0b00000,
0b01010,
0b11111,
0b11111,
0b11111,
0b01110,
0b00100,
0b00000
};
byte smiley[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b10001,
0b01110,
0b00000
};
byte frownie[8] = {
0b00000,
0b00000,
0b01010,
0b00000,
0b00000,
0b00000,
0b01110,
0b10001
};
byte armsDown[8] = {
0b00100,
0b01010,
0b00100,
0b00100,
0b01110,
0b10101,
0b00100,
0b01010
};
byte armsUp[8] = {
0b00100,
0b01010,
0b00100,
0b10101,
0b01110,
0b00100,
0b00100,
0b01010
};
byte run1[8] = {
0b00100,
0b00110,
0b00100,
0b00101,
0b01110,
0b10100,
0b00110,
0b01001
};
byte run2[8] = {
0b00100,
0b00110,
0b00100,
0b10100,
0b01110,
0b00101,
0b01100,
0b10010
};
class CollisionResult {
public:
virtual bool gameOver() = 0;
virtual int bonus() = 0;
virtual String cType() = 0;
};
class EndGame : public CollisionResult {
public:
bool gameOver() {
return true;
}
int bonus() {
return 0;
}
String cType() {
return String("EndGame");
}
};
class Reward : public CollisionResult {
public:
bool gameOver() {
return false;
}
int bonus() {
return 10;
}
String cType() {
return String("Reward");
}
};
class NoCollision : public CollisionResult {
public:
bool gameOver() {
return false;
}
int bonus() {
return 0;
}
String cType() {
return String("NoCollision");
}
};
NoCollision *nc = new NoCollision();
EndGame *eg = new EndGame();
Reward *reward = new Reward();
class RoadObject {
protected:
int track;
int position;
public:
RoadObject(int track, int position);
int getTrack();
int getPosition();
virtual void update(int roadLength);
String toString();
virtual char displayChar() = 0;
virtual CollisionResult* getCollisionResult();
CollisionResult* collision(int track , int position) {
if (this->getTrack() == track && this->position == position) {
this->position = roadLength;
return this->getCollisionResult();
} else {
return nc;
}
};
};
class Spike : public RoadObject {
public:
Spike(int track, int position): RoadObject(track, position)
{};
char displayChar() {
return '*';
}
CollisionResult* getCollisionResult() {
return eg;
}
};
class DeathSpike : public RoadObject {
public:
DeathSpike(int track, int position): RoadObject(track, position)
{};
char displayChar() {
return '<';
}
CollisionResult* getCollisionResult() {
return eg;
}
void update(int roadLength) {
this->position = this->position - 1;
if (this->position % 3 == 0) {
this->track = (this->track + 1 ) % 2;
}
if (this->position < 0 ) {
this->position = roadLength;
}
}
};
class Coin : public RoadObject {
public:
Coin(int track, int position): RoadObject(track, position)
{};
char displayChar() {
return 'o';
}
CollisionResult* getCollisionResult() {
return reward;
}
};
RoadObject::RoadObject(int track, int position) {
this->track = track;
this->position = position;
}
int RoadObject::getTrack() {
return this->track;
}
int RoadObject::getPosition() {
return this->position;
}
void RoadObject::update(int roadLength) {
this->position = this->position - 1;
if (this->position < 0 ) {
this->position = roadLength;
}
}
String RoadObject::toString() {
return String("Spike position:") + this->position + " track: " + this->track;
}
class Road {
private:
RoadObject **road;
public:
Road(RoadObject **road) {
this->road = road;
}
RoadObject* getObject(int i) {
return road[i];
}
int getHighestPosition() {
int pos = 0;
for (int i = 0; i < roadSize; i++) {
RoadObject* s = this->road[i];
pos = max(pos, s->getPosition());
}
return pos;
}
void update() {
for (int i = 0; i < roadSize; i++) {
RoadObject* s = this->road[i];
if (s->getPosition() <= 0) {
s->update(this->getHighestPosition() + 2);
} else {
s->update(roadLength);
}
}
}
};
class Level {
private:
Road* road;
String name;
int scoreThreshold;
int delayTime;
public:
Level( Road* road, String name, int scoreThreshold, int delayTime) {
this->road = road;
this->name = name;
this->scoreThreshold = scoreThreshold;
this->delayTime = delayTime;
}
int getDelay() {
return delayTime;
}
Road* getRoad() {
return this->road;
}
String getName() {
return this->name;
}
bool levelComplete(int score) {
return score >= scoreThreshold;
}
};
RoadObject *levelOneRoad[roadSize] = {
new Spike(0, 3),
new Spike(1, 7),
new Spike(0, 9),
new Spike(1, 12),
new Spike(1, 13),
new Spike(0, 15),
new Spike(1, 18),
new Spike(0, 20),
new Spike(0, 25),
new Spike(0, 27),
new Spike(1, 29),
new Spike(1, 32),
new Coin(0, 4),
new Coin(1, 14),
new Coin(1, 25)
};
RoadObject *levelTwoRoad[roadSize] = {
new Coin(0, 3),
new Spike(1, 7),
new Spike(0, 9),
new Spike(1, 12),
new Spike(1, 13),
new Spike(0, 15),
new Spike(1, 18),
new Spike(1, 19),
new Spike(1, 20),
new Spike(0, 25),
new Spike(0, 27),
new Spike(1, 29),
new Spike(1, 32),
new Coin(1, 14),
new Coin(1, 25)
};
RoadObject *levelThreeRoad[roadSize] = {
new Coin(0, 3),
new DeathSpike(1, 7),
new DeathSpike(0, 9),
new DeathSpike(1, 12),
new DeathSpike(1, 16),
new DeathSpike(0, 18),
new DeathSpike(1, 20),
new DeathSpike(1, 22),
new DeathSpike(0, 26),
new DeathSpike(0, 29),
new DeathSpike(0, 33),
new DeathSpike(1, 36),
new DeathSpike(1, 40),
new DeathSpike(1, 44),
new Coin(1, 25)
};
Level levels[3] = { Level( new Road(levelOneRoad), "L1 Easy Town", 100, 900) ,
Level(new Road(levelTwoRoad), "Level 2", 200, 600) ,
Level(new Road(levelThreeRoad), "L3 Death Valley", 300, 900)
};
int playerTrack = 1;
int playerColumnPosition = 1;
int delayTime = 900;
int delayTimeCountdown = delayTime;
int levelDelayCountdown = 1000;
int oldKeyPress = DOWN;
int keyPressDelay = delayTime;
int score = 0;
bool gameOver = false;
int currentLevelIndex = 0;
bool newLevel = true;
void setup() {
//Serial.begin(115200);
// initialize LCD and set up the number of columns and rows:
lcd.begin(16, 2);
// create a new character
lcd.createChar(0, heart);
// create a new character
lcd.createChar(1, armsDown);
// create a new character
lcd.createChar(2, armsUp);
// create a new character
lcd.createChar(3, run1);
// create a new character
lcd.createChar(4, run2);
// set the cursor to the top left
lcd.setCursor(0, 0);
}
void loop() {
Level level = levels[currentLevelIndex];
if (newLevel) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(level.getName());
newLevel = false;
delay(2000);
}
if (gameOver) return;
int keyPress = translateKeyPress(analogRead(0));
// Serial.println(keyPress);
delayTimeCountdown--;
levelDelayCountdown--;
drawPlayer(delayTime, keyPress);
gameOver = detectCollision(level);
if (levelDelayCountdown <= 0) {
score++;
level.getRoad()->update();
drawBlocks(level);
gameOver = detectCollision(level);
levelDelayCountdown = level.getDelay();
}
if (gameOver) {
runGameOver();
}
if (level.levelComplete(score)) {
currentLevelIndex++;
newLevel = true;
}
if (delayTimeCountdown <= 0)
delayTimeCountdown = delayTime;
}
bool detectCollision(Level level) {
bool collision = false;
for (int i = 0; i < roadSize; i++) {
RoadObject *s = level.getRoad()->getObject(i);
CollisionResult *result = s->collision(playerTrack, playerColumnPosition);
if (result->gameOver()) {
collision = true;
}
score += result->bonus();
}
return collision;
}
void runGameOver() {
lcd.setCursor(playerColumnPosition, playerTrack);
lcd.write(2);
delay(500);
lcd.setCursor(playerColumnPosition, playerTrack);
lcd.write(1);
delay(1000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.write(byte(2));
lcd.setCursor(0, 0);
lcd.print(String("Score:") + score);
lcd.setCursor(0, 1);
lcd.print("Hit RST to play");
lcd.setCursor(15, 0);
lcd.write(byte(0));
delay(10000);
}
void drawBlocks(Level level) {
String displayBuffer[2] = {" ",
" "
};
for (int i = 0; i < roadSize; i++) {
RoadObject *o = level.getRoad()->getObject(i);
if (o->getPosition() < columns) {
displayBuffer[o->getTrack()].setCharAt(o->getPosition(), o->displayChar());
}
}
//Serial.print(displayBuffer[0]);
//Serial.println("#");
//Serial.print(displayBuffer[1]);
//Serial.println("#");
lcd.setCursor(0, 0);
lcd.print(displayBuffer[0]);
lcd.setCursor(0, 1);
lcd.print(displayBuffer[1]);
// dumpArray();
}
void drawPlayer(int delayTime, int keyPress) {
if (keyPress != oldKeyPress ) {
if (keyPress == UP) {
clearPlayer(playerColumnPosition, 1);
playerTrack = 0;
}
if (keyPress == DOWN) {
clearPlayer(playerColumnPosition, 0);
playerTrack = 1;
}
// if (keyPress == RIGHT) {
// clearPlayer(playerColumnPosition, playerTrack);
// playerColumnPosition = min(playerColumnPosition + 1, columns - 1);
// }
// if (keyPress == LEFT) {
// clearPlayer(playerColumnPosition, playerTrack);
// playerColumnPosition = max(0, playerColumnPosition - 1);
// }
oldKeyPress = keyPress;
keyPressDelay = delayTime;
}
keyPressDelay--;
if (delayTimeCountdown >= delayTime / 2) {
lcd.setCursor(playerColumnPosition, playerTrack);
lcd.write(3);
} else {
lcd.setCursor(playerColumnPosition, playerTrack);
lcd.write(4);
}
}
void clearPlayer(int column, int row) {
lcd.setCursor(column, row);
lcd.print(" ");
}
int translateKeyPress(int x) {
if (x < 60) {
return RIGHT;
}
else if (x < 200) {
return UP;
}
else if (x < 400) {
return DOWN;
}
else if (x < 600) {
return LEFT;
}
else if (x < 800) {
return SELECT;
}
else {
return UNKNOWN_KEY;
}
}
void dumpArray(Level level) {
for (int i = 0; i < roadSize; i++) {
//Serial.println(String("----") + i);
RoadObject *o = level.getRoad()->getObject(i);
if (o == NULL) {
//Serial.println("we have null");
}
if (o->toString() == NULL) {
//Serial.println("we have toString null");
}
//Serial.println(String("loc:") + i + " " + o->toString() + o->displayChar());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment