/**
* @file oledVersion.ino
* @brief SSD1306 OLED 디스플레이를 이용한 3x3 슬롯 머신 게임
*
* [하드웨어]
* - SSD1306 128x64 I2C OLED
* - SDA: A4 (Arduino Uno 기준)
* - SCL: A5 (Arduino Uno 기준)
*
* [라이브러리]
* - Adafruit_GFX
* - Adafruit_SSD1306
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- OLED 설정 ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- 설정 및 상수 ---
const unsigned long COUNTDOWN_MS = 2500;
const unsigned long UPDATE_INTERVAL_MS = 100; // OLED는 갱신 주기를 더 빠르게 (부드러운 카운트다운)
const int GRID_SIZE = 3;
// 슬롯에 사용할 심볼들 (OLED 공간상 2~3글자 권장)
const char* symbols[] = {"Cherry", "Lemon", "Apple", "Grape", "7!!", "Star"};
const int numSymbols = 6;
// --- 상태 정의 ---
enum GameState {
STATE_MENU,
STATE_COUNTDOWN,
STATE_RESULT
};
GameState currentState = STATE_MENU;
unsigned long stateStartTime = 0;
String symbolsGrid[GRID_SIZE][GRID_SIZE];
// --- 함수 선언 ---
void updateDisplay();
void handleSerialInput();
bool checkWin();
void drawGrid();
void drawSymbols();
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0));
// OLED 초기화
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.display();
Serial.println(F("OLED Slot Machine Ready."));
Serial.println(F("1: Start | 2: Stop | 3: Reset"));
}
void loop() {
handleSerialInput();
updateDisplay();
}
/**
* @brief 현재 상태에 따라 OLED 화면 갱신
*/
void updateDisplay() {
display.clearDisplay();
switch (currentState) {
case STATE_MENU:
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(25, 10);
display.println(F("SLOT MACHINE"));
display.drawRect(10, 25, 108, 30, SSD1306_WHITE);
display.setCursor(20, 35);
display.println(F("Press '1' Start"));
break;
case STATE_COUNTDOWN: {
unsigned long elapsed = millis() - stateStartTime;
if (elapsed >= COUNTDOWN_MS) {
// 카운트다운 완료 시점에 결과 생성
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
symbolsGrid[i][j] = symbols[random(numSymbols)];
}
}
currentState = STATE_RESULT;
} else {
float remaining = (float)(COUNTDOWN_MS - elapsed) / 1000.0;
display.setTextSize(3);
display.setTextColor(SSD1306_WHITE);
display.setCursor(35, 20);
display.print(remaining, 1);
}
break;
}
case STATE_RESULT:
// 결과 출력 (한 번만 수행하도록 STATE_MENU 등으로 바로 전환하지 않고 처리)
// STATE_COUNTDOWN에서 STATE_RESULT로 넘어오는 시점에 한 번만 실행되도록 logic 구성
drawGrid();
drawSymbols();
display.setTextSize(1);
if (checkWin()) {
display.setCursor(45, 55);
display.println(F("WIN!!!"));
} else {
display.setCursor(45, 55);
display.println(F("LOSE..."));
}
break;
}
display.display();
}
/**
* @brief 시리얼 입력을 처리하여 상태 전환 (OLED 버전 호환)
*/
void handleSerialInput() {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
input.trim();
if (input == "1" || input == "시작") {
if (currentState == STATE_MENU || currentState == STATE_RESULT) {
// 사용자 입력 시점을 이용하여 랜덤 시드 업데이트 (랜덤성 강화)
randomSeed(millis() + analogRead(0));
currentState = STATE_COUNTDOWN;
stateStartTime = millis();
}
} else if (input == "2" || input == "멈춤" || input == "3" || input == "리셋") {
currentState = STATE_MENU;
}
}
}
/**
* @brief 3x3 격자 그리기
*/
void drawGrid() {
// 전체 높이 64 중 상단 50까지 격자 사용
for (int i = 0; i <= GRID_SIZE; i++) {
// 가로선
display.drawFastHLine(0, i * 16, 128, SSD1306_WHITE);
// 세로선
display.drawFastVLine(i * 42, 0, 48, SSD1306_WHITE);
}
}
/**
* @brief 격자 내에 심볼 단어 출력
*/
void drawSymbols() {
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
// 각 칸의 중앙 즈음에 텍스트 배치
// 한 칸 크기: 가로 42, 세로 16
display.setCursor(j * 42 + 2, i * 16 + 4);
display.print(symbolsGrid[i][j]);
}
}
}
/**
* @brief 가로, 세로, 대각선 일치 여부 확인
*/
bool checkWin() {
for (int i = 0; i < GRID_SIZE; i++) {
if (symbolsGrid[i][0] == symbolsGrid[i][1] && symbolsGrid[i][1] == symbolsGrid[i][2]) return true;
}
for (int j = 0; j < GRID_SIZE; j++) {
if (symbolsGrid[0][j] == symbolsGrid[1][j] && symbolsGrid[1][j] == symbolsGrid[2][j]) return true;
}
if (symbolsGrid[0][0] == symbolsGrid[1][1] && symbolsGrid[1][1] == symbolsGrid[2][2]) return true;
if (symbolsGrid[0][2] == symbolsGrid[1][1] && symbolsGrid[1][1] == symbolsGrid[2][0]) return true;
return false;
}