Higher or Lower Arduino Game

by davidkolb1 in Circuits > Arduino

1370 Views, 6 Favorites, 0 Comments

Higher or Lower Arduino Game

IMG_1089.jpeg
IMG_1284.jpeg
IMG_1259.jpeg

This first ever Arduino project of mine is a simple card game where you are given a card from a standard deck of cards and you need to guess whether the next card will be either higher, lower or equal to the card you just got. Then try to get as long of a streak as you can without getting a wrong answer.

The concept is derived from a simple drinking game called "bussen". I made this prototype so that I could use it in the drinking game without the possibility of having a cheating dealer (me).

The project was quite difficult for me as I am a complete newbie to Arduino's, but I feel like I managed quite well. I hope you enjoy the results.

Supplies

Components:

  • Arduino Uno
  • 1 B10k rotary potentiometer
  • 3 buttons
  • 3 10k ohm resistors
  • LCD 1602 screen
  • wires wires wires

Casing:

  • 6 pieces of wood with a thickness of 1cm, other dimensions below
  • 2x 12x5 cm for the sides
  • 2x 12x17,5 cm for the front and back
  • 2x 7x19,5 cm for the top and bottom pieces


  • a saw
  • chisels
  • a nail gun and nails
  • wood glue
  • 4 small screws

Prototyping

IMG_1259.jpeg

I created my prototype on a breadboard for the ease of testing. I used all the components listed above. I ran into some trouble as the breadboard was split into two sides which I was not aware of. After a lot of testing the buttons finally registered and were able to be identified as lower, equal, and higher. This took me about a week of on and off working to get right.

Hardware, a Mess

IMG_1276.jpeg
image.png
image.png
image.png

While the first image might be a little jarring to look at, the other three diagrams might give you some comfort. The buttons are attached as pull-up resistors where we ground the pin so that when it is not pushed we get a definite one and a zero when it is. We do need the resistors so that we don't short circuit. For the LCD I used this intractable https://www.instructables.com/LCD-1602-With-Arduino-Uno-R3/ to help me out and it also links to the required code. Make sure you fiddle around with the potentiometer to make sure the LCD works after turning it on.

Software, Also a Mess?

Here is a breakdown of the code and what it does where! The code itself has also been commented to give an explanation as to what everything does.


#include <LiquidCrystal.h> // Include the LiquidCrystal library for LCD screen control

#include <math.h>     // Standard C library for mathematical operations

#include <stdio.h>    // Standard C library for input and output operations

#include <string.h>    // Standard C library for string handling functions

// These are like labels for our buttons. It makes the code easier to read later.

#define LOWER_BUTTON 2

#define EQUAL_BUTTON 7

#define HIGHER_BUTTON 8

// This sets up our LCD screen and tells the Arduino which pins it's connected to.

LiquidCrystal lcd(4, 6, 10, 11, 12, 13);

// This is a list of card names, just like a deck of cards you'd play with.

const char* cardDeck[] = {

  "Ace of Spades", "2 of Spades", "3 of Spades", "4 of Spades", "5 of Spades", "6 of Spades", "7 of Spades", "8 of Spades", "9 of Spades", "10 of Spades", "Jack of Spades", "Queen of Spades", "King of Spades",

  "Ace of Hearts", "2 of Hearts", "3 of Hearts", "4 of Hearts", "5 of Hearts", "6 of Hearts", "7 of Hearts", "8 of Hearts", "9 of Hearts", "10 of Hearts", "Jack of Hearts", "Queen of Hearts", "King of Hearts",

  "Ace of Diamonds", "2 of Diamonds", "3 of Diamonds", "4 of Diamonds", "5 of Diamonds", "6 of Diamonds", "7 of Diamonds", "8 of Diamonds", "9 of Diamonds", "10 of Diamonds", "Jack of Diamonds", "Queen of Diamonds", "King of Diamonds",

  "Ace of Clubs", "2 of Clubs", "3 of Clubs", "4 of Clubs", "5 of Clubs", "6 of Clubs", "7 of Clubs", "8 of Clubs", "9 of Clubs", "10 of Clubs", "Jack of Clubs", "Queen of Clubs", "King of Clubs"

};

// This calculates how many cards are in our virtual deck.

int numCards = sizeof(cardDeck) / sizeof(cardDeck[0]);

// This keeps track of how many times in a row the player has guessed correctly.

int streakCounter = 0;

// This will store the player's guess each round.

char playerGuess;

// This remembers the value of the last card we showed to the player.

int prevCardValue = -1;

// This function turns card names into numbers so we can compare them.

int getCardValue(const char* cardName) {

  if (strstr(cardName, "Ace")) {

    return 1;

  } else if (strstr(cardName, "2")) {

    return 2;

  } else if (strstr(cardName, "3")) {

    return 3;

  } else if (strstr(cardName, "4")) {

    return 4;

  } else if (strstr(cardName, "5")) {

    return 5;

  } else if (strstr(cardName, "6")) {

    return 6;

  } else if (strstr(cardName, "7")) {

    return 7;

  } else if (strstr(cardName, "8")) {

    return 8;

  } else if (strstr(cardName, "9")) {

    return 9;

  } else if (strstr(cardName, "10")) {

    return 10;

  } else if (strstr(cardName, "Jack")) {

    return 11;

  } else if (strstr(cardName, "Queen")) {

    return 12;

  } else if (strstr(cardName, "King")) {

    return 13;

  } else {

    return -1; // Invalid card name

  }

}

// After we show a card, we don't want to show it again. This function removes it from our virtual deck.

void removeFromArray(int index) {

  if (index < 0 || index >= numCards) {

    return;

  }

  for (int i = index; i < numCards - 1; i++) {

    cardDeck[i] = cardDeck[i + 1];

  }

  numCards--;

}

// This updates the player's correct guessing streak on the LCD.

void updateStreakCounter() {

  lcd.setCursor(0, 1);

  lcd.print("Streak: ");

  lcd.print(streakCounter);

}

// This figures out if one card is less than, equal to, or greater than another card.

char isLowerEqualOrHigher(int prevCardValue, int nextCardValue) {

  if (nextCardValue > prevCardValue) {

    return '>';

  } else if (nextCardValue < prevCardValue) {

    return '<';

  } else {

    return '=';

  }

}

// This asks the player to guess if the next card will be lower, equal, or higher.

void promptUser() {

  lcd.clear();

  lcd.print("< = or > ?");

  bool buttonPressed = false;

  while(!buttonPressed) {

    if (digitalRead(LOWER_BUTTON) == HIGH) {

      playerGuess = '<';

      lcd.clear();

      lcd.print("You chose lower");

      buttonPressed = true;

    } else if (digitalRead(EQUAL_BUTTON) == HIGH) {

      playerGuess = '=';

      lcd.clear();

      lcd.print("You chose equal");

      buttonPressed = true;

    } else if (digitalRead(HIGHER_BUTTON) == HIGH) {

      playerGuess = '>';

      lcd.clear();

      lcd.print("You chose higher");

      buttonPressed = true;

    }

  }

  delay(1000);

}

// If we run out of cards, this function resets everything so we can play again.

void resetDeck() {

  for (int i = 0; i < sizeof(cardDeck) / sizeof(cardDeck[0]); i++) {

    cardDeck[i] = originalCardDeck[i];

  }

}

// When the Arduino starts, this function runs one time to set everything up.

void setup() {

  Serial.begin(9600);

  randomSeed(analogRead(0));

  pinMode(2, INPUT);

  pinMode(7, INPUT);

  pinMode(8, INPUT);

  lcd.begin(16, 2);

  lcd.home();

}

// After setuo, this function runs over and over. It makes the game run continuously.

void loop() {

    // If we've shown all the cards (deck is empty), then the game ends. 

  if (numCards == 0) {

    lcd.clear(); // Clear the LCD screen.

    lcd.print("End of Game!"); // Display that the game has ended.

    lcd.setCursor(0, 1); // Move to the second line of the LCD.

    lcd.print("Streak: ");

    lcd.print(streakCounter); // Display the player's final score (streak).

    delay(5000); // Wait for 5 seconds.

    lcd.clear();

    lcd.print("Restarting..."); // Tell the player that the game is restarting.

    delay(2000); // Wait for 2 seconds.

     

    // Reset the game

    resetDeck(); // Bring all the cards back into the deck.

    numCards = sizeof(cardDeck) / sizeof(cardDeck[0]); // Reset the card count.

    streakCounter = 0; // Reset the player's score.

    return; // Skip the rest of the loop and start from the beginning.

  }



// Pick a random card from our virtual deck.

  int cardIndex = random(numCards);

  int currentCardValue = getCardValue(cardDeck[cardIndex]);

  lcd.clear(); // Clear the LCD screen.

  lcd.print(cardDeck[cardIndex]); // Show the player the randomly chosen card.

  delay(2000); // Wait for 2 seconds.

  // Ask the player to make their guess.

  promptUser();

  // Check if the player's guess was correct.

  if (playerGuess == isLowerEqualOrHigher(prevCardValue, currentCardValue)) {

    streakCounter++; // Increase the player's score if they were right.

    lcd.clear();

    lcd.print("Correct!"); // Tell the player they were correct.

  } else {

    streakCounter = 0; // Reset the score to 0 if they were wrong.

    lcd.clear();

    lcd.print("Incorrect!"); // Tell the player they were incorrect.

  }

  updateStreakCounter(); // Update the player's score on the LCD.

  prevCardValue = currentCardValue; // Remember the card we just showed for the next round.

  removeFromArray(cardIndex); // Remove the card we just showed so it doesn't appear again.

  delay(2000); // Wait for 2 seconds before starting the next round.

}



  1. Library Inclusions: The code includes the necessary libraries for LCD screen control (LiquidCrystal.h), mathematical operations (math.h), and input/output operations (stdio.h and string.h).
  2. Button Definitions: The buttons used in the game are defined and assigned labels for readability (LOWER_BUTTON, EQUAL_BUTTON, HIGHER_BUTTON).
  3. LCD Setup: The LCD screen is initialized with its pin connections (4, 6, 10, 11, 12, 13) using the LiquidCrystal library.
  4. Card Deck: An array cardDeck is defined, containing card names as strings. This array represents the virtual deck of cards used in the game.
  5. Variables: Several variables are declared to track game state and user interaction:
  • numCards: Tracks the number of remaining cards in the virtual deck.
  • streakCounter: Keeps count of consecutive correct guesses.
  • playerGuess: Stores the player's guess for each round.
  • prevCardValue: Stores the value of the last card shown to the player.
  1. Function Definitions:
  • getCardValue(): Maps card names to numerical values for comparison.
  • removeFromArray(): Removes a card from the virtual deck array.
  • updateStreakCounter(): Updates the player's streak on the LCD screen.
  • isLowerEqualOrHigher(): Compares two card values and returns '<', '=', or '>'.
  • promptUser(): Prompts the player to make a guess using buttons and displays their choice on the LCD screen.
  • resetDeck(): Resets the virtual deck and game state when all cards have been shown.
  1. setup() Function: The setup function initializes the necessary components and pins:
  • Initializes serial communication.
  • Seeds the random number generator with an analog reading.
  • Sets button pins as inputs.
  • Initializes the LCD screen with a 16x2 configuration.
  1. loop() Function: This function runs continuously after the setup and manages the core game logic:
  • Checks if all cards have been shown, in which case it displays the final score and restarts the game.
  • Picks a random card from the virtual deck.
  • Displays the card on the LCD screen for 2 seconds.
  • Prompts the player to make a guess.
  • Compares the player's guess with the relationship between the previous and current card values and updates the score accordingly.
  • Updates the streak display on the LCD screen.
  • Updates prevCardValue for the next round.
  • Removes the shown card from the virtual deck.
  • Adds a delay of 2 seconds before starting the next round.


Casing, Not That Big of a Mess!

IMG_1088.jpeg
IMG_1089.jpeg
IMG_1090.jpeg
IMG_1298.jpeg
IMG_1300.jpeg
IMG_1299.jpeg

This casing is basically as easy as it can get. For the casing I used mostly oak and a little bit of walnut for a little frame around the LCD screen. I chose to work with regular wood instead of MDF because I enjoy working with the material and I think it gives a lot more control and beauty. The front panel has 2 open spaces, one for the LCD and one for the buttons. The Arduino is attached to the back. panel with 4 screws. The side panels are attached between the front and back. The top and bottom lie within these four pieces. They are all glued together and then secured with the nail gun so make sure that everything inside is sorted out and working. I also left a small opening on the right side panel where the potentiometer and power cable are attached.

I've also attached some of the sketches that I made for an initial design where the casing would resemble a saloon or a dealer with a little bowtie. Sadly I ran into time issues and did not manage to make these designs into reality.

Reflection

While the project was certainly a challenge, I do believe that I made it a little harder on my self by being stubborn. I was very resistant to the whole project and did not want to make it at all. Now the project is finished and I am proud of myself for accomplishing what I did. If I were to change anything I would probably go more over the top with the casing. Another idea I had was to 3d model and print the casing to resemble a bus from the side. I think it would have worked well for the drinking game.