#include <LiquidCrystal.h>
// Symbol class to manage custom characters
class Symbol {
public:
String name;
int nameSize;
int symbolId;
byte* characterBytes[8]; // Array of pointers to hold up to 8 byte arrays
int bytesCount; // Number of byte arrays for this symbol
// Positions for each character (x, y coordinates)
struct Position {
int x;
int y;
};
Position positions[8];
// Constructor
Symbol(String symbolName, int id, byte* bytes[], int numBytes) {
name = symbolName;
nameSize = symbolName.length();
symbolId = id;
bytesCount = numBytes;
// Copy the byte array pointers
for (int i = 0; i < numBytes && i < 8; i++) {
characterBytes[i] = bytes[i];
}
}
// Set position for each character
void setPosition(int index, int x, int y) {
if (index >= 0 && index < bytesCount) {
positions[index].x = x;
positions[index].y = y;
}
}
// Register and place on LCD
void placeOnLCD(LiquidCrystal& lcd) {
for (int i = 0; i < bytesCount; i++) {
lcd.createChar(symbolId + i, characterBytes[i]);
lcd.setCursor(positions[i].x, positions[i].y);
lcd.write(symbolId + i);
}
}
};
// Class to manage an individual LCD display
class SlotDisplay {
private:
LiquidCrystal* lcd;
bool isCorrect;
bool isLocked;
int targetSymbol;
int currentSymbol;
Symbol* symbols[5]; // Array to hold Cherry, Currency, Diamond, Bar, Seven symbols
public:
SlotDisplay(LiquidCrystal* lcdObj, int target) {
lcd = lcdObj;
isCorrect = false;
isLocked = false;
targetSymbol = target;
currentSymbol = -1;
}
void setSymbols(Symbol* cherry, Symbol* currency, Symbol* diamond, Symbol* bar, Symbol* seven) {
symbols[0] = cherry;
symbols[1] = currency;
symbols[2] = diamond;
symbols[3] = bar;
symbols[4] = seven;
}
void display(int symbolIndex) {
// If the display is locked, don't change it
if (isLocked) return;
currentSymbol = symbolIndex;
lcd->clear();
if (symbolIndex >= 0 && symbolIndex <= 4) { // Updated to include Seven symbol
symbols[symbolIndex]->placeOnLCD(*lcd);
} else {
lcd->setCursor(0, 1);
lcd->print(symbolIndex);
}
// Check if this is the target symbol
isCorrect = (symbolIndex == targetSymbol);
}
// New method to lock in the current symbol (used by cheat only)
void lockSymbol() {
if (currentSymbol == targetSymbol) {
isLocked = true;
isCorrect = true;
}
}
bool isDisplayCorrect() {
return isCorrect;
}
bool isDisplayLocked() {
return isLocked;
}
int getCurrentSymbol() {
return currentSymbol;
}
void reset() {
lcd->clear();
isCorrect = false;
isLocked = false;
currentSymbol = -1;
}
};
// Main game class
class SlotMachine {
private:
SlotDisplay* displays[3];
const int buttonPin;
int buttonState;
int lastButtonState;
unsigned long lastTapTime;
int tapCount;
bool flag;
double moneyCount;
bool allCorrect;
int currentPosition;
int targetSequence[3];
Symbol* cherrySymbol;
Symbol* currencySymbol;
Symbol* diamondSymbol;
Symbol* barSymbol;
Symbol* sevenSymbol;
public:
SlotMachine(int button, LiquidCrystal* lcd1, LiquidCrystal* lcd2, LiquidCrystal* lcd3)
: buttonPin(button) {
// Initialize variables
buttonState = LOW;
lastButtonState = LOW;
lastTapTime = 0;
tapCount = 0;
flag = true;
moneyCount = 50;
allCorrect = false;
currentPosition = 0;
// Set target sequence - what symbols we're looking for on each display
targetSequence[0] = 2;
targetSequence[1] = 2;
targetSequence[2] = 2;
// Initialize displays
displays[0] = new SlotDisplay(lcd1, targetSequence[0]);
displays[1] = new SlotDisplay(lcd2, targetSequence[1]);
displays[2] = new SlotDisplay(lcd3, targetSequence[2]);
// Setup symbols
setupSymbols();
// Assign symbols to displays
for (int i = 0; i < 3; i++) {
displays[i]->setSymbols(cherrySymbol, currencySymbol, diamondSymbol, barSymbol, sevenSymbol);
}
// Setup pin mode
pinMode(buttonPin, INPUT);
}
void setupSymbols() {
// Cherry symbol setup
static byte cherry1x8[] = { B00010, B00100, B01000, B10000, B00000, B00000, B00000, B0000 };
static byte cherry0x6[] = { B00000, B00000, B00001, B00011, B00011, B00010, B00011, B00001 };
static byte cherry0x7[] = { B00000, B11100, B11110, B11111, B11111, B11111, B00110, B11100 };
static byte cherry0x8[] = { B00000, B00000, B00000, B00000, B10000, B01000, B00100, B00011 };
static byte cherry1x6[] = { B00001, B00011, B00011, B00010, B00011, B00001, B00000, B00000 };
static byte cherry1x7[] = { B11100, B11110, B11111, B11111, B00111, B11110, B11100, B00000 };
static byte cherry1x9[] = { B10000, B01000, B01000, B00000, B00000, B00000, B00000, B00000 };
byte* cherryBytes[] = {
cherry0x6, cherry0x7, cherry0x8, cherry1x6, cherry1x7, cherry1x8, cherry1x9
};
cherrySymbol = new Symbol("Cherry", 0, cherryBytes, 7);
cherrySymbol->setPosition(0, 6, 0); // cherry0x6
cherrySymbol->setPosition(1, 7, 0); // cherry0x7
cherrySymbol->setPosition(2, 8, 0); // cherry0x8
cherrySymbol->setPosition(3, 6, 1); // cherry1x6
cherrySymbol->setPosition(4, 7, 1); // cherry1x7
cherrySymbol->setPosition(5, 8, 1); // cherry1x8
cherrySymbol->setPosition(6, 9, 1); // cherry1x9
// Currency symbol setup
static byte currency1x8[] = { B01100, B11111, B01100, B01100, B01100, B11100, B11000, B00000 };
static byte currency0x6[] = { B00000, B00110, B01110, B01100, B01100, B01100, B11111, B01100 };
static byte currency0x7[] = { B00000, B00011, B00111, B01100, B01100, B01100, B11111, B01100 };
static byte currency0x8[] = { B00000, B10000, B11000, B01100, B01100, B01100, B11111, B01100 };
static byte currency1x6[] = { B01100, B11111, B01100, B01100, B01100, B00111, B00011, B00000 };
static byte currency1x7[] = { B01100, B11111, B01100, B01100, B01100, B11000, B10000, B00000 };
byte* currencyBytes[] = {
currency0x6, currency0x7, currency0x8, currency1x6, currency1x7, currency1x8
};
currencySymbol = new Symbol("Currency", 1, currencyBytes, 6);
currencySymbol->setPosition(0, 6, 0); // currency0x6
currencySymbol->setPosition(1, 7, 0); // currency0x7
currencySymbol->setPosition(2, 8, 0); // currency0x8
currencySymbol->setPosition(3, 6, 1); // currency1x6
currencySymbol->setPosition(4, 7, 1); // currency1x7
currencySymbol->setPosition(5, 8, 1); // currency1x8
// Diamond symbol setup
static byte diamond0x8[] = { B00000, B10000, B11100, B01100, B00110, B10010, B11110, B11110 };
static byte diamond0x6[] = { B00000, B00000, B00000, B00000, B00000, B00011, B00011, B01111 };
static byte diamond0x7[] = { B00000, B00111, B00100, B11111, B11111, B11111, B11111, B11111 };
static byte diamond1x6[] = { B01111, B00011, B00011, B00000, B00000, B00000, B00000, B00000 };
static byte diamond1x7[] = { B11111, B11111, B11111, B11111, B11111, B00111, B00111, B00000 };
static byte diamond1x8[] = { B11110, B11110, B11110, B11110, B11100, B11100, B10000, B00000 };
byte* diamondBytes[] = {
diamond0x8, diamond0x6, diamond0x7, diamond1x6, diamond1x7, diamond1x8
};
diamondSymbol = new Symbol("Diamond", 2, diamondBytes, 6);
diamondSymbol->setPosition(0, 8, 0); // diamond0x7
diamondSymbol->setPosition(1, 6, 0); // diamond0x8
diamondSymbol->setPosition(2, 7, 0); // diamond0x9
diamondSymbol->setPosition(3, 6, 1); // diamond1x7
diamondSymbol->setPosition(4, 7, 1); // diamond1x8
diamondSymbol->setPosition(5, 8, 1); // diamond1x9
// Bar symbol setup
static byte bar1x8[] = { B10001, B11110, B00000, B00000, B11111, B10001, B10001, B01110 };
static byte bar0x7[] = { B11111, B10000, B10000, B11111, B00000, B00000, B11111, B00000 };
static byte bar0x8[] = { B11111, B10001, B10001, B01111, B00000, B00000, B11110, B10001 };
static byte bar1x7[] = { B00000, B11111, B00000, B00000, B11111, B00000, B00000, B11111 };
byte* barBytes[] = {
bar0x7, bar0x8, bar1x7, bar1x8
};
barSymbol = new Symbol("bar", 3, barBytes, 4);
barSymbol->setPosition(0, 7, 0); // bar0x7
barSymbol->setPosition(1, 8, 0); // bar0x8
barSymbol->setPosition(2, 7, 1); // bar1x7
barSymbol->setPosition(3, 8, 1); // bar1x8
// Seven symbol setup
static byte seven0x6[] = { B00000, B00000, B00000, B00000, B10000, B11111, B11111, B11111 };
static byte seven0x7[] = { B00000, B00011, B00011, B00001, B00001, B00001, B11001, B11111 };
static byte seven0x8[] = { B00000, B11111, B11111, B11110, B11110, B11110, B11110, B11110 };
static byte seven1x6[] = { B11111, B11111, B11111, B10000, B00000, B00000, B00000, B00000 };
static byte seven1x7[] = { B11111, B11111, B11111, B11111, B00111, B00001, B00000, B00000 };
static byte seven1x8[] = { B11110, B11110, B11110, B11111, B11111, B11111, B01111, B00000 };
byte* sevenBytes[] = {
seven0x6, seven0x7, seven0x8, seven1x6, seven1x7, seven1x8
};
sevenSymbol = new Symbol("Seven", 4, sevenBytes, 6);
sevenSymbol->setPosition(0, 6, 0); // seven0x6
sevenSymbol->setPosition(1, 7, 0); // seven0x7
sevenSymbol->setPosition(2, 8, 0); // seven0x8
sevenSymbol->setPosition(3, 6, 1); // seven1x6
sevenSymbol->setPosition(4, 7, 1); // seven1x7
sevenSymbol->setPosition(5, 8, 1); // seven1x8
}
void update() {
if (allCorrect) return;
buttonState = digitalRead(buttonPin);
// Handle single click (pull the slot)
if (buttonState == HIGH && flag == true) {
singleClick();
delay(50);
flag = false;
}
if (buttonState == LOW) {
flag = true;
delay(50);
}
// Handle rapid tapping (cheat code)
if (buttonState == LOW && lastButtonState == HIGH) {
// Check if taps are within 0.85s of each other
if (millis() - lastTapTime < 850) {
tapCount++;
} else {
tapCount = 1; // Reset if too slow
}
lastTapTime = millis(); // Update last tap time
}
// Check for cheat code activation
if (tapCount == 6 && millis() - lastTapTime >= 850) {
activateCherry();
tapCount = 0;
checkAllCorrect();
} else if (tapCount == 8 && millis() - lastTapTime >= 850) {
activateCurrency();
tapCount = 0;
checkAllCorrect();
} else if (tapCount == 7 && millis() - lastTapTime >= 850) {
activateDiamond();
tapCount = 0;
checkAllCorrect();
} else if (tapCount == 3 && millis() - lastTapTime >= 850) {
activateBar();
tapCount = 0;
checkAllCorrect();
} else if (tapCount == 5 && millis() - lastTapTime >= 850) {
activateSeven();
tapCount = 0;
checkAllCorrect();
}
lastButtonState = buttonState;
}
void singleClick() {
moneyCount = moneyCount - 1;
Serial.print("£");
Serial.println(moneyCount);
// Generate random numbers for each display
for (int i = 0; i < 3; i++) {
if (!displays[i]->isDisplayLocked()) {
int randomSymbol = random(0, 5); // Now includes seven (0, 1, 2, 3, 4)
displays[i]->display(randomSymbol);
}
}
// Check if we've won by getting matching symbols
checkForRandomWin();
}
// Modified method to check for different win conditions
void checkForRandomWin() {
// Get the first display's symbol
int firstSymbol = displays[0]->getCurrentSymbol();
// Check if all displays show the same symbol
bool allSame = true;
for (int i = 1; i < 3; i++) {
if (displays[i]->getCurrentSymbol() != firstSymbol) {
allSame = false;
break;
}
}
// If all displays show the same symbol, check which win condition
if (allSame && firstSymbol >= 0) {
// Check if the matching symbols are the target sequence
bool isTargetSequence = true;
for (int i = 0; i < 3; i++) {
if (displays[i]->getCurrentSymbol() != targetSequence[i]) {
isTargetSequence = false;
break;
}
}
if (isTargetSequence) {
// Major win - all symbols match the target sequence
allCorrect = true;
Serial.println("You Win Collect £60");
moneyCount = moneyCount + 60;
Serial.print("£");
Serial.println(moneyCount);
// Lock the symbols
for (int i = 0; i < 3; i++) {
displays[i]->lockSymbol();
}
} else {
// Minor win - all symbols match but not the target sequence
Serial.println("Minor Win +£10");
moneyCount = moneyCount + 10;
Serial.print("£");
Serial.println(moneyCount);
// Game continues - don't lock symbols or set allCorrect
}
}
}
void activateCherry() {
if (currentPosition >= 3) return;
// Display cherry on current display
displays[currentPosition]->display(0);
// If it's the correct symbol, lock it in and advance
if (0 == targetSequence[currentPosition]) {
displays[currentPosition]->lockSymbol();
currentPosition++;
}
}
void activateCurrency() {
if (currentPosition >= 3) return;
// Display currency on current display
displays[currentPosition]->display(1);
// If it's the correct symbol, lock it in and advance
if (1 == targetSequence[currentPosition]) {
displays[currentPosition]->lockSymbol();
currentPosition++;
}
}
void activateDiamond() {
if (currentPosition >= 3) return;
// Display diamond on current display
displays[currentPosition]->display(2);
// If it's the correct symbol, lock it in and advance
if (2 == targetSequence[currentPosition]) {
displays[currentPosition]->lockSymbol();
currentPosition++;
}
}
void activateBar() {
if (currentPosition >= 3) return;
// Display bar on current display
displays[currentPosition]->display(3);
// If it's the correct symbol, lock it in and advance
if (3 == targetSequence[currentPosition]) {
displays[currentPosition]->lockSymbol();
currentPosition++;
}
}
void activateSeven() {
if (currentPosition >= 3) return;
// Display seven on current display
displays[currentPosition]->display(4);
// If it's the correct symbol, lock it in and advance
if (4 == targetSequence[currentPosition]) {
displays[currentPosition]->lockSymbol();
currentPosition++;
}
}
void checkAllCorrect() {
if (currentPosition >= 3) {
// All displays are locked and correct
allCorrect = true;
Serial.println("You Win Collect £60");
moneyCount = moneyCount + 60;
Serial.print("£");
Serial.println(moneyCount);
}
}
void reset() {
for (int i = 0; i < 3; i++) {
displays[i]->reset();
}
currentPosition = 0;
allCorrect = false;
}
double getMoney() {
return moneyCount;
}
// Cleanup function for memory management
~SlotMachine() {
delete cherrySymbol;
delete currencySymbol;
delete diamondSymbol;
delete barSymbol;
delete sevenSymbol;
for (int i = 0; i < 3; i++) {
delete displays[i];
}
}
};
// Main program
LiquidCrystal lcdOne(22, 23, 4, 5, 6, 7);
LiquidCrystal lcdTwo(24, 25, 10, 11, 12, 13);
LiquidCrystal lcdThree(14, 15, 16, 17, 18, 19);
const int buttonPin = 8;
SlotMachine* slotMachine;
void setup() {
Serial.begin(9600);
randomSeed(analogRead(A0));
lcdOne.begin(16, 2);
lcdTwo.begin(16, 2);
lcdThree.begin(16, 2);
slotMachine = new SlotMachine(buttonPin, &lcdOne, &lcdTwo, &lcdThree);
Serial.print("£");
Serial.println(slotMachine->getMoney());
}
void loop() {
slotMachine->update();
}