#include <Adafruit_ILI9341.h>
#define TFT_CLK 18
#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_CS 15
#define TFT_RST 4
#define TFT_DC 2
#define JOY_X 34
#define JOY_Y 35
#define JOY_SW 32
#define CARD_WIDTH 40
#define CARD_HEIGHT 40
#define CARD_MARGIN 16
#define NUM_CARDS 16
#define COVERED 0
#define UNCOVERED 1
#define REMOVED 2
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
struct Card {
int color = -1;
int state;
};
Card cards[NUM_CARDS];
int selectedCardIndex = -1;
int numUncoveredCards = 0;
void initializeGame();
void drawBoard();
void handleInput();
void uncoverCard(int index);
void checkMatch();
void gameOver();
void setup() {
tft.begin();
tft.setRotation(3);
Serial.begin(9600);
initializeGame();
}
void loop() {
handleInput();
}
void initializeGame() {
int colors[] = {ILI9341_RED, ILI9341_GREEN, ILI9341_BLUE, ILI9341_YELLOW, ILI9341_CYAN, ILI9341_MAGENTA, ILI9341_PINK, ILI9341_LIGHTGREY};
for (int i = 0; i < NUM_CARDS; i++) {
cards[i].state = COVERED;
}
for (int i = 0; i < NUM_CARDS / 2; i++) {
int color = colors[i];
int index1 = random(NUM_CARDS);
int index2 = random(NUM_CARDS);
while (cards[index1].color != -1 || cards[index2].color != -1 || index1 == index2) {
index1 = random(NUM_CARDS);
index2 = random(NUM_CARDS);
}
cards[index1].color = color;
cards[index2].color = color;
}
selectedCardIndex = -1;
numUncoveredCards = 0;
drawBoard();
}
void drawBoard() {
tft.fillScreen(ILI9341_BLACK);
for (int i = 0; i < NUM_CARDS; i++) {
int row = i / 4;
int col = i % 4;
int x = col * (CARD_WIDTH + CARD_MARGIN) + CARD_MARGIN;
int y = row * (CARD_HEIGHT + CARD_MARGIN) + CARD_MARGIN;
int color;
if (cards[i].state == COVERED) {
color = ILI9341_DARKGREY;
} else {
color = cards[i].color;
}
tft.fillRect(x, y, CARD_WIDTH, CARD_HEIGHT, color);
}
}
int getButtonState() {
static bool rise = false;
if (digitalRead(JOY_SW)) {
if (rise) {
rise = false;
return 1;
} else {
return 0;
}
} else {
rise = true;
return 0;
}
}
int getJOYselect() {
static int preRow = 0;
static int preCol = 0;
int row = preRow;
int col = preCol;
static bool upRise = false;
static bool downRise = false;
static bool rightRise = false;
static bool leftRise = false;
if (analogRead(JOY_X) > 2100) {
if (upRise) {
upRise = false;
row++;
}
} else {
upRise = true;
}
if (analogRead(JOY_X) < 2000) {
if (downRise) {
downRise = false;
row--;
}
} else {
downRise = true;
}
if (analogRead(JOY_Y) > 2100) {
if (rightRise) {
rightRise = false;
col++;
}
} else {
rightRise = true;
}
if (analogRead(JOY_Y) < 2000) {
if (leftRise) {
leftRise = false;
col--;
}
} else {
leftRise = true;
}
if (row < 0) {
row = 0;
}
if (col < 0) {
col = 0;
}
if (row >= 4) {
row = 3;
}
if (col >= 4) {
col = 3;
}
int x = col * (CARD_WIDTH + CARD_MARGIN) + CARD_MARGIN;
int y = row * (CARD_HEIGHT + CARD_MARGIN) + CARD_MARGIN;
tft.fillRect(x + 40, y + 40, 8, 8, ILI9341_RED);
if (preRow != row || preCol != col) {
int x = preCol * (CARD_WIDTH + CARD_MARGIN) + CARD_MARGIN;
int y = preRow * (CARD_HEIGHT + CARD_MARGIN) + CARD_MARGIN;
tft.fillRect(x + 40, y + 40, 8, 8, ILI9341_BLACK);
preRow = row;
preCol = col;
}
return row * 4 + col;
}
void handleInput() {
int index = getJOYselect();
int buttonState = getButtonState();
if (buttonState) {
if (selectedCardIndex == -1) {
if (cards[index].state == COVERED) {
uncoverCard(index);
selectedCardIndex = index;
}
} else {
if (index != selectedCardIndex && cards[index].state == COVERED) {
uncoverCard(index);
delay(1000);
checkMatch(index, selectedCardIndex);
}
selectedCardIndex = -1;
}
}
}
void uncoverCard(int index) {
cards[index].state = UNCOVERED;
numUncoveredCards++;
int row = index / 4;
int col = index % 4;
int x = col * (CARD_WIDTH + CARD_MARGIN) + CARD_MARGIN;
int y = row * (CARD_HEIGHT + CARD_MARGIN) + CARD_MARGIN;
int color = cards[index].color;
tft.fillRect(x, y, CARD_WIDTH, CARD_HEIGHT, color);
}
void checkMatch(int A, int B) {
int rowA = A / 4;
int colA = A % 4;
int xA = colA * (CARD_WIDTH + CARD_MARGIN) + CARD_MARGIN;
int yA = rowA * (CARD_HEIGHT + CARD_MARGIN) + CARD_MARGIN;
int rowB = B / 4;
int colB = B % 4;
int xB = colB * (CARD_WIDTH + CARD_MARGIN) + CARD_MARGIN;
int yB = rowB * (CARD_HEIGHT + CARD_MARGIN) + CARD_MARGIN;
if (cards[A].color == cards[B].color) {
cards[A].state = REMOVED;
cards[B].state = REMOVED;
tft.fillRect(xA, yA, CARD_WIDTH, CARD_HEIGHT, ILI9341_BLACK);
tft.fillRect(xB, yB, CARD_WIDTH, CARD_HEIGHT, ILI9341_BLACK);
numUncoveredCards -= 2;
if (numUncoveredCards == 0) {
gameOver();
}
} else {
cards[A].state = COVERED;
cards[B].state = COVERED;
tft.fillRect(xA, yA, CARD_WIDTH, CARD_HEIGHT, ILI9341_DARKGREY);
tft.fillRect(xB, yB, CARD_WIDTH, CARD_HEIGHT, ILI9341_DARKGREY);
}
}
void gameOver() {
tft.fillScreen(ILI9341_BLACK);
tft.setCursor(20, 100);
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(3);
tft.println("Game Over!");
delay(3000);
initializeGame();
}