// 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 #include #include // 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) { gui_clear(win, tile->widget, GUI_LAYER_FG); 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); */ gui_draw_image_filter(win, tile->widget, GUI_LAYER_FG, vec2(0, 0), vec2(TILE, TILE), filter, icon); } static void mouseclick(struct gui_event_mouse *event) { vec2 clicked = vec2(0, 0); for (u32 x = 0; x < SIZE; x++) for (u32 y = 0; y < SIZE; y++) if (tiles[x][y].widget == event->widget) 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'; gui_clear(win, selected_piece->widget, GUI_LAYER_FG); load_image(clicked_piece); gui_window_redraw(win); selected = vec2(-1, -1); } else if (clicked_piece->piece) { 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) { gui_widget_margin(win, gui_widget_main(win), vec2(0, 0)); gui_widget_layout(win, gui_widget_main(win), GUI_VLAYOUT); for (u8 y = 0; y < 8; y++) { u8 row = gui_widget(win, gui_widget_main(win), vec2(TILE * 8, TILE)); gui_widget_margin(win, row, vec2(0, 0)); for (u8 x = 0; x < 8; x++) { u32 widget = gui_widget(win, row, vec2(TILE, TILE)); assert((signed)widget > 0); u8 colored = (x + y + 1) % 2 == 0; #if !WHITE_STARTS colored = !colored; #endif gui_fill(win, widget, GUI_LAYER_BG, colored ? DARK_COLOR : LIGHT_COLOR); struct piece *tile = &tiles[x][y]; gui_widget_listen(win, widget, GUI_LISTEN_MOUSECLICK, (u32)mouseclick); tile->widget = widget; if (tile->piece) load_image(tile); } } gui_window_redraw(win); } int main(void) { win = gui_window_custom(APPNAME, vec2(0, 0), vec2(TILE * 8, TILE * 8)); fen_parse(START_FEN); draw_board(); gui_loop(); return 0; }