Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TicTacToe #2065

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/Alarm.cpp
displayapp/screens/Styles.cpp
displayapp/screens/WeatherSymbols.cpp
displayapp/screens/TicTacToe.cpp
displayapp/Colors.cpp
displayapp/widgets/Counter.cpp
displayapp/widgets/PageIndicator.cpp
Expand Down Expand Up @@ -832,7 +833,7 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
# add_definitions(-DCLOCK_CONFIG_LOG_LEVEL=4)
# add_definitions(-DRTC_CONFIG_LOG_ENABLED=1)
# add_definitions(-DRTC_CONFIG_LOG_LEVEL=4)

# Nimble Logging
add_definitions(-DMYNEWT_VAL_NEWT_FEATURE_LOGCFG=1)
# add_definitions(-DMYNEWT_VAL_LOG_LEVEL=0)
Expand Down Expand Up @@ -1130,4 +1131,3 @@ endif()
if(BUILD_RESOURCES)
add_subdirectory(resources)
endif()

1 change: 1 addition & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "displayapp/screens/Dice.h"
#include "displayapp/screens/Weather.h"
#include "displayapp/screens/PassKey.h"
#include "displayapp/screens/TicTacToe.h"
#include "displayapp/screens/Error.h"

#include "drivers/Cst816s.h"
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/Apps.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace Pinetime {
Dice,
Weather,
PassKey,
TicTacToe,
QuickSettings,
Settings,
SettingWatchFace,
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ else ()
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Metronome")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Navigation")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Weather")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::TicTacToe")
#set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Motion")
set(USERAPP_TYPES "${DEFAULT_USER_APP_TYPES}" CACHE STRING "List of user apps to build into the firmware")
endif ()
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743"
"range": "0xf294, 0xf242, 0xf54b, 0xf21e, 0xf1e6, 0xf017, 0xf129, 0xf03a, 0xf185, 0xf560, 0xf001, 0xf3fd, 0xf1fc, 0xf45d, 0xf59f, 0xf5a0, 0xf027, 0xf028, 0xf6a9, 0xf04b, 0xf04c, 0xf048, 0xf051, 0xf095, 0xf3dd, 0xf04d, 0xf2f2, 0xf024, 0xf252, 0xf569, 0xf06e, 0xf015, 0xf00c, 0xf0f3, 0xf522, 0xf743, 0xf00d"
}
],
"bpp": 1,
Expand Down
1 change: 1 addition & 0 deletions src/displayapp/screens/Symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace Pinetime {
static constexpr const char* eye = "\xEF\x81\xAE";
static constexpr const char* home = "\xEF\x80\x95";
static constexpr const char* sleep = "\xEE\xBD\x84";
static constexpr const char* times = "\xEF\x80\x8D ";

// fontawesome_weathericons.c
// static constexpr const char* sun = "\xEF\x86\x85";
Expand Down
178 changes: 178 additions & 0 deletions src/displayapp/screens/TicTacToe.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
#include "displayapp/screens/TicTacToe.h"
#include <nrf_log.h>
using namespace Pinetime::Applications::Screens;

TicTacToe::TicTacToe() {
lv_style_init(&cellStyle);
lv_style_set_border_color(&cellStyle, LV_STATE_DEFAULT, LV_COLOR_GRAY);
lv_style_set_border_width(&cellStyle, LV_STATE_DEFAULT, 4);
lv_style_set_bg_color(&cellStyle, LV_STATE_DEFAULT, LV_COLOR_BLACK);
lv_style_set_pad_top(&cellStyle, LV_STATE_DEFAULT, 28);
lv_style_set_pad_bottom(&cellStyle, LV_STATE_DEFAULT, 28);
lv_style_set_border_side(&cellStyle, LV_STATE_DEFAULT, LV_BORDER_SIDE_FULL);

playField = lv_table_create(lv_scr_act(), NULL);
lv_table_set_col_cnt(playField, 3);
lv_table_set_row_cnt(playField, 3);
lv_obj_set_event_cb(playField, this->pressCallback);
lv_obj_add_style(playField, LV_TABLE_PART_CELL1, &cellStyle);

for (uint8_t column = 0; column < 3; column++) {
lv_table_set_col_width(playField, column, LV_HOR_RES / 3);
}

for (uint8_t column = 0; column < 3; column++) {
for (uint8_t row = 0; row < 3; row++) {
lv_table_set_cell_align(playField, column, row, LV_LABEL_ALIGN_CENTER);
}
}

lv_obj_align(playField, NULL, LV_ALIGN_CENTER, 0, 0);
playField->user_data = this;
}

bool TicTacToe::checkCellValue(uint16_t column, uint16_t row, PlayerType player) {
const char* cell_content = lv_table_get_cell_value(this->playField, column, row);
if (strlen(cell_content) == 0 || this->charToPlayer(cell_content[0]) != player) {
return false;
}
return true;
}

void TicTacToe::pressCallback(lv_obj_t* obj, lv_event_t event) {
if (event == LV_EVENT_PRESSED) {
TicTacToe* tic_tac_toe = static_cast<TicTacToe*>(obj->user_data);
uint16_t column, row;
lv_res_t result = lv_table_get_pressed_cell(tic_tac_toe->playField, &column, &row);

if (result == LV_RES_INV) {
return;
}
const char* cellContent = lv_table_get_cell_value(tic_tac_toe->playField, column, row);

if (strlen(cellContent) != 0) {
return;
}

if (tic_tac_toe->currentPlayer == PlayerType::X) {
lv_table_set_cell_value(tic_tac_toe->playField, column, row, "X");
tic_tac_toe->currentPlayer = PlayerType::O;
} else {
lv_table_set_cell_value(tic_tac_toe->playField, column, row, "O");
tic_tac_toe->currentPlayer = PlayerType::X;
}
PlayerType hasWon = tic_tac_toe->hasWon();
if (hasWon != PlayerType::UNKNOWN) {
tic_tac_toe->showWin(hasWon);
} else if (tic_tac_toe->hasEnded()) {
tic_tac_toe->showDraw();
}
}
}

bool TicTacToe::checkPath(uint16_t startColumn, uint16_t startRow, uint16_t endColumn, uint16_t endRow, PlayerType player) {
for (uint16_t column = startColumn; column <= endColumn; column++) {
for (uint16_t row = startRow; row <= endRow; row++) {
if (!this->checkCellValue(column, row, player)) {
return false;
}
}
}
return true;
}

bool TicTacToe::checkPlayer(PlayerType player) {
// Check Rows
for (uint8_t row = 0; row <= 2; row++) {
if (this->checkPath(0, row, 2, row, player)) {
return true;
}
}

// Check Columns
for (uint8_t column = 0; column <= 2; column++) {
if (this->checkPath(column, 0, column, 2, player)) {
return true;
}
}

// Check Diagonal
return this->checkDiagonal(player);
}

bool TicTacToe::checkDiagonal(PlayerType player) {
uint16_t row = 0;
bool won = true;

// Top-Left to Bottom-Right
for (uint16_t column = 0; column <= 2; column++) {
if (!this->checkCellValue(column, row, player)) {
won = false;
break;
}
row++;
}

if(won) {
return true;
}

// Top-Right to Bottom-Left
row = 2;
for (uint16_t column = 0; column <= 2; column++) {
if (!this->checkCellValue(column, row, player)) {
return false;
}
row--;
}
return true;
}

TicTacToe::PlayerType TicTacToe::hasWon() {
if (this->checkPlayer(PlayerType::X)) {
return PlayerType::X;
} else if (this->checkPlayer(PlayerType::O)) {
return PlayerType::O;
}
return PlayerType::UNKNOWN;
}

bool TicTacToe::hasEnded() {
for (uint16_t column = 0; column < 3; column++) {
for (uint16_t row = 0; row < 3; row++) {
const char* cellContent = lv_table_get_cell_value(this->playField, column, row);

if (strlen(cellContent) == 0) {
return false;
}
}
}
return true;
}

lv_obj_t* TicTacToe::createLabel(int16_t x_offset) {
lv_obj_clean(lv_scr_act());

lv_obj_t* label = lv_label_create(lv_scr_act(), nullptr);
lv_obj_set_width(label, LV_HOR_RES);
lv_obj_set_height(label, LV_VER_RES);
lv_obj_align(label, lv_scr_act(), LV_ALIGN_CENTER, x_offset, 0);
lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);

return label;
}

void TicTacToe::showDraw() {
lv_obj_t* label = this->createLabel(-30);
lv_label_set_text_static(label, "Draw !!!");
}

void TicTacToe::showWin(PlayerType player) {
lv_obj_t* label = this->createLabel(-32);
lv_label_set_text_fmt(label, "%c Won !!!", player);
}

TicTacToe::~TicTacToe() {
lv_style_reset(&cellStyle);
lv_obj_clean(lv_scr_act());
}
63 changes: 63 additions & 0 deletions src/displayapp/screens/TicTacToe.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include "displayapp/apps/Apps.h"
#include "displayapp/screens/Screen.h"
#include "displayapp/Controllers.h"

#include "Symbols.h"

namespace Pinetime {
namespace Applications {
namespace Screens {
class TicTacToe : public Screen {
public:
enum class PlayerType : unsigned char { X = 'X', O = 'O', UNKNOWN = 0x00 };

explicit TicTacToe();
~TicTacToe() override;

private:
lv_style_t cellStyle;
lv_obj_t* playField;

PlayerType currentPlayer = PlayerType::X;

bool checkPath(uint16_t startX, uint16_t startY, uint16_t endX, uint16_t endY, PlayerType to_check);
bool checkDiagonal(PlayerType to_check);
bool checkPlayer(PlayerType to_check);
bool checkCellValue(uint16_t x, uint16_t y, PlayerType player);

lv_obj_t* createLabel(int16_t x_offset);

void showWin(PlayerType player);
void showDraw();

PlayerType hasWon();
bool hasEnded();

PlayerType charToPlayer(char inp) {
switch (inp) {
case 'X':
return PlayerType::X;
case 'O':
return PlayerType::O;
default:
return PlayerType::UNKNOWN;
}
}

static void pressCallback(lv_obj_t* obj, lv_event_t event);
};
}

template <>
struct AppTraits<Apps::TicTacToe> {
static constexpr Apps app = Apps::TicTacToe;
static constexpr const char* icon = Screens::Symbols::times;

static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::TicTacToe();
}
};
}
}
Loading