aboutsummaryrefslogtreecommitdiff
path: root/apps/chess/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/chess/main.c')
-rw-r--r--apps/chess/main.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/apps/chess/main.c b/apps/chess/main.c
new file mode 100644
index 0000000..94739ef
--- /dev/null
+++ b/apps/chess/main.c
@@ -0,0 +1,252 @@
+// MIT License, Copyright (c) 2021 Marvin Borner
+// Ugly chess implementation to find the limits of libgui
+// Asserts are generally not needed but don't hurt either
+
+#include <assert.h>
+#include <libgui/gui.h>
+#include <print.h>
+
+// Config
+#define SIZE 8
+#define TILE 48
+#define WHITE_STARTS 1
+#define DARK_COLOR 0xff946f51
+#define LIGHT_COLOR 0xfff0d9b5
+#define START_FEN "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
+
+// Pieces
+#define NONE 0
+#define KING 1
+#define PAWN 2
+#define KNIGHT 3
+#define BISHOP 5
+#define ROOK 6
+#define QUEEN 7
+#define WHITE 8
+#define BLACK 16
+
+// Masks
+#define TYPE_MASK 7
+#define BLACK_MASK BLACK
+#define WHITE_MASK WHITE
+#define COLOR_MASK (WHITE_MASK | BLACK_MASK)
+
+// Macros
+#define COLOR(piece) (piece & COLOR_MASK)
+#define IS_COLOR(piece, color) (COLOR(piece) == color)
+#define TYPE(piece) (piece & TYPE_MASK)
+#define IS_ROOK_OR_QUEEN(piece) ((piece & 6) == 6)
+#define IS_BISHOP_OR_QUEEN(piece) ((piece & 5) == 5)
+#define IS_SLIDING_PIECE(piece) ((piece & 4) != 0)
+
+struct piece {
+ u32 piece;
+ u32 widget;
+ char name[8];
+ struct {
+ u8 moved : 1;
+ // idk
+ } bits;
+};
+
+typedef struct piece board[SIZE][SIZE];
+
+static u32 win = 0; // Window
+static board tiles = { 0 }; // Matrix
+static vec2 selected = { -1, -1 }; // Selected tile
+
+static void load_image(struct piece *tile)
+{
+ assert(gui_clear(win, tile->widget, GUI_LAYER_FG) == EOK);
+
+ char icon[48] = { 0 };
+ snprintf(icon, sizeof(icon), "/icons/chess-%s-%d.png", tile->name, TILE);
+ enum gfx_filter filter = IS_COLOR(tile->piece, BLACK) ? GFX_FILTER_NONE : GFX_FILTER_INVERT;
+
+ /* assert(gui_fill(win, tile->widget, GUI_LAYER_FG, 0) == EOK); */
+ assert(gui_load_image_filter(win, tile->widget, GUI_LAYER_FG, vec2(0, 0), vec2(TILE, TILE),
+ filter, icon) == EOK);
+}
+
+static void mouseclick(u32 widget_id, vec2 pos)
+{
+ UNUSED(pos);
+
+ vec2 clicked = vec2(0, 0);
+ for (u32 x = 0; x < SIZE; x++)
+ for (u32 y = 0; y < SIZE; y++)
+ if (tiles[x][y].widget == widget_id)
+ clicked = vec2(x, y);
+
+ struct piece *clicked_piece = &tiles[clicked.x][clicked.y];
+
+ if (vec2_eq(clicked, selected))
+ return;
+
+ if (selected.x != (u32)-1) {
+ struct piece *selected_piece = &tiles[selected.x][selected.y];
+
+ clicked_piece->piece = selected_piece->piece;
+ selected_piece->piece = 0;
+
+ strlcpy(clicked_piece->name, selected_piece->name, sizeof(clicked_piece->name));
+ selected_piece->name[0] = '\0';
+
+ assert(gui_clear(win, selected_piece->widget, GUI_LAYER_FG) == EOK);
+ load_image(clicked_piece);
+
+ assert(gui_redraw_window(win) == EOK);
+
+ selected = vec2(-1, -1);
+ } else if (clicked_piece->piece) {
+ assert(gui_redraw_widget(win, clicked_piece->widget) == EOK);
+ selected = clicked;
+ }
+}
+
+static const char *resolve_name(u32 piece, char buf[8])
+{
+ const char *name = NULL;
+ switch (piece & TYPE_MASK) {
+ case KING:
+ name = "king";
+ break;
+ case PAWN:
+ name = "pawn";
+ break;
+ case KNIGHT:
+ name = "knight";
+ break;
+ case BISHOP:
+ name = "bishop";
+ break;
+ case ROOK:
+ name = "rook";
+ break;
+ case QUEEN:
+ name = "queen";
+ break;
+ default:
+ err(1, "Unknown piece %d\n", piece);
+ }
+
+ strlcpy(buf, name, 8);
+
+ return buf;
+}
+
+static u32 fen_resolve_letter(char ch)
+{
+ u32 piece = 0;
+
+ switch (ch) {
+ case 'k':
+ piece = KING | BLACK;
+ break;
+ case 'K':
+ piece = KING | WHITE;
+ break;
+ case 'p':
+ piece = PAWN | BLACK;
+ break;
+ case 'P':
+ piece = PAWN | WHITE;
+ break;
+ case 'n':
+ piece = KNIGHT | BLACK;
+ break;
+ case 'N':
+ piece = KNIGHT | WHITE;
+ break;
+ case 'b':
+ piece = BISHOP | BLACK;
+ break;
+ case 'B':
+ piece = BISHOP | WHITE;
+ break;
+ case 'r':
+ piece = ROOK | BLACK;
+ break;
+ case 'R':
+ piece = ROOK | WHITE;
+ break;
+ case 'q':
+ piece = QUEEN | BLACK;
+ break;
+ case 'Q':
+ piece = QUEEN | WHITE;
+ break;
+ default:
+ err(1, "Invalid letter (%c)!\n", ch);
+ }
+
+ return piece;
+}
+
+// TODO: Add more than basic fen support
+static void fen_parse(const char *fen)
+{
+ if (!fen || !*fen)
+ return;
+
+ u8 x = 0, y = 0;
+ for (const char *p = fen; p && *p; p++) {
+ if (*p == ' ')
+ break;
+
+ if (*p == '/') {
+ x = 0;
+ y++;
+ continue;
+ }
+
+ if (*p >= '0' && *p <= '9')
+ continue;
+
+ u32 piece = fen_resolve_letter(*p);
+
+ tiles[x][y].piece = piece;
+ resolve_name(piece, tiles[x][y].name);
+
+ x++;
+ }
+}
+
+static void draw_board(void)
+{
+ for (u8 x = 0; x < 8; x++) {
+ for (u8 y = 0; y < 8; y++) {
+ u32 widget =
+ gui_new_widget(win, vec2(TILE, TILE), vec2(TILE * x, TILE * y));
+ assert((signed)widget > 0);
+
+ u8 colored = (x + y + 1) % 2 == 0;
+#if !WHITE_STARTS
+ colored = !colored;
+#endif
+ assert(gui_fill(win, widget, GUI_LAYER_BG,
+ colored ? DARK_COLOR : LIGHT_COLOR) == EOK);
+
+ struct piece *tile = &tiles[x][y];
+ assert(gui_listen_widget(win, widget, GUI_LISTEN_MOUSECLICK,
+ (u32)mouseclick) == EOK);
+
+ tile->widget = widget;
+
+ if (tile->piece)
+ load_image(tile);
+ }
+ }
+
+ assert(gui_redraw_window(win) == EOK);
+}
+
+int main(void)
+{
+ assert(gui_new_window(&win) == EOK);
+ fen_parse(START_FEN);
+ draw_board();
+
+ gui_loop();
+ return 0;
+}