Skip to content

Commit 9eb3516

Browse files
committed
add sample tic_tac_toe app
1 parent 2f80d30 commit 9eb3516

File tree

4 files changed

+175
-0
lines changed

4 files changed

+175
-0
lines changed

apps/tic_tac_toe/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following five lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(tic_tac_toe)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
idf_component_register(SRCS "tic_tac_toe.c"
2+
INCLUDE_DIRS "."
3+
REQUIRES app_update)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## IDF Component Manager Manifest File
2+
dependencies:
3+
espressif/esp-box: "^3.1.0"
4+
#espressif/esp-box-3: "^1.2.0"
5+
# Workaround for i2c: CONFLICT! driver_ng is not allowed to be used with this old driver
6+
esp_codec_dev:
7+
public: true
8+
version: "==1.1.0"
9+
## Required IDF version
10+
idf:
11+
version: ">=5.0.0"
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#include "freertos/FreeRTOS.h"
2+
#include "freertos/task.h"
3+
#include "freertos/semphr.h"
4+
#include "esp_system.h"
5+
#include "esp_log.h"
6+
#include "lvgl.h"
7+
#include "bsp/esp-bsp.h"
8+
9+
#define TAG "TicTacToe"
10+
11+
static lv_obj_t *btn_grid[3][3];
12+
static char board[3][3];
13+
static bool player_turn = true; // true for 'X', false for 'O'
14+
static bool event_in_progress = false; // Flag for event handling
15+
static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; // Mutex for critical sections
16+
17+
static void reset_game() {
18+
memset(board, 0, sizeof(board));
19+
for (int row = 0; row < 3; ++row) {
20+
for (int col = 0; col < 3; ++col) {
21+
lv_obj_t *btn = btn_grid[row][col];
22+
lv_label_set_text(lv_obj_get_child(btn, 0), "");
23+
lv_obj_clear_state(btn, LV_STATE_DISABLED);
24+
}
25+
}
26+
player_turn = true;
27+
}
28+
29+
static void close_message_box(lv_timer_t *timer) {
30+
lv_obj_t *msg_box = (lv_obj_t *)lv_timer_get_user_data(timer);
31+
lv_msgbox_close(msg_box);
32+
lv_timer_delete(timer);
33+
reset_game();
34+
}
35+
36+
static void show_message(const char *text) {
37+
ESP_LOGI(TAG, "%s", text);
38+
lv_obj_t *msg_box = lv_msgbox_create(NULL);
39+
lv_msgbox_add_text(msg_box, text);
40+
lv_obj_center(msg_box);
41+
lv_timer_create(close_message_box, 2000, msg_box);
42+
}
43+
44+
static bool check_winner(char player) {
45+
for (int i = 0; i < 3; ++i) {
46+
// Check rows
47+
if (board[i][0] == player && board[i][1] == player && board[i][2] == player)
48+
return true;
49+
// Check columns
50+
if (board[0][i] == player && board[1][i] == player && board[2][i] == player)
51+
return true;
52+
}
53+
// Check diagonals
54+
if (board[0][0] == player && board[1][1] == player && board[2][2] == player)
55+
return true;
56+
if (board[0][2] == player && board[1][1] == player && board[2][0] == player)
57+
return true;
58+
return false;
59+
}
60+
61+
static void event_handler(lv_event_t *e) {
62+
taskENTER_CRITICAL(&mux); // Enter critical section
63+
if (event_in_progress) {
64+
taskEXIT_CRITICAL(&mux); // Exit critical section if an event is already in progress
65+
return;
66+
}
67+
event_in_progress = true;
68+
taskEXIT_CRITICAL(&mux); // Exit critical section
69+
70+
lv_event_code_t code = lv_event_get_code(e);
71+
lv_obj_t *btn = lv_event_get_target(e);
72+
73+
if (code == LV_EVENT_CLICKED) {
74+
uint32_t id = (uint32_t)lv_event_get_user_data(e);
75+
uint8_t row = id / 3;
76+
uint8_t col = id % 3;
77+
if (board[row][col] == 0) {
78+
board[row][col] = player_turn ? 'X' : 'O';
79+
lv_label_set_text(lv_obj_get_child(btn, 0), player_turn ? "X" : "O");
80+
lv_obj_add_state(btn, LV_STATE_DISABLED); // Disable the button after being clicked
81+
82+
if (check_winner(player_turn ? 'X' : 'O')) {
83+
show_message(player_turn ? "Player X wins!" : "Player O wins!");
84+
} else if (memchr(board, 0, sizeof(board)) == NULL) {
85+
show_message("It's a draw!");
86+
} else {
87+
player_turn = !player_turn;
88+
}
89+
}
90+
}
91+
92+
taskENTER_CRITICAL(&mux); // Enter critical section
93+
event_in_progress = false; // Mark the event as processed
94+
taskEXIT_CRITICAL(&mux); // Exit critical section
95+
}
96+
97+
void lvgl_task(void *pvParameter) {
98+
while (1) {
99+
vTaskDelay(pdMS_TO_TICKS(1000)); // Add a small delay to prevent watchdog issues
100+
}
101+
}
102+
103+
void reset_to_factory_app() {
104+
// Get the partition structure for the factory partition
105+
const esp_partition_t *factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
106+
if (factory_partition != NULL) {
107+
if (esp_ota_set_boot_partition(factory_partition) == ESP_OK) {
108+
printf("Set boot partition to factory, restarting now.\n");
109+
} else {
110+
printf("Failed to set boot partition to factory.\n");
111+
}
112+
} else {
113+
printf("Factory partition not found.\n");
114+
}
115+
116+
fflush(stdout);
117+
}
118+
119+
void app_main(void) {
120+
// Reset to factory app for the next boot.
121+
// It should return to graphical bootloader.
122+
reset_to_factory_app();
123+
124+
// Initialize the BSP
125+
bsp_i2c_init();
126+
bsp_display_start();
127+
lv_init();
128+
129+
// Create a grid for buttons
130+
lv_obj_t *grid = lv_obj_create(lv_scr_act());
131+
static lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
132+
static lv_coord_t row_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
133+
lv_obj_set_grid_dsc_array(grid, col_dsc, row_dsc);
134+
lv_obj_set_size(grid, 240, 240);
135+
lv_obj_align(grid, LV_ALIGN_CENTER, 0, 0);
136+
137+
for (int row = 0; row < 3; ++row) {
138+
for (int col = 0; col < 3; ++col) {
139+
lv_obj_t *btn = lv_btn_create(grid);
140+
lv_obj_set_grid_cell(btn, LV_GRID_ALIGN_STRETCH, col, 1, LV_GRID_ALIGN_STRETCH, row, 1);
141+
lv_obj_t *label = lv_label_create(btn);
142+
lv_label_set_text(label, "");
143+
lv_obj_center(label);
144+
lv_obj_add_event_cb(btn, event_handler, LV_EVENT_CLICKED, (void *)(row * 3 + col));
145+
btn_grid[row][col] = btn;
146+
}
147+
}
148+
149+
bsp_display_backlight_on();
150+
151+
reset_game();
152+
153+
// Create a task to handle LVGL
154+
xTaskCreate(lvgl_task, "lvgl_task", 16384, NULL, 1, NULL);
155+
}

0 commit comments

Comments
 (0)