Browse Source

Add tictactoe from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: tictactoe
git-subtree-mainline: d5c788f10b1aa37d1a6620bf801a8fae4fe83ea3
git-subtree-split: 549d3e6f7140e6a807414a9129813cc4fb173e6c
Willy-JL 2 years ago
parent
commit
e3e3fc1175
5 changed files with 402 additions and 0 deletions
  1. 1 0
      tictactoe/.gitsubtree
  2. 14 0
      tictactoe/application.fam
  3. BIN
      tictactoe/img/1.png
  4. BIN
      tictactoe/tictactoe_10px.png
  5. 387 0
      tictactoe/tictactoe_game.c

+ 1 - 0
tictactoe/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev base_pack/tictactoe_game

+ 14 - 0
tictactoe/application.fam

@@ -0,0 +1,14 @@
+App(
+    appid="tictactoe",
+    name="Tic Tac Toe",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="tictactoe_game_app",
+    requires=["gui"],
+    stack_size=1 * 1024,
+    order=40,
+    fap_icon="tictactoe_10px.png",
+    fap_category="Games",
+    fap_author="@xMasterX & @gotnull",
+    fap_version="1.1",
+    fap_description="Tic Tac Toe game, for 2 players, play on one device",
+)

BIN
tictactoe/img/1.png


BIN
tictactoe/tictactoe_10px.png


+ 387 - 0
tictactoe/tictactoe_game.c

@@ -0,0 +1,387 @@
+#include <furi.h>
+#include <gui/gui.h>
+#include <input/input.h>
+#include <stdlib.h>
+#include <gui/view.h>
+#include <dolphin/dolphin.h>
+
+#define TAG "TicTacToe"
+
+typedef enum { EventTypeTick, EventTypeKey } EventType;
+
+typedef struct {
+    FuriMutex* mutex;
+    FuriTimer* timer;
+    uint8_t selBoxX;
+    uint8_t selBoxY;
+
+    uint8_t selX;
+    uint8_t selY;
+
+    uint16_t scoreX;
+    uint16_t scoreO;
+
+    char player;
+
+    char field[3][3];
+    bool fieldx[3][3];
+
+    uint8_t coords[3];
+
+    bool button_state;
+
+} TicTacToeState;
+
+typedef struct {
+    EventType type;
+    InputEvent input;
+} GameEvent;
+
+void drawCross(Canvas* const canvas, uint8_t x, uint8_t y) {
+    canvas_draw_line(canvas, x, y, x + 9, y + 9); // top left - bottom right slash
+    canvas_draw_line(canvas, x + 9, y, x, y + 9); // down left - top right slash
+}
+
+void drawCircle(Canvas* const canvas, uint8_t x, uint8_t y) {
+    canvas_draw_circle(canvas, x + 4, y + 5, 5);
+}
+
+void player_switch(TicTacToeState* ts) {
+    if(ts->player == 'O') {
+        ts->player = 'X';
+    } else if(ts->player == 'X') {
+        ts->player = 'O';
+    }
+}
+
+void tictactoe_draw(Canvas* canvas, TicTacToeState* ts) {
+    // Draws the game field
+    canvas_draw_frame(canvas, 0, 0, 64, 64); // frame
+    canvas_draw_line(canvas, 0, 21, 63, 21); // horizontal line
+    canvas_draw_line(canvas, 0, 42, 63, 42); // horizontal line
+    canvas_draw_line(canvas, 21, 0, 21, 63); // vertical line
+    canvas_draw_line(canvas, 42, 0, 42, 63); // vertical line
+
+    // Draws the game field elements (X or O)
+    for(uint8_t i = 0; i <= 2; i++) {
+        for(uint8_t j = 0; j <= 2; j++) {
+            if(ts->field[i][j] == 'O') {
+                drawCircle(canvas, ts->coords[i], ts->coords[j]);
+            } else if(ts->field[i][j] == 'X') {
+                drawCross(canvas, ts->coords[i], ts->coords[j]);
+            }
+        }
+    }
+
+    // Draws the selection box
+    if(ts->selX == 1) {
+        ts->selBoxX = 1;
+    } else if(ts->selX == 2) {
+        ts->selBoxX = 22;
+    } else if(ts->selX == 3) {
+        ts->selBoxX = 43;
+    }
+
+    if(ts->selY == 1) {
+        ts->selBoxY = 1;
+    } else if(ts->selY == 2) {
+        ts->selBoxY = 22;
+    } else if(ts->selY == 3) {
+        ts->selBoxY = 43;
+    }
+
+    canvas_set_color(canvas, ColorBlack);
+    canvas_draw_frame(canvas, ts->selBoxX, ts->selBoxY, 20, 20);
+    canvas_draw_frame(canvas, ts->selBoxX + 1, ts->selBoxY + 1, 18, 18);
+
+    // Draws the sidebar
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str(canvas, 81, 10, "SCORE");
+    canvas_draw_str(canvas, 75, 24, "X:");
+
+    char scoreXBuffer[10];
+    snprintf(scoreXBuffer, sizeof(scoreXBuffer), "%d", ts->scoreX);
+    canvas_draw_str(canvas, 88, 24, scoreXBuffer);
+    canvas_draw_str(canvas, 75, 35, "O:");
+
+    char scoreOBuffer[10];
+    snprintf(scoreOBuffer, sizeof(scoreOBuffer), "%d", ts->scoreO);
+    canvas_draw_str(canvas, 88, 35, scoreOBuffer);
+
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 75, 46, "Player:");
+
+    if(ts->player == 'X') {
+        drawCross(canvas, 93, 50);
+    } else if(ts->player == 'O') {
+        drawCircle(canvas, 93, 50);
+    }
+}
+
+void clear_game_field(TicTacToeState* ts) {
+    // Clears the game field arrays
+    for(uint8_t i = 0; i <= 2; i++) {
+        for(uint8_t j = 0; j <= 2; j++) {
+            ts->field[i][j] = ' ';
+            ts->fieldx[i][j] = false;
+        }
+    }
+
+    ts->selX = 2; // Centers the selection box on X axis
+    ts->selY = 2; // Centers the selection box on Y axis
+}
+
+void reset_game_data(TicTacToeState* ts) {
+    ts->scoreO = 0;
+    ts->scoreX = 0;
+    ts->player = 'X';
+}
+
+void draw_win(Canvas* canvas, char player, TicTacToeState* ts) {
+    // Handles the score table
+    if(player == 'X') {
+        ts->scoreX++;
+    } else if(player == 'O') {
+        ts->scoreO++;
+    }
+
+    // Switches the players
+    player_switch(ts);
+
+    // Draws the board with players switched
+    tictactoe_draw(canvas, ts);
+
+    // Clear the game field
+    clear_game_field(ts);
+
+    // Draw the new board
+    tictactoe_draw(canvas, ts);
+}
+
+static void tictactoe_state_init(TicTacToeState* tictactoe_state) {
+    // Set the initial game state
+    tictactoe_state->selX = 2;
+    tictactoe_state->selY = 2;
+    tictactoe_state->player = 'X';
+    tictactoe_state->coords[0] = 6;
+    tictactoe_state->coords[1] = 27;
+    tictactoe_state->coords[2] = 48;
+    tictactoe_state->button_state = false;
+
+    clear_game_field(tictactoe_state);
+
+    reset_game_data(tictactoe_state);
+}
+
+static void tictactoe_draw_callback(Canvas* const canvas, void* ctx) {
+    furi_assert(ctx);
+    TicTacToeState* ticst = ctx;
+    furi_mutex_acquire(ticst->mutex, FuriWaitForever);
+
+    if(ticst->selX > 3) {
+        ticst->selX = 3;
+    } else if(ticst->selX < 1) {
+        ticst->selX = 1;
+    }
+
+    if(ticst->selY > 3) {
+        ticst->selY = 3;
+    } else if(ticst->selY < 1) {
+        ticst->selY = 1;
+    }
+
+    // Assigns the game field elements their value (X or O) when the OK button is pressed
+    if(ticst->button_state) {
+        ticst->button_state = false;
+        for(uint8_t i = 0; i <= 2; i++) {
+            for(uint8_t j = 0; j <= 2; j++) {
+                if((ticst->selX == i + 1) && (ticst->selY == j + 1) &&
+                   (ticst->fieldx[i][j] == false)) {
+                    if(ticst->player == 'X') {
+                        ticst->field[i][j] = 'X';
+                        ticst->fieldx[i][j] = true;
+                        player_switch(ticst);
+                    } else if(ticst->player == 'O') {
+                        ticst->field[i][j] = 'O';
+                        ticst->fieldx[i][j] = true;
+                        player_switch(ticst);
+                    }
+                }
+            }
+        }
+    }
+
+    // Checks the game field for winning combinations
+    if((ticst->field[0][0] == 'X') && (ticst->field[1][0] == 'X') && (ticst->field[2][0] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[0][1] == 'X') && (ticst->field[1][1] == 'X') &&
+        (ticst->field[2][1] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[0][2] == 'X') && (ticst->field[1][2] == 'X') &&
+        (ticst->field[2][2] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[0][0] == 'X') && (ticst->field[0][1] == 'X') &&
+        (ticst->field[0][2] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[1][0] == 'X') && (ticst->field[1][1] == 'X') &&
+        (ticst->field[1][2] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[2][0] == 'X') && (ticst->field[2][1] == 'X') &&
+        (ticst->field[2][2] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[0][0] == 'X') && (ticst->field[1][1] == 'X') &&
+        (ticst->field[2][2] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[2][0] == 'X') && (ticst->field[1][1] == 'X') &&
+        (ticst->field[0][2] == 'X')) {
+        draw_win(canvas, 'X', ticst);
+    } else if(
+        (ticst->field[0][0] == 'O') && (ticst->field[1][0] == 'O') &&
+        (ticst->field[2][0] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[0][1] == 'O') && (ticst->field[1][1] == 'O') &&
+        (ticst->field[2][1] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[0][2] == 'O') && (ticst->field[1][2] == 'O') &&
+        (ticst->field[2][2] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[0][0] == 'O') && (ticst->field[0][1] == 'O') &&
+        (ticst->field[0][2] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[1][0] == 'O') && (ticst->field[1][1] == 'O') &&
+        (ticst->field[1][2] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[2][0] == 'O') && (ticst->field[2][1] == 'O') &&
+        (ticst->field[2][2] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[0][0] == 'O') && (ticst->field[1][1] == 'O') &&
+        (ticst->field[2][2] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->field[2][0] == 'O') && (ticst->field[1][1] == 'O') &&
+        (ticst->field[0][2] == 'O')) {
+        draw_win(canvas, 'O', ticst);
+    } else if(
+        (ticst->fieldx[0][0] == true) && (ticst->fieldx[0][1] == true) &&
+        (ticst->fieldx[0][2] == true) && (ticst->fieldx[1][0] == true) &&
+        (ticst->fieldx[1][1] == true) && (ticst->fieldx[1][2] == true) &&
+        (ticst->fieldx[2][0] == true) && (ticst->fieldx[2][1] == true) &&
+        (ticst->fieldx[2][2] == true)) {
+        draw_win(canvas, 'T', ticst);
+    }
+
+    tictactoe_draw(canvas, ticst);
+
+    furi_mutex_release(ticst->mutex);
+}
+
+static void tictactoe_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
+    furi_assert(event_queue);
+
+    GameEvent event = {.type = EventTypeKey, .input = *input_event};
+    furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static void tictactoe_update_timer_callback(FuriMessageQueue* event_queue) {
+    furi_assert(event_queue);
+
+    GameEvent event = {.type = EventTypeTick};
+    furi_message_queue_put(event_queue, &event, 0);
+}
+
+int32_t tictactoe_game_app(void* p) {
+    UNUSED(p);
+    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent));
+
+    TicTacToeState* tictactoe_state = malloc(sizeof(TicTacToeState));
+
+    tictactoe_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+
+    if(!tictactoe_state->mutex) {
+        FURI_LOG_E(TAG, "Cannot create mutex\r\n");
+        furi_message_queue_free(event_queue);
+        free(tictactoe_state);
+        return 255;
+    }
+
+    // Set system callbacks
+    ViewPort* view_port = view_port_alloc();
+    view_port_draw_callback_set(view_port, tictactoe_draw_callback, tictactoe_state);
+    view_port_input_callback_set(view_port, tictactoe_input_callback, event_queue);
+
+    tictactoe_state->timer =
+        furi_timer_alloc(tictactoe_update_timer_callback, FuriTimerTypePeriodic, event_queue);
+    furi_timer_start(tictactoe_state->timer, furi_kernel_get_tick_frequency() / 22);
+
+    tictactoe_state_init(tictactoe_state);
+
+    // Open GUI and register view_port
+    Gui* gui = furi_record_open(RECORD_GUI);
+    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+    // Call dolphin deed on game start
+    dolphin_deed(DolphinDeedPluginGameStart);
+
+    GameEvent event;
+    for(bool processing = true; processing;) {
+        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
+        furi_mutex_acquire(tictactoe_state->mutex, FuriWaitForever);
+
+        if(event_status == FuriStatusOk) {
+            // Key events
+            if(event.type == EventTypeKey) {
+                if(event.input.type == InputTypePress) {
+                    switch(event.input.key) {
+                    case InputKeyBack:
+                        processing = false;
+                        break;
+                    case InputKeyRight:
+                        tictactoe_state->selX++;
+                        break;
+                    case InputKeyLeft:
+                        tictactoe_state->selX--;
+                        break;
+                    case InputKeyUp:
+                        tictactoe_state->selY--;
+                        break;
+                    case InputKeyDown:
+                        tictactoe_state->selY++;
+                        break;
+                    case InputKeyOk:
+                        tictactoe_state->button_state = true;
+                        break;
+                    default:
+                        break;
+                    }
+                }
+            }
+        }
+
+        furi_mutex_release(tictactoe_state->mutex);
+        view_port_update(view_port);
+    }
+
+    furi_timer_free(tictactoe_state->timer);
+    view_port_enabled_set(view_port, false);
+    gui_remove_view_port(gui, view_port);
+    furi_record_close(RECORD_GUI);
+    view_port_free(view_port);
+    furi_message_queue_free(event_queue);
+    furi_mutex_free(tictactoe_state->mutex);
+    free(tictactoe_state);
+
+    return 0;
+}