Table of Contents
Catch Coins
Catch Coins is a project by Olivier with hardware support from Jose. It is a projected intended to learn more about Arduino.
Oliver managed to get it to a working state within a day's work.
The latest source code is available on GitHub.
Hardware
-
Arduino UNO
-
16x2 LCD display
-
Shield to connect them together.
Possible Extensions
-
Add LEDs that blink red or green when the user scores or dies
-
Add an alcohol sensor to change the speed depending on drunk you are
-
Add some IR to send monsters to another player's screen (multiplayer! woohoo!)
Sample Code
- catchcoins.ino
-
//Sample using LiquidCrystal library #include <LiquidCrystal.h> // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // define some values used by the panel and buttons int lcd_key = 0; int adc_key_in = 0; #define PLAYER_CHAR 0 #define COIN_CHAR 1 #define MONSTER_RIGHT_CHAR 2 #define MONSTER_LEFT_CHAR 3 #define MONSTER_CLOSED_CHAR 4 #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 #define DIR_RIGHT 0 #define DIR_LEFT 1 String whiteLine = " "; int playerPos[2] = {0,0}; // {x,y} int coinPos[2] = {0,0}; // {x,y} int monsters[2][3] = {{0,0,DIR_RIGHT},{15,1,DIR_LEFT}}; // {x,y,dir} int score = 0; boolean walls = true; // used if you want an infinite field (loop from one side to the other) long loopNumber=0; boolean playerIsOnCoin() { return playerPos[0] == coinPos[0] && playerPos[1] == coinPos[1]; } boolean playerMeetMonster() { return (playerPos[0] == monsters[0][0] && playerPos[1] == monsters[0][1]) || (playerPos[0] == monsters[1][0] && playerPos[1] == monsters[1][1]); } // process the player's move void processPos() { if (playerIsOnCoin()) { score++; generateNewCoin(); } if (playerMeetMonster()) { score--; } } void moveMonsters() { if(loopNumber % 3 == 0) { Serial.print("loop is "); Serial.println(loopNumber); for(int i = 0; i<2; i++) { int* monsterPos = monsters[i]; if(monsterPos[0] == 15) { monsterPos[2] = DIR_LEFT; monsterPos[0] = 14; } else if(monsterPos[0] == 0) { monsterPos[2] = DIR_RIGHT; monsterPos[0] = 1; } else if(monsterPos[2] == DIR_RIGHT) { monsterPos[0] += 1; } else { monsterPos[0] -= 1; } } } } void generateNewCoin() { while(playerIsOnCoin()) { coinPos[0] = random(0,16); coinPos[1] = random(0,2); } } // draw the field void drawField() { showLines(whiteLine,whiteLine); lcd.setCursor(coinPos[0],coinPos[1]); lcd.write((uint8_t)COIN_CHAR); lcd.setCursor(playerPos[0],playerPos[1]); lcd.write((uint8_t)PLAYER_CHAR); for(int i = 0; i<2; i++) { lcd.setCursor(monsters[i][0],monsters[i][1]); if(loopNumber % 3 == 1) { lcd.write((uint8_t)MONSTER_CLOSED_CHAR); } else if(monsters[i][2] == DIR_RIGHT) { lcd.write((uint8_t)MONSTER_RIGHT_CHAR); } else { lcd.write((uint8_t)MONSTER_LEFT_CHAR); } } } // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... } void niceDisplayLoop1(char c1,char c2,boolean indirect) { int val; for( int i=0; i<8; i++) { char o = i % 2 == 0 ? c1 : c2; char p = i % 2 == 0 ? c2 : c1; val = indirect ? i : 7-i; lcd.setCursor(val,1); lcd.print(o); val = indirect ? 15-i : 8+i; lcd.setCursor(val,1); lcd.print(p); val = indirect ? 7-i : i; lcd.setCursor(val,0); lcd.print(p); val = indirect ? 8+i :15-i ; lcd.setCursor(val,0); lcd.print(o); delay(100); } } void crossAppear(String line1, String line2,boolean stay) { int max = stay? 16 : 32; char charToPrint; for( int i=0; i<16; i++) { for( int k=0; k<=i; k++) { lcd.setCursor(k,0); charToPrint = 15+k-i < line1.length() ? line1[15-i+k] : ' '; lcd.print(charToPrint); lcd.setCursor(15-k,1); charToPrint = i-k < line2.length() ? line2[i-k] : ' '; lcd.print(charToPrint); } delay(80); } if(!stay) { delay(50); blinkDisplay(line1,line2,4,100); delay(200); for( int i=0; i<16; i++) { for( int k=i; k<16; k++) { lcd.setCursor(k,0); charToPrint = k-i-1 < line1.length() ? line1[k-i-1]:' '; lcd.print(charToPrint); lcd.setCursor(15-k,1); charToPrint = i-k+16 < line2.length() ? line2[i-k+16]:' '; lcd.print(charToPrint); } delay(80); } } } void showLines(String line1, String line2) { lcd.setCursor(0,0); lcd.print(line1); lcd.setCursor(0,1); lcd.print(line2); } void blinkDisplay(String line1, String line2, int times, int wait) { for(int i=0;i<times;i++) { showLines(line1,line2); delay(wait); showLines(whiteLine,whiteLine); delay(wait); } showLines(line1,line2); } void initLcd() { byte player[8] = { B01110, B00100, B01110, B10101, B00100, B01010, B01010, }; lcd.createChar(PLAYER_CHAR,player); byte monster_left[8] = { B01100, B01110, B00111, B00011, B00111, B01110, B01100, }; lcd.createChar(MONSTER_LEFT_CHAR,monster_left); byte monster_right[8] = { B00110, B01110, B11100, B11000, B11100, B01110, B00110, }; lcd.createChar(MONSTER_RIGHT_CHAR,monster_right); byte monster_closed[8] = { B00000, B00000, B11111, B11111, B11111, B00000, B00000, }; lcd.createChar(MONSTER_CLOSED_CHAR,monster_closed); byte coin[8] = { B00100, B01010, B10001, B10101, B10001, B01010, B00100, }; lcd.createChar(COIN_CHAR,coin); lcd.begin(16, 2); // start the library } void displayStartSequence() { //niceDisplayLoop1(' ',' ',false); //niceDisplayLoop1('*','-',true); //niceDisplayLoop1('*','-',false); crossAppear(" Catch coins "," oO0Oo..oO0Oo ",false); crossAppear(" but don't "," get caught ",false); } void showScores() { crossAppear(" Your score is ",String(score),false); } void selectKeyPressed() { showScores(); } void leftKeyPressed() { if(playerPos[0] == 0) { playerPos[0] = walls ? 0 : 15; } else { playerPos[0] -= 1; } processPos(); } void rightKeyPressed() { if(playerPos[0] == 15) { playerPos[0] = walls ? 15 : 0; } else { playerPos[0] += 1; } } void upKeyPressed() { if(playerPos[1] == 0) { playerPos[1] = walls ? 0 : 1; } else { playerPos[1] -= 1; } } void downKeyPressed() { if(playerPos[1] == 1) { playerPos[1] = walls ? 1 : 0; } else { playerPos[1] = 1; } } void noKeyPressed() {} void setup() { Serial.begin(9600); randomSeed(analogRead(2)); initLcd(); displayStartSequence(); generateNewCoin(); drawField(); } void loop() { loopNumber++; long time = millis(); moveMonsters(); int lcd_key = read_LCD_buttons(); switch (lcd_key) { case btnRIGHT: { rightKeyPressed(); break; } case btnLEFT: { leftKeyPressed(); break; } case btnUP: { upKeyPressed(); break; } case btnDOWN: { downKeyPressed(); break; } case btnSELECT: { selectKeyPressed(); break; } case btnNONE: { noKeyPressed(); break; } } processPos(); drawField(); if(millis()-time < 100) { delay(100-millis()+time); } }