more apps

This commit is contained in:
RogueMaster
2022-09-16 20:16:27 -04:00
parent 039d683ea5
commit 61e922399d
29 changed files with 6824 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
App(
appid="APPS_Calculator",
name="Calculator",
apptype=FlipperAppType.EXTERNAL,
entry_point="calculator_app",
cdefines=["APP_CALCULATOR"],
requires=["gui"],
stack_size=1 * 1024,
order=45,
fap_icon="calcIcon.png",
fap_category="Misc",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,453 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stdbool.h> // Header-file for boolean data-type.
#include <string.h> // Header-file for string functions.
#include "tinyexpr.h" // Header-file for the TinyExpr library.
#include <stdio.h>
#include <stdlib.h>
const short MAX_TEXT_LENGTH = 20;
typedef struct {
short x;
short y;
} selectedPosition;
typedef struct {
selectedPosition position;
//string with the inputted calculator text
char text[20];
short textLength;
char log[20];
} Calculator;
char getKeyAtPosition(short x, short y) {
if(x == 0 && y == 0) {
return 'C';
}
if(x == 1 && y == 0) {
return '<';
}
if(x == 2 && y == 0) {
return '%';
}
if(x == 3 && y == 0) {
return '/';
}
if(x == 0 && y == 1) {
return '1';
}
if(x == 1 && y == 1) {
return '2';
}
if(x == 2 && y == 1) {
return '3';
}
if(x == 3 && y == 1) {
return '*';
}
if(x == 0 && y == 2) {
return '4';
}
if(x == 1 && y == 2) {
return '5';
}
if(x == 2 && y == 2) {
return '6';
}
if(x == 3 && y == 2) {
return '-';
}
if(x == 0 && y == 3) {
return '7';
}
if(x == 1 && y == 3) {
return '8';
}
if(x == 2 && y == 3) {
return '9';
}
if(x == 3 && y == 3) {
return '+';
}
if(x == 0 && y == 4) {
return '(';
}
if(x == 1 && y == 4) {
return '0';
}
if(x == 2 && y == 4) {
return '.';
}
if(x == 3 && y == 4) {
return '=';
}
return ' ';
}
short calculateStringWidth(const char* str, short lenght) {
/* widths:
1 = 2
2, 3, 4, 5, 6, 7, 8, 9, 0, X, -, +, . = = 5
%, / = 7
S = 5
(, ) = 3
*/
short width = 0;
for(short i = 0; i < lenght; i++) {
switch(str[i]) {
case '1':
width += 2;
break;
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
case '*':
case '-':
case '+':
case '.':
width += 5;
break;
case '%':
case '/':
width += 7;
break;
case 'S':
width += 5;
break;
case '(':
case ')':
width += 3;
break;
default:
break;
}
width += 1;
}
return width;
}
void generate_calculator_layout(Canvas* canvas) {
//draw dotted lines
for(int i = 0; i <= 64; i++) {
if(i % 2 == 0) {
canvas_draw_dot(canvas, i, 14);
canvas_draw_dot(canvas, i, 33);
}
if(i % 2 == 1) {
canvas_draw_dot(canvas, i, 15);
canvas_draw_dot(canvas, i, 34);
}
}
//draw horizontal lines
canvas_draw_box(canvas, 0, 41, 64, 2);
canvas_draw_box(canvas, 0, 57, 64, 2);
canvas_draw_box(canvas, 0, 73, 64, 2);
canvas_draw_box(canvas, 0, 89, 64, 2);
canvas_draw_box(canvas, 0, 105, 64, 2);
canvas_draw_box(canvas, 0, 121, 64, 2);
//draw vertical lines
canvas_draw_box(canvas, 0, 43, 1, 80);
canvas_draw_box(canvas, 15, 43, 2, 80);
canvas_draw_box(canvas, 31, 43, 2, 80);
canvas_draw_box(canvas, 47, 43, 2, 80);
canvas_draw_box(canvas, 63, 43, 1, 80);
//draw buttons
//row 1 (C, ;, %, ÷)
canvas_draw_str(canvas, 5, 54, "C");
canvas_draw_str(canvas, 19, 54, " <-");
canvas_draw_str(canvas, 35, 54, " %");
canvas_draw_str(canvas, 51, 54, " /");
//row 2 (1, 2, 3, X)
canvas_draw_str(canvas, 5, 70, " 1");
canvas_draw_str(canvas, 19, 70, " 2");
canvas_draw_str(canvas, 35, 70, " 3");
canvas_draw_str(canvas, 51, 70, " X");
//row 3 (4, 5, 6, -)
canvas_draw_str(canvas, 5, 86, " 4");
canvas_draw_str(canvas, 19, 86, " 5");
canvas_draw_str(canvas, 35, 86, " 6");
canvas_draw_str(canvas, 51, 86, " -");
//row 4 (7, 8, 9, +)
canvas_draw_str(canvas, 5, 102, " 7");
canvas_draw_str(canvas, 19, 102, " 8");
canvas_draw_str(canvas, 35, 102, " 9");
canvas_draw_str(canvas, 51, 102, " +");
//row 5 (+/-, 0, ., =)
canvas_draw_str(canvas, 3, 118, "( )");
canvas_draw_str(canvas, 19, 118, " 0");
canvas_draw_str(canvas, 35, 118, " .");
canvas_draw_str(canvas, 51, 118, " =");
};
void calculator_draw_callback(Canvas* canvas, void* ctx) {
const Calculator* calculator_state = acquire_mutex((ValueMutex*)ctx, 25);
UNUSED(ctx);
canvas_clear(canvas);
//show selected button
short startX = 1;
short startY = 43;
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(
canvas,
startX + (calculator_state->position.x) * 16,
(startY) + (calculator_state->position.y) * 16,
16,
16);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(
canvas,
startX + (calculator_state->position.x) * 16 + 2,
(startY) + (calculator_state->position.y) * 16 + 2,
10,
10);
canvas_set_color(canvas, ColorBlack);
generate_calculator_layout(canvas);
//draw text
short stringWidth = calculateStringWidth(calculator_state->text, calculator_state->textLength);
short startingPosition = 5;
if(stringWidth > 60) {
startingPosition += 60 - (stringWidth + 5);
}
canvas_set_color(canvas, ColorBlack);
canvas_draw_str(canvas, startingPosition, 28, calculator_state->text);
//canvas_draw_str(canvas, 10, 10, calculator_state->log);
//draw cursor
canvas_draw_box(canvas, stringWidth + 5, 29, 5, 1);
release_mutex((ValueMutex*)ctx, calculator_state);
}
void calculator_input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
void calculate(Calculator* calculator_state) {
double result;
result = te_interp(calculator_state->text, 0);
calculator_state->textLength = 0;
calculator_state->text[0] = '\0';
// sprintf(calculator_state->text, "%f", result);
//invert sign if negative
if(result < 0) {
calculator_state->text[calculator_state->textLength++] = '-';
result = -result;
}
//get numbers before and after decimal
int beforeDecimal = result;
int afterDecimal = (result - beforeDecimal) * 100;
char beforeDecimalString[10];
char afterDecimalString[10];
int i = 0;
//parse to a string
while(beforeDecimal > 0) {
beforeDecimalString[i++] = beforeDecimal % 10 + '0';
beforeDecimal /= 10;
}
// invert string
for(int j = 0; j < i / 2; j++) {
char temp = beforeDecimalString[j];
beforeDecimalString[j] = beforeDecimalString[i - j - 1];
beforeDecimalString[i - j - 1] = temp;
}
//add it to the answer
for(int j = 0; j < i; j++) {
calculator_state->text[calculator_state->textLength++] = beforeDecimalString[j];
}
i = 0;
if(afterDecimal > 0) {
while(afterDecimal > 0) {
afterDecimalString[i++] = afterDecimal % 10 + '0';
afterDecimal /= 10;
}
// invert string
for(int j = 0; j < i / 2; j++) {
char temp = afterDecimalString[j];
afterDecimalString[j] = afterDecimalString[i - j - 1];
afterDecimalString[i - j - 1] = temp;
}
//add decimal point
calculator_state->text[calculator_state->textLength++] = '.';
//add numbers after decimal
for(int j = 0; j < i; j++) {
calculator_state->text[calculator_state->textLength++] = afterDecimalString[j];
}
}
calculator_state->text[calculator_state->textLength] = '\0';
}
int32_t calculator_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
Calculator* calculator_state = malloc(sizeof(Calculator));
ValueMutex calculator_state_mutex;
if(!init_mutex(&calculator_state_mutex, calculator_state, sizeof(Calculator))) {
//FURI_LOG_E("calculator", "cannot create mutex\r\n");
free(calculator_state);
return -1;
}
// Configure view port
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, calculator_draw_callback, &calculator_state_mutex);
view_port_input_callback_set(view_port, calculator_input_callback, event_queue);
view_port_set_orientation(view_port, ViewPortOrientationVertical);
// Register view port in GUI
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
//NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
InputEvent event;
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
//break out of the loop if the back key is pressed
if(event.type == InputTypeShort && event.key == InputKeyBack) {
break;
}
if(event.type == InputTypeShort) {
switch(event.key) {
case InputKeyUp:
if(calculator_state->position.y > 0) {
calculator_state->position.y--;
}
break;
case InputKeyDown:
if(calculator_state->position.y < 4) {
calculator_state->position.y++;
}
break;
case InputKeyLeft:
if(calculator_state->position.x > 0) {
calculator_state->position.x--;
}
break;
case InputKeyRight:
if(calculator_state->position.x < 3) {
calculator_state->position.x++;
}
break;
case InputKeyOk: {
//add the selected button to the text
//char* text = calculator_state->text;
// short* textLength = &calculator_state->textLength;
char key =
getKeyAtPosition(calculator_state->position.x, calculator_state->position.y);
switch(key) {
case 'C':
while(calculator_state->textLength > 0) {
calculator_state->text[calculator_state->textLength--] = '\0';
}
calculator_state->text[0] = '\0';
calculator_state->log[2] = key;
break;
case '<':
calculator_state->log[2] = key;
if(calculator_state->textLength > 0) {
calculator_state->text[--calculator_state->textLength] = '\0';
} else {
calculator_state->text[0] = '\0';
}
break;
case '=':
calculator_state->log[2] = key;
calculate(calculator_state);
break;
case '%':
case '/':
case '*':
case '-':
case '+':
case '.':
case '(':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0':
if(calculator_state->textLength < MAX_TEXT_LENGTH) {
calculator_state->text[calculator_state->textLength++] = key;
calculator_state->text[calculator_state->textLength] = '\0';
}
//calculator_state->log[1] = calculator_state->text[*textLength];
break;
default:
break;
}
}
default:
break;
}
view_port_update(view_port);
}
if(event.type == InputTypeLong) {
switch(event.key) {
case InputKeyOk:
if (calculator_state->position.x == 0 && calculator_state->position.y == 4) {
if(calculator_state->textLength < MAX_TEXT_LENGTH) {
calculator_state->text[calculator_state->textLength++] = ')';
calculator_state->text[calculator_state->textLength] = '\0';
}
view_port_update(view_port);
}
break;
default:
break;
}
}
}
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
return 0;
}

View File

@@ -0,0 +1,661 @@
// SPDX-License-Identifier: Zlib
/*
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
*
* Copyright (c) 2015-2020 Lewis Van Winkle
*
* http://CodePlea.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgement in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
/* COMPILE TIME OPTIONS */
/* Exponentiation associativity:
For a^b^c = (a^b)^c and -a^b = (-a)^b do nothing.
For a^b^c = a^(b^c) and -a^b = -(a^b) uncomment the next line.*/
/* #define TE_POW_FROM_RIGHT */
/* Logarithms
For log = base 10 log do nothing
For log = natural log uncomment the next line. */
/* #define TE_NAT_LOG */
#include "tinyexpr.h"
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#ifndef NAN
#define NAN (0.0/0.0)
#endif
#ifndef INFINITY
#define INFINITY (1.0/0.0)
#endif
typedef double (*te_fun2)(double, double);
enum {
TOK_NULL = TE_CLOSURE7+1, TOK_ERROR, TOK_END, TOK_SEP,
TOK_OPEN, TOK_CLOSE, TOK_NUMBER, TOK_VARIABLE, TOK_INFIX
};
enum {TE_CONSTANT = 1};
typedef struct state {
const char *start;
const char *next;
int type;
union {double value; const double *bound; const void *function;};
void *context;
const te_variable *lookup;
int lookup_len;
} state;
#define TYPE_MASK(TYPE) ((TYPE)&0x0000001F)
#define IS_PURE(TYPE) (((TYPE) & TE_FLAG_PURE) != 0)
#define IS_FUNCTION(TYPE) (((TYPE) & TE_FUNCTION0) != 0)
#define IS_CLOSURE(TYPE) (((TYPE) & TE_CLOSURE0) != 0)
#define ARITY(TYPE) ( ((TYPE) & (TE_FUNCTION0 | TE_CLOSURE0)) ? ((TYPE) & 0x00000007) : 0 )
#define NEW_EXPR(type, ...) new_expr((type), (const te_expr*[]){__VA_ARGS__})
static te_expr *new_expr(const int type, const te_expr *parameters[]) {
const int arity = ARITY(type);
const int psize = sizeof(void*) * arity;
const int size = (sizeof(te_expr) - sizeof(void*)) + psize + (IS_CLOSURE(type) ? sizeof(void*) : 0);
te_expr *ret = malloc(size);
memset(ret, 0, size);
if (arity && parameters) {
memcpy(ret->parameters, parameters, psize);
}
ret->type = type;
ret->bound = 0;
return ret;
}
void te_free_parameters(te_expr *n) {
if (!n) return;
switch (TYPE_MASK(n->type)) {
case TE_FUNCTION7: case TE_CLOSURE7: te_free(n->parameters[6]); /* Falls through. */
case TE_FUNCTION6: case TE_CLOSURE6: te_free(n->parameters[5]); /* Falls through. */
case TE_FUNCTION5: case TE_CLOSURE5: te_free(n->parameters[4]); /* Falls through. */
case TE_FUNCTION4: case TE_CLOSURE4: te_free(n->parameters[3]); /* Falls through. */
case TE_FUNCTION3: case TE_CLOSURE3: te_free(n->parameters[2]); /* Falls through. */
case TE_FUNCTION2: case TE_CLOSURE2: te_free(n->parameters[1]); /* Falls through. */
case TE_FUNCTION1: case TE_CLOSURE1: te_free(n->parameters[0]);
}
}
void te_free(te_expr *n) {
if (!n) return;
te_free_parameters(n);
free(n);
}
static double pi(void) {return 3.14159265358979323846;}
static double e(void) {return 2.71828182845904523536;}
static double fac(double a) {/* simplest version of fac */
if (a < 0.0)
return NAN;
if (a > UINT_MAX)
return INFINITY;
unsigned int ua = (unsigned int)(a);
unsigned long int result = 1, i;
for (i = 1; i <= ua; i++) {
if (i > ULONG_MAX / result)
return INFINITY;
result *= i;
}
return (double)result;
}
static double ncr(double n, double r) {
if (n < 0.0 || r < 0.0 || n < r) return NAN;
if (n > UINT_MAX || r > UINT_MAX) return INFINITY;
unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i;
unsigned long int result = 1;
if (ur > un / 2) ur = un - ur;
for (i = 1; i <= ur; i++) {
if (result > ULONG_MAX / (un - ur + i))
return INFINITY;
result *= un - ur + i;
result /= i;
}
return result;
}
static double npr(double n, double r) {return ncr(n, r) * fac(r);}
#ifdef _MSC_VER
#pragma function (ceil)
#pragma function (floor)
#endif
static const te_variable functions[] = {
/* must be in alphabetical order */
{"abs", fabs, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"acos", acos, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"asin", asin, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"atan", atan, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"atan2", atan2, TE_FUNCTION2 | TE_FLAG_PURE, 0},
{"ceil", ceil, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"cos", cos, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0},
{"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0},
#ifdef TE_NAT_LOG
{"log", log, TE_FUNCTION1 | TE_FLAG_PURE, 0},
#else
{"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0},
#endif
{"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0},
{"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0},
{"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0},
{"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0},
{"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"sinh", sinh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"sqrt", sqrt, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"tan", tan, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{"tanh", tanh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
{0, 0, 0, 0}
};
static const te_variable *find_builtin(const char *name, int len) {
int imin = 0;
int imax = sizeof(functions) / sizeof(te_variable) - 2;
/*Binary search.*/
while (imax >= imin) {
const int i = (imin + ((imax-imin)/2));
int c = strncmp(name, functions[i].name, len);
if (!c) c = '\0' - functions[i].name[len];
if (c == 0) {
return functions + i;
} else if (c > 0) {
imin = i + 1;
} else {
imax = i - 1;
}
}
return 0;
}
static const te_variable *find_lookup(const state *s, const char *name, int len) {
int iters;
const te_variable *var;
if (!s->lookup) return 0;
for (var = s->lookup, iters = s->lookup_len; iters; ++var, --iters) {
if (strncmp(name, var->name, len) == 0 && var->name[len] == '\0') {
return var;
}
}
return 0;
}
static double add(double a, double b) {return a + b;}
static double sub(double a, double b) {return a - b;}
static double mul(double a, double b) {return a * b;}
static double divide(double a, double b) {return a / b;}
static double negate(double a) {return -a;}
static double comma(double a, double b) {(void)a; return b;}
void next_token(state *s) {
s->type = TOK_NULL;
do {
if (!*s->next){
s->type = TOK_END;
return;
}
/* Try reading a number. */
if ((s->next[0] >= '0' && s->next[0] <= '9') || s->next[0] == '.') {
s->value = strtod(s->next, (char**)&s->next);
s->type = TOK_NUMBER;
} else {
/* Look for a variable or builtin function call. */
if (isalpha(s->next[0])) {
const char *start;
start = s->next;
while (isalpha(s->next[0]) || isdigit(s->next[0]) || (s->next[0] == '_')) s->next++;
const te_variable *var = find_lookup(s, start, s->next - start);
if (!var) var = find_builtin(start, s->next - start);
if (!var) {
s->type = TOK_ERROR;
} else {
switch(TYPE_MASK(var->type))
{
case TE_VARIABLE:
s->type = TOK_VARIABLE;
s->bound = var->address;
break;
case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3: /* Falls through. */
case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7: /* Falls through. */
s->context = var->context; /* Falls through. */
case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3: /* Falls through. */
case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7: /* Falls through. */
s->type = var->type;
s->function = var->address;
break;
}
}
} else {
/* Look for an operator or special character. */
switch (s->next++[0]) {
case '+': s->type = TOK_INFIX; s->function = add; break;
case '-': s->type = TOK_INFIX; s->function = sub; break;
case '*': s->type = TOK_INFIX; s->function = mul; break;
case '/': s->type = TOK_INFIX; s->function = divide; break;
case '^': s->type = TOK_INFIX; s->function = pow; break;
case '%': s->type = TOK_INFIX; s->function = fmod; break;
case '(': s->type = TOK_OPEN; break;
case ')': s->type = TOK_CLOSE; break;
case ',': s->type = TOK_SEP; break;
case ' ': case '\t': case '\n': case '\r': break;
default: s->type = TOK_ERROR; break;
}
}
}
} while (s->type == TOK_NULL);
}
static te_expr *list(state *s);
static te_expr *expr(state *s);
static te_expr *power(state *s);
static te_expr *base(state *s) {
/* <base> = <constant> | <variable> | <function-0> {"(" ")"} | <function-1> <power> | <function-X> "(" <expr> {"," <expr>} ")" | "(" <list> ")" */
te_expr *ret;
int arity;
switch (TYPE_MASK(s->type)) {
case TOK_NUMBER:
ret = new_expr(TE_CONSTANT, 0);
ret->value = s->value;
next_token(s);
break;
case TOK_VARIABLE:
ret = new_expr(TE_VARIABLE, 0);
ret->bound = s->bound;
next_token(s);
break;
case TE_FUNCTION0:
case TE_CLOSURE0:
ret = new_expr(s->type, 0);
ret->function = s->function;
if (IS_CLOSURE(s->type)) ret->parameters[0] = s->context;
next_token(s);
if (s->type == TOK_OPEN) {
next_token(s);
if (s->type != TOK_CLOSE) {
s->type = TOK_ERROR;
} else {
next_token(s);
}
}
break;
case TE_FUNCTION1:
case TE_CLOSURE1:
ret = new_expr(s->type, 0);
ret->function = s->function;
if (IS_CLOSURE(s->type)) ret->parameters[1] = s->context;
next_token(s);
ret->parameters[0] = power(s);
break;
case TE_FUNCTION2: case TE_FUNCTION3: case TE_FUNCTION4:
case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7:
case TE_CLOSURE2: case TE_CLOSURE3: case TE_CLOSURE4:
case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
arity = ARITY(s->type);
ret = new_expr(s->type, 0);
ret->function = s->function;
if (IS_CLOSURE(s->type)) ret->parameters[arity] = s->context;
next_token(s);
if (s->type != TOK_OPEN) {
s->type = TOK_ERROR;
} else {
int i;
for(i = 0; i < arity; i++) {
next_token(s);
ret->parameters[i] = expr(s);
if(s->type != TOK_SEP) {
break;
}
}
if(s->type != TOK_CLOSE || i != arity - 1) {
s->type = TOK_ERROR;
} else {
next_token(s);
}
}
break;
case TOK_OPEN:
next_token(s);
ret = list(s);
if (s->type != TOK_CLOSE) {
s->type = TOK_ERROR;
} else {
next_token(s);
}
break;
default:
ret = new_expr(0, 0);
s->type = TOK_ERROR;
ret->value = NAN;
break;
}
return ret;
}
static te_expr *power(state *s) {
/* <power> = {("-" | "+")} <base> */
int sign = 1;
while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
if (s->function == sub) sign = -sign;
next_token(s);
}
te_expr *ret;
if (sign == 1) {
ret = base(s);
} else {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, base(s));
ret->function = negate;
}
return ret;
}
#ifdef TE_POW_FROM_RIGHT
static te_expr *factor(state *s) {
/* <factor> = <power> {"^" <power>} */
te_expr *ret = power(s);
int neg = 0;
if (ret->type == (TE_FUNCTION1 | TE_FLAG_PURE) && ret->function == negate) {
te_expr *se = ret->parameters[0];
free(ret);
ret = se;
neg = 1;
}
te_expr *insertion = 0;
while (s->type == TOK_INFIX && (s->function == pow)) {
te_fun2 t = s->function;
next_token(s);
if (insertion) {
/* Make exponentiation go right-to-left. */
te_expr *insert = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, insertion->parameters[1], power(s));
insert->function = t;
insertion->parameters[1] = insert;
insertion = insert;
} else {
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s));
ret->function = t;
insertion = ret;
}
}
if (neg) {
ret = NEW_EXPR(TE_FUNCTION1 | TE_FLAG_PURE, ret);
ret->function = negate;
}
return ret;
}
#else
static te_expr *factor(state *s) {
/* <factor> = <power> {"^" <power>} */
te_expr *ret = power(s);
while (s->type == TOK_INFIX && (s->function == pow)) {
te_fun2 t = s->function;
next_token(s);
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, power(s));
ret->function = t;
}
return ret;
}
#endif
static te_expr *term(state *s) {
/* <term> = <factor> {("*" | "/" | "%") <factor>} */
te_expr *ret = factor(s);
while (s->type == TOK_INFIX && (s->function == mul || s->function == divide || s->function == fmod)) {
te_fun2 t = s->function;
next_token(s);
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, factor(s));
ret->function = t;
}
return ret;
}
static te_expr *expr(state *s) {
/* <expr> = <term> {("+" | "-") <term>} */
te_expr *ret = term(s);
while (s->type == TOK_INFIX && (s->function == add || s->function == sub)) {
te_fun2 t = s->function;
next_token(s);
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, term(s));
ret->function = t;
}
return ret;
}
static te_expr *list(state *s) {
/* <list> = <expr> {"," <expr>} */
te_expr *ret = expr(s);
while (s->type == TOK_SEP) {
next_token(s);
ret = NEW_EXPR(TE_FUNCTION2 | TE_FLAG_PURE, ret, expr(s));
ret->function = comma;
}
return ret;
}
#define TE_FUN(...) ((double(*)(__VA_ARGS__))n->function)
#define M(e) te_eval(n->parameters[e])
double te_eval(const te_expr *n) {
if (!n) return NAN;
switch(TYPE_MASK(n->type)) {
case TE_CONSTANT: return n->value;
case TE_VARIABLE: return *n->bound;
case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3:
case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7:
switch(ARITY(n->type)) {
case 0: return TE_FUN(void)();
case 1: return TE_FUN(double)(M(0));
case 2: return TE_FUN(double, double)(M(0), M(1));
case 3: return TE_FUN(double, double, double)(M(0), M(1), M(2));
case 4: return TE_FUN(double, double, double, double)(M(0), M(1), M(2), M(3));
case 5: return TE_FUN(double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4));
case 6: return TE_FUN(double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5));
case 7: return TE_FUN(double, double, double, double, double, double, double)(M(0), M(1), M(2), M(3), M(4), M(5), M(6));
default: return NAN;
}
case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3:
case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
switch(ARITY(n->type)) {
case 0: return TE_FUN(void*)(n->parameters[0]);
case 1: return TE_FUN(void*, double)(n->parameters[1], M(0));
case 2: return TE_FUN(void*, double, double)(n->parameters[2], M(0), M(1));
case 3: return TE_FUN(void*, double, double, double)(n->parameters[3], M(0), M(1), M(2));
case 4: return TE_FUN(void*, double, double, double, double)(n->parameters[4], M(0), M(1), M(2), M(3));
case 5: return TE_FUN(void*, double, double, double, double, double)(n->parameters[5], M(0), M(1), M(2), M(3), M(4));
case 6: return TE_FUN(void*, double, double, double, double, double, double)(n->parameters[6], M(0), M(1), M(2), M(3), M(4), M(5));
case 7: return TE_FUN(void*, double, double, double, double, double, double, double)(n->parameters[7], M(0), M(1), M(2), M(3), M(4), M(5), M(6));
default: return NAN;
}
default: return NAN;
}
}
#undef TE_FUN
#undef M
static void optimize(te_expr *n) {
/* Evaluates as much as possible. */
if (n->type == TE_CONSTANT) return;
if (n->type == TE_VARIABLE) return;
/* Only optimize out functions flagged as pure. */
if (IS_PURE(n->type)) {
const int arity = ARITY(n->type);
int known = 1;
int i;
for (i = 0; i < arity; ++i) {
optimize(n->parameters[i]);
if (((te_expr*)(n->parameters[i]))->type != TE_CONSTANT) {
known = 0;
}
}
if (known) {
const double value = te_eval(n);
te_free_parameters(n);
n->type = TE_CONSTANT;
n->value = value;
}
}
}
te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) {
state s;
s.start = s.next = expression;
s.lookup = variables;
s.lookup_len = var_count;
next_token(&s);
te_expr *root = list(&s);
if (s.type != TOK_END) {
te_free(root);
if (error) {
*error = (s.next - s.start);
if (*error == 0) *error = 1;
}
return 0;
} else {
optimize(root);
if (error) *error = 0;
return root;
}
}
double te_interp(const char *expression, int *error) {
te_expr *n = te_compile(expression, 0, 0, error);
double ret;
if (n) {
ret = te_eval(n);
te_free(n);
} else {
ret = NAN;
}
return ret;
}
static void pn (const te_expr *n, int depth) {
int i, arity;
printf("%*s", depth, "");
switch(TYPE_MASK(n->type)) {
case TE_CONSTANT: printf("%f\n", n->value); break;
case TE_VARIABLE: printf("bound %p\n", n->bound); break;
case TE_FUNCTION0: case TE_FUNCTION1: case TE_FUNCTION2: case TE_FUNCTION3:
case TE_FUNCTION4: case TE_FUNCTION5: case TE_FUNCTION6: case TE_FUNCTION7:
case TE_CLOSURE0: case TE_CLOSURE1: case TE_CLOSURE2: case TE_CLOSURE3:
case TE_CLOSURE4: case TE_CLOSURE5: case TE_CLOSURE6: case TE_CLOSURE7:
arity = ARITY(n->type);
printf("f%d", arity);
for(i = 0; i < arity; i++) {
printf(" %p", n->parameters[i]);
}
printf("\n");
for(i = 0; i < arity; i++) {
pn(n->parameters[i], depth + 1);
}
break;
}
}
void te_print(const te_expr *n) {
pn(n, 0);
}

View File

@@ -0,0 +1,87 @@
// SPDX-License-Identifier: Zlib
/*
* TINYEXPR - Tiny recursive descent parser and evaluation engine in C
*
* Copyright (c) 2015-2020 Lewis Van Winkle
*
* http://CodePlea.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgement in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef TINYEXPR_H
#define TINYEXPR_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct te_expr {
int type;
union {double value; const double *bound; const void *function;};
void *parameters[1];
} te_expr;
enum {
TE_VARIABLE = 0,
TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3,
TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7,
TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3,
TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7,
TE_FLAG_PURE = 32
};
typedef struct te_variable {
const char *name;
const void *address;
int type;
void *context;
} te_variable;
/* Parses the input expression, evaluates it, and frees it. */
/* Returns NaN on error. */
double te_interp(const char *expression, int *error);
/* Parses the input expression and binds variables. */
/* Returns NULL on error. */
te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error);
/* Evaluates the expression. */
double te_eval(const te_expr *n);
/* Prints debugging information on the syntax tree. */
void te_print(const te_expr *n);
/* Frees the expression. */
/* This is safe to call on NULL pointers. */
void te_free(te_expr *n);
#ifdef __cplusplus
}
#endif
#endif /*TINYEXPR_H*/

View File

@@ -0,0 +1,12 @@
App(
appid="INTAPP_Chess",
name="Chess",
apptype=FlipperAppType.EXTERNAL,
entry_point="chess_app",
cdefines=["APP_CHESS"],
requires=["storage","gui"],
stack_size= 4 * 1024,
order=50,
fap_icon="chessIcon.png",
fap_category="Games",
)

View File

@@ -0,0 +1,682 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <gui/icon_i.h>
#include "fast_chess.h"
static bool flag = true;
static bool should_exit = false;
// static bool ai_should_make_move = false;
static bool thinking = false;
static bool should_update_screen = true;
static uint32_t anim = 0;
static char white_move_str[8] = "", black_move_str[8] = ""; // last moves
static NotificationApp* notification;
const uint8_t _I_Chess_0[] = {
0x01, 0x00, 0x2a, 0x01, 0x80, 0x7f, 0xc0, 0x2c, 0x0f, 0xf0, 0x7f, 0x83, 0xfc, 0x1f, 0xe0, 0xff,
0x07, 0xf8, 0x3f, 0xc1, 0xde, 0x0f, 0xf0, 0x7f, 0x83, 0xfc, 0x1f, 0xe0, 0xff, 0x07, 0xf8, 0x3f,
0xc2, 0x1e, 0x0f, 0xf0, 0x7f, 0x80, 0x07, 0x00, 0x0f, 0xf1, 0x7f, 0xc0, 0x41, 0xf8, 0x1e, 0x18,
0x0f, 0xf2, 0xfe, 0x1f, 0xb8, 0x0e, 0x0a, 0x07, 0x80, 0x81, 0x83, 0xea, 0x05, 0x62, 0x01, 0x8c,
0x30, 0x7d, 0x50, 0x48, 0x80, 0x0c, 0x66, 0x00, 0xfa, 0x84, 0x42, 0x00, 0x63, 0x40, 0x07, 0xd4,
0x42, 0x08, 0x54, 0x24, 0xf5, 0xc0, 0x8f, 0xd9, 0x70, 0x3f, 0xa2, 0x7a, 0xb0, 0x69, 0xee, 0x57,
0xa0, 0x3e, 0xa8, 0x0b, 0xfc, 0xc0, 0x4f, 0xc1, 0xe5, 0x3c, 0x07, 0xcd, 0x03, 0x81, 0x41, 0x06,
0x0f, 0x0c, 0x1f, 0x32, 0x08, 0x04, 0x98, 0x46, 0x31, 0xf3, 0x10, 0x83, 0xdd, 0x58, 0x33, 0x88,
0x07, 0x02, 0xfe, 0x82, 0x10, 0x7c, 0x88, 0x47, 0x81, 0x73, 0x07, 0xcf, 0x42, 0x07, 0xc0, 0x80,
0x78, 0x3e, 0x2b, 0x21, 0x07, 0xbf, 0xc2, 0x1f, 0x00, 0x81, 0x83, 0xef, 0xc1, 0x1f, 0x7e, 0x0f,
0x83, 0xff, 0x04, 0x47, 0xc7, 0x82, 0x7e, 0x42, 0x10, 0x7d, 0x96, 0xc4, 0x06, 0x71, 0x60, 0x7c,
0x3e, 0x48, 0x1e, 0x52, 0xa5, 0xfc, 0xff, 0xd1, 0x62, 0x8f, 0x1a, 0xa8, 0x3e, 0x7f, 0xd0, 0x31,
0x19, 0x07, 0xeb, 0xf9, 0x07, 0x80, 0x58, 0x2c, 0x01, 0xfa, 0xfc, 0x20, 0x06, 0x21, 0x80, 0x0f,
0xd7, 0xc1, 0x00, 0x20, 0x01, 0xaa, 0xa3, 0xe1, 0x00, 0x60, 0x01, 0x95, 0x03, 0xe7, 0x80, 0x24,
0x38, 0xa0, 0x3e, 0x7f, 0x1c, 0x73, 0x00, 0x9d, 0x8c, 0x02, 0xdc, 0x0d, 0x7c, 0x04, 0xa0, 0x2e,
0xe1, 0x07, 0xc5, 0xc2, 0xeb, 0x00, 0x9f, 0x40, 0x12, 0x42, 0x0f, 0x8d, 0xc4, 0x69, 0x22, 0x3c,
0x11, 0x7d, 0x5e, 0x20, 0xa0, 0x41, 0x30, 0x98, 0x3d, 0xf1, 0x1f, 0xff, 0xfa, 0x00, 0xcb, 0xd1,
0x10, 0x2e, 0x18, 0x3e, 0xa4, 0x00, 0xfe, 0xa0, 0x03, 0xfb, 0x00, 0x6b, 0x20, 0x7d, 0xc0, 0x21,
0xc0, 0xfe, 0xf8, 0x3f, 0xc4, 0x1f, 0x90, 0x0f, 0xe0, 0x40, 0xc1, 0xf6, 0x05, 0x30,
};
const uint8_t* const _I_Chess[] = {_I_Chess_0};
const uint8_t _I_Chess_Selection1_0[] = {
0x00,
0x55,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0xAA,
};
const uint8_t* const _I_Chess_Selection1[] = {_I_Chess_Selection1_0};
const uint8_t _I_Chess_Selection2_0[] = {
0x00,
0xAA,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x55,
};
const uint8_t* const _I_Chess_Selection2[] = {_I_Chess_Selection2_0};
const uint8_t _I_Chess_bb_0[] = {
0x00,
0x0C,
0x1A,
0x3D,
0x1E,
0x0C,
0x3F,
};
const uint8_t* const _I_Chess_bb[] = {_I_Chess_bb_0};
const uint8_t _I_Chess_bw_0[] = {
0x00,
0x0C,
0x16,
0x23,
0x16,
0x0C,
0x3F,
};
const uint8_t* const _I_Chess_bw[] = {_I_Chess_bw_0};
const uint8_t _I_Chess_kb_0[] = {
0x00,
0x0C,
0x2D,
0x21,
0x12,
0x0C,
0x3F,
};
const uint8_t* const _I_Chess_kb[] = {_I_Chess_kb_0};
const uint8_t _I_Chess_kw_0[] = {
0x00,
0x0C,
0x21,
0x21,
0x12,
0x0C,
0x3F,
};
const uint8_t* const _I_Chess_kw[] = {_I_Chess_kw_0};
const uint8_t _I_Chess_nb_0[] = {
0x00,
0x06,
0x0F,
0x1F,
0x2E,
0x0E,
0x3F,
};
const uint8_t* const _I_Chess_nb[] = {_I_Chess_nb_0};
const uint8_t _I_Chess_nw_0[] = {
0x00,
0x06,
0x09,
0x11,
0x2A,
0x0A,
0x3F,
};
const uint8_t* const _I_Chess_nw[] = {_I_Chess_nw_0};
const uint8_t _I_Chess_old_0[] = {
0x01, 0x00, 0x35, 0x01, 0x80, 0x7f, 0xc0, 0x2c, 0x0f, 0xf0, 0x7f, 0x83, 0xfc, 0x1f, 0xe0, 0xff,
0x07, 0xf8, 0x3f, 0xc0, 0x03, 0x80, 0x0f, 0x70, 0x3f, 0xe0, 0x10, 0x11, 0x77, 0x40, 0x7f, 0x97,
0xf0, 0xfd, 0xc0, 0x70, 0x50, 0x3c, 0x04, 0x0c, 0x1f, 0x50, 0x2b, 0x10, 0x0c, 0x61, 0x80, 0xfa,
0x82, 0x44, 0x00, 0x63, 0x30, 0x07, 0xd4, 0x22, 0x10, 0x03, 0x1a, 0x00, 0x3e, 0xa2, 0x10, 0x42,
0xa1, 0x90, 0x2d, 0x01, 0x97, 0x03, 0xfa, 0x03, 0xe7, 0x01, 0x83, 0x5f, 0xf8, 0x3f, 0xa8, 0x00,
0xfc, 0xc0, 0x4f, 0xc1, 0xe5, 0x3c, 0x07, 0xcd, 0x03, 0x81, 0x41, 0x06, 0x0f, 0x0c, 0x1f, 0x32,
0x08, 0x04, 0x98, 0x46, 0x31, 0xf8, 0x08, 0xfa, 0x15, 0x83, 0x38, 0x80, 0x70, 0x2f, 0xf0, 0x20,
0x7d, 0x08, 0x47, 0x81, 0x73, 0x07, 0xcf, 0x42, 0x07, 0xc0, 0x80, 0x78, 0x3e, 0x30, 0x40, 0x7c,
0x7c, 0x21, 0xf0, 0x08, 0x18, 0x3e, 0xfc, 0x11, 0xf7, 0xe0, 0xf8, 0x3f, 0xe0, 0xfa, 0x9f, 0x90,
0x84, 0x1f, 0x65, 0xb1, 0x01, 0x9c, 0x59, 0x9d, 0x21, 0x34, 0x95, 0x2f, 0xe7, 0xfe, 0xee, 0x14,
0x78, 0xd5, 0x41, 0xf3, 0xfe, 0x81, 0x88, 0xc8, 0x3f, 0x5f, 0xc8, 0x3c, 0x02, 0xc1, 0x60, 0x0f,
0xd7, 0xe1, 0x00, 0x31, 0x0c, 0x00, 0x7e, 0xbe, 0x08, 0x01, 0x00, 0x08, 0x7e, 0x90, 0x04, 0x00,
0x10, 0xfd, 0x70, 0x00, 0x65, 0x00, 0x8a, 0x0f, 0xeb, 0x8e, 0x60, 0x13, 0xa9, 0x07, 0xa3, 0x5f,
0x01, 0x28, 0x0c, 0x08, 0x1f, 0x37, 0x0b, 0xac, 0x02, 0x7d, 0x00, 0x49, 0x08, 0x3e, 0x37, 0x11,
0xa4, 0x88, 0xf0, 0x45, 0xf5, 0x78, 0x82, 0x81, 0x44, 0xc2, 0x40, 0xf8, 0xc4, 0x7f, 0xff, 0xe8,
0x03, 0x07, 0xc4, 0x40, 0x18, 0x40, 0xfb, 0x90, 0x03, 0xfa, 0x80, 0x0f, 0x50, 0x84, 0x60, 0x0d,
0x62, 0x20, 0xd8, 0x70, 0x3f, 0xbe, 0x0f, 0xf1, 0x07, 0xe4, 0x03, 0xf8, 0x10, 0x40, 0x7d, 0x01,
0x0c, 0x1f, 0x77, 0xf2, 0x91, 0x83, 0xeb, 0x7e, 0x1f, 0xdf, 0xa5, 0x7c, 0x1d, 0x90, 0x0d, 0x54,
0xa8, 0x1f, 0xb5, 0x68, 0xa8, 0x7f, 0xa1, 0x40, 0xfd, 0xaa, 0xc1, 0x41, 0xfb, 0xa1, 0x81, 0x03,
0xf5, 0xa0, 0x80, 0xff, 0x07, 0xce, 0x01, 0x9c, 0x80,
};
const uint8_t* const _I_Chess_old[] = {_I_Chess_old_0};
const uint8_t _I_Chess_pb_0[] = {
0x00,
0x00,
0x0C,
0x1E,
0x1E,
0x0C,
0x1E,
};
const uint8_t* const _I_Chess_pb[] = {_I_Chess_pb_0};
const uint8_t _I_Chess_pw_0[] = {
0x00,
0x00,
0x0C,
0x12,
0x12,
0x0C,
0x1E,
};
const uint8_t* const _I_Chess_pw[] = {_I_Chess_pw_0};
const uint8_t _I_Chess_qb_0[] = {
0x00,
0x2D,
0x2D,
0x2D,
0x1E,
0x1E,
0x3F,
};
const uint8_t* const _I_Chess_qb[] = {_I_Chess_qb_0};
const uint8_t _I_Chess_qw_0[] = {
0x00,
0x2D,
0x2D,
0x2D,
0x1E,
0x1E,
0x3F,
};
const uint8_t* const _I_Chess_qw[] = {_I_Chess_qw_0};
const uint8_t _I_Chess_rb_0[] = {
0x00,
0x2D,
0x2D,
0x1E,
0x1E,
0x1E,
0x3F,
};
const uint8_t* const _I_Chess_rb[] = {_I_Chess_rb_0};
const uint8_t _I_Chess_rw_0[] = {
0x00,
0x2D,
0x2D,
0x12,
0x12,
0x12,
0x3F,
};
const uint8_t* const _I_Chess_rw[] = {_I_Chess_rw_0};
const Icon I_Chess_Selection2 =
{.width = 8, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_Selection2};
const Icon I_Chess_old =
{.width = 128, .height = 64, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_old};
const Icon I_Chess_Selection1 =
{.width = 8, .height = 8, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_Selection1};
const Icon I_Chess =
{.width = 128, .height = 64, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess};
const Icon I_Chess_kb =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_kb};
const Icon I_Chess_rw =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_rw};
const Icon I_Chess_rb =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_rb};
const Icon I_Chess_kw =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_kw};
const Icon I_Chess_qb =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_qb};
const Icon I_Chess_qw =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_qw};
const Icon I_Chess_pw =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_pw};
const Icon I_Chess_pb =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_pb};
const Icon I_Chess_nb =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_nb};
const Icon I_Chess_bw =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_bw};
const Icon I_Chess_bb =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_bb};
const Icon I_Chess_nw =
{.width = 6, .height = 6, .frame_count = 1, .frame_rate = 0, .frames = _I_Chess_nw};
typedef struct {
uint8_t col, row;
} _Position;
typedef struct {
enum {
None = 0,
Pawn,
King,
Queen,
Bishop,
Knight,
Rook,
} type;
enum { White, Black } side;
} Piece;
static const _Position PosNone = {.col = 255, .row = 255};
// static Piece board[8][8]; // col, row
static _Position sel, move_from = PosNone, move_to = PosNone;
Game* game;
// uint8_t sel_col = 0, sel_row = 0;
// static enum {
// SelectingFrom,
// SelectingTo
// } state = SelectingFrom;
// static void reset_board() {
// memset(board, 0, sizeof(board));
// board[0][0].type = Rook;
// board[1][0].type = Knight;
// board[2][0].type = Bishop;
// board[3][0].type = Queen;
// board[4][0].type = King;
// board[5][0].type = Bishop;
// board[6][0].type = Knight;
// board[7][0].type = Rook;
// board[0][1].type = Pawn;
// board[1][1].type = Pawn;
// board[2][1].type = Pawn;
// board[3][1].type = Pawn;
// board[4][1].type = Pawn;
// board[5][1].type = Pawn;
// board[6][1].type = Pawn;
// board[7][1].type = Pawn;
// board[0][7].type = Rook; board[0][7].side = Black;
// board[1][7].type = Knight; board[1][7].side = Black;
// board[2][7].type = Bishop; board[2][7].side = Black;
// board[3][7].type = Queen; board[3][7].side = Black;
// board[4][7].type = King; board[4][7].side = Black;
// board[5][7].type = Bishop; board[5][7].side = Black;
// board[6][7].type = Knight; board[6][7].side = Black;
// board[7][7].type = Rook; board[7][7].side = Black;
// board[0][6].type = Pawn; board[0][6].side = Black;
// board[1][6].type = Pawn; board[1][6].side = Black;
// board[2][6].type = Pawn; board[2][6].side = Black;
// board[3][6].type = Pawn; board[3][6].side = Black;
// board[4][6].type = Pawn; board[4][6].side = Black;
// board[5][6].type = Pawn; board[5][6].side = Black;
// board[6][6].type = Pawn; board[6][6].side = Black;
// board[7][6].type = Pawn; board[7][6].side = Black;
// }
// static const Icon* get_icon(const Piece* piece) {
// if (piece->side == White) {
// switch (piece->type) {
// case Pawn: return &I_Chess_pw;
// case King: return &I_Chess_kw;
// case Queen: return &I_Chess_qw;
// case Bishop: return &I_Chess_bw;
// case Knight: return &I_Chess_nw;
// case Rook: return &I_Chess_rw;
// default: return NULL;
// }
// } else {
// switch (piece->type) {
// case Pawn: return &I_Chess_pb;
// case King: return &I_Chess_kb;
// case Queen: return &I_Chess_qb;
// case Bishop: return &I_Chess_bb;
// case Knight: return &I_Chess_nb;
// case Rook: return &I_Chess_rb;
// default: return NULL;
// }
// }
// }
static void notify_click() {
// static const NotificationSequence sequence = {
// &message_click,
// &message_delay_1,
// &message_sound_off,
// NULL,
// };
// notification_message_block(notification, &sequence);
notification_message(notification, &sequence_single_vibro);
}
static const Icon* _get_icon(uint8_t file, uint8_t rank) {
char piece = getPieceChar((FILES_BB[file] & RANKS_BB[7 - rank]), &(game->position.board));
switch(piece) {
case 'P':
return &I_Chess_pw;
case 'K':
return &I_Chess_kw;
case 'Q':
return &I_Chess_qw;
case 'B':
return &I_Chess_bw;
case 'N':
return &I_Chess_nw;
case 'R':
return &I_Chess_rw;
case 'p':
return &I_Chess_pb;
case 'k':
return &I_Chess_kb;
case 'q':
return &I_Chess_qb;
case 'b':
return &I_Chess_bb;
case 'n':
return &I_Chess_nb;
case 'r':
return &I_Chess_rb;
default:
return NULL;
}
}
static int get_position(uint8_t file, uint8_t rank) {
return 8 * rank + file;
}
static int get_rank(int position) {
return (int)(position / 8);
}
static int get_file(int position) {
return position % 8;
}
static void make_move(uint8_t file1, uint8_t rank1, uint8_t file2, uint8_t rank2) {
int from = get_position(file1, rank1);
int to = get_position(file2, rank2);
Move move = generateMove(from, to);
if(!isLegalMove(&game->position, move)) {
return;
}
makeMove(game, move);
move2str(white_move_str, game, game->moveListLen - 1);
notify_click();
black_move_str[0] = 0;
anim = furi_hal_get_tick();
thinking = true;
}
static int32_t make_ai_move(void* context) {
// thinking = true;
int depth = 1;
Move move;
Node node =
iterativeDeepeningAlphaBeta(&(game->position), (char)depth, INT32_MIN, INT32_MAX, FALSE);
move = node.move;
makeMove(game, move);
move2str(black_move_str, game, game->moveListLen - 1);
notify_click();
thinking = false;
anim = furi_hal_get_tick();
return 0;
}
static FuriThread* worker_thread = NULL;
static int32_t ai_thread(void* context) {
while(true) {
if(should_exit) break;
if(thinking) make_ai_move(context);
furi_delay_ms(100);
}
return 0;
}
static void run_ai_thread() {
if(worker_thread == NULL) {
worker_thread = furi_thread_alloc();
}
furi_thread_set_name(worker_thread, "ChessEngine");
furi_thread_set_stack_size(worker_thread, 7000);
// furi_thread_set_context(thread, bad_usb);
furi_thread_set_callback(worker_thread, ai_thread);
furi_thread_start(worker_thread);
// furi_thread_join(worker_thread);
// furi_thread_free(worker_thread);
}
static void chess_draw_callback(Canvas* canvas, void* ctx) {
should_update_screen = false;
canvas_clear(canvas);
// canvas_set_color(canvas, flag ? ColorBlack : ColorWhite);
canvas_draw_icon(canvas, 0, 0, &I_Chess);
if(!thinking) {
canvas_set_color(canvas, (sel.col + sel.row) % 2 != 0 ? ColorBlack : ColorWhite);
canvas_draw_icon(
canvas,
sel.col * 8,
(7 - sel.row) * 8,
flag ? &I_Chess_Selection1 : &I_Chess_Selection2);
if(move_from.col != 255) {
canvas_set_color(
canvas, (move_from.col + move_from.row) % 2 != 0 ? ColorBlack : ColorWhite);
canvas_draw_icon(
canvas,
move_from.col * 8,
(7 - move_from.row) * 8,
flag ? &I_Chess_Selection1 : &I_Chess_Selection2);
}
}
// print moves
if(game->moveListLen > 0) {
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
// int num = game->moveListLen;
// char white_str[8], black_str[8] = "...";
// if (num == 0) {
// } else if (num % 2 == 0) {
// // white move
// move2str(white_str, game, game->moveListLen - 2);
// move2str(black_str, game, game->moveListLen - 1);
// } else {
// move2str(white_str, game, game->moveListLen - 1);
// }
char str[28];
sprintf(str, "%d. %s %s", (game->moveListLen + 1) / 2, white_move_str, black_move_str);
canvas_draw_str(canvas, 75, 12, str);
}
Move last_move = getLastMove(game);
for(uint8_t row = 0; row < 8; row++) {
for(uint8_t col = 0; col < 8; col++) {
bool white_field = (row + col) % 2 != 0;
// if (!white_field) {
// canvas_draw_box(canvas, col * 8, row * 8, 8, 8);
// }
const Icon* icon = _get_icon(col, row);
if(icon != NULL) {
int x = col * 8;
int y = row * 8;
int dt = furi_hal_get_tick() - anim;
if(anim && dt >= 300) {
anim = 0;
}
if(anim && last_move && get_file(getTo(last_move)) == col &&
get_rank(getTo(last_move)) == (7 - row)) {
// moving piece
uint8_t from_x = get_file(getFrom(last_move)) * 8;
uint8_t from_y = (7 - get_rank(getFrom(last_move))) * 8;
x = from_x + (x - from_x) * dt / 300;
y = from_y + (y - from_y) * dt / 300;
}
canvas_set_color(canvas, white_field ? ColorWhite : ColorBlack);
canvas_draw_icon(canvas, x + 1, y + 1, icon);
}
// if (board[col][7 - row].type != None) {
// canvas_set_color(canvas, white_field ? ColorWhite : ColorBlack);
// canvas_draw_icon(canvas, col * 8 + 1, row * 8 + 1, get_icon(&board[col][7 - row]));
// }
}
}
// for (uint8_t i = 0; i < 4; i++) {
// canvas_draw_dot(canvas, sel_col * 8, sel_row * 8);
// canvas_draw_dot(canvas, sel_col * 8 + 2, sel_row * 8);
// canvas_draw_dot(canvas, sel_col * 8, sel_row * 8);
// canvas_draw_dot(canvas, sel_col * 8, sel_row * 8);
// }
// canvas_draw_disc(canvas, GUI_DISPLAY_WIDTH / 2 - 40, GUI_DISPLAY_HEIGHT / 2, 15);
// canvas_set_color(canvas, flag ? ColorBlack : ColorWhite);
// canvas_draw_disc(canvas, GUI_DISPLAY_WIDTH / 2, GUI_DISPLAY_HEIGHT / 2, 15);
}
static void chess_input_callback(InputEvent* event, void* ctx) {
if(event->type == InputTypeShort) {
if(event->key == InputKeyLeft) {
sel.col = (sel.col == 0) ? 0 : sel.col - 1;
} else if(event->key == InputKeyRight) {
sel.col++;
} else if(event->key == InputKeyDown) {
sel.row = (sel.row == 0) ? 0 : sel.row - 1;
} else if(event->key == InputKeyUp) {
sel.row++;
} else if(event->key == InputKeyOk) {
if(move_from.col == 255) {
move_from = sel;
} else if(move_to.col == 255) {
move_to = sel;
make_move(move_from.col, move_from.row, move_to.col, move_to.row);
// thinking = true;
// ai_should_make_move = true;
// make_ai_move_threaded();
// Piece piece = board[move_from.col][move_from.row];
// board[move_from.col][move_from.row].type = None;
// board[move_to.col][move_to.row] = piece;
move_from = PosNone;
move_to = PosNone;
}
} else if(event->key == InputKeyBack) {
should_exit = true;
}
sel.col = CLAMP(sel.col, 7, 0);
sel.row = CLAMP(sel.row, 7, 0);
}
}
static void setup_engine() {
// int depth = 1; // DEFAULT_AI_DEPTH;
getInitialGame(game);
// Move move;
// Node node = iterativeDeepeningAlphaBeta(&(game.position), (char) depth, INT32_MIN, INT32_MAX, FALSE);
// move = node.move;
// node = iterativeDeepeningAlphaBeta(&(game.position), (char) 2, INT32_MIN, INT32_MAX, FALSE);
// node = iterativeDeepeningAlphaBeta(&(game.position), (char) 3, INT32_MIN, INT32_MAX, FALSE);
// printf("%d\n", move);
}
// void test_engine() {
// FuriThread* thread; //
// thread = furi_thread_alloc();
// furi_thread_set_name(thread, "ChessEngine");
// furi_thread_set_stack_size(thread, 20000);
// // furi_thread_set_context(thread, bad_usb);
// furi_thread_set_callback(thread, setup_engine);
// furi_thread_start(thread);
// furi_thread_join(thread);
// furi_thread_free(thread);
// }
int32_t chess_app(void* p) {
UNUSED(p);
// Configure view port
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, chess_draw_callback, NULL);
view_port_input_callback_set(view_port, chess_input_callback, NULL);
// Register view port in GUI
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
notification = furi_record_open(RECORD_NOTIFICATION);
should_exit = false;
game = malloc(sizeof(Game));
setup_engine();
run_ai_thread();
// test_engine();
while(!should_exit) {
furi_delay_ms(100);
if(!thinking) {
flag = !flag;
should_update_screen = true;
}
if(anim) {
should_update_screen = true;
}
if(should_update_screen) {
view_port_update(view_port);
}
// flag = true;
// delay(40);
// flag = false;
// view_port_update(view_port);
// delay(80);
}
furi_thread_join(worker_thread);
furi_thread_free(worker_thread);
worker_thread = NULL;
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_record_close(RECORD_GUI);
free(game);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,388 @@
/*
* fast-chess.h
*
* Created on: 20 de set de 2016
* Author: fvj
*/
#ifndef FAST_CHESS_H_
#define FAST_CHESS_H_
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdint.h>
#define ENGINE_VERSION "v1.8.1"
#define ENGINE_NAME "github.com/fredericojordan/fast-chess " ENGINE_VERSION
#define HUMAN_NAME "Unknown Human Player"
#define NUM_SQUARES (64)
#define ENDGAME_PIECE_COUNT (7)
#define COLOR_MASK (1 << 3)
#define WHITE (0)
#define BLACK (1 << 3)
#define PIECE_MASK (0x7)
#define EMPTY (0)
#define PAWN (1)
#define KNIGHT (2)
#define BISHOP (3)
#define ROOK (4)
#define QUEEN (5)
#define KING (6)
#define ALL_SQUARES (0xFFFFFFFFFFFFFFFF)
#define FILE_A (0x0101010101010101)
#define FILE_B (0x0202020202020202)
#define FILE_C (0x0404040404040404)
#define FILE_D (0x0808080808080808)
#define FILE_E (0x1010101010101010)
#define FILE_F (0x2020202020202020)
#define FILE_G (0x4040404040404040)
#define FILE_H (0x8080808080808080)
#define RANK_1 (0x00000000000000FF)
#define RANK_2 (0x000000000000FF00)
#define RANK_3 (0x0000000000FF0000)
#define RANK_4 (0x00000000FF000000)
#define RANK_5 (0x000000FF00000000)
#define RANK_6 (0x0000FF0000000000)
#define RANK_7 (0x00FF000000000000)
#define RANK_8 (0xFF00000000000000)
#define DIAG_A1H8 (0x8040201008040201)
#define ANTI_DIAG_H1A8 (0x0102040810204080)
#define LIGHT_SQUARES (0x55AA55AA55AA55AA)
#define DARK_SQUARES (0xAA55AA55AA55AA55)
#define CASTLE_KINGSIDE_WHITE (1 << 0)
#define CASTLE_QUEENSIDE_WHITE (1 << 1)
#define CASTLE_KINGSIDE_BLACK (1 << 2)
#define CASTLE_QUEENSIDE_BLACK (1 << 3)
#define BOOL char
#ifndef FALSE
#define TRUE (1)
#define FALSE (0)
#endif
typedef uint_fast64_t Bitboard;
typedef int Move;
#define MAX_BOOK_ENTRY_LEN (300)
#define MAX_PLYS_PER_GAME (1024)
#define MAX_FEN_LEN (100)
// #define MAX_BRANCHING_FACTOR (218) /* R6R/3Q4/1Q4Q1/4Q3/2Q4Q/Q4Q2/pp1Q4/kBNN1KB1 w - - 0 1 3Q4/1Q4Q1/4Q3/2Q4R/Q4Q2/3Q4/1Q4Rp/1K1BBNNk w - - 0 1 */
#define MAX_BRANCHING_FACTOR (100) // okalachev
#define MAX_ATTACKING_PIECES (12)
#define DEFAULT_AI_DEPTH (3)
typedef struct {
Bitboard whiteKing;
Bitboard whiteQueens;
Bitboard whiteRooks;
Bitboard whiteKnights;
Bitboard whiteBishops;
Bitboard whitePawns;
Bitboard blackKing;
Bitboard blackQueens;
Bitboard blackRooks;
Bitboard blackKnights;
Bitboard blackBishops;
Bitboard blackPawns;
} Board;
typedef struct {
Board board;
char toMove;
char epSquare;
char castlingRights;
unsigned int halfmoveClock;
unsigned int fullmoveNumber;
} Position;
typedef struct {
Position position;
unsigned int moveListLen;
Move moveList[MAX_PLYS_PER_GAME];
char positionHistory[MAX_PLYS_PER_GAME][MAX_FEN_LEN];
} Game;
typedef struct {
Move move;
int score;
} Node;
typedef struct {
int depth;
Position pos;
int* alpha;
int* beta;
BOOL verbose;
} ThreadInfo;
extern char FILES[8];
extern char RANKS[8];
extern Bitboard FILES_BB[8];
extern Bitboard RANKS_BB[8];
extern char INITIAL_FEN[];
extern Board INITIAL_BOARD;
extern int PIECE_VALUES[];
#define DOUBLED_PAWN_PENALTY (10)
#define ISOLATED_PAWN_PENALTY (20)
#define BACKWARDS_PAWN_PENALTY (8)
#define PASSED_PAWN_BONUS (20)
#define ROOK_SEMI_OPEN_FILE_BONUS (10)
#define ROOK_OPEN_FILE_BONUS (15)
#define ROOK_ON_SEVENTH_BONUS (20)
extern int PAWN_BONUS[];
extern int KNIGHT_BONUS[];
extern int BISHOP_BONUS[];
extern int KING_BONUS[];
extern int KING_ENDGAME_BONUS[];
extern int FLIP_VERTICAL[];
void getInitialGame(Game* game);
void getFenGame(Game* game, char fen[]);
void insertPiece(Board* board, Bitboard position, char pieceCode);
int loadFen(Position* position, char fen[]);
int toFen(char* fen, Position* position);
int toMinFen(char* fen, Position* position);
void getMovelistGame(Game* game, char moves[]);
// ========= UTILITY =========
BOOL fromInitial(Game* game);
Bitboard index2bb(int index);
int str2index(char* str);
Bitboard str2bb(char* str);
BOOL isSet(Bitboard bb, int index);
Bitboard lsb(Bitboard bb);
Bitboard msb(Bitboard bb);
int bb2index(Bitboard bb);
char* movelist2str(Game* game);
Move getLastMove(Game* game);
BOOL startsWith(const char* str, const char* pre);
int countBookOccurrences(Game* game);
Move getBookMove(Game* game);
char getFile(int position);
char getRank(int position);
Move generateMove(int leavingSquare, int arrivingSquare);
int getFrom(Move move);
int getTo(Move move);
int char2piece(char pieceCode);
int bb2piece(Bitboard position, Board* board);
char bb2char(Bitboard position, Board* board);
char* bb2str(Bitboard position, Board* board);
void printBitboard(Bitboard bitboard);
char getPieceChar(Bitboard position, Board* board);
void printBoard(Board* board);
void printGame(Game* game);
Bitboard not(Bitboard bb);
char opponent(char color);
int countBits(Bitboard bb);
void sortNodes(Node* sortedNodes, Node* nodes, int len, char color);
void printMove(Move move);
void printFullMove(Move move, Board* board);
void printLegalMoves(Position* position);
void printNode(Node node);
void getTimestamp(char* timestamp);
void dumpContent(Game* game);
void dumpPGN(Game* game, char color, BOOL hasAI);
void move2str(char* str, Game* game, int moveNumber);
BOOL isAmbiguous(Position* posBefore, Move move);
unsigned long hashPosition(Position* position);
void writeToHashFile(Position* position, int evaluation, int depth);
// ====== BOARD FILTERS ======
Bitboard getColoredPieces(Board* board, char color);
Bitboard getEmptySquares(Board* board);
Bitboard getOccupiedSquares(Board* board);
Bitboard getTwinPieces(Bitboard position, Board* board);
Bitboard fileFilter(Bitboard positions);
Bitboard rankFilter(Bitboard positions);
// ======= DIRECTIONS ========
Bitboard east(Bitboard bb);
Bitboard west(Bitboard bb);
Bitboard north(Bitboard bb);
Bitboard south(Bitboard bb);
Bitboard NE(Bitboard bb);
Bitboard NW(Bitboard bb);
Bitboard SE(Bitboard bb);
Bitboard SW(Bitboard bb);
Bitboard WNW(Bitboard moving_piece);
Bitboard ENE(Bitboard moving_piece);
Bitboard NNW(Bitboard moving_piece);
Bitboard NNE(Bitboard moving_piece);
Bitboard ESE(Bitboard moving_piece);
Bitboard WSW(Bitboard moving_piece);
Bitboard SSE(Bitboard moving_piece);
Bitboard SSW(Bitboard moving_piece);
// ========== PAWN ===========
Bitboard getPawns(Board* board);
Bitboard pawnSimplePushes(Bitboard moving_piece, Board* board, char color);
Bitboard pawnDoublePushes(Bitboard moving_piece, Board* board, char color);
Bitboard pawnPushes(Bitboard moving_piece, Board* board, char color);
Bitboard pawnEastAttacks(Bitboard moving_piece, Board* board, char color);
Bitboard pawnWestAttacks(Bitboard moving_piece, Board* board, char color);
Bitboard pawnAttacks(Bitboard moving_piece, Board* board, char color);
Bitboard pawnSimpleCaptures(Bitboard moving_piece, Board* board, char color);
Bitboard pawnEpCaptures(Bitboard moving_piece, Position* position, char color);
Bitboard pawnCaptures(Bitboard moving_piece, Position* position, char color);
Bitboard pawnMoves(Bitboard moving_piece, Position* position, char color);
BOOL isDoublePush(int leaving, int arriving);
char getEpSquare(int leaving);
BOOL isDoubledPawn(Bitboard position, Board* board, char color);
BOOL isIsolatedPawn(Bitboard position, Board* board, char color);
BOOL isBackwardsPawn(Bitboard position, Board* board, char color);
BOOL isPassedPawn(Bitboard position, Board* board, char color);
BOOL isOpenFile(Bitboard position, Board* board);
BOOL isSemiOpenFile(Bitboard position, Board* board);
// ========== KNIGHT =========
Bitboard getKnights(Board* board);
Bitboard knightAttacks(Bitboard moving_piece);
Bitboard knightMoves(Bitboard moving_piece, Board* board, char color);
// ========== KING ===========
Bitboard getKing(Board* board, char color);
Bitboard kingAttacks(Bitboard moving_piece);
Bitboard kingMoves(Bitboard moving_piece, Board* board, char color);
BOOL canCastleKingside(Position* position, char color);
BOOL canCastleQueenside(Position* position, char color);
char removeCastlingRights(char original_rights, char removed_rights);
// ========== BISHOP =========
Bitboard getBishops(Board* board);
Bitboard NE_ray(Bitboard bb);
Bitboard SE_ray(Bitboard bb);
Bitboard NW_ray(Bitboard bb);
Bitboard SW_ray(Bitboard bb);
Bitboard NE_attack(Bitboard single_piece, Board* board, char color);
Bitboard NW_attack(Bitboard single_piece, Board* board, char color);
Bitboard SE_attack(Bitboard single_piece, Board* board, char color);
Bitboard SW_attack(Bitboard single_piece, Board* board, char color);
Bitboard diagonalAttacks(Bitboard single_piece, Board* board, char color);
Bitboard antiDiagonalAttacks(Bitboard single_piece, Board* board, char color);
Bitboard bishopAttacks(Bitboard moving_pieces, Board* board, char color);
Bitboard bishopMoves(Bitboard moving_piece, Board* board, char color);
// ========== ROOK ===========
Bitboard getRooks(Board* board);
Bitboard northRay(Bitboard moving_pieces);
Bitboard southRay(Bitboard moving_pieces);
Bitboard eastRay(Bitboard moving_pieces);
Bitboard westRay(Bitboard moving_pieces);
Bitboard northAttack(Bitboard single_piece, Board* board, char color);
Bitboard southAttack(Bitboard single_piece, Board* board, char color);
Bitboard fileAttacks(Bitboard single_piece, Board* board, char color);
Bitboard eastAttack(Bitboard single_piece, Board* board, char color);
Bitboard westAttack(Bitboard single_piece, Board* board, char color);
Bitboard rankAttacks(Bitboard single_piece, Board* board, char color);
Bitboard rookAttacks(Bitboard moving_piece, Board* board, char color);
Bitboard rookMoves(Bitboard moving_piece, Board* board, char color);
// ========== QUEEN ==========
Bitboard getQueens(Board* board);
Bitboard queenAttacks(Bitboard moving_piece, Board* board, char color);
Bitboard queenMoves(Bitboard moving_piece, Board* board, char color);
// ======== MAKE MOVE ========
void clearPositions(Board* board, Bitboard positions);
void movePiece(Board* board, Move move);
void updatePosition(Position* newPosition, Position* position, Move move);
void makeMove(Game* game, Move move);
void unmakeMove(Game* game);
// ======== MOVE GEN =========
Bitboard getMoves(Bitboard movingPiece, Position* position, char color);
int pseudoLegalMoves(Move* moves, Position* position, char color);
Bitboard getAttacks(Bitboard movingPiece, Board* board, char color);
int countAttacks(Bitboard target, Board* board, char color);
BOOL isAttacked(Bitboard target, Board* board, char color);
BOOL isCheck(Board* board, char color);
BOOL isLegalMove(Position* position, Move move);
int legalMoves(Move* legalMoves, Position* position, char color);
int legalMovesCount(Position* position, char color);
int staticOrderLegalMoves(Move* orderedLegalMoves, Position* position, char color);
int legalCaptures(Move* legalCaptures, Position* position, char color);
// ====== GAME CONTROL =======
BOOL isCheckmate(Position* position);
BOOL isStalemate(Position* position);
BOOL hasInsufficientMaterial(Board* board);
BOOL isEndgame(Board* board);
BOOL isOver75MovesRule(Position* position);
BOOL hasGameEnded(Position* position);
void printOutcome(Position* position);
// ========== EVAL ===========
int winScore(char color);
int materialSum(Board* board, char color);
int materialBalance(Board* board);
int positionalBonus(Board* board, char color);
int positionalBalance(Board* board);
int endNodeEvaluation(Position* position);
int staticEvaluation(Position* position);
int getCaptureSequence(Move* captures, Position* position, int targetSquare);
int staticExchangeEvaluation(Position* position, int targetSquare);
int quiescenceEvaluation(Position* position);
// ========= SEARCH ==========
Node staticSearch(Position* position);
Node quiescenceSearch(Position* position);
Node alphaBeta(Position* position, char depth, int alpha, int beta);
int alphaBetaNodes(Node* nodes, Position* position, char depth);
Node iterativeDeepeningAlphaBeta(Position* position, char depth, int alpha, int beta, BOOL verbose);
Node pIDAB(Position* position, char depth, int* p_alpha, int* p_beta);
Node pIDABhashed(Position* position, char depth, int* p_alpha, int* p_beta);
Move getRandomMove(Position* position);
Move getAIMove(Game* game, int depth);
Move parseMove(char* move);
Move getPlayerMove();
Move suggestMove(char fen[], int depth);
// Parallel processing currently only implemented for Windows
#ifdef _WIN32
DWORD WINAPI evaluatePositionThreadFunction(LPVOID lpParam);
DWORD WINAPI evaluatePositionThreadFunctionHashed(LPVOID lpParam);
Node idabThreaded(Position* position, int depth, BOOL verbose);
Node idabThreadedBestFirst(Position* position, int depth, BOOL verbose);
Node idabThreadedBestFirstHashed(Position* position, int depth, BOOL verbose);
#endif
// ===== PLAY LOOP (TEXT) ====
void playTextWhite(int depth);
void playTextBlack(int depth);
void playTextAs(char color, int depth);
void playTextRandomColor(int depth);
// ===========================
#endif /* FAST_CHESS_H_ */

View File

@@ -0,0 +1,12 @@
App(
appid="INTAPP_Chip8",
name="CHIP8 Emulator",
apptype=FlipperAppType.EXTERNAL,
entry_point="chip8_app",
cdefines=["APP_CHIP8"],
requires=["gui"],
stack_size=4 * 1024,
order=60,
fap_icon="chip8Icon.png",
fap_category="Misc",
)

View File

@@ -0,0 +1,203 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <lib/toolbox/args.h>
#include <storage/storage.h>
#include "chip8.h"
#include "emulator_core/flipper_chip.h"
#define TAG "Chip8Emulator"
#define WORKER_TAG TAG "Worker"
#define FILE_BUFFER_LEN 16
typedef enum {
WorkerEvtToggle = (1 << 1),
WorkerEvtEnd = (1 << 2),
} WorkerEvtFlags;
struct Chip8Emulator {
Chip8State st;
string_t file_path;
FuriThread* thread;
};
static int32_t chip8_worker(void* context) {
Chip8Emulator* chip8 = context;
FURI_LOG_I(WORKER_TAG, "Start furi record open");
Storage* furi_storage_record = furi_record_open(RECORD_STORAGE);
FURI_LOG_I(WORKER_TAG, "furi record opened");
FURI_LOG_I(WORKER_TAG, "Start storage file alloc");
File* rom_file = storage_file_alloc(furi_storage_record);
FURI_LOG_I(
WORKER_TAG, "Start storage file open, path = %s", string_get_cstr(chip8->file_path));
uint8_t* rom_data = malloc(4096);
FURI_LOG_I(WORKER_TAG, "4096 array gotten");
while(1) {
if(chip8->st.worker_state == WorkerStateBackPressed) {
FURI_LOG_I(WORKER_TAG, "WorkerStateBackPressed");
break;
}
if(chip8->st.worker_state == WorkerStateLoadingRom) {
bool is_file_opened = storage_file_open(
rom_file, string_get_cstr(chip8->file_path), FSAM_READ, FSOM_OPEN_EXISTING);
if(!is_file_opened) {
FURI_LOG_I(WORKER_TAG, "Cannot open storage");
storage_file_close(rom_file);
storage_file_free(rom_file);
chip8->st.worker_state = WorkerStateRomLoadError;
return 0;
}
FURI_LOG_I(WORKER_TAG, "File was opened, try read this");
int rom_len = read_rom_data(rom_file, rom_data);
FURI_LOG_I(WORKER_TAG, "Rom data finished reading");
FURI_LOG_I(WORKER_TAG, "Load chip8 core data");
t_chip8_load_game(chip8->st.t_chip8_state, rom_data, rom_len);
FURI_LOG_I(WORKER_TAG, "chip8 core data loaded");
FURI_LOG_I(WORKER_TAG, "Wipe screen start");
for(int i = 0; i < CHIP8_SCREEN_H; i++) {
FURI_LOG_I(WORKER_TAG, "Wipe screen line %d", i);
for(int j = 0; j < CHIP8_SCREEN_W; j++) {
chip8->st.t_chip8_state->screen[i][j] = 0;
}
furi_delay_ms(15);
}
FURI_LOG_I(WORKER_TAG, "Wipe screen end");
chip8->st.worker_state = WorkerStateRomLoaded;
}
if(chip8->st.worker_state == WorkerStateRomLoaded) {
if(chip8->st.t_chip8_state->go_render) {
continue;
}
t_chip8_execute_next_opcode(chip8->st.t_chip8_state);
FURI_LOG_I(
"chip8_executing",
"current: 0x%X next: 0x%X",
chip8->st.t_chip8_state->current_opcode,
chip8->st.t_chip8_state->next_opcode);
furi_delay_ms(2);
//t_chip8_tick(chip8->st.t_chip8_state);
}
}
FURI_LOG_I("CHIP8", "Prepare to ending app");
storage_file_close(rom_file);
storage_file_free(rom_file);
t_chip8_free_memory(chip8->st.t_chip8_state, free);
FURI_LOG_I("CHIP8", "End ending");
return 0;
}
Chip8Emulator* chip8_make_emulator(string_t file_path) {
furi_assert(file_path);
FURI_LOG_I("CHIP8", "make emulator, file_path=", string_get_cstr(file_path));
Chip8Emulator* chip8 = malloc(sizeof(Chip8Emulator));
string_init(chip8->file_path);
string_set(chip8->file_path, file_path);
chip8->st.worker_state = WorkerStateLoadingRom;
chip8->st.t_chip8_state = t_chip8_init(malloc);
// FURI_LOG_I(WORKER_TAG, "Start wipe screen");
// furi_delay_ms(1500);
// for (int i = 0; i < CHIP8_SCREEN_H; i++)
// {
// FURI_LOG_I(WORKER_TAG, "Start wipe line %d", i);
// for (int j = 0; j < CHIP8_SCREEN_W; j++)
// {
// chip8->st.t_chip8_state->screen[i][j] = 0;
// }
// }
// FURI_LOG_I(WORKER_TAG, "End wipe screen");
chip8->thread = furi_thread_alloc();
furi_thread_set_name(chip8->thread, "Chip8Worker");
furi_thread_set_stack_size(chip8->thread, 4096);
furi_thread_set_context(chip8->thread, chip8);
furi_thread_set_callback(chip8->thread, chip8_worker);
furi_thread_start(chip8->thread);
return chip8;
}
void chip8_close_emulator(Chip8Emulator* chip8) {
FURI_LOG_I("chip_8_close_emulator", "start");
furi_assert(chip8);
furi_thread_flags_set(furi_thread_get_id(chip8->thread), WorkerEvtEnd);
furi_thread_join(chip8->thread);
furi_thread_free(chip8->thread);
string_clear(chip8->file_path);
free(chip8);
FURI_LOG_I("chip_8_close_emulator", "end");
}
void chip8_toggle(Chip8Emulator* chip8) {
furi_assert(chip8);
furi_thread_flags_set(furi_thread_get_id(chip8->thread), WorkerEvtToggle);
}
Chip8State* chip8_get_state(Chip8Emulator* chip8) {
furi_assert(chip8);
return &(chip8->st);
}
uint16_t read_rom_data(File* file, uint8_t* data) {
furi_assert(file);
furi_assert(data);
const uint8_t buffer_size = 32;
uint16_t file_pointer = 0;
uint8_t buff[buffer_size];
while(1) {
uint16_t bytes_were_read = storage_file_read(file, buff, buffer_size);
if(bytes_were_read == 0) {
break;
}
for(uint16_t i = 0; i < bytes_were_read; i++) {
data[file_pointer] = buff[i];
file_pointer++;
}
}
return file_pointer;
}
void chip8_set_back_pressed(Chip8Emulator* chip8) {
chip8->st.worker_state = WorkerStateBackPressed;
chip8->st.t_chip8_state->go_render = true;
FURI_LOG_I(WORKER_TAG, "SET BACK PRESSED. EMULATION IS STOPPED");
}
void chip8_set_up_pressed(Chip8Emulator* chip8) {
chip8->st.t_chip8_state->go_render = true;
t_chip8_set_input(chip8->st.t_chip8_state, k_1);
FURI_LOG_I(WORKER_TAG, "UP PRESSED");
}
void chip8_set_down_pressed(Chip8Emulator* chip8) {
chip8->st.t_chip8_state->go_render = true;
t_chip8_set_input(chip8->st.t_chip8_state, k_4);
FURI_LOG_I(WORKER_TAG, "DOWN PRESSED");
}
void chip8_release_keyboard(Chip8Emulator* chip8) {
chip8->st.t_chip8_state->go_render = true;
t_chip8_release_input(chip8->st.t_chip8_state);
FURI_LOG_I(WORKER_TAG, "chip8_release_keyboard Release input");
}

View File

@@ -0,0 +1,45 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <furi.h>
#include <m-string.h>
#include <storage/storage.h>
#include "emulator_core/flipper_chip.h"
#define CHIP8_SCREEN_W 64
#define CHIP8_SCREEN_H 32
typedef struct Chip8Emulator Chip8Emulator;
typedef enum {
WorkerStateLoadingRom,
WorkerStateRomLoaded,
WorkerStateRomLoadError,
WorkerStateBackPressed,
} WorkerState;
typedef struct {
WorkerState worker_state;
t_chip8_state* t_chip8_state;
} Chip8State;
Chip8Emulator* chip8_make_emulator(string_t file_path);
void chip8_close_emulator(Chip8Emulator* chip8);
void chip8_set_back_pressed(Chip8Emulator* chip8);
void chip8_set_up_pressed(Chip8Emulator* chip8);
void chip8_set_down_pressed(Chip8Emulator* chip8);
void chip8_release_keyboard(Chip8Emulator* chip8);
Chip8State* chip8_get_state(Chip8Emulator* chip8);
void chip8_toggle(Chip8Emulator* chip8);
uint16_t read_rom_data(File* file, uint8_t* data);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,89 @@
#include "chip8_app_i.h"
#include <furi.h>
static bool chip8_app_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
Chip8App* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool chip8_app_back_event_callback(void* context) {
furi_assert(context);
Chip8App* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void chip8_app_tick_event_callback(void* context) {
furi_assert(context);
Chip8App* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
uint8_t** chip8_backup_screen_alloc() {
FURI_LOG_I("chip8", "chip8_backup_screen_alloc start");
uint8_t** backup_screen = malloc(SCREEN_HEIGHT * sizeof(size_t));
for(int i = 0; i < SCREEN_HEIGHT; i++) {
backup_screen[i] = malloc(SCREEN_WIDTH * sizeof(uint8_t));
for(int j = 0; j < SCREEN_WIDTH; j++) {
backup_screen[i][j] = 0;
}
}
FURI_LOG_I("chip8", "chip8_backup_screen_alloc end");
return backup_screen;
}
Chip8App* chip8_app_alloc() {
Chip8App* app = malloc(sizeof(Chip8App));
app->gui = furi_record_open(RECORD_GUI);
app->dialogs = furi_record_open(RECORD_DIALOGS);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&chip8_scene_handlers, app);
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, chip8_app_tick_event_callback, 100);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, chip8_app_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, chip8_app_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->chip8_view = chip8_alloc();
app->backup_screen = chip8_backup_screen_alloc();
view_dispatcher_add_view(app->view_dispatcher, Chip8WorkView, chip8_get_view(app->chip8_view));
scene_manager_next_scene(app->scene_manager, Chip8FileSelectView);
return app;
}
void chip8_app_free(Chip8App* app) {
FURI_LOG_I("CHIP8", "chip8_app_free started");
furi_assert(app);
view_dispatcher_remove_view(app->view_dispatcher, Chip8FileSelectView);
view_dispatcher_remove_view(app->view_dispatcher, Chip8WorkView);
chip8_free(app->chip8_view);
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_DIALOGS);
free(app);
}
int32_t chip8_app(void* p) {
Chip8App* chip8_app = chip8_app_alloc();
view_dispatcher_run(chip8_app->view_dispatcher);
chip8_app_free(chip8_app);
return 0;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct Chip8App Chip8App;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,33 @@
#pragma once
#include "chip8_app.h"
#include "scenes/chip8_scene.h"
#include "chip8.h"
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <dialogs/dialogs.h>
#include <gui/modules/variable_item_list.h>
#include "views/chip8_view.h"
#define CHIP8_APP_PATH_FOLDER "/any/chip8"
#define CHIP8_APP_EXTENSION ".ch8"
struct Chip8App {
Gui* gui;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
DialogsApp* dialogs;
string_t file_name;
uint8_t** backup_screen;
Chip8View* chip8_view;
Chip8Emulator* chip8;
};
typedef enum {
Chip8FileSelectView,
Chip8WorkView,
} Chip8AppView;

View File

@@ -0,0 +1,319 @@
//
// Created by dwdraugr on 24.11.2021.
//
#include "flipper_chip.h"
#include "flipper_fonts.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
static uint8_t randbyte();
static void draw_sprite(t_chip8_state* state, uint8_t x, uint8_t y, uint8_t n);
static void error_stop(t_chip8_state* state, uint16_t opcode);
t_chip8_state* t_chip8_init(void* (*system_malloc)(size_t)) {
t_chip8_state* state = system_malloc(sizeof(t_chip8_state));
state->PC = MEMORY_START_POSITION;
state->SP = 0;
state->I = 0;
state->delay_timer = 0;
state->sound_timer = 0;
state->go_render = false;
state->next_opcode = 0;
state->memory = system_malloc(MEMORY_SIZE * sizeof(uint8_t));
// memset(state->memory, 0, MEMORY_SIZE);
state->screen = system_malloc(SCREEN_HEIGHT * sizeof(size_t));
for(int i = 0; i < SCREEN_HEIGHT; i++) {
state->screen[i] = system_malloc(SCREEN_WIDTH * sizeof(uint8_t));
// memset(state->screen[i], 0, SCREEN_WIDTH);
}
state->V = system_malloc(CPU_REGISTER_NUMBER * sizeof(uint8_t));
// memset(state->V, 0, CPU_REGISTER_NUMBER);
state->stack = system_malloc(CPU_STACK_DEPTH * sizeof(uint16_t));
// memset(state->stack, 0, CPU_STACK_DEPTH * sizeof(short));
state->key = system_malloc(KEYS_NUMBER * sizeof(uint8_t));
// memset(state->key, 0, KEYS_NUMBER);
memcpy(state->memory, font_small, FONT_SMALL);
srand(time(NULL));
return state;
}
bool t_chip8_load_game(t_chip8_state* state, const uint8_t* rom, int rom_size) {
if(MEMORY_ROM_SIZE < rom_size) {
return false;
}
memcpy(&state->memory[MEMORY_START_POSITION], rom, rom_size);
return true;
}
void t_chip8_free_memory(t_chip8_state* state, void (*system_free)(void*)) {
system_free(state->memory);
for(int i = 0; i < SCREEN_HEIGHT; i++) {
system_free(state->screen[i]);
}
system_free(state->screen);
system_free(state->V);
system_free(state->key);
system_free(state->stack);
system_free(state);
}
void t_chip8_execute_next_opcode(t_chip8_state* state) {
static bool isWaitInput = false;
static uint8_t register_number = 255;
uint16_t opcode = state->memory[state->PC] << 8 | state->memory[state->PC + 1];
uint8_t x = (opcode >> 8) & 0x000F;
uint8_t y = (opcode >> 4) & 0x000F;
uint8_t n = opcode & 0x000F;
uint8_t kk = opcode & 0x00FF;
uint16_t nnn = opcode & 0x0FFF;
// jump to input-wait opcode
if(isWaitInput) {
opcode = 0xF000;
kk = 0x0A;
x = register_number;
}
state->current_opcode = opcode & 0xF000;
switch(opcode & 0xF000) {
case 0x0000:
switch(kk) {
case 0x00E0:
for(int i = 0; i < SCREEN_HEIGHT; i++) {
for(int j = 0; j < SCREEN_WIDTH; j++) {
state->screen[i][j] = 0;
}
}
state->PC += 2;
break;
case 0x00EE:
state->PC = state->stack[--state->SP];
break;
default:
error_stop(state, opcode);
}
break;
case 0x1000:
state->PC = nnn;
break;
case 0x2000:
state->stack[state->SP++] = state->PC + 2;
state->PC = nnn;
break;
case 0x3000:
state->PC += (state->V[x] == kk) ? 4 : 2;
break;
case 0x4000:
state->PC += (state->V[x] != kk) ? 4 : 2;
break;
case 0x5000:
state->PC += (state->V[x] == state->V[y]) ? 4 : 2;
break;
case 0x6000:
state->V[x] = kk;
state->PC += 2;
break;
case 0x7000:
state->V[x] += kk;
state->PC += 2;
break;
case 0x8000:
switch(n) {
case 0x0:
state->V[x] = state->V[y];
break;
case 0x1:
state->V[x] |= state->V[y];
break;
case 0x2:
state->V[x] &= state->V[y];
break;
case 0x3:
state->V[x] ^= state->V[y];
break;
case 0x4:
state->V[0xF] = (int)state->V[x] + (int)state->V[y] ? 1 : 0;
state->V[x] += state->V[y];
break;
case 0x5:
state->V[0xF] = state->V[x] > state->V[y] ? 1 : 0;
state->V[x] -= state->V[y];
break;
case 0x6:
state->V[0xF] = state->V[x] & 0x1;
state->V[x] >>= 1;
break;
case 0x7:
state->V[0xF] = state->V[y] > state->V[x] ? 1 : 0;
state->V[x] = state->V[y] - state->V[x];
break;
case 0xE:
state->V[0xF] = (state->V[x] >> 7) & 0x1;
state->V[x] <<= 1;
break;
default:
error_stop(state, opcode);
}
state->PC += 2;
break;
case 0x9000:
switch(n) {
case 0x0:
state->PC += state->V[x] != state->V[y] ? 4 : 2;
break;
default:
error_stop(state, opcode);
}
break;
case 0xA000:
state->I = nnn;
state->PC += 2;
break;
case 0xB000:
state->PC = nnn + state->V[0];
break;
case 0xC000:
state->V[x] = randbyte() & kk;
state->PC += 2;
break;
case 0xD000:
draw_sprite(state, state->V[x], state->V[y], n);
state->go_render = true;
state->PC += 2;
break;
case 0xE000:
switch(kk) {
case 0x9E:
state->PC += state->key[state->V[x]] ? 4 : 2;
break;
case 0xA1:
state->PC += !state->key[state->V[x]] ? 4 : 2;
break;
default:
error_stop(state, opcode);
}
break;
case 0xF000:
switch(kk) {
case 0x07:
state->V[x] = state->delay_timer;
state->PC += 2;
break;
case 0x0A:
for(int i = 0; i < KEYS_NUMBER; i++) {
if(state->key[i]) {
state->V[x] = i;
isWaitInput = false;
goto exit_input_wait;
}
isWaitInput = true;
register_number = x;
}
exit_input_wait:
state->PC += 2;
break;
case 0x15:
state->delay_timer = state->V[x];
state->PC += 2;
break;
case 0x18:
state->sound_timer = state->V[x];
state->PC += 2;
break;
case 0x1E:
state->V[0xF] = state->I + state->V[x] > 0xFFF ? 1 : 0;
state->I += state->V[x];
state->PC += 2;
break;
case 0x29:
state->I = FONT_BYTES_PER_CHAR * state->V[x];
state->PC += 2;
break;
case 0x33:
state->memory[state->I] = (state->V[x] % 1000) / 100;
state->memory[state->I + 1] = (state->V[x] % 100) / 10;
state->memory[state->I + 2] = state->V[x] % 10;
state->PC += 2;
break;
case 0x55:
memcpy(state->memory, state->V, x);
state->I += x + 1;
state->PC += 2;
break;
case 0x65:
for(int i = 0; i <= x; i++) {
state->V[i] = state->memory[state->I + i];
}
state->I += x + 1;
state->PC += 2;
break;
default:
error_stop(state, opcode);
}
break;
default:
error_stop(state, opcode);
}
state->next_opcode = state->memory[state->PC] << 8 | state->memory[state->PC + 1];
state->next_opcode &= 0xf000;
}
void t_chip8_tick(t_chip8_state* state) {
if(state->delay_timer > 0) {
--state->delay_timer;
}
if(state->sound_timer > 0) {
--state->sound_timer;
}
}
uint8_t** t_chip8_get_screen(t_chip8_state* state) {
return (uint8_t**)state->screen;
}
void t_chip8_set_input(t_chip8_state* state, t_keys key) {
state->key[key] = 1;
}
void t_chip8_release_input(t_chip8_state* state) {
for(int i = 0; i < KEYS_NUMBER; i++) {
state->key[i] = 0;
}
}
static uint8_t randbyte() {
return rand() % 256;
}
static void draw_sprite(t_chip8_state* state, uint8_t x, uint8_t y, uint8_t n) {
unsigned row = y, col = x;
unsigned byte_index;
unsigned bit_index;
state->V[0xF] = 0;
for(byte_index = 0; byte_index < n; byte_index++) {
uint8_t byte = state->memory[state->I + byte_index];
for(bit_index = 0; bit_index < 8; bit_index++) {
uint8_t bit = (byte >> bit_index) & 0x1;
uint8_t* pixel_pointer = &state->screen[(row + byte_index) % SCREEN_HEIGHT]
[(col + (7 - bit_index)) % SCREEN_WIDTH];
if(bit == 1 && *pixel_pointer == 1) state->V[0xF] = 1;
*pixel_pointer = *pixel_pointer ^ bit;
}
}
}
static void error_stop(t_chip8_state* state, uint16_t opcode) {
exit(100);
}

View File

@@ -0,0 +1,74 @@
//
// Created by dwdraugr on 24.11.2021.
//
#ifndef FLIPPER_CHIP_FLIPPER_CHIP_H
#define FLIPPER_CHIP_FLIPPER_CHIP_H
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#define CPU_REGISTER_NUMBER 0x10
#define CPU_STACK_DEPTH 0x10
#define MEMORY_SIZE 0x1000
#define MEMORY_START_POSITION 0x200
#define MEMORY_ROM_SIZE (MEMORY_SIZE - MEMORY_START_POSITION)
#define SCREEN_WIDTH 64
#define SCREEN_HEIGHT 32
#define SCREEN_SIZE (SCREEN_WIDTH * SCREEN_HEIGHT)
#define KEYS_NUMBER 0x10
typedef struct s_flipper_state {
uint16_t I;
uint16_t PC;
uint8_t SP;
uint8_t* V; // CPU_REGISTER_NUMBER
uint16_t* stack; // CPU_STACK_DEPTH
uint8_t* memory; // MEMORY_SIZE
uint8_t delay_timer;
uint8_t sound_timer;
uint8_t** screen; // SCREEN_HEIGHT * SCREEN_WIDTH
uint8_t* key; // KEYS_NUMBER
bool go_render;
int current_opcode;
int next_opcode;
} t_chip8_state;
// keyboard layout
// 1 2 3 C
// 4 5 6 D
// 7 8 9 E
// A 0 B F
typedef enum e_keys {
k_x,
k_1,
k_2,
k_3,
k_q,
k_w,
k_e,
k_a,
k_s,
k_d,
k_z,
k_c,
k_4,
k_r,
k_f,
k_v,
} t_keys;
t_chip8_state* t_chip8_init(void* (*system_malloc)(size_t));
bool t_chip8_load_game(t_chip8_state* state, const uint8_t* rom, int rom_size);
void t_chip8_execute_next_opcode(t_chip8_state* state);
void t_chip8_tick(t_chip8_state* state);
uint8_t** t_chip8_get_screen(t_chip8_state* state);
void t_chip8_free_memory(t_chip8_state* state, void (*system_free)(void*));
void t_chip8_set_input(t_chip8_state* state, t_keys key);
void t_chip8_release_input(t_chip8_state* state);
#endif //FLIPPER_CHIP_FLIPPER_CHIP_H

View File

@@ -0,0 +1,30 @@
//
// Created by dwdraugr on 24.11.2021.
//
#ifndef FLIPPER_CHIP_FLIPPER_FONTS_H
#define FLIPPER_CHIP_FLIPPER_FONTS_H
#define FONT_BYTES_PER_CHAR 5
#define FONT_SMALL (16 * 5)
unsigned char font_small[FONT_SMALL] = {
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
};
#endif //FLIPPER_CHIP_FLIPPER_FONTS_H

View File

@@ -0,0 +1,26 @@
#include "chip8_scene.h"
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const chip8_scene_on_enter_handlers[])(void*) = {
#include "chip8_scene_config.h"
};
#undef ADD_SCENE
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const chip8_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "chip8_scene_config.h"
};
#undef ADD_SCENE
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const chip8_scene_on_exit_handlers[])(void* context) = {
#include "chip8_scene_config.h"
};
#undef ADD_SCENE
const SceneManagerHandlers chip8_scene_handlers = {
.on_enter_handlers = chip8_scene_on_enter_handlers,
.on_event_handlers = chip8_scene_on_event_handlers,
.on_exit_handlers = chip8_scene_on_exit_handlers,
.scene_num = Chip8SceneNum,
};

View File

@@ -0,0 +1,25 @@
#pragma once
#include <gui/scene_manager.h>
#define ADD_SCENE(prefix, name, id) Chip8Scene##id,
typedef enum {
#include "chip8_scene_config.h"
Chip8SceneNum,
} Chip8Scene;
#undef ADD_SCENE
extern const SceneManagerHandlers chip8_scene_handlers;
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "chip8_scene_config.h"
#undef ADD_SCENE
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "chip8_scene_config.h"
#undef ADD_SCENE
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "chip8_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,2 @@
ADD_SCENE(chip8, file_select, FileSelect)
ADD_SCENE(chip8, work, Work)

View File

@@ -0,0 +1,45 @@
#include "../chip8_app_i.h"
#include "furi_hal_power.h"
static bool chip8_file_select(Chip8App* chip8) {
furi_assert(chip8);
string_init(chip8->file_name);
string_set_str(chip8->file_name, CHIP8_APP_PATH_FOLDER);
// string_set_str(file_path, chip8->file_name);
bool res = dialog_file_browser_show(
chip8->dialogs,
chip8->file_name,
chip8->file_name,
CHIP8_APP_EXTENSION,
true,
&I_unknown_10px,
false);
FURI_LOG_I(
"Chip8_file_browser_show", "chip8->file_name: %s", string_get_cstr(chip8->file_name));
FURI_LOG_I("Chip8_file_browser_show", "res: %d", res);
return res;
}
void chip8_scene_file_select_on_enter(void* context) {
Chip8App* chip8 = context;
if(chip8_file_select(chip8)) {
FURI_LOG_I(
"Chip8", "chip8_file_select, file_name = %s", string_get_cstr(chip8->file_name));
scene_manager_next_scene(chip8->scene_manager, Chip8WorkView);
} else {
view_dispatcher_stop(chip8->view_dispatcher);
}
}
bool chip8_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void chip8_scene_file_select_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,90 @@
#include "../chip8_app_i.h"
#include "../views/chip8_view.h"
#include "furi_hal.h"
void chip8_scene_ok_callback(InputType type, void* context) {
furi_assert(context);
Chip8App* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, type);
}
void chip8_scene_back_callback(Chip8View* view, InputType type, void* context) {
furi_assert(context);
Chip8App* app = context;
chip8_set_back_pressed(app->chip8);
chip8_set_state(view, chip8_get_state(app->chip8));
// view_dispatcher_send_custom_event(app->view_dispatcher, type);
view_dispatcher_stop(app->view_dispatcher);
}
void chip8_scene_up_callback(InputType type, void* context) {
furi_assert(context);
Chip8App* app = context;
chip8_set_up_pressed(app->chip8);
// view_dispatcher_send_custom_event(app->view_dispatcher, type);
}
void chip8_scene_down_callback(InputType type, void* context) {
furi_assert(context);
Chip8App* app = context;
chip8_set_down_pressed(app->chip8);
// view_dispatcher_send_custom_event(app->view_dispatcher, type);
}
void chip8_scene_release_callback(InputType type, void* context) {
furi_assert(context);
Chip8App* app = context;
chip8_release_keyboard(app->chip8);
// view_dispatcher_send_custom_event(app->view_dispatcher, type);
}
bool chip8_scene_work_on_event(void* context, SceneManagerEvent event) {
Chip8App* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
chip8_toggle(app->chip8);
consumed = true;
} else if(event.type == SceneManagerEventTypeTick) {
Chip8State* state = chip8_get_state(app->chip8);
chip8_set_state(app->chip8_view, state);
}
return consumed;
}
void chip8_scene_work_on_enter(void* context) {
Chip8App* app = context;
chip8_set_file_name(app->chip8_view, app->file_name);
string_t file_tmp;
string_init(file_tmp);
string_printf(file_tmp, "%s", string_get_cstr(app->file_name));
FURI_LOG_I("chip8_scene_work_on_enter", "file_name: %s", string_get_cstr(file_tmp));
FURI_LOG_I("chip8_scene_work_on_enter", "START SET BACKUP SCREEN");
chip8_set_backup_screen(app->chip8_view, app->backup_screen);
FURI_LOG_I("chip8_scene_work_on_enter", "END SET BACKUP SCREEN");
app->chip8 = chip8_make_emulator(file_tmp);
string_clear(file_tmp);
chip8_set_state(app->chip8_view, chip8_get_state(app->chip8));
chip8_set_ok_callback(app->chip8_view, chip8_scene_ok_callback, app);
chip8_set_back_callback(app->chip8_view, chip8_scene_back_callback, app);
chip8_set_up_callback(app->chip8_view, chip8_scene_up_callback, app);
chip8_set_down_callback(app->chip8_view, chip8_scene_down_callback, app);
chip8_set_release_callback(app->chip8_view, chip8_scene_release_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, Chip8WorkView);
}
void chip8_scene_work_on_exit(void* context) {
Chip8App* app = context;
chip8_close_emulator(app->chip8);
}

View File

@@ -0,0 +1,207 @@
#include <gui/elements.h>
#include "chip8_view.h"
#include "../chip8.h"
#include "../emulator_core/flipper_chip.h"
struct Chip8View {
View* view;
Chip8ViewCallback callback;
void* context;
Chip8ViewKeyBackCallback backCallback;
Chip8ViewKeyUpCallback upCallback;
Chip8ViewKeyDownCallback downCallback;
Chip8ViewReleaseCallback releaseCallback;
};
typedef struct {
string_t file_name;
Chip8State state;
uint8_t** backup_screen;
} Chip8Model;
static void chip8_draw_callback(Canvas* canvas, void* _model) {
Chip8Model* model = _model;
if(model->state.worker_state == WorkerStateLoadingRom) {
canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18);
}
if(model->state.worker_state == WorkerStateRomLoaded) {
while(!model->state.t_chip8_state->go_render) {
for(int y = 0; y < CHIP8_SCREEN_H; y++) {
for(int x = 0; x < CHIP8_SCREEN_W; x++) {
if(model->backup_screen[y][x] == 0) {
canvas_set_color(canvas, ColorWhite);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_box(canvas, x * 2, y * 2, 2, 2);
//canvas_draw_dot(canvas, x, y);
}
}
return;
}
uint8_t** screen = t_chip8_get_screen(model->state.t_chip8_state);
for(int y = 0; y < CHIP8_SCREEN_H; y++) {
for(int x = 0; x < CHIP8_SCREEN_W; x++) {
if(screen[y][x] == 0) {
canvas_set_color(canvas, ColorWhite);
} else {
canvas_set_color(canvas, ColorBlack);
}
canvas_draw_box(canvas, x * 2, y * 2, 2, 2);
model->backup_screen[y][x] = screen[y][x];
//canvas_draw_dot(canvas, x, y);
}
}
model->state.t_chip8_state->go_render = false;
}
if(model->state.worker_state == WorkerStateRomLoadError) {
canvas_draw_icon(canvas, 4, 22, &I_Error_18x18);
}
}
static bool chip8_input_callback(InputEvent* event, void* context) {
FURI_LOG_I("Chip8", "received input");
furi_assert(context);
Chip8View* chip8 = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyOk) {
consumed = true;
furi_assert(chip8->callback);
chip8->callback(InputTypeShort, chip8->context);
}
if(event->key == InputKeyBack) {
consumed = true;
furi_assert(chip8->callback);
chip8->backCallback(chip8, InputTypeShort, chip8->context);
}
if(event->key == InputKeyUp) {
consumed = true;
furi_assert(chip8->upCallback);
chip8->upCallback(InputTypeShort, chip8->context);
}
if(event->key == InputKeyDown) {
consumed = true;
furi_assert(chip8->downCallback);
chip8->downCallback(InputTypeShort, chip8->context);
}
}
if(event->type == InputTypeRelease) {
chip8->releaseCallback(InputTypeShort, chip8->context);
}
return consumed;
}
Chip8View* chip8_alloc() {
Chip8View* chip8 = malloc(sizeof(Chip8View));
chip8->view = view_alloc();
view_allocate_model(chip8->view, ViewModelTypeLocking, sizeof(Chip8Model));
view_set_context(chip8->view, chip8);
view_set_draw_callback(chip8->view, chip8_draw_callback);
view_set_input_callback(chip8->view, chip8_input_callback);
return chip8;
}
void chip8_free(Chip8View* chip8) {
furi_assert(chip8);
view_free(chip8->view);
free(chip8);
}
View* chip8_get_view(Chip8View* chip8) {
furi_assert(chip8);
return chip8->view;
}
void chip8_set_ok_callback(Chip8View* chip8, Chip8ViewCallback callback, void* context) {
furi_assert(chip8);
furi_assert(callback);
with_view_model(
chip8->view, (Chip8Model * model) {
chip8->callback = callback;
chip8->context = context;
return false;
});
}
void chip8_set_back_callback(Chip8View* chip8, Chip8ViewKeyBackCallback callback, void* context) {
furi_assert(chip8);
furi_assert(callback);
with_view_model(
chip8->view, (Chip8Model * model) {
chip8->backCallback = callback;
chip8->context = context;
return true;
});
}
void chip8_set_up_callback(Chip8View* chip8, Chip8ViewKeyUpCallback callback, void* context) {
furi_assert(chip8);
furi_assert(callback);
with_view_model(
chip8->view, (Chip8Model * model) {
chip8->upCallback = callback;
chip8->context = context;
return true;
});
}
void chip8_set_down_callback(Chip8View* chip8, Chip8ViewKeyDownCallback callback, void* context) {
furi_assert(chip8);
furi_assert(callback);
with_view_model(
chip8->view, (Chip8Model * model) {
chip8->downCallback = callback;
chip8->context = context;
return true;
});
}
void chip8_set_file_name(Chip8View* chip8, string_t name) {
furi_assert(name);
with_view_model(
chip8->view, (Chip8Model * model) {
*model->file_name = *name;
return false;
});
}
void chip8_set_backup_screen(Chip8View* chip8, uint8_t** screen) {
furi_assert(screen);
with_view_model(
chip8->view, (Chip8Model * model) {
model->backup_screen = screen;
return false;
});
}
void chip8_set_state(Chip8View* chip8, Chip8State* st) {
furi_assert(st);
with_view_model(
chip8->view, (Chip8Model * model) {
memcpy(&(model->state), st, sizeof(Chip8State));
return true;
});
}
void chip8_set_release_callback(Chip8View* chip8, Chip8ViewReleaseCallback callback, void* context) {
furi_assert(chip8);
furi_assert(callback);
with_view_model(
chip8->view, (Chip8Model * model) {
chip8->releaseCallback = callback;
chip8->context = context;
return true;
});
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/view.h>
#include "../chip8.h"
typedef struct Chip8View Chip8View;
typedef void (*Chip8ViewCallback)(InputType type, void* context);
typedef void (*Chip8ViewKeyBackCallback)(Chip8View* view, InputType type, void* context);
typedef void (*Chip8ViewKeyUpCallback)(InputType type, void* context);
typedef void (*Chip8ViewKeyDownCallback)(InputType type, void* context);
typedef void (*Chip8ViewReleaseCallback)(InputType type, void* context);
Chip8View* chip8_alloc();
void chip8_free(Chip8View* chip8);
View* chip8_get_view(Chip8View* chip8);
void chip8_set_ok_callback(Chip8View* chip8, Chip8ViewCallback callback, void* context);
void chip8_set_back_callback(Chip8View* chip8, Chip8ViewKeyBackCallback callback, void* context);
void chip8_set_up_callback(Chip8View* chip8, Chip8ViewKeyUpCallback callback, void* context);
void chip8_set_down_callback(Chip8View* chip8, Chip8ViewKeyDownCallback callback, void* context);
void chip8_set_release_callback(Chip8View* chip8, Chip8ViewReleaseCallback callback, void* context);
void chip8_set_backup_screen(Chip8View* chip8, uint8_t** screen);
void chip8_set_file_name(Chip8View* chip8, string_t name);
void chip8_set_state(Chip8View* chip8, Chip8State* st);

View File

@@ -0,0 +1,12 @@
App(
appid="APPS_MouseJiggler",
name="Mouse Jiggler",
apptype=FlipperAppType.EXTERNAL,
entry_point="mouse_jiggler_app",
cdefines=["APP_MOUSE_JIGGLER"],
requires=["gui"],
stack_size=1 * 1024,
order=150,
fap_icon="mjigIcon.png",
fap_category="Misc",
)

View File

@@ -0,0 +1,141 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#define MOUSE_MOVE_SHORT 5
#define MOUSE_MOVE_LONG 20
typedef enum {
EventTypeInput,
EventTypeKey,
} EventType;
typedef struct {
EventType type;
InputEvent input;
} UsbMouseEvent;
typedef struct {
bool running;
} MouseJigglerState;
static void mouse_jiggler_render_callback(Canvas* canvas, void* ctx) {
const MouseJigglerState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25);
if(plugin_state == NULL) {
return;
}
canvas_set_font(canvas, FontPrimary);
canvas_draw_str(canvas, 2, 12, "USB Mouse Jiggler");
if(!plugin_state->running) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 27, " -> STOPPED");
canvas_draw_str(canvas, 2, 51, "Press [ok] to start");
canvas_draw_str(canvas, 2, 63, "Press [back] to exit");
} else {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 2, 27, " -> RUNNING");
canvas_draw_str(canvas, 2, 51, "Press [back] to stop");
}
release_mutex((ValueMutex*)ctx, plugin_state);
}
static void mouse_jiggler_input_callback(InputEvent* input_event, void* ctx) {
FuriMessageQueue* event_queue = ctx;
furi_assert(event_queue);
UsbMouseEvent event = {.type = EventTypeKey, .input = *input_event};
furi_message_queue_put(event_queue, &event, FuriWaitForever);
}
static void mouse_jiggler_state_init(MouseJigglerState* const plugin_state) {
plugin_state->running = false;
}
int32_t mouse_jiggler_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(UsbMouseEvent));
MouseJigglerState* plugin_state = malloc(sizeof(MouseJigglerState));
if(plugin_state == NULL) {
FURI_LOG_E("MouseJiggler", "MouseJigglerState: malloc error\r\n");
return 255;
}
mouse_jiggler_state_init(plugin_state);
ValueMutex state_mutex;
if(!init_mutex(&state_mutex, plugin_state, sizeof(MouseJigglerState))) {
FURI_LOG_E("MouseJiggler", "cannot create mutex\r\n");
furi_message_queue_free(event_queue);
free(plugin_state);
return 255;
}
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, mouse_jiggler_render_callback, &state_mutex);
view_port_input_callback_set(view_port, mouse_jiggler_input_callback, event_queue);
FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config();
furi_hal_usb_set_config(&usb_hid, NULL);
// Open GUI and register view_port
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
UsbMouseEvent event;
//bool status = 0;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
MouseJigglerState* plugin_state = (MouseJigglerState*)acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
if(event.type == EventTypeKey) {
if(event.input.type == InputTypePress) {
switch(event.input.key) {
case InputKeyOk:
if(!plugin_state->running) {
plugin_state->running = true;
}
break;
case InputKeyBack:
if(!plugin_state->running) {
processing = false;
} else {
plugin_state->running = false;
}
break;
default:
break;
}
}
}
}
if(plugin_state->running) {
furi_hal_hid_mouse_move(MOUSE_MOVE_SHORT, 0);
furi_delay_ms(500);
furi_hal_hid_mouse_move(-MOUSE_MOVE_SHORT, 0);
furi_delay_ms(500);
}
view_port_update(view_port);
release_mutex(&state_mutex, plugin_state);
}
furi_hal_usb_set_config(usb_mode_prev, NULL);
// remove & free all stuff created by app
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);
delete_mutex(&state_mutex);
return 0;
}

View File

@@ -0,0 +1,10 @@
App(
appid="APPS_Paint",
name="Paint",
apptype=FlipperAppType.PLUGIN,
entry_point="paint_app",
cdefines=["APP_PAINT"],
requires=["gui"],
stack_size=2 * 1024,
order=175,
)

View File

@@ -0,0 +1,148 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stdbool.h> // Header-file for boolean data-type.
typedef struct selected_position {
int x;
int y;
} selected_position;
typedef struct {
selected_position selected;
bool board[32][16];
bool isDrawing;
} PaintData;
void paint_draw_callback(Canvas* canvas, void* ctx) {
const PaintData* paint_state = acquire_mutex((ValueMutex*)ctx, 25);
UNUSED(ctx);
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
//draw the canvas(32x16) on screen(144x64) using 4x4 tiles
for(int y = 0; y < 16; y++) {
for(int x = 0; x < 32; x++) {
if(paint_state->board[x][y]) {
canvas_draw_box(canvas, x * 4, y * 4, 4, 4);
}
}
}
//draw cursor as a 4x4 black box with a 2x2 white box inside
canvas_set_color(canvas, ColorBlack);
canvas_draw_box(canvas, paint_state->selected.x * 4, paint_state->selected.y * 4, 4, 4);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(
canvas, paint_state->selected.x * 4 + 1, paint_state->selected.y * 4 + 1, 2, 2);
//release the mutex
release_mutex((ValueMutex*)ctx, paint_state);
}
void paint_input_callback(InputEvent* input_event, void* ctx) {
furi_assert(ctx);
FuriMessageQueue* event_queue = ctx;
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
}
int32_t paint_app(void* p) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
PaintData* paint_state = malloc(sizeof(PaintData));
ValueMutex paint_state_mutex;
if(!init_mutex(&paint_state_mutex, paint_state, sizeof(PaintData))) {
FURI_LOG_E("paint", "cannot create mutex\r\n");
free(paint_state);
return -1;
}
// Configure view port
ViewPort* view_port = view_port_alloc();
view_port_draw_callback_set(view_port, paint_draw_callback, &paint_state_mutex);
view_port_input_callback_set(view_port, paint_input_callback, event_queue);
// Register view port in GUI
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
//NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
InputEvent event;
while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
//break out of the loop if the back key is pressed
if(event.type == InputTypeShort && event.key == InputKeyBack) {
break;
}
//check the key pressed and change x and y accordingly
if(event.type == InputTypeShort) {
switch(event.key) {
case InputKeyUp:
paint_state->selected.y -= 1;
break;
case InputKeyDown:
paint_state->selected.y += 1;
break;
case InputKeyLeft:
paint_state->selected.x -= 1;
break;
case InputKeyRight:
paint_state->selected.x += 1;
break;
case InputKeyOk:
paint_state->board[paint_state->selected.x][paint_state->selected.y] =
!paint_state->board[paint_state->selected.x][paint_state->selected.y];
break;
default:
break;
}
//check if cursor position is out of bounds and reset it to the closest position
if(paint_state->selected.x < 0) {
paint_state->selected.x = 0;
}
if(paint_state->selected.x > 31) {
paint_state->selected.x = 31;
}
if(paint_state->selected.y < 0) {
paint_state->selected.y = 0;
}
if(paint_state->selected.y > 15) {
paint_state->selected.y = 15;
}
if(paint_state->isDrawing == true) {
paint_state->board[paint_state->selected.x][paint_state->selected.y] = true;
}
view_port_update(view_port);
}
if(event.key == InputKeyBack && event.type == InputTypeLong) {
paint_state->board[1][1] = true;
for(int y = 0; y < 16; y++) {
for(int x = 0; x < 32; x++) {
paint_state->board[x][y] = false;
}
}
view_port_update(view_port);
}
if(event.key == InputKeyOk && event.type == InputTypeLong) {
paint_state->isDrawing = !paint_state->isDrawing;
paint_state->board[paint_state->selected.x][paint_state->selected.y] = true;
view_port_update(view_port);
}
}
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_message_queue_free(event_queue);
free(paint_state);
furi_record_close(RECORD_NOTIFICATION);
furi_record_close(RECORD_GUI);
return 0;
}