diff --git a/cudoku.c b/cudoku.c index a453e40..99b5fa1 100644 --- a/cudoku.c +++ b/cudoku.c @@ -24,6 +24,8 @@ static const int quad_indices[] = { 1, 2, 3 }; +static const Color mistake_color = {1.f, 0.f, 0.f, 0.5f}; + void set_scale_factor(int width, int height, float *x, float *y) { if (width > height) { float ratio = (float)height / (float)width; @@ -174,11 +176,12 @@ void prepare_selection_box(unsigned int *vao, unsigned int *vbo) { glBindVertexArray(0); } -void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x, int y, float *transform) { + +void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x, int y, float *transform, Color color) { use_shader(shader); set_mat4f(shader, "transform", transform); - set_vec4f(shader, "aColor", 0.4, 0.4, 1.0, 0.5); + set_vec4f(shader, "aColor", color.r, color.g, color.b, color.a); glBindVertexArray(vao); @@ -211,6 +214,21 @@ void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x glBindVertexArray(0); } +void highlight_mistakes(Shader shader, unsigned int vao, unsigned vbo, float *transform, Cudoku *game) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + Cell cell = game->board[i][j]; + if (cell.value != 0 && cell.value != game->solution[i][j]) { + draw_selection_box(shader, vao, vbo, i, j, transform, mistake_color); + } + } + } +} + +void toggle_check(Cudoku *game) { + game->should_highlight_mistakes = !game->should_highlight_mistakes; +} + void do_selection(Cudoku *game, int x, int y, int width, int height, float x_scale, float y_scale) { float board_x_start = (width / 2.f) - ((width * x_scale) / 2); float board_y_start = (height / 2.f) - ((height * y_scale) / 2); @@ -232,6 +250,19 @@ void do_selection(Cudoku *game, int x, int y, int width, int height, float x_sca game->selection.y = cell_x; } +void reset_state(Cudoku *game) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + game->board[i][j].value = 0; + game->board[i][j].is_locked = false; + } + } + + game->should_draw_selection = false; + game->should_highlight_mistakes = false; + game->has_won = false; +} + bool check_win(Cudoku *game) { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { @@ -242,6 +273,7 @@ bool check_win(Cudoku *game) { game->has_won = true; game->should_draw_selection = false; + game->should_highlight_mistakes = false; return true; } @@ -464,6 +496,7 @@ void remove_numbers(Cudoku *game) { } void generate_random_board(Cudoku *game) { + reset_state(game); int size = 9; // fill diagonal boxes 1, 5, 9 int digits[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; @@ -498,4 +531,14 @@ void generate_random_board(Cudoku *game) { remove_numbers(game); } +void reset_board(Cudoku *game) { + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (game->board[i][j].is_locked == false) { + game->board[i][j].value = 0; + } + } + } +} + diff --git a/cudoku.h b/cudoku.h index b6a3692..4ba5001 100644 --- a/cudoku.h +++ b/cudoku.h @@ -20,8 +20,16 @@ typedef struct Cudoku { bool has_won; Vec2 selection; bool should_draw_selection; + bool should_highlight_mistakes; } Cudoku; +typedef struct Color { + float r; + float g; + float b; + float a; +} Color; + void set_scale_factor(int width, int height, float *x, float *y); unsigned int prepare_bg(bool use_texture, unsigned int *texture); void draw_bg_grid_shader(Shader shader, unsigned int vao, float* transform, float resolution); @@ -30,9 +38,12 @@ void draw_numbers(Shader shader, unsigned int vao, unsigned int vbo, float *tran unsigned int prepare_win_overlay(); void draw_win_overlay(Shader win_shader, Shader font_shader, unsigned int vao, unsigned int font_vao, unsigned int font_vbo, float *transform); void prepare_selection_box(unsigned int *vao, unsigned int *vbo); -void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x, int y, float *transform); +void draw_selection_box(Shader shader, unsigned int vao, unsigned int vbo, int x, int y, float *transform, Color color); +void highlight_mistakes(Shader shader, unsigned int vao, unsigned vbo, float *transform, Cudoku *game); +void toggle_check(Cudoku *game); void do_selection(Cudoku *game, int x, int y, int width, int height, float x_scale, float y_scale); void set_selected_number(Cudoku *game, int number); void move_selection(Cudoku *game, int x, int y); void toggle_selection(Cudoku *game); void generate_random_board(Cudoku *game); +void reset_board(Cudoku *game); diff --git a/main.c b/main.c index 267ecc8..396878c 100644 --- a/main.c +++ b/main.c @@ -22,7 +22,7 @@ void usage() { void handle_keypress(XEvent xev, Cudoku *game) { if (XLookupKeysym(&xev.xkey, 0) == XK_r) { - /* reset_board(&game); */ + reset_board(game); } else if (XLookupKeysym(&xev.xkey, 0) >= XK_0 && XLookupKeysym(&xev.xkey, 0) <= XK_9) { set_selected_number(game, XLookupKeysym(&xev.xkey, 0) - XK_0); } else if (XLookupKeysym(&xev.xkey, 0) == XK_BackSpace || @@ -52,12 +52,12 @@ void handle_keypress(XEvent xev, Cudoku *game) { XLookupKeysym(&xev.xkey, 0) == XK_Return || XLookupKeysym(&xev.xkey, 0) == XK_space) { toggle_selection(game); - /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_n) { */ - /* new_board(&game); */ - /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_f) { */ - /* toggle_fullscreen(display, window); */ - /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_c) { */ - /* toggle_check(&game); */ + } else if (XLookupKeysym(&xev.xkey, 0) == XK_n) { + generate_random_board(game); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_F11) { + toggle_fullscreen(display, window); + } else if (XLookupKeysym(&xev.xkey, 0) == XK_c) { + toggle_check(game); /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_m) { */ /* toggle_mute(&game); */ /* } else if (XLookupKeysym(&xev.xkey, 0) == XK_F1) { */ @@ -149,6 +149,7 @@ int main(int argc, char *argv[]) { Shader selection_shader = create_shader("shaders/board_v.vert", "shaders/quad.frag"); unsigned int selection_vao, selection_vbo; + Color selection_color = {0.4f, 0.4f, 1.0f, 0.5f}; prepare_selection_box(&selection_vao, &selection_vbo); Shader win_shader = create_shader("shaders/board_v.vert", "shaders/quad.frag"); @@ -209,10 +210,14 @@ int main(int argc, char *argv[]) { selection_vbo, game.selection.x, game.selection.y, - (float *)transform); + (float *)transform, + selection_color); draw_numbers(font_shader, font_vao, font_vbo, (float *)transform, game.board); + if (game.should_highlight_mistakes) + highlight_mistakes(selection_shader, selection_vao, selection_vbo, (float *)transform, &game); + if (game.has_won) { draw_win_overlay(win_shader, font_shader, win_vao, font_vao, font_vbo, (float *)transform); } diff --git a/x11.c b/x11.c index 9959dab..8fdfa4a 100644 --- a/x11.c +++ b/x11.c @@ -1,9 +1,12 @@ +#include #include +#include #include static const uint win_width = 900; static const uint win_height = 900; +static bool fullscreen = False; Colormap colormap; GLXContext context; @@ -113,3 +116,46 @@ void close_x11(Display **display, Window *window) { gladLoaderUnloadGLX(); } +void go_fullscreen(Display *display, Window window) +{ + XEvent xev; + Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; + XSendEvent(display, DefaultRootWindow(display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void return_fullscreen(Display *display, Window window) +{ + XEvent xev; + Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False); + Atom fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE + xev.xclient.data.l[1] = fullscreen; + xev.xclient.data.l[2] = 0; + XSendEvent(display, DefaultRootWindow(display), False, + SubstructureNotifyMask | SubstructureRedirectMask, &xev); +} + +void toggle_fullscreen(Display *display, Window window) { + if (fullscreen) { + return_fullscreen(display, window); + } else { + go_fullscreen(display, window); + } + + fullscreen = !fullscreen; +} diff --git a/x11.h b/x11.h index dd0a346..ae7aaa4 100644 --- a/x11.h +++ b/x11.h @@ -5,3 +5,4 @@ void resize_x11_window(Display *display, Window window); int init_x11(Display **display, Window *window); void close_x11(Display **display, Window *window); +void toggle_fullscreen(Display *display, Window window);