Struan Clark vor 2 Jahren
Ursprung
Commit
940d066b2d
7 geänderte Dateien mit 448 neuen und 695 gelöschten Zeilen
  1. 3 3
      application.fam
  2. 0 500
      flip_chess_app.c
  3. 7 32
      flipchess.c
  4. 1 1
      flipchess.h
  5. 0 1
      scenes/flipchess_scene_menu.c
  6. 435 156
      views/flipchess_scene_1.c
  7. 2 2
      views/flipchess_startscreen.c

+ 3 - 3
application.fam

@@ -1,5 +1,5 @@
 App(
-    appid="flip_chess",
+    appid="flipchess",
     name="Chess",
     apptype=FlipperAppType.EXTERNAL,
     entry_point="flip_chess_app",
@@ -8,9 +8,9 @@ App(
     ],
     stack_size=2 * 1024,
     order=40,
-    fap_icon="keycard_10px.png",
+    fap_icon="flipbip_10px.png",
     fap_category="Games",
-    fap_description="Crypto toolkit for Flipper",
+    fap_description="Chess for Flipper",
     fap_author="Struan Clark (xtruan)",
     fap_weburl="https://github.com/xtruan/flip-chess",
 )

+ 0 - 500
flip_chess_app.c

@@ -1,500 +0,0 @@
-#include <furi.h>
-#include <gui/gui.h>
-#include <input/input.h>
-#include <stdlib.h>
-#include <dolphin/dolphin.h>
-
-#include <furi_hal_random.h>
-
-#define SCL_960_CASTLING 0 // setting to 1 compiles a 960 version of smolchess
-#define XBOARD_DEBUG 0 // will create files with xboard communication
-#define SCL_EVALUATION_FUNCTION SCL_boardEvaluateStatic
-
-#define SCL_DEBUG_AI 0
-
-#include "smallchesslib.h"
-
-typedef struct {
-    uint8_t status;
-    FuriMutex* mutex;
-} ChessState;
-
-typedef enum {
-    EventTypeTick,
-    EventTypeKey,
-} EventType;
-
-typedef struct {
-    EventType type;
-    InputEvent input;
-} Event;
-
-uint8_t paramPlayerW = 0;
-uint8_t paramPlayerB = 0;
-
-// uint8_t paramBoard = 1;
-uint8_t paramAnalyze = 255; // depth of analysis
-uint8_t paramMoves = 0;
-//uint8_t paramXboard = 0;
-uint8_t paramInfo = 1;
-//uint8_t paramDraw = 1;
-uint8_t paramFlipBoard = 0;
-//uint8_t paramHelp = 0;
-uint8_t paramExit = 0;
-uint16_t paramStep = 0;
-char* paramFEN = NULL;
-char* paramPGN = NULL;
-//uint16_t paramRandom = 0;
-//uint8_t paramBlind = 0;
-
-int clockSeconds = -1;
-SCL_Game game;
-SCL_Board startState = SCL_BOARD_START_STATE;
-int16_t random960PosNumber = -1;
-
-uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH];
-uint8_t selected = 255;
-char* msg = "Flip Chess";
-
-void putImagePixel(uint8_t pixel, uint16_t index) {
-    picture[index] = pixel;
-}
-
-uint8_t stringsEqual(const char* s1, const char* s2, int max) {
-    for(int i = 0; i < max; ++i) {
-        if(*s1 != *s2) return 0;
-
-        if(*s1 == 0) return 1;
-
-        s1++;
-        s2++;
-    }
-
-    return 1;
-}
-
-int16_t makeAIMove(SCL_Board board, uint8_t* s0, uint8_t* s1, char* prom) {
-    uint8_t level = SCL_boardWhitesTurn(board) ? paramPlayerW : paramPlayerB;
-    uint8_t depth = (level > 0) ? level : 1;
-    uint8_t extraDepth = 3;
-    uint8_t endgameDepth = 1;
-    uint8_t randomness = game.ply < 2 ? 1 : 0; /* in first moves increase randomness for different 
-                             openings */
-    uint8_t rs0, rs1;
-
-    SCL_gameGetRepetiotionMove(&game, &rs0, &rs1);
-
-    if(clockSeconds >= 0) // when using clock, choose AI params accordingly
-    {
-        if(clockSeconds <= 5) {
-            depth = 1;
-            extraDepth = 2;
-            endgameDepth = 0;
-        } else if(clockSeconds < 15) {
-            depth = 2;
-            extraDepth = 2;
-        } else if(clockSeconds < 100) {
-            depth = 2;
-        } else if(clockSeconds < 5 * 60) {
-            depth = 3;
-        } else {
-            depth = 3;
-            extraDepth = 4;
-        }
-    }
-
-    return SCL_getAIMove(
-        board,
-        depth,
-        extraDepth,
-        endgameDepth,
-        SCL_boardEvaluateStatic,
-        SCL_randomBetter,
-        randomness,
-        rs0,
-        rs1,
-        s0,
-        s1,
-        prom);
-}
-
-void initGame() {
-    SCL_randomBetterSeed(furi_hal_random_get());
-
-#if SCL_960_CASTLING
-    if(random960PosNumber < 0) random960PosNumber = SCL_randomBetter();
-#endif
-
-    if(random960PosNumber >= 0) random960PosNumber %= 960;
-
-    if(paramFEN != NULL)
-        SCL_boardFromFEN(startState, paramFEN);
-    else if(paramPGN != NULL) {
-        SCL_Record record;
-        SCL_recordFromPGN(record, paramPGN);
-        SCL_boardInit(startState);
-        SCL_recordApply(record, startState, paramStep);
-    }
-#if SCL_960_CASTLING
-    else
-        SCL_boardInit960(startState, random960PosNumber);
-#endif
-
-    SCL_gameInit(&game, startState);
-
-    if(paramAnalyze != 255) {
-        char p;
-
-        uint8_t move[] = {0, 0};
-
-        paramPlayerW = paramAnalyze;
-        paramPlayerB = paramAnalyze;
-
-        int16_t evaluation = makeAIMove(game.board, &(move[0]), &(move[1]), &p);
-
-        if(paramAnalyze == 0) evaluation = SCL_boardEvaluateStatic(game.board);
-
-        char moveStr[5];
-        moveStr[4] = 0;
-
-        SCL_squareToString(move[0], moveStr);
-        SCL_squareToString(move[1], moveStr + 2);
-
-        //printf("%lf (%d)\n", ((double)evaluation) / ((double)SCL_VALUE_PAWN), evaluation);
-        //puts(moveStr);
-
-        return 0;
-    }
-
-    if(paramMoves) {
-        char string[256];
-
-        for(int i = 0; i < 64; ++i)
-            if(game.board[i] != '.' &&
-               SCL_pieceIsWhite(game.board[i]) == SCL_boardWhitesTurn(game.board)) {
-                SCL_SquareSet possibleMoves = SCL_SQUARE_SET_EMPTY;
-
-                SCL_boardGetMoves(game.board, i, possibleMoves);
-
-                SCL_SQUARE_SET_ITERATE_BEGIN(possibleMoves)
-                SCL_moveToString(game.board, i, iteratedSquare, 'q', string);
-                //printf("%s ", string);
-                SCL_SQUARE_SET_ITERATE_END
-            }
-
-        return 0;
-    }
-}
-
-static void flip_chess_render_callback(Canvas* const canvas, void* ctx) {
-    furi_assert(ctx);
-    const ChessState* chess_state = ctx;
-    furi_mutex_acquire(chess_state->mutex, FuriWaitForever);
-
-    // Before the function is called, the state is set with the canvas_reset(canvas)
-
-    // Frame
-    canvas_draw_frame(canvas, 0, 0, 128, 64);
-
-    // Message
-    canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 66, 10, msg);
-
-    // Board
-    for(uint16_t y = 0; y < SCL_BOARD_PICTURE_WIDTH; y++) {
-        for(uint16_t x = 0; x < SCL_BOARD_PICTURE_WIDTH; x++) {
-            if(picture[x + (y * SCL_BOARD_PICTURE_WIDTH)]) {
-                canvas_draw_dot(canvas, x, y);
-            }
-        }
-    }
-
-    furi_mutex_release(chess_state->mutex);
-}
-
-static void flip_chess_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
-    furi_assert(event_queue);
-
-    Event event = {.type = EventTypeKey, .input = *input_event};
-    furi_message_queue_put(event_queue, &event, FuriWaitForever);
-}
-
-int32_t flip_chess_app(void* p) {
-    UNUSED(p);
-
-    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Event));
-    dolphin_deed(DolphinDeedPluginStart);
-
-    ChessState* chess_state = malloc(sizeof(ChessState));
-
-    chess_state->status = 0;
-
-    chess_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
-    if(!chess_state->mutex) {
-        FURI_LOG_E("FlipChess", "cannot create mutex\r\n");
-        furi_message_queue_free(event_queue);
-        free(chess_state);
-        return 255;
-    }
-
-    ViewPort* view_port = view_port_alloc();
-    view_port_draw_callback_set(view_port, flip_chess_render_callback, chess_state);
-    view_port_input_callback_set(view_port, flip_chess_input_callback, event_queue);
-
-    // Open GUI and register view_port
-    Gui* gui = furi_record_open(RECORD_GUI);
-    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
-    Event event;
-
-    furi_hal_random_init();
-    initGame();
-
-    //char string[256];
-    SCL_SquareSet squareSet = SCL_SQUARE_SET_EMPTY;
-    char moveString[16];
-    moveString[0] = 0;
-    SCL_SquareSet moveHighlight = SCL_SQUARE_SET_EMPTY;
-    uint8_t squareFrom = 255;
-    uint8_t squareTo = 255;
-
-    SCL_drawBoard(game.board, putImagePixel, selected, squareSet, paramFlipBoard);
-
-    for(bool processing = true; processing;) {
-        FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
-
-        furi_mutex_acquire(chess_state->mutex, FuriWaitForever);
-
-        if(event_status == FuriStatusOk) {
-            // press events
-            if(event.type == EventTypeKey) {
-                if(event.input.type == InputTypePress) {
-                    switch(event.input.key) {
-                    case InputKeyUp:
-                        selected = (selected + 8) % 64;
-                        SCL_drawBoard(
-                            game.board, putImagePixel, selected, squareSet, paramFlipBoard);
-                        break;
-                    case InputKeyDown:
-                        selected = (selected + 56) % 64;
-                        SCL_drawBoard(
-                            game.board, putImagePixel, selected, squareSet, paramFlipBoard);
-                        break;
-                    case InputKeyRight:
-                        selected = (selected + 1) % 64;
-                        SCL_drawBoard(
-                            game.board, putImagePixel, selected, squareSet, paramFlipBoard);
-                        break;
-                    case InputKeyLeft:
-                        selected = (selected + 63) % 64;
-                        SCL_drawBoard(
-                            game.board, putImagePixel, selected, squareSet, paramFlipBoard);
-                        break;
-                    case InputKeyOk: ;
-
-                        if (chess_state->status == 1) {
-                            squareFrom = selected;
-                            chess_state->status = 2;
-                        } else {
-                            squareTo = selected;
-                            chess_state->status = 1;
-                        }
-
-
-                        // // // // //
-
-                        // 0: none, 1: player, 2: AI, 3: undo
-                        uint8_t moveType = 0;
-
-                        //for(int i = 0; i < 40; ++i) putchar('\n');
-                        //putchar('\n');
-
-                        if(game.ply > 0) {
-                            msg =
-                                (SCL_boardWhitesTurn(game.board) ? "black played" :
-                                                                   "white  played");
-                            // printf(" played ");
-
-                            uint8_t s0, s1;
-                            char p;
-
-                            SCL_recordGetMove(game.record, game.ply - 1, &s0, &s1, &p);
-                            SCL_moveToString(game.board, s0, s1, p, moveString);
-                            msg = moveString;
-                            //printf("%s\n", moveString);
-                        }
-
-                        msg =
-                            (SCL_boardWhitesTurn(game.board) ? "white to move" : "black to move");
-                        //printf(" to move\n");
-
-                        // if(paramInfo) {
-                        //     //putchar('\n');
-
-                        //     if(random960PosNumber >= 0)
-                        //         printf("960 random position number: %d\n", random960PosNumber);
-
-                        //     printf("ply number: %d\n", game.ply);
-
-                        //     //SCL_boardToFEN(game.board, string);
-                        //     //printf("FEN: %s\n", string);
-
-                        //     int16_t eval = SCL_boardEvaluateStatic(game.board);
-                        //     printf(
-                        //         "board static evaluation: %lf (%d)\n",
-                        //         ((double)eval) / ((double)SCL_VALUE_PAWN),
-                        //         eval);
-                        //     printf("board hash: %u\n", SCL_boardHash32(game.board));
-                        //     printf("phase: ");
-
-                        //     switch(SCL_boardEstimatePhase(game.board)) {
-                        //     case SCL_PHASE_OPENING:
-                        //         puts("opening");
-                        //         break;
-                        //     case SCL_PHASE_ENDGAME:
-                        //         puts("endgame");
-                        //         break;
-                        //     default:
-                        //         puts("midgame");
-                        //         break;
-                        //     }
-
-                        //     printf(
-                        //         "en passant: %d\n",
-                        //         ((game.board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f) + 1) % 16);
-                        //     printf(
-                        //         "50 move rule count: %d\n", game.board[SCL_BOARD_MOVE_COUNT_BYTE]);
-
-                        //     // if(paramFEN == NULL && paramPGN == NULL) {
-                        //     //     //printf("PGN: ");
-                        //     //     //SCL_printPGN(game.record, putCharacter, startState);
-                        //     //     //putchar('\n');
-                        //     // }
-                        // }
-
-                        if(game.state != SCL_GAME_STATE_PLAYING || paramExit) break;
-
-                        //uint8_t squareFrom = 0;
-                        //uint8_t squareTo = 0;
-                        char movePromote = 'q';
-
-                        if((SCL_boardWhitesTurn(game.board) && paramPlayerW == 0) ||
-                           (!SCL_boardWhitesTurn(game.board) && paramPlayerB == 0)) {
-                            
-                            // printf("\nmove: ");
-                            // scanf("%s", string);
-                            // char string[256];
-
-                            // if(stringsEqual(string, "undo", 5))
-                            //     moveType = 3;
-                            // else if(stringsEqual(string, "quit", 5))
-                            //     break;
-                            // else {
-                                //squareFrom = selected; //SCL_stringToSquare(string);
-                                //squareTo = selected; //SCL_stringToSquare(string + 2);
-
-                                //uint8_t r =
-                                //    SCL_stringToMove(string, &squareFrom, &squareTo, &movePromote);
-
-                                if(squareFrom != 255) {
-                                    if((game.board[squareFrom] != '.') &&
-                                       (SCL_pieceIsWhite(game.board[squareFrom]) ==
-                                        SCL_boardWhitesTurn(game.board))) {
-                                        SCL_boardGetMoves(game.board, squareFrom, squareSet);
-
-                                        if(SCL_squareSetContains(squareSet, squareTo)) {
-                                            moveType = 1;
-                                        }
-                                    }
-                                }
-                            // }
-                        } else {
-                            makeAIMove(game.board, &squareFrom, &squareTo, &movePromote);
-                            moveType = 2;
-                        }
-
-                        if(moveType == 1 || moveType == 2) {
-                            SCL_moveToString(
-                                game.board, squareFrom, squareTo, movePromote, moveString);
-
-                            SCL_gameMakeMove(&game, squareFrom, squareTo, movePromote);
-
-                            SCL_squareSetClear(moveHighlight);
-                            SCL_squareSetAdd(moveHighlight, squareFrom);
-                            SCL_squareSetAdd(moveHighlight, squareTo);
-                        } else if(moveType == 3) {
-                            if(paramPlayerW != 0 || paramPlayerB != 0) SCL_gameUndoMove(&game);
-
-                            SCL_gameUndoMove(&game);
-                            SCL_squareSetClear(moveHighlight);
-                        }
-
-                        //putchar('\n');
-
-                        SCL_drawBoard(
-                            game.board, putImagePixel, selected, squareSet, paramFlipBoard);
-
-                        switch(game.state) {
-                        case SCL_GAME_STATE_WHITE_WIN:
-                            msg = "white wins";
-                            break;
-
-                        case SCL_GAME_STATE_BLACK_WIN:
-                            msg = "black wins";
-                            break;
-
-                        case SCL_GAME_STATE_DRAW_STALEMATE:
-                            msg = "draw (stalemate)";
-                            break;
-
-                        case SCL_GAME_STATE_DRAW_REPETITION:
-                            msg = "draw (repeated position)";
-                            break;
-
-                        case SCL_GAME_STATE_DRAW_DEAD:
-                            msg = "draw (dead position)";
-                            break;
-
-                        case SCL_GAME_STATE_DRAW:
-                            msg = "draw";
-                            break;
-
-                        case SCL_GAME_STATE_DRAW_50:
-                            msg = "draw (50 move rule)";
-                            break;
-
-                        default:
-                            //msg = "game over";
-                            break;
-
-                            // // // // //
-                        }
-
-                        break;
-                    case InputKeyBack:
-                        processing = false;
-                        break;
-                    default:
-                        break;
-                    }
-                }
-            }
-        }
-
-        view_port_update(view_port);
-        furi_mutex_release(chess_state->mutex);
-    }
-
-    // Reset GPIO pins to default state
-    //furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-
-    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(chess_state->mutex);
-    free(chess_state);
-
-    return 0;
-}

+ 7 - 32
flipchess.c

@@ -27,28 +27,12 @@ static void text_input_callback(void* context) {
 
     // check that there is text in the input
     if(strlen(app->input_text) > 0) {
-        if(app->input_state == FlipChessTextInputPassphrase) {
-            if(app->passphrase == FlipChessPassphraseOn) {
-                strcpy(app->passphrase_text, app->input_text);
-            }
-            // clear input text
-            memzero(app->input_text, TEXT_BUFFER_SIZE);
-            // reset input state
-            app->input_state = FlipChessTextInputDefault;
-            handled = true;
-            view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdSettings);
-        } else if(app->input_state == FlipChessTextInputMnemonic) {
-            if(app->import_from_mnemonic == 1) {
-                strcpy(app->import_mnemonic_text, app->input_text);
+        if(app->input_state == FlipChessTextInputGame) {
+            if(app->import_game == 1) {
+                strcpy(app->import_game_text, app->input_text);
 
                 int status = FlipChessStatusSuccess;
-                // Check if the mnemonic is valid
-                if(mnemonic_check(app->import_mnemonic_text) == 0)
-                    status = FlipChessStatusMnemonicCheckError; // 13 = mnemonic check error
-                // Save the mnemonic to persistent storage
-                else if(!flipchess_save_file_secure(app->import_mnemonic_text))
-                    status = FlipChessStatusSaveError; // 12 = save error
-
+                
                 if(status == FlipChessStatusSuccess) {
                     //notification_message(app->notification, &sequence_blink_cyan_100);
                     flipchess_play_happy_bump(app);
@@ -56,11 +40,7 @@ static void text_input_callback(void* context) {
                     //notification_message(app->notification, &sequence_blink_red_100);
                     flipchess_play_long_bump(app);
                 }
-
-                memzero(app->import_mnemonic_text, TEXT_BUFFER_SIZE);
             }
-            // clear input text
-            memzero(app->input_text, TEXT_BUFFER_SIZE);
             // reset input state
             app->input_state = FlipChessTextInputDefault;
             handled = true;
@@ -69,8 +49,6 @@ static void text_input_callback(void* context) {
     }
 
     if(!handled) {
-        // clear input text
-        memzero(app->input_text, TEXT_BUFFER_SIZE);
         // reset input state
         app->input_state = FlipChessTextInputDefault;
         view_dispatcher_switch_to_view(app->view_dispatcher, FlipChessViewIdMenu);
@@ -100,14 +78,11 @@ FlipChess* flipchess_app_alloc() {
 
     // Settings
     app->haptic = FlipChessHapticOn;
-    app->led = FlipChessLedOn;
-    app->bip39_strength = FlipChessStrength256; // 256 bits (24 words)
-    app->passphrase = FlipChessPassphraseOff;
+    app->white_mode = FlipChessPlayerHuman;
+    app->black_mode = FlipChessPlayerAI1;
 
     // Main menu
-    app->bip44_coin = FlipChessCoinBTC0; // 0 (BTC)
-    app->overwrite_saved_seed = 0;
-    app->import_from_mnemonic = 0;
+    app->import_game = 0;
 
     // Text input
     app->input_state = FlipChessTextInputDefault;

+ 1 - 1
flipchess.h

@@ -15,7 +15,7 @@
 #include "views/flipchess_startscreen.h"
 #include "views/flipchess_scene_1.h"
 
-#define FLIPCHESS_VERSION "v1.0.0"
+#define FLIPCHESS_VERSION "v0.0.1"
 
 #define TEXT_BUFFER_SIZE 256
 

+ 0 - 1
scenes/flipchess_scene_menu.c

@@ -1,5 +1,4 @@
 #include "../flipchess.h"
-#include "../helpers/flipchess_file.h"
 
 enum SubmenuIndex {
     SubmenuIndexScene1New = 10,

+ 435 - 156
views/flipchess_scene_1.c

@@ -6,7 +6,7 @@
 #include <gui/elements.h>
 //#include <dolphin/dolphin.h>
 #include <string.h>
-#include "flipchess_icons.h"
+//#include "flipchess_icons.h"
 #include "../helpers/flipchess_haptic.h"
 
 #define SCL_960_CASTLING 0 // setting to 1 compiles a 960 version of smolchess
@@ -14,7 +14,7 @@
 #define SCL_EVALUATION_FUNCTION SCL_boardEvaluateStatic
 #define SCL_DEBUG_AI 0
 
-#include "chess/smallchesslib.h"
+#include "../chess/smallchesslib.h"
 
 #define MAX_TEXT_LEN 30 // 30 = max length of text
 #define MAX_TEXT_BUF (MAX_TEXT_LEN + 1) // max length of text + null terminator
@@ -25,30 +25,280 @@ struct FlipChessScene1 {
     void* context;
 };
 typedef struct {
-    uint8_t paramPlayerW = 0;
-    uint8_t paramPlayerB = 0;
+    uint8_t paramPlayerW;
+    uint8_t paramPlayerB;
 
     // uint8_t paramBoard = 1;
-    uint8_t paramAnalyze = 255; // depth of analysis
-    uint8_t paramMoves = 0;
+    uint8_t paramAnalyze; // depth of analysis
+    uint8_t paramMoves;
     //uint8_t paramXboard = 0;
-    uint8_t paramInfo = 1;
+    uint8_t paramInfo;
     //uint8_t paramDraw = 1;
-    uint8_t paramFlipBoard = 0;
+    uint8_t paramFlipBoard;
     //uint8_t paramHelp = 0;
-    uint8_t paramExit = 0;
-    uint16_t paramStep = 0;
-    char* paramFEN = NULL;
-    char* paramPGN = NULL;
+    uint8_t paramExit;
+    uint16_t paramStep;
+    char* paramFEN;
+    char* paramPGN;
     //uint16_t paramRandom = 0;
     //uint8_t paramBlind = 0;
 
-    int clockSeconds = -1;
+    int clockSeconds;
     SCL_Game game;
-    SCL_Board startState = SCL_BOARD_START_STATE;
-    int16_t random960PosNumber = -1;
+    SCL_Board startState;
+    int16_t random960PosNumber;
+
+    //uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH];
+    uint8_t selected;
+    char* msg;
+
+    SCL_SquareSet squareSet;
+    char moveString[16];
+    SCL_SquareSet moveHighlight;
+    uint8_t squareFrom;
+    uint8_t squareTo;
+
 } FlipChessScene1Model;
 
+uint8_t picture[SCL_BOARD_PICTURE_WIDTH * SCL_BOARD_PICTURE_WIDTH];
+
+void flipchess_putImagePixel(uint8_t pixel, uint16_t index) {
+    picture[index] = pixel;
+}
+
+uint8_t flipchess_stringsEqual(const char* s1, const char* s2, int max) {
+    for(int i = 0; i < max; ++i) {
+        if(*s1 != *s2) return 0;
+
+        if(*s1 == 0) return 1;
+
+        s1++;
+        s2++;
+    }
+
+    return 1;
+}
+
+int16_t flipchess_makeAIMove(
+    SCL_Board board,
+    uint8_t* s0,
+    uint8_t* s1,
+    char* prom,
+    FlipChessScene1Model* model) {
+    uint8_t level = SCL_boardWhitesTurn(board) ? model->paramPlayerW : model->paramPlayerB;
+    uint8_t depth = (level > 0) ? level : 1;
+    uint8_t extraDepth = 3;
+    uint8_t endgameDepth = 1;
+    uint8_t randomness =
+        model->game.ply < 2 ? 1 : 0; /* in first moves increase randomness for different 
+                             openings */
+    uint8_t rs0, rs1;
+
+    SCL_gameGetRepetiotionMove(&(model->game), &rs0, &rs1);
+
+    if(model->clockSeconds >= 0) // when using clock, choose AI params accordingly
+    {
+        if(model->clockSeconds <= 5) {
+            depth = 1;
+            extraDepth = 2;
+            endgameDepth = 0;
+        } else if(model->clockSeconds < 15) {
+            depth = 2;
+            extraDepth = 2;
+        } else if(model->clockSeconds < 100) {
+            depth = 2;
+        } else if(model->clockSeconds < 5 * 60) {
+            depth = 3;
+        } else {
+            depth = 3;
+            extraDepth = 4;
+        }
+    }
+
+    return SCL_getAIMove(
+        board,
+        depth,
+        extraDepth,
+        endgameDepth,
+        SCL_boardEvaluateStatic,
+        SCL_randomBetter,
+        randomness,
+        rs0,
+        rs1,
+        s0,
+        s1,
+        prom);
+}
+
+uint8_t flipchess_round(FlipChessScene1Model* model) {
+    // 0: none, 1: player, 2: AI, 3: undo
+    uint8_t moveType = 0;
+
+    //for(int i = 0; i < 40; ++i) putchar('\n');
+    //putchar('\n');
+
+    if(model->game.ply > 0) {
+        model->msg = (SCL_boardWhitesTurn(model->game.board) ? "black played" : "white  played");
+        // printf(" played ");
+
+        uint8_t s0, s1;
+        char p;
+
+        SCL_recordGetMove(model->game.record, model->game.ply - 1, &s0, &s1, &p);
+        SCL_moveToString(model->game.board, s0, s1, p, model->moveString);
+        model->msg = model->moveString;
+        //printf("%s\n", moveString);
+    }
+
+    model->msg = (SCL_boardWhitesTurn(model->game.board) ? "white to move" : "black to move");
+    //printf(" to move\n");
+
+    // if(paramInfo) {
+    //     //putchar('\n');
+
+    //     if(random960PosNumber >= 0)
+    //         printf("960 random position number: %d\n", random960PosNumber);
+
+    //     printf("ply number: %d\n", game.ply);
+
+    //     //SCL_boardToFEN(game.board, string);
+    //     //printf("FEN: %s\n", string);
+
+    //     int16_t eval = SCL_boardEvaluateStatic(game.board);
+    //     printf(
+    //         "board static evaluation: %lf (%d)\n",
+    //         ((double)eval) / ((double)SCL_VALUE_PAWN),
+    //         eval);
+    //     printf("board hash: %u\n", SCL_boardHash32(game.board));
+    //     printf("phase: ");
+
+    //     switch(SCL_boardEstimatePhase(game.board)) {
+    //     case SCL_PHASE_OPENING:
+    //         puts("opening");
+    //         break;
+    //     case SCL_PHASE_ENDGAME:
+    //         puts("endgame");
+    //         break;
+    //     default:
+    //         puts("midgame");
+    //         break;
+    //     }
+
+    //     printf(
+    //         "en passant: %d\n",
+    //         ((game.board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f) + 1) % 16);
+    //     printf(
+    //         "50 move rule count: %d\n", game.board[SCL_BOARD_MOVE_COUNT_BYTE]);
+
+    //     // if(paramFEN == NULL && paramPGN == NULL) {
+    //     //     //printf("PGN: ");
+    //     //     //SCL_printPGN(game.record, putCharacter, startState);
+    //     //     //putchar('\n');
+    //     // }
+    // }
+
+    if(model->game.state != SCL_GAME_STATE_PLAYING || model->paramExit) break;
+
+    //uint8_t squareFrom = 0;
+    //uint8_t squareTo = 0;
+    char movePromote = 'q';
+
+    if((SCL_boardWhitesTurn(model->game.board) && model->paramPlayerW == 0) ||
+       (!SCL_boardWhitesTurn(model->game.board) && model->paramPlayerB == 0)) {
+        // printf("\nmove: ");
+        // scanf("%s", string);
+        // char string[256];
+
+        // if(stringsEqual(string, "undo", 5))
+        //     moveType = 3;
+        // else if(stringsEqual(string, "quit", 5))
+        //     break;
+        // else {
+        //squareFrom = selected; //SCL_stringToSquare(string);
+        //squareTo = selected; //SCL_stringToSquare(string + 2);
+
+        //uint8_t r =
+        //    SCL_stringToMove(string, &squareFrom, &squareTo, &movePromote);
+
+        if(model->squareFrom != 255) {
+            if((model->game.board[model->squareFrom] != '.') &&
+               (SCL_pieceIsWhite(model->game.board[model->squareFrom]) ==
+                SCL_boardWhitesTurn(model->game.board))) {
+                SCL_boardGetMoves(model->game.board, model->squareFrom, model->squareSet);
+
+                if(SCL_squareSetContains(model->squareSet, model->squareTo)) {
+                    moveType = 1;
+                }
+            }
+        }
+        // }
+    } else {
+        flipchess_makeAIMove(model->game.board, &(model->squareFrom), &(model->squareTo), &movePromote, model);
+        moveType = 2;
+    }
+
+    if(moveType == 1 || moveType == 2) {
+        SCL_moveToString(
+            model->game.board, model->squareFrom, model->squareTo, movePromote, model->moveString);
+
+        SCL_gameMakeMove(&(model->game), model->squareFrom, model->squareTo, movePromote);
+
+        SCL_squareSetClear(model->moveHighlight);
+        SCL_squareSetAdd(model->moveHighlight, model->squareFrom);
+        SCL_squareSetAdd(model->moveHighlight, model->squareTo);
+    } else if(moveType == 3) {
+        if(model->paramPlayerW != 0 || model->paramPlayerB != 0) SCL_gameUndoMove(&(model->game));
+
+        SCL_gameUndoMove(&(model->game));
+        SCL_squareSetClear(model->moveHighlight);
+    }
+
+    //putchar('\n');
+
+    SCL_drawBoard(
+        model->game.board,
+        flipchess_putImagePixel,
+        model->selected,
+        model->squareSet,
+        model->paramFlipBoard);
+
+    switch(model->game.state) {
+    case SCL_GAME_STATE_WHITE_WIN:
+        model->msg = "white wins";
+        break;
+
+    case SCL_GAME_STATE_BLACK_WIN:
+        model->msg = "black wins";
+        break;
+
+    case SCL_GAME_STATE_DRAW_STALEMATE:
+        model->msg = "draw (stalemate)";
+        break;
+
+    case SCL_GAME_STATE_DRAW_REPETITION:
+        model->msg = "draw (repeated position)";
+        break;
+
+    case SCL_GAME_STATE_DRAW_DEAD:
+        model->msg = "draw (dead position)";
+        break;
+
+    case SCL_GAME_STATE_DRAW:
+        model->msg = "draw";
+        break;
+
+    case SCL_GAME_STATE_DRAW_50:
+        model->msg = "draw (50 move rule)";
+        break;
+
+    default:
+        //model->msg = "game over";
+        break;
+    }
+
+    return FlipChessStatusSuccess;
+}
+
 void flipchess_scene_1_set_callback(
     FlipChessScene1* instance,
     FlipChessScene1Callback callback,
@@ -64,17 +314,131 @@ void flipchess_scene_1_draw(Canvas* canvas, FlipChessScene1Model* model) {
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
 
-    
+    // Frame
+    canvas_draw_frame(canvas, 0, 0, 128, 64);
+
+    // Message
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 66, 10, model->msg);
+
+    // Board
+    for(uint16_t y = 0; y < SCL_BOARD_PICTURE_WIDTH; y++) {
+        for(uint16_t x = 0; x < SCL_BOARD_PICTURE_WIDTH; x++) {
+            if(picture[x + (y * SCL_BOARD_PICTURE_WIDTH)]) {
+                canvas_draw_dot(canvas, x, y);
+            }
+        }
+    }
 }
 
 static int flipchess_scene_1_model_init(
     FlipChessScene1Model* const model,
     const int white_mode,
     const int black_mode) {
-    
+
     model->paramPlayerW = white_mode;
     model->paramPlayerB = black_mode;
 
+    model->paramAnalyze = 255; // depth of analysis
+    model->paramMoves = 0;
+    model->paramInfo = 1;
+    model->paramFlipBoard = 0;
+    model->paramExit = 0;
+    model->paramStep = 0;
+    model->paramFEN = NULL;
+    model->paramPGN = NULL;
+    
+    model->clockSeconds = -1;
+    SCL_Board emptyStartState = SCL_BOARD_START_STATE;
+    model->startState = &emptyStartState;
+    model->random960PosNumber = -1;
+
+    model->selected = 255;
+    model->msg = "Flip Chess";
+
+    SCL_SquareSet emptySquareSet = SCL_SQUARE_SET_EMPTY;
+    model->squareSet = &emptySquareSet;
+    model->moveString[0] = '\0';
+    SCL_SquareSet emptyMoveHighlight = SCL_SQUARE_SET_EMPTY;
+    model->moveHighlight = &emptyMoveHighlight;
+    model->squareFrom = 255;
+    model->squareTo = 255;
+
+    furi_hal_random_init();
+    SCL_randomBetterSeed(furi_hal_random_get());
+
+#if SCL_960_CASTLING
+    if(model->random960PosNumber < 0) model->random960PosNumber = SCL_randomBetter();
+#endif
+
+    if(model->random960PosNumber >= 0) model->random960PosNumber %= 960;
+
+    if(model->paramFEN != NULL)
+        SCL_boardFromFEN(model->startState, model->paramFEN);
+    else if(model->paramPGN != NULL) {
+        SCL_Record record;
+        SCL_recordFromPGN(record, model->paramPGN);
+        SCL_boardInit(model->startState);
+        SCL_recordApply(record, model->startState, model->paramStep);
+    }
+#if SCL_960_CASTLING
+    else
+        SCL_boardInit960(model->startState, model->random960PosNumber);
+#endif
+
+    SCL_gameInit(&(model->game), model->startState);
+
+    if(model->paramAnalyze != 255) {
+        char p;
+        uint8_t move[] = {0, 0};
+
+        model->paramPlayerW = model->paramAnalyze;
+        model->paramPlayerB = model->paramAnalyze;
+
+        int16_t evaluation = flipchess_makeAIMove(model->game.board, &(move[0]), &(move[1]), &p, model);
+
+        if(model->paramAnalyze == 0) evaluation = SCL_boardEvaluateStatic(model->game.board);
+
+        char moveStr[5];
+        moveStr[4] = 0;
+
+        SCL_squareToString(move[0], moveStr);
+        SCL_squareToString(move[1], moveStr + 2);
+
+        //printf("%lf (%d)\n", ((double)evaluation) / ((double)SCL_VALUE_PAWN), evaluation);
+        //puts(moveStr);
+
+        return FlipChessStatusReturn;
+    }
+
+    if(model->paramMoves) {
+        char string[256];
+
+        for(int i = 0; i < 64; ++i)
+            if(model->game.board[i] != '.' &&
+               SCL_pieceIsWhite(model->game.board[i]) == SCL_boardWhitesTurn(model->game.board)) {
+                SCL_SquareSet possibleMoves = SCL_SQUARE_SET_EMPTY;
+
+                SCL_boardGetMoves(model->game.board, i, possibleMoves);
+
+                SCL_SQUARE_SET_ITERATE_BEGIN(possibleMoves)
+                SCL_moveToString(model->game.board, i, iteratedSquare, 'q', string);
+                //printf("%s ", string);
+                SCL_SQUARE_SET_ITERATE_END
+            }
+
+        return FlipChessStatusReturn;
+    }
+
+    model->moveString[0] = 0;
+
+    SCL_drawBoard(
+        model->game.board,
+        flipchess_putImagePixel,
+        model->selected,
+        model->squareSet,
+        model->paramFlipBoard);
+
     // 0 = success
     return FlipChessStatusSuccess;
 }
@@ -83,11 +447,6 @@ bool flipchess_scene_1_input(InputEvent* event, void* context) {
     furi_assert(context);
     FlipChessScene1* instance = context;
 
-    // Ignore input if busy
-    // if(s_busy) {
-    //     return false;
-    // }
-
     if(event->type == InputTypeRelease) {
         switch(event->key) {
         case InputKeyBack:
@@ -101,48 +460,74 @@ bool flipchess_scene_1_input(InputEvent* event, void* context) {
                 true);
             break;
         case InputKeyRight:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    model->selected = (model->selected + 1) % 64;
+                    SCL_drawBoard(
+                        model->game.board,
+                        flipchess_putImagePixel,
+                        model->selected,
+                        model->squareSet,
+                        model->paramFlipBoard);
+                },
+                true);
+            break;
         case InputKeyDown:
             with_view_model(
                 instance->view,
                 FlipChessScene1Model * model,
                 {
-                    //UNUSED(model);
-                    int page = (model->page + 1) % (PAGE_ADDR_END + 1);
-                    if(page == 0) {
-                        page = PAGE_INFO;
-                    }
-                    model->page = page;
+                    model->selected = (model->selected + 56) % 64;
+                    SCL_drawBoard(
+                        model->game.board,
+                        flipchess_putImagePixel,
+                        model->selected,
+                        model->squareSet,
+                        model->paramFlipBoard);
                 },
                 true);
             break;
         case InputKeyLeft:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    model->selected = (model->selected + 63) % 64;
+                    SCL_drawBoard(
+                        model->game.board,
+                        flipchess_putImagePixel,
+                        model->selected,
+                        model->squareSet,
+                        model->paramFlipBoard);
+                },
+                true);
+            break;
         case InputKeyUp:
             with_view_model(
                 instance->view,
                 FlipChessScene1Model * model,
                 {
-                    //UNUSED(model);
-                    int page = (model->page - 1) % (PAGE_ADDR_END + 1);
-                    if(page == 0) {
-                        page = PAGE_ADDR_END;
-                    }
-                    model->page = page;
+                    model->selected = (model->selected + 8) % 64;
+                    SCL_drawBoard(
+                        model->game.board,
+                        flipchess_putImagePixel,
+                        model->selected,
+                        model->squareSet,
+                        model->paramFlipBoard);
                 },
                 true);
             break;
-        // case InputKeyRight:
         case InputKeyOk:
-            // with_view_model(
-            //     instance->view,
-            //     FlipChessScene1Model * model,
-            //     {
-            //         if(model->page >= PAGE_ADDR_BEGIN && model->page <= PAGE_ADDR_END) {
-
-            //         }
-            //     },
-            //     true);
-            // break;
-        // case InputKeyLeft:
+            with_view_model(
+                instance->view,
+                FlipChessScene1Model * model,
+                {
+                    flipchess_round(model);
+                },
+                true);
+            break;
         case InputKeyMAX:
             break;
         }
@@ -155,38 +540,7 @@ void flipchess_scene_1_exit(void* context) {
     FlipChessScene1* instance = (FlipChessScene1*)context;
 
     with_view_model(
-        instance->view,
-        FlipChessScene1Model * model,
-        {
-            model->page = PAGE_LOADING;
-            model->strength = FlipChessStrength256;
-            model->coin = FlipChessCoinBTC0;
-            memzero(model->seed, 64);
-            // if mnemonic_only is true, then we don't need to free the data here
-            if(!model->mnemonic_only) {
-                memzero((void*)model->mnemonic, strlen(model->mnemonic));
-                free((void*)model->mnemonic);
-                memzero((void*)model->node, sizeof(HDNode));
-                free((void*)model->node);
-                memzero((void*)model->xprv_root, strlen(model->xprv_root));
-                memzero((void*)model->xprv_account, strlen(model->xprv_account));
-                memzero((void*)model->xpub_account, strlen(model->xpub_account));
-                memzero((void*)model->xprv_extended, strlen(model->xprv_extended));
-                memzero((void*)model->xpub_extended, strlen(model->xpub_extended));
-                free((void*)model->xprv_root);
-                free((void*)model->xprv_account);
-                free((void*)model->xpub_account);
-                free((void*)model->xprv_extended);
-                free((void*)model->xpub_extended);
-                for(int a = 0; a < NUM_ADDRS; a++) {
-                    memzero((void*)model->recv_addresses[a], MAX_ADDR_BUF);
-                    free((void*)model->recv_addresses[a]);
-                }
-            }
-        },
-        true);
-
-    flipchess_scene_1_clear_text();
+        instance->view, FlipChessScene1Model * model, { model->selected = 255; }, true);
 }
 
 void flipchess_scene_1_enter(void* context) {
@@ -195,71 +549,20 @@ void flipchess_scene_1_enter(void* context) {
 
     FlipChess* app = instance->context;
 
-    // BIP39 Strength setting
-    int strength = 256; // FlipChessStrength256 // 24 words (256 bit)
-    if(app->bip39_strength == FlipChessStrength128) {
-        strength = 128; // 12 words (128 bit)
-    } else if(app->bip39_strength == FlipChessStrength192) {
-        strength = 192; // 18 words (192 bit)
-    }
-
-    // BIP39 Passphrase setting
-    const char* passphrase_text = "";
-    if(app->passphrase == FlipChessPassphraseOn && strlen(app->passphrase_text) > 0) {
-        passphrase_text = app->passphrase_text;
-        s_warn_insecure = false;
-    } else {
-        s_warn_insecure = true;
-    }
-
-    // BIP44 Coin setting
-    const uint32_t coin = app->bip44_coin;
-    // coin_name, derivation_path
-    s_derivation_text = COIN_TEXT_ARRAY[coin][1];
-
-    // Overwrite the saved seed with a new one setting
-    bool overwrite = app->overwrite_saved_seed != 0;
-    if(overwrite) {
-        s_derivation_text = TEXT_NEW_WALLET;
-    }
-
     flipchess_play_happy_bump(app);
-    //notification_message(app->notification, &sequence_blink_cyan_100);
-    flipchess_led_set_rgb(app, 255, 0, 0);
 
     with_view_model(
         instance->view,
         FlipChessScene1Model * model,
         {
-            // s_busy = true;
-
             const int status =
-                flipchess_scene_1_model_init(model, strength, coin, overwrite, passphrase_text);
+                flipchess_scene_1_model_init(model, app->white_mode, app->white_mode);
 
-            // nonzero status, free the mnemonic
+            // nonzero status
             if(status != FlipChessStatusSuccess) {
-                memzero((void*)model->mnemonic, strlen(model->mnemonic));
-                free((void*)model->mnemonic);
-            }
-
-            // if error, set the error message
-            if(status == FlipChessStatusSaveError) {
-                model->mnemonic = "ERROR:,Save error";
-                model->page = PAGE_MNEMONIC;
-                flipchess_play_long_bump(app);
-            } else if(status == FlipChessStatusLoadError) {
-                model->mnemonic = "ERROR:,Load error";
-                model->page = PAGE_MNEMONIC;
-                flipchess_play_long_bump(app);
-            } else if(status == FlipChessStatusMnemonicCheckError) {
-                model->mnemonic = "ERROR:,Mnemonic check error";
-                model->page = PAGE_MNEMONIC;
-                flipchess_play_long_bump(app);
             }
 
-            // s_busy = false;
-
-            // if overwrite is set and mnemonic generated, return from scene immediately
+            // if return status, return from scene immediately
             if(status == FlipChessStatusReturn) {
                 instance->callback(FlipChessCustomEventScene1Back, instance->context);
             }
@@ -277,17 +580,6 @@ FlipChessScene1* flipchess_scene_1_alloc() {
     view_set_enter_callback(instance->view, flipchess_scene_1_enter);
     view_set_exit_callback(instance->view, flipchess_scene_1_exit);
 
-    // allocate the address node
-    s_addr_node = (HDNode*)malloc(sizeof(HDNode));
-
-    // allocate the display text
-    s_disp_text1 = (char*)malloc(MAX_TEXT_BUF);
-    s_disp_text2 = (char*)malloc(MAX_TEXT_BUF);
-    s_disp_text3 = (char*)malloc(MAX_TEXT_BUF);
-    s_disp_text4 = (char*)malloc(MAX_TEXT_BUF);
-    s_disp_text5 = (char*)malloc(MAX_TEXT_BUF);
-    s_disp_text6 = (char*)malloc(MAX_TEXT_BUF);
-
     return instance;
 }
 
@@ -297,19 +589,6 @@ void flipchess_scene_1_free(FlipChessScene1* instance) {
     with_view_model(
         instance->view, FlipChessScene1Model * model, { UNUSED(model); }, true);
 
-    // free the address node
-    memzero(s_addr_node, sizeof(HDNode));
-    free(s_addr_node);
-
-    // free the display text
-    flipchess_scene_1_clear_text();
-    free(s_disp_text1);
-    free(s_disp_text2);
-    free(s_disp_text3);
-    free(s_disp_text4);
-    free(s_disp_text5);
-    free(s_disp_text6);
-
     view_free(instance->view);
     free(instance);
 }

+ 2 - 2
views/flipchess_startscreen.c

@@ -3,7 +3,7 @@
 #include <furi_hal.h>
 #include <input/input.h>
 #include <gui/elements.h>
-#include "flipchess_icons.h"
+//#include "flipchess_icons.h"
 
 struct FlipChessStartscreen {
     View* view;
@@ -30,7 +30,7 @@ void flipchess_startscreen_draw(Canvas* canvas, FlipChessStartscreenModel* model
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
 
-    canvas_draw_icon(canvas, 1, 33, &I_Auth_62x31);
+    //canvas_draw_icon(canvas, 1, 33, &I_Auth_62x31);
 
     canvas_set_font(canvas, FontPrimary);
     canvas_draw_str(canvas, 18, 11, "Chess");