Last active
June 29, 2025 01:30
-
-
Save palaniraja/d5a55f9bd1f990410c8a0099844cec91 to your computer and use it in GitHub Desktop.
vibecoding Sudoku for M5PaperS3 with static puzzle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <M5GFX.h> | |
M5GFX display; | |
// Battery percentage tracking | |
int last_battery_percent = -1; | |
unsigned long last_battery_check = 0; | |
// E-ink friendly colors | |
#define COLOR_BG TFT_WHITE | |
#define COLOR_GRID 0xC618 // light gray (RGB565: 19818) | |
#define COLOR_BOLD TFT_BLACK | |
#define COLOR_NUM TFT_BLACK | |
#define COLOR_SEL 0x7BEF // lighter gray (RGB565: 31727) | |
#define COLOR_FIXED_BG 0x8410 // 50% black (mid-gray RGB565) | |
#define COLOR_FIXED_FG TFT_BLACK | |
#define COLOR_HILITE 0xDEFB // very light gray for row/col highlight | |
// Draw battery percentage at the top right | |
void drawBatteryPercent(M5GFX& display, int percent, int cell_size) { | |
int w = display.width(); | |
// Use same font size as digits | |
int fontSize = (int)((float)cell_size / 16); | |
display.setTextSize(fontSize); | |
display.setTextColor(COLOR_NUM, COLOR_BG); | |
display.setTextDatum(top_right); | |
char buf[16]; | |
snprintf(buf, sizeof(buf), "%d%%", percent); | |
// Clear the area first | |
int text_w = display.textWidth(buf); | |
display.fillRect(w - text_w - 16, 0, text_w + 16, fontSize + 8, COLOR_BG); | |
display.drawString(buf, w - 8, 8); | |
} | |
// Simple 9x9 Sudoku puzzle (0 = empty) | |
static constexpr uint8_t sudoku_init[9][9] = { | |
{5,3,0, 0,7,0, 0,0,0}, | |
{6,0,0, 1,9,5, 0,0,0}, | |
{0,9,8, 0,0,0, 0,6,0}, | |
{8,0,0, 0,6,0, 0,0,3}, | |
{4,0,0, 8,0,3, 0,0,1}, | |
{7,0,0, 0,2,0, 0,0,6}, | |
{0,6,0, 0,0,0, 2,8,0}, | |
{0,0,0, 4,1,9, 0,0,5}, | |
{0,0,0, 0,8,0, 0,7,9} | |
}; | |
uint8_t sudoku[9][9]; | |
int sel_x = 0, sel_y = 0; | |
// Store grid geometry for touch handling | |
int grid_x0, grid_y0, cell_size, grid_size; | |
// Input bar geometry | |
int inputbar_x0, inputbar_y0, inputbar_w, inputbar_h, inputbar_cell; | |
// 9-bit mask for pencilled digits per cell | |
uint16_t pencil[9][9] = {0}; | |
bool penMode = true; // true = pen, false = pencil | |
// Sleep button state | |
bool isSleeping = false; | |
// Forward declarations for utility functions | |
void drawButton(M5GFX& display, int x, int y, int w, int h, const char* label, int fontSize, uint16_t fill, uint16_t border, uint16_t text); | |
int buttonWidth(M5GFX& display, const char* label, int fontSize, int pad); | |
bool isTouchInButton(int tx, int ty, int x, int y, int w, int h); | |
void handleInputBarButtons(int tx, int ty, int inputbar_x0, int inputbar_y0, int inputbar_cell, int inputbar_h, int inputbar_w, int inputbar_cell_local, int inputbar_y, int inputbar_h_local); | |
// Draws Pen/Pencil mode bar above input bar, same sizing as validate/hint | |
void drawModeBar(M5GFX& display, int modebar_y, int modebar_h, int modebar_cell, int bar_x0) { | |
const char* penStr = "Pen"; | |
const char* pencilStr = "Pencil"; | |
const char* clearStr = "Clear"; | |
display.setTextSize((float)modebar_cell / 16); | |
int pen_w = display.textWidth(penStr) + modebar_cell / 2; | |
int pencil_w = display.textWidth(pencilStr) + modebar_cell / 2; | |
int clear_w = display.textWidth(clearStr) + modebar_cell / 2; | |
int pen_x = bar_x0; | |
int pencil_x = pen_x + pen_w + modebar_cell / 3; | |
int clear_x = pencil_x + pencil_w + modebar_cell / 3; | |
// Pen button | |
display.fillRect(pen_x, modebar_y, pen_w, modebar_h, penMode ? COLOR_SEL : COLOR_GRID); | |
display.drawRect(pen_x, modebar_y, pen_w, modebar_h, COLOR_BOLD); | |
display.setTextColor(COLOR_NUM, penMode ? COLOR_SEL : COLOR_GRID); | |
display.setTextDatum(middle_center); | |
display.drawString(penStr, pen_x + pen_w / 2, modebar_y + modebar_h / 2); | |
// Pencil button | |
display.fillRect(pencil_x, modebar_y, pencil_w, modebar_h, !penMode ? COLOR_SEL : COLOR_GRID); | |
display.drawRect(pencil_x, modebar_y, pencil_w, modebar_h, COLOR_BOLD); | |
display.setTextColor(COLOR_NUM, !penMode ? COLOR_SEL : COLOR_GRID); | |
display.drawString(pencilStr, pencil_x + pencil_w / 2, modebar_y + modebar_h / 2); | |
// Clear button | |
display.fillRect(clear_x, modebar_y, clear_w, modebar_h, COLOR_GRID); | |
display.drawRect(clear_x, modebar_y, clear_w, modebar_h, COLOR_BOLD); | |
display.setTextColor(COLOR_NUM, COLOR_GRID); | |
display.drawString(clearStr, clear_x + clear_w / 2, modebar_y + modebar_h / 2); | |
} | |
void clearSelectedCell() { | |
if (sudoku_init[sel_y][sel_x] == 0) { | |
sudoku[sel_y][sel_x] = 0; | |
pencil[sel_y][sel_x] = 0; | |
} | |
} | |
void drawPencilBar(M5GFX& display, int pencilbar_y, int pencilbar_h, int pencilbar_cell) { | |
// Draws the pencilled digits for the selected cell as a bar | |
int x0 = grid_x0; | |
uint16_t mask = pencil[sel_y][sel_x]; | |
int shown = 0; | |
for (int i = 0; i < 9; ++i) { | |
if (mask & (1 << i)) { | |
int x = x0 + shown * pencilbar_cell; | |
display.fillRect(x, pencilbar_y, pencilbar_cell - 2, pencilbar_h, COLOR_BG); // white background | |
display.drawRect(x, pencilbar_y, pencilbar_cell - 2, pencilbar_h, COLOR_BOLD); | |
display.setTextColor(COLOR_NUM, COLOR_BG); | |
display.setTextDatum(middle_center); | |
display.setTextSize((float)pencilbar_cell / 16); | |
display.drawNumber(i + 1, x + pencilbar_cell / 2, pencilbar_y + pencilbar_h / 2); | |
shown++; | |
} | |
} | |
// Fill the rest with background (clear unused slots) | |
for (int i = shown; i < 9; ++i) { | |
int x = x0 + i * pencilbar_cell; | |
display.fillRect(x, pencilbar_y, pencilbar_cell - 2, pencilbar_h, COLOR_GRID); | |
display.drawRect(x, pencilbar_y, pencilbar_cell - 2, pencilbar_h, COLOR_BOLD); | |
} | |
} | |
void drawPencilDigits(M5GFX& display, int x, int y, uint16_t mask) { | |
int cell_x = grid_x0 + x * cell_size; | |
int cell_y = grid_y0 + y * cell_size; | |
int sub = 3; // 3x3 grid | |
int sub_w = cell_size / sub; | |
display.setTextColor(COLOR_NUM, COLOR_BG); | |
display.setTextDatum(middle_center); | |
display.setTextSize((float)cell_size / 32); | |
for (int n = 1; n <= 9; ++n) { | |
if (mask & (1 << (n - 1))) { | |
int sx = cell_x + ((n - 1) % 3) * sub_w + sub_w / 2; | |
int sy = cell_y + ((n - 1) / 3) * sub_w + sub_w / 2; | |
display.drawNumber(n, sx, sy); | |
} | |
} | |
} | |
void drawInputBar(M5GFX& display, int inputbar_y, int inputbar_h) { | |
// Draws 1-9 input bar at the given y, hiding digits present in 3x3 box of selected cell | |
int w = display.width(); | |
inputbar_x0 = grid_x0; | |
inputbar_y0 = inputbar_y; | |
inputbar_w = grid_size; | |
inputbar_cell = inputbar_w / 9; | |
// Compute mask of digits present in 3x3 box | |
int box_x = (sel_x / 3) * 3; | |
int box_y = (sel_y / 3) * 3; | |
bool present[10] = {false}; | |
for (int dy = 0; dy < 3; ++dy) { | |
for (int dx = 0; dx < 3; ++dx) { | |
int v = sudoku[box_y + dy][box_x + dx]; | |
if (v > 0 && v <= 9) present[v] = true; | |
} | |
} | |
for (int i = 0; i < 9; ++i) { | |
int x = inputbar_x0 + i * inputbar_cell; | |
if (present[i + 1]) { | |
// Hide digit (draw empty) | |
display.fillRect(x, inputbar_y0, inputbar_cell - 2, inputbar_h, COLOR_GRID); | |
display.drawRect(x, inputbar_y0, inputbar_cell - 2, inputbar_h, COLOR_BOLD); | |
} else { | |
display.fillRect(x, inputbar_y0, inputbar_cell - 2, inputbar_h, COLOR_GRID); | |
display.drawRect(x, inputbar_y0, inputbar_cell - 2, inputbar_h, COLOR_BOLD); | |
display.setTextColor(COLOR_NUM, COLOR_GRID); | |
display.setTextDatum(middle_center); | |
display.setTextSize((float)inputbar_cell / 16); | |
display.drawNumber(i + 1, x + inputbar_cell / 2, inputbar_y0 + inputbar_h / 2); | |
} | |
} | |
// Draw Validate and Hint buttons below input bar (fit to string, same font size as digits) | |
const char* validateStr = "Validate"; | |
const char* hintStr = "Hint"; | |
const char* sleepStr = isSleeping ? "sleeping" : "Sleep"; | |
int fontSize = (float)inputbar_cell / 16; | |
int pad = inputbar_cell / 2; | |
int validate_w = buttonWidth(display, validateStr, fontSize, pad); | |
int hint_w = buttonWidth(display, hintStr, fontSize, pad); | |
int sleep_w = buttonWidth(display, sleepStr, fontSize, pad); | |
int btn_h = inputbar_h * 0.7; | |
int btn_y = inputbar_y0 + inputbar_h + 8; | |
int btn_x = inputbar_x0; | |
drawButton(display, btn_x, btn_y, validate_w, btn_h, validateStr, fontSize, COLOR_GRID, COLOR_BOLD, COLOR_NUM); | |
btn_x += validate_w + inputbar_cell / 3; | |
drawButton(display, btn_x, btn_y, hint_w, btn_h, hintStr, fontSize, COLOR_GRID, COLOR_BOLD, COLOR_NUM); | |
btn_x += hint_w + inputbar_cell / 3; | |
drawButton(display, btn_x, btn_y, sleep_w, btn_h, sleepStr, fontSize, COLOR_GRID, COLOR_BOLD, COLOR_NUM); | |
} | |
void drawSudokuGrid(M5GFX& display) { | |
int w = display.width(); | |
int h = display.height(); | |
grid_size = 540; // Use full width | |
cell_size = grid_size / 9; | |
grid_x0 = (w - grid_size) / 2; | |
// Center grid vertically with all bars | |
int total_bar_height = cell_size * 1.2 * 3 + cell_size * 0.7 * 1 + 10 + 8 * 3; // input, pencil, mode, validate/hint, paddings | |
grid_y0 = (h - (grid_size + total_bar_height)) / 2 + 48; // shift down for battery row | |
// Calculate bar positions | |
int inputbar_h = cell_size * 1.2; | |
int modebar_h = inputbar_h * 0.7; | |
int pencilbar_h = inputbar_h; | |
int pencilbar_y = grid_y0 + grid_size + 10; | |
int modebar_y = pencilbar_y + pencilbar_h + 8; | |
int inputbar_y = modebar_y + modebar_h + 8; | |
int pencilbar_cell = grid_size / 9; | |
int modebar_cell = grid_size / 9; | |
int bar_x0 = grid_x0; | |
// Background | |
display.fillRect(0, 48, w, h - 48, COLOR_BG); // leave battery row untouched | |
// Highlight row and column (full coverage) | |
for (int i = 0; i < 9; ++i) { | |
// Row | |
display.fillRect(grid_x0 + i * cell_size, grid_y0 + sel_y * cell_size, cell_size, cell_size, COLOR_HILITE); | |
// Column | |
display.fillRect(grid_x0 + sel_x * cell_size, grid_y0 + i * cell_size, cell_size, cell_size, COLOR_HILITE); | |
} | |
// Highlight selected cell | |
display.fillRect(grid_x0 + sel_x * cell_size, grid_y0 + sel_y * cell_size, cell_size, cell_size, COLOR_SEL); | |
// Draw grid lines | |
for (int i = 0; i <= 9; ++i) { | |
int thick = (i % 3 == 0) ? 3 : 1; | |
uint16_t color = (i % 3 == 0) ? COLOR_BOLD : COLOR_GRID; | |
display.fillRect(grid_x0 + i * cell_size - thick/2, grid_y0, thick, grid_size, color); | |
display.fillRect(grid_x0, grid_y0 + i * cell_size - thick/2, grid_size, thick, color); | |
} | |
// Draw numbers (no in-cell pencil digits) | |
for (int y = 0; y < 9; ++y) { | |
for (int x = 0; x < 9; ++x) { | |
int cx = grid_x0 + x * cell_size + cell_size / 2; | |
int cy = grid_y0 + y * cell_size + cell_size / 2; | |
if (sudoku_init[y][x] != 0) { | |
// Fixed digit | |
display.fillRect(grid_x0 + x * cell_size, grid_y0 + y * cell_size, cell_size, cell_size, COLOR_FIXED_BG); | |
display.setTextColor(COLOR_FIXED_FG, COLOR_FIXED_BG); | |
display.setTextDatum(middle_center); | |
display.setTextSize((float)cell_size / 16); | |
display.drawNumber(sudoku_init[y][x], cx, cy); | |
} else if (sudoku[y][x] != 0) { | |
display.setTextColor(COLOR_NUM, COLOR_BG); | |
display.setTextDatum(middle_center); | |
display.setTextSize((float)cell_size / 16); | |
display.drawNumber(sudoku[y][x], cx, cy); | |
} | |
} | |
} | |
drawPencilBar(display, pencilbar_y, pencilbar_h, pencilbar_cell); | |
drawModeBar(display, modebar_y, modebar_h, modebar_cell, bar_x0); | |
drawInputBar(display, inputbar_y, inputbar_h); | |
} | |
// Dummy solution for hint/validation (replace with real solver if needed) | |
uint8_t solution[9][9] = { | |
{5,3,4,6,7,8,9,1,2}, | |
{6,7,2,1,9,5,3,4,8}, | |
{1,9,8,3,4,2,5,6,7}, | |
{8,5,9,7,6,1,4,2,3}, | |
{4,2,6,8,5,3,7,9,1}, | |
{7,1,3,9,2,4,8,5,6}, | |
{9,6,1,5,3,7,2,8,4}, | |
{2,8,7,4,1,9,6,3,5}, | |
{3,4,5,2,8,6,1,7,9} | |
}; | |
void showHint() { | |
if (sudoku_init[sel_y][sel_x] == 0) { | |
sudoku[sel_y][sel_x] = solution[sel_y][sel_x]; | |
} | |
} | |
void validateCell() { | |
if (sudoku[sel_y][sel_x] == solution[sel_y][sel_x]) { | |
// Show green highlight for correct | |
display.fillRect(grid_x0 + sel_x * cell_size, grid_y0 + sel_y * cell_size, cell_size, cell_size, 0x07E0); // green | |
drawSudokuGrid(display); | |
delay(500); | |
} else { | |
// Save the old digit | |
int old_digit = sudoku[sel_y][sel_x]; | |
// Replace with 'x' (show as a string) | |
display.fillRect(grid_x0 + sel_x * cell_size, grid_y0 + sel_y * cell_size, cell_size, cell_size, 0xF800); // red | |
display.setTextColor(COLOR_BG, 0xF800); | |
display.setTextDatum(middle_center); | |
display.setTextSize((float)cell_size / 16); | |
int cx = grid_x0 + sel_x * cell_size + cell_size / 2; | |
int cy = grid_y0 + sel_y * cell_size + cell_size / 2; | |
display.drawString("x", cx, cy); | |
delay(2000); | |
// Restore the old digit | |
drawSudokuGrid(display); | |
} | |
} | |
void setCell(int value) { | |
if (penMode) { | |
if (sudoku_init[sel_y][sel_x] == 0) { | |
sudoku[sel_y][sel_x] = value; | |
pencil[sel_y][sel_x] = 0; // clear pencil marks | |
} | |
} else { | |
// Toggle pencil mark | |
if (sudoku_init[sel_y][sel_x] == 0) { | |
pencil[sel_y][sel_x] ^= (1 << (value - 1)); | |
} | |
} | |
} | |
void onWakeup() { | |
isSleeping = false; | |
drawSudokuGrid(display); | |
} | |
void setup(void) | |
{ | |
display.begin(); | |
display.setRotation(0); // Portrait: 540x960 for M5PaperS3 | |
if (display.isEPD()) { | |
display.setEpdMode(epd_mode_t::epd_fastest); | |
display.invertDisplay(true); | |
display.clear(COLOR_BG); | |
} | |
// Copy initial puzzle | |
for (int y = 0; y < 9; ++y) | |
for (int x = 0; x < 9; ++x) | |
sudoku[y][x] = sudoku_init[y][x]; | |
onWakeup(); | |
} | |
void highlightSameNumbers(int value) { | |
int w = display.width(); | |
int h = display.height(); | |
grid_size = 540; // Use full width, match main layout | |
cell_size = grid_size / 9; | |
grid_x0 = (w - grid_size) / 2; | |
int total_bar_height = cell_size * 1.2 * 3 + cell_size * 0.7 * 1 + 10 + 8 * 3; | |
grid_y0 = (h - (grid_size + total_bar_height)) / 2 + 48; // shift down for battery row | |
int inputbar_h = cell_size * 1.2; | |
int modebar_h = inputbar_h * 0.7; | |
int pencilbar_h = inputbar_h; | |
int pencilbar_y = grid_y0 + grid_size + 10; | |
int modebar_y = pencilbar_y + pencilbar_h + 8; | |
int inputbar_y = modebar_y + modebar_h + 8; | |
// Redraw background, highlights, and grid lines | |
display.fillRect(0, 0, w, h, COLOR_BG); | |
for (int i = 0; i < 9; ++i) { | |
display.fillRect(grid_x0 + i * cell_size, grid_y0 + sel_y * cell_size, cell_size, cell_size, COLOR_HILITE); | |
display.fillRect(grid_x0 + sel_x * cell_size, grid_y0 + i * cell_size, cell_size, cell_size, COLOR_HILITE); | |
} | |
display.fillRect(grid_x0 + sel_x * cell_size, grid_y0 + sel_y * cell_size, cell_size, cell_size, COLOR_SEL); | |
for (int i = 0; i <= 9; ++i) { | |
int thick = (i % 3 == 0) ? 3 : 1; | |
uint16_t color = (i % 3 == 0) ? COLOR_BOLD : COLOR_GRID; | |
display.fillRect(grid_x0 + i * cell_size - thick/2, grid_y0, thick, grid_size, color); | |
display.fillRect(grid_x0, grid_y0 + i * cell_size - thick/2, grid_size, thick, color); | |
} | |
// Draw numbers, bold for same value | |
for (int y = 0; y < 9; ++y) { | |
for (int x = 0; x < 9; ++x) { | |
int cx = grid_x0 + x * cell_size + cell_size / 2; | |
int cy = grid_y0 + y * cell_size + cell_size / 2; | |
int num = (sudoku_init[y][x] != 0) ? sudoku_init[y][x] : sudoku[y][x]; | |
bool isFixed = sudoku_init[y][x] != 0; | |
if (num != 0) { | |
float sz = ((num == value) ? ((float)cell_size / 12) : ((float)cell_size / 16)); | |
uint16_t fg = isFixed ? COLOR_FIXED_FG : COLOR_NUM; | |
uint16_t bg = isFixed ? COLOR_FIXED_BG : COLOR_BG; | |
display.fillRect(grid_x0 + x * cell_size, grid_y0 + y * cell_size, cell_size, cell_size, bg); | |
display.setTextColor(fg, bg); | |
display.setTextDatum(middle_center); | |
display.setTextSize(sz); | |
display.drawNumber(num, cx, cy); | |
} | |
} | |
} | |
// Draw input bar in highlight mode | |
drawInputBar(display, inputbar_y, inputbar_h); | |
} | |
void loop(void) | |
{ | |
bool redraw = false; | |
int16_t tx, ty; | |
int w = display.width(); | |
int h = display.height(); | |
grid_size = 540; | |
cell_size = grid_size / 9; | |
grid_x0 = (w - grid_size) / 2; | |
int total_bar_height = cell_size * 1.2 * 3 + cell_size * 0.7 * 1 + 10 + 8 * 3; | |
grid_y0 = (h - (grid_size + total_bar_height)) / 2 + 48; // shift down for battery row | |
int inputbar_h = cell_size * 1.2; | |
int modebar_h = inputbar_h * 0.7; | |
int pencilbar_h = inputbar_h; | |
int pencilbar_y = grid_y0 + grid_size + 10; | |
int modebar_y = pencilbar_y + pencilbar_h + 8; | |
int inputbar_y = modebar_y + modebar_h + 8; | |
int pencilbar_cell = grid_size / 9; | |
int modebar_cell = grid_size / 9; | |
int bar_x0 = grid_x0; | |
int inputbar_x0_local = grid_x0; | |
int inputbar_w_local = grid_size; | |
int inputbar_cell_local = inputbar_w_local / 9; | |
// --- Battery percentage update (every 5 min) --- | |
unsigned long now = millis(); | |
if (now - last_battery_check >= 3000000UL || last_battery_check == 0) { | |
int battery_percent = 100; | |
#ifdef M5PAPER | |
battery_percent = M5.getBatteryLevel(); | |
#endif | |
if (battery_percent != last_battery_percent) { | |
drawBatteryPercent(display, battery_percent, cell_size); | |
last_battery_percent = battery_percent; | |
} | |
last_battery_check = now; | |
} | |
if (display.getTouch(&tx, &ty)) { | |
// Check pencil bar (toggle pencil digits) | |
if (!penMode && tx >= grid_x0 && tx < grid_x0 + grid_size && ty >= pencilbar_y && ty < pencilbar_y + pencilbar_h) { | |
int idx = (tx - grid_x0) / pencilbar_cell; | |
if (idx >= 0 && idx < 9) { | |
setCell(idx + 1); | |
redraw = true; | |
delay(200); | |
} | |
} | |
// Check mode bar (now with Clear button) | |
const char* penStr = "Pen"; | |
const char* pencilStr = "Pencil"; | |
const char* clearStr = "Clear"; | |
display.setTextSize((float)modebar_cell / 16); | |
int pen_w = display.textWidth(penStr) + modebar_cell / 2; | |
int pencil_w = display.textWidth(pencilStr) + modebar_cell / 2; | |
int clear_w = display.textWidth(clearStr) + modebar_cell / 2; | |
int pen_x = bar_x0; | |
int pencil_x = pen_x + pen_w + modebar_cell / 3; | |
int clear_x = pencil_x + pencil_w + modebar_cell / 3; | |
if (tx >= pen_x && tx < pen_x + pen_w && ty >= modebar_y && ty < modebar_y + modebar_h) { | |
penMode = true; | |
redraw = true; | |
delay(200); | |
} else if (tx >= pencil_x && tx < pencil_x + pencil_w && ty >= modebar_y && ty < modebar_y + modebar_h) { | |
penMode = false; | |
redraw = true; | |
delay(200); | |
} else if (tx >= clear_x && tx < clear_x + clear_w && ty >= modebar_y && ty < modebar_y + modebar_h) { | |
clearSelectedCell(); | |
redraw = true; | |
delay(200); | |
} | |
// Check if touch is inside grid | |
else if (tx >= grid_x0 && tx < grid_x0 + grid_size && ty >= grid_y0 && ty < grid_y0 + grid_size) { | |
int x = (tx - grid_x0) / cell_size; | |
int y = (ty - grid_y0) / cell_size; | |
sel_x = x; | |
sel_y = y; | |
int selected = (sudoku_init[y][x] != 0) ? sudoku_init[y][x] : sudoku[y][x]; | |
if (selected != 0) { | |
highlightSameNumbers(selected); | |
delay(3000); | |
} | |
redraw = true; | |
delay(200); | |
} else if (tx >= inputbar_x0_local && tx < inputbar_x0_local + inputbar_w_local && ty >= inputbar_y && ty < inputbar_y + inputbar_h) { | |
int idx = (tx - inputbar_x0_local) / inputbar_cell_local; | |
if (idx >= 0 && idx < 9) { | |
setCell(idx + 1); | |
redraw = true; | |
delay(200); | |
} | |
} else { | |
handleInputBarButtons(tx, ty, inputbar_x0_local, inputbar_y0, inputbar_cell, inputbar_h, inputbar_w, inputbar_cell_local, inputbar_y, inputbar_h); | |
} | |
} | |
if (redraw) { | |
drawSudokuGrid(display); | |
} | |
} | |
// Utility: Draw a button with label | |
void drawButton(M5GFX& display, int x, int y, int w, int h, const char* label, int fontSize, uint16_t fill, uint16_t border, uint16_t text) { | |
display.fillRect(x, y, w, h, fill); | |
display.drawRect(x, y, w, h, border); | |
display.setTextColor(text, fill); | |
display.setTextDatum(middle_center); | |
display.setTextSize(fontSize); | |
display.drawString(label, x + w / 2, y + h / 2); | |
} | |
// Utility: Compute button width | |
int buttonWidth(M5GFX& display, const char* label, int fontSize, int pad) { | |
display.setTextSize(fontSize); | |
return display.textWidth(label) + pad; | |
} | |
// Utility: Check if touch is inside a button | |
bool isTouchInButton(int tx, int ty, int x, int y, int w, int h) { | |
return (tx >= x && tx < x + w && ty >= y && ty < y + h); | |
} | |
void handleInputBarButtons(int tx, int ty, int inputbar_x0, int inputbar_y0, int inputbar_cell, int inputbar_h, int inputbar_w, int inputbar_cell_local, int inputbar_y, int inputbar_h_local) { | |
const char* validateStr = "Validate"; | |
const char* hintStr = "Hint"; | |
const char* sleepStr = isSleeping ? "sleeping" : "Sleep"; | |
int fontSize = (float)inputbar_cell_local / 16; | |
int pad = inputbar_cell_local / 2; | |
int validate_w = buttonWidth(display, validateStr, fontSize, pad); | |
int hint_w = buttonWidth(display, hintStr, fontSize, pad); | |
int sleep_w = buttonWidth(display, sleepStr, fontSize, pad); | |
int btn_h = inputbar_h_local * 0.7; | |
int btn_y = inputbar_y + inputbar_h_local + 8; | |
int btn_x = inputbar_x0; | |
if (isTouchInButton(tx, ty, btn_x, btn_y, validate_w, btn_h)) { | |
validateCell(); | |
drawSudokuGrid(display); | |
delay(200); | |
return; | |
} | |
btn_x += validate_w + inputbar_cell_local / 3; | |
if (isTouchInButton(tx, ty, btn_x, btn_y, hint_w, btn_h)) { | |
showHint(); | |
drawSudokuGrid(display); | |
delay(200); | |
return; | |
} | |
btn_x += hint_w + inputbar_cell_local / 3; | |
if (isTouchInButton(tx, ty, btn_x, btn_y, sleep_w, btn_h)) { | |
isSleeping = true; | |
drawSudokuGrid(display); | |
delay(200); | |
return; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[platformio] | |
default_envs = M5PaperS3 | |
[env:M5PaperS3] | |
platform = [email protected] | |
board = esp32-s3-devkitm-1 | |
monitor_speed = 115200 | |
framework = arduino | |
; board_build.partitions = default_16MB.csv | |
board_upload.flash_size = 16MB | |
board_upload.maximum_size = 16777216 | |
board_build.arduino.memory_type = qio_opi | |
build_flags = | |
-DESP32S3 | |
-DBOARD_HAS_PSRAM | |
-DCORE_DEBUG_LEVEL=5 | |
-DARDUINO_USB_CDC_ON_BOOT=1 | |
-DARDUINO_USB_MODE=1 | |
lib_deps = | |
epdiy=https://github.com/vroland/epdiy.git#d84d26ebebd780c4c9d4218d76fbe2727ee42b47 | |
M5Unified=https://github.com/m5stack/M5Unified |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment