#include <FastLED.h>
#include <LiquidCrystal_I2C.h>
#define NUM_LEDS (9 * 10) + 4 //(9 Lights per Row * 10 Rows) + 4 Solution LEDs
#define DATA_PIN 2
#define GUESS_BUTTON 11
#define DELETE_BUTTON 10
#define NEW_GAME_BUTTON 12
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
#define RED_BUTTON 9
#define GREEN_BUTTON 8
#define BLUE_BUTTON 7
#define YELLOW_BUTTON 6
#define PURPLE_BUTTON 5
#define ORANGE_BUTTON 4
#define RIGHT_COLOR_PEG 2
#define RIGHT_COLOR_AND_POSITION_PEG 1
#define GUESS0_LED 8
#define GUESS1_LED 17
#define GUESS2_LED 26
#define GUESS3_LED 35
#define GUESS4_LED 44
#define GUESS5_LED 53
#define GUESS6_LED 62
#define GUESS7_LED 71
#define GUESS8_LED 80
#define GUESS9_LED 89
#define SOLUTION0_LED 93
#define SOLUTION1_LED 92
#define SOLUTION2_LED 91
#define SOLUTION3_LED 90
const bool debug = false;
const byte solutionLEDs[] = {SOLUTION0_LED, SOLUTION1_LED, SOLUTION2_LED, SOLUTION3_LED};
const byte guessLEDs[10][9] =
{{GUESS0_LED, 7, 6, 5, 4, 3, 2, 1, 0},
{GUESS1_LED, 16, 15, 14, 13, 12, 11, 10, 9},
{GUESS2_LED, 25, 24, 23, 22, 21, 20, 19, 18},
{GUESS3_LED, 34, 33, 32, 31, 30, 29, 28, 27},
{GUESS4_LED, 43, 42, 41, 40, 39, 38, 37, 36},
{GUESS5_LED, 52, 51, 50, 49, 48, 47, 46, 45},
{GUESS6_LED, 61, 60, 59, 58, 57, 56, 55, 54},
{GUESS7_LED, 70, 69, 68, 67, 66, 65, 64, 63},
{GUESS8_LED, 79, 78, 77, 76, 75, 74, 73, 72},
{GUESS9_LED, 88, 87, 86, 85, 84, 83, 82, 81}};
const byte pushButtons[] = {RED_BUTTON, GREEN_BUTTON, BLUE_BUTTON, YELLOW_BUTTON, PURPLE_BUTTON, ORANGE_BUTTON};
CRGB leds[NUM_LEDS];
CRGB colorMap[10];
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
int currentLEDIndex = 0;
CRGB currentLEDColor;
bool gameStarted = false;
bool gameOver = false;
int currentGuess = 0;
int currentGuessDigit = 0;
byte code[4];
bool showSolution = false;
byte guesses[10][8] = {{0}}; //10-position array for rows,
// with 8-position array for Guess + Feedback for that row
void setup() {
if (debug) {
Serial.begin(9600);
}
//Initialize the LED strip and Color Map
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); // GRB ordering is assumed
colorMap[RED_BUTTON] = CRGB::Red;
colorMap[GREEN_BUTTON] = CRGB::Lime;
colorMap[BLUE_BUTTON] = CRGB::Blue;
colorMap[YELLOW_BUTTON] = CRGB::Yellow;
colorMap[PURPLE_BUTTON] = CRGB::Purple;
colorMap[ORANGE_BUTTON] = CRGB::DarkOrange;
colorMap[RIGHT_COLOR_AND_POSITION_PEG] = CRGB::Lime;
colorMap[RIGHT_COLOR_PEG] = CRGB::Yellow;
//Initialize Push Buttons
pinMode(NEW_GAME_BUTTON, INPUT_PULLUP);
pinMode(GUESS_BUTTON, INPUT_PULLUP);
pinMode(DELETE_BUTTON, INPUT_PULLUP);
pinMode(RED_BUTTON, INPUT_PULLUP);
pinMode(GREEN_BUTTON, INPUT_PULLUP);
pinMode(BLUE_BUTTON, INPUT_PULLUP);
pinMode(YELLOW_BUTTON, INPUT_PULLUP);
pinMode(PURPLE_BUTTON, INPUT_PULLUP);
pinMode(ORANGE_BUTTON, INPUT_PULLUP);
//Initialize the LCD Display
lcd.begin(20, 4);
lcd.backlight();
currentLEDColor = CRGB::Red;
displayTitle();
randomSeed(analogRead(A0));
}
void displayTitle() {
bool titleDisplayed = false;
int titleFrame = 0;
String blankLine = " ";
String titleLine1 = "Mastermind v1.0";
String titleLine2 = " coded by M Borges";
String titleLine3 = blankLine;
String titleLine4 = " Press [New Game]";
String titleLineAll = titleLine1 + titleLine2 + titleLine3 + titleLine4;
int titleLine1index = 20;
int titleLine2index = 20;
int titleLine4index = 20;
while (!titleDisplayed) {
titleFrame++;
if (titleFrame > 0 && titleLine1index > 0) {
titleLine1index--;
}
if (titleFrame > 10 && titleLine2index > 0) {
titleLine2index--;
}
if (titleFrame > 20 && titleLine4index > 0) {
titleLine4index--;
}
if (titleLine1index==0 && titleLine2index==0 && titleLine4index==0) {
titleDisplayed = true;
}
//Title 1
if (titleLine1index < 20) {
String title = blankLine.substring(0,titleLine1index) + titleLine1.substring(titleLine1index);
if (title.length() < 20) {
title.concat(blankLine.substring(0,20-title.length()));
}
lcd.setCursor(0,0);
lcd.print(title);
}
//Title 2
if (titleLine2index < 20) {
String title = blankLine.substring(0,titleLine2index) + titleLine2.substring(titleLine2index);
if (title.length() < 20) {
title.concat(blankLine.substring(0,20-title.length()));
}
lcd.setCursor(0,1);
lcd.print(title);
}
//Title 3
lcd.setCursor(0,2);
lcd.print(titleLine3);
//Title 4
if (titleLine4index < 20) {
String title = blankLine.substring(0,titleLine4index) + titleLine4.substring(titleLine4index);
if (title.length() < 20) {
title.concat(blankLine.substring(0,20-title.length()));
}
lcd.setCursor(0,3);
lcd.print(title);
}
delay(15);
}
}
byte readInput() {
while (true) {
if (digitalRead(NEW_GAME_BUTTON) == LOW) {
return NEW_GAME_BUTTON;
} else if (digitalRead(GUESS_BUTTON) == LOW) {
return GUESS_BUTTON;
} else if (digitalRead(DELETE_BUTTON) == LOW) {
return DELETE_BUTTON;
} else if (digitalRead(RED_BUTTON) == LOW) {
return RED_BUTTON;
} else if (digitalRead(GREEN_BUTTON) == LOW) {
return GREEN_BUTTON;
} else if (digitalRead(BLUE_BUTTON) == LOW) {
return BLUE_BUTTON;
} else if (digitalRead(YELLOW_BUTTON) == LOW) {
return YELLOW_BUTTON;
} else if (digitalRead(PURPLE_BUTTON) == LOW) {
return PURPLE_BUTTON;
} else if (digitalRead(ORANGE_BUTTON) == LOW) {
return ORANGE_BUTTON;
}
delay(1);
}
}
void loop() {
switch (byte inputByte=readInput()) {
case NEW_GAME_BUTTON:
if (!gameStarted || gameOver) {
newGame();
}
break;
case RED_BUTTON:
case GREEN_BUTTON:
case BLUE_BUTTON:
case YELLOW_BUTTON:
case PURPLE_BUTTON:
case ORANGE_BUTTON:
if (gameStarted && !gameOver) {
enterCode(inputByte);
}
// colorAllLeds(colorMap[inputByte]);
break;
case GUESS_BUTTON:
if (gameStarted && !gameOver) {
if (!checkGuess()) {
checkGameOver();
}
}
break;
case DELETE_BUTTON:
if (gameStarted && !gameOver) {
deleteGuess();
}
break;
}
delay(100);
updateLEDMap();
}
void colorAllLeds(CRGB color) {
for (int i=0; i<NUM_LEDS; i++) {
leds[i] = color;
}
FastLED.show();
}
void enterCode(byte codeDigit) {
if (currentGuessDigit<4) {
guesses[currentGuess][currentGuessDigit] = codeDigit;
currentGuessDigit++;
} else {
lcdPrintGuessIsComplete();
}
}
bool checkGuess() {
int scoreIndex = 4;
bool codeUsed[4] = {false,false,false,false};
bool guessUsed[4] = {false,false,false,false};
if (currentGuessDigit<4) {
lcdPrintGuessIncomplete();
return false;
} else {
//Check for Right Color and Position Pegs
for (int i=0;i<4;i++) {
if (guesses[currentGuess][i] == code[i]) {
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_AND_POSITION_PEG;
codeUsed[i] = true;
guessUsed[i] = true;
};
}
if (scoreIndex==8) {
gameOver = true;
lcdPrintWonGame();
return true;
}
//Check for Right Color pegs only for pegs that are still guessable
if (!guessUsed[0] && !codeUsed[1] && guesses[currentGuess][0] == code[1]) {
guessUsed[0] = true;
codeUsed[1] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[0] && !codeUsed[2] && guesses[currentGuess][0] == code[2]) {
guessUsed[0] = true;
codeUsed[2] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[0] && !codeUsed[3] && guesses[currentGuess][0] == code[3]) {
guessUsed[0] = true;
codeUsed[3] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[1] && !codeUsed[0] && guesses[currentGuess][1] == code[0]) {
guessUsed[1] = true;
codeUsed[0] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[1] && !codeUsed[2] && guesses[currentGuess][1] == code[2]) {
guessUsed[1] = true;
codeUsed[2] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[1] && !codeUsed[3] && guesses[currentGuess][1] == code[3]) {
guessUsed[1] = true;
codeUsed[3] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[2] && !codeUsed[0] && guesses[currentGuess][2] == code[0]) {
guessUsed[2] = true;
codeUsed[0] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[2] && !codeUsed[1] && guesses[currentGuess][2] == code[1]) {
guessUsed[2] = true;
codeUsed[1] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[2] && !codeUsed[3] && guesses[currentGuess][2] == code[3]) {
guessUsed[2] = true;
codeUsed[3] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[3] && !codeUsed[0] && guesses[currentGuess][3] == code[0]) {
guessUsed[3] = true;
codeUsed[0] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[3] && !codeUsed[1] && guesses[currentGuess][3] == code[1]) {
guessUsed[3] = true;
codeUsed[1] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
if (!guessUsed[3] && !codeUsed[2] && guesses[currentGuess][3] == code[2]) {
guessUsed[3] = true;
codeUsed[2] = true;
guesses[currentGuess][scoreIndex++] = RIGHT_COLOR_PEG;
}
currentGuess++;
currentGuessDigit = 0;
lcdPrintInstructions();
return false;
}
}
void deleteGuess() {
for (int i=0; i<8; i++) {
guesses[currentGuess][i] = 0;
}
currentGuessDigit = 0;
lcdPrintInstructions();
}
void checkGameOver() {
if (currentGuess==10) {
gameStarted = false;
gameOver = true;
lcdPrintLostGame();
}
}
void newGame() {
//Reset all LEDs
for (int i=0; i<10; i++) {
for (int j=0; j<9; j++) {
guesses[i][j] = 0;
}
}
//Set new Code
code[0] = random(ORANGE_BUTTON, RED_BUTTON);
code[1] = random(ORANGE_BUTTON, RED_BUTTON);
code[2] = random(ORANGE_BUTTON, RED_BUTTON);
code[3] = random(ORANGE_BUTTON, RED_BUTTON);
if (debug) {
code[0] = 5;
code[1] = 5;
code[2] = 9;
code[3] = 4;
Serial.print("Code: ");
Serial.print(code[0]);
Serial.print(code[1]);
Serial.print(code[2]);
Serial.println(code[3]);
}
lcdPrintInstructions();
currentGuess = 0;
currentGuessDigit = 0;
gameStarted = true;
gameOver = false;
}
void lcdPrintInstructions() {
lcd.setCursor(0,0);
lcd.print("Press color buttons ");
lcd.setCursor(0,1);
lcd.print("to start guessing ");
lcd.setCursor(0,2);
lcd.print(" ");
lcd.setCursor(0,3);
lcd.print("[CHECK] [DELETE]");
}
void lcdPrintGuessIncomplete() {
lcd.setCursor(0,0);
lcd.print("Your guess is ");
lcd.setCursor(0,1);
lcd.print(" incomplete");
}
void lcdPrintGuessIsComplete() {
lcd.setCursor(0,0);
lcd.print("Your guess is done ");
lcd.setCursor(0,1);
lcd.print("Hit Check or Delete ");
}
void lcdPrintLostGame() {
lcd.setCursor(0,0);
lcd.print(" You Lose! ");
lcd.setCursor(0,1);
lcd.print(" <whomp whomp> ");
lcd.setCursor(0,3);
lcd.print(" Press [New Game] ");
}
void lcdPrintWonGame() {
lcd.setCursor(0,0);
lcd.print(" You Win! ");
lcd.setCursor(0,1);
lcd.print(" Congratulations !! ");
lcd.setCursor(0,3);
lcd.print(" Press [New Game] ");
}
void updateLEDMap() {
if (gameStarted || gameOver) {
for (int i=0; i<10; i++) {
leds[guessLEDs[i][0]] = CRGB::Black;
leds[guessLEDs[i][1]] = colorMap[guesses[i][0]];
leds[guessLEDs[i][2]] = colorMap[guesses[i][1]];
leds[guessLEDs[i][3]] = colorMap[guesses[i][2]];
leds[guessLEDs[i][4]] = colorMap[guesses[i][3]];
leds[guessLEDs[i][5]] = colorMap[guesses[i][4]];
leds[guessLEDs[i][6]] = colorMap[guesses[i][5]];
leds[guessLEDs[i][7]] = colorMap[guesses[i][6]];
leds[guessLEDs[i][8]] = colorMap[guesses[i][7]];
}
leds[guessLEDs[currentGuess][0]] = CRGB::Red;
if (showSolution || gameOver) {
for (int i=0; i<4; i++) {
leds[solutionLEDs[i]] = colorMap[code[i]];
}
} else {
for (int i=0; i<4; i++) {
leds[solutionLEDs[i]] = CRGB::Black;
}
}
FastLED.show();
}
}