aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlec Goncharow <alec@goncharow.dev>2026-07-10 12:22:02 -0400
committerAlec Goncharow <alec@goncharow.dev>2026-07-10 12:22:02 -0400
commit356465b85ba7e16eaaec68cbc22cd5e84a0a2a44 (patch)
tree9be1bae951ab3de710eedcbc3a12e5c639d36d71
parent93ad8a5cdeb8421bcf02195bc470257935062138 (diff)
Some old patches
-rw-r--r--src/apps/maze_app.jai110
-rw-r--r--src/main.jai1
-rw-r--r--src/ui_panel.jai24
3 files changed, 121 insertions, 14 deletions
diff --git a/src/apps/maze_app.jai b/src/apps/maze_app.jai
index b3b588c..7efb0ab 100644
--- a/src/apps/maze_app.jai
+++ b/src/apps/maze_app.jai
@@ -1,6 +1,7 @@
#import "Basic";
#import "Math";
#import "String";
+#import "Random";
Maze_Data :: struct {
rows: int;
@@ -23,6 +24,17 @@ Maze_Data :: struct {
font: *Simp.Dynamic_Font;
panel: UI_Panel;
+
+ // Generator State
+ is_generating: bool;
+ frontier: [..] Int2;
+ time_since_last_step: float;
+ generate_speed: float;
+}
+
+Int2 :: struct {
+ x: int; // col
+ y: int; // row
}
init_maze_app :: (widget: *Widget_State) {
@@ -49,17 +61,20 @@ init_maze_app :: (widget: *Widget_State) {
// Setup UI Panel
data.panel.x = 20.0;
- data.panel.y = cast(float)window_height - 300.0;
+ data.panel.y = cast(float)window_height - 340.0;
data.panel.w = 200.0;
- data.panel.h = 240.0;
+ data.panel.h = 280.0;
data.panel.userdata = data; // Give callback access to our data!
data.panel.app_owner = widget; // Bind panel visibility to this app!
array_add(*data.panel.elements, .{type=.HEADER, id=5, x0=0.0, y0=data.panel.h - 40.0, x1=data.panel.w, y1=data.panel.h, label="Controls"});
// Inputs (from top to bottom, note Simp Y=0 is bottom, so highest Y is top)
- array_add(*data.panel.elements, .{type=.TEXT_INPUT, id=1, x0=10.0, y0=data.panel.h - 100.0, x1=190.0, y1=data.panel.h - 60.0, label="Width:", input_buf=*data.width_input});
- array_add(*data.panel.elements, .{type=.TEXT_INPUT, id=2, x0=10.0, y0=data.panel.h - 160.0, x1=190.0, y1=data.panel.h - 120.0, label="Height:", input_buf=*data.height_input});
+ array_add(*data.panel.elements, .{type=.TEXT_INPUT, id=1, x0=10.0, y0=data.panel.h - 100.0, x1=190.0, y1=data.panel.h - 60.0, label="Width:", input_buf=*data.width_input, text_filter=ui_filter_for_nums });
+ array_add(*data.panel.elements, .{type=.TEXT_INPUT, id=2, x0=10.0, y0=data.panel.h - 160.0, x1=190.0, y1=data.panel.h - 120.0, label="Height:", input_buf=*data.height_input,text_filter=ui_filter_for_nums });
+
+ // Generate Button
+ array_add(*data.panel.elements, .{type=.BUTTON, id=3, x0=10.0, y0=data.panel.h - 220.0, x1=190.0, y1=data.panel.h - 180.0, label="Generate", action=generate_maze_callback});
// Back button at bottom
array_add(*data.panel.elements, .{type=.BUTTON, id=4, x0=10.0, y0=10.0, x1=190.0, y1=50.0, label="Back", action=(userdata: *void) { switch_app(*menu_state); }});
@@ -75,31 +90,101 @@ init_maze_app :: (widget: *Widget_State) {
widget.update = (widget: *Widget_State, dt: float) {
data := cast(*Maze_Data) widget.data;
-
- // maze gen / solving step happens in here maybe?
- };
-
- widget.shutdown = (widget: *Widget_State) {
- data := cast(*Maze_Data) widget.data;
- ui_unregister_panel(*data.panel);
+ if data.is_generating {
+ data.time_since_last_step += dt;
+ steps := cast(int)(data.time_since_last_step * data.generate_speed);
+ if steps > 0 {
+ data.time_since_last_step -= cast(float)steps / data.generate_speed;
+ for 1..steps {
+ if data.frontier.count > 0 {
+ idx := cast(int)(random_get() % cast(u64)data.frontier.count);
+ cell := data.frontier[idx];
+ array_ordered_remove_by_index(*data.frontier, idx);
+
+ if !data.grid[cell.y * data.cols + cell.x] continue; // already visited
+
+ empty_neighbors: [..] Int2;
+ if cell.y >= 2 && !data.grid[(cell.y - 2) * data.cols + cell.x] array_add(*empty_neighbors, .{cell.x, cell.y - 2});
+ if cell.y < data.rows - 2 && !data.grid[(cell.y + 2) * data.cols + cell.x] array_add(*empty_neighbors, .{cell.x, cell.y + 2});
+ if cell.x >= 2 && !data.grid[cell.y * data.cols + cell.x - 2] array_add(*empty_neighbors, .{cell.x - 2, cell.y});
+ if cell.x < data.cols - 2 && !data.grid[cell.y * data.cols + cell.x + 2] array_add(*empty_neighbors, .{cell.x + 2, cell.y});
+
+ if empty_neighbors.count > 0 {
+ n_idx := cast(int)(random_get() % cast(u64)empty_neighbors.count);
+ n := empty_neighbors[n_idx];
+
+ wall_x := (cell.x + n.x) / 2;
+ wall_y := (cell.y + n.y) / 2;
+ data.grid[wall_y * data.cols + wall_x] = false;
+ data.grid[cell.y * data.cols + cell.x] = false;
+
+ if cell.y >= 3 && data.grid[(cell.y - 2) * data.cols + cell.x] array_add(*data.frontier, .{cell.x, cell.y - 2});
+ if cell.y < data.rows - 3 && data.grid[(cell.y + 2) * data.cols + cell.x] array_add(*data.frontier, .{cell.x, cell.y + 2});
+ if cell.x >= 3 && data.grid[cell.y * data.cols + cell.x - 2] array_add(*data.frontier, .{cell.x - 2, cell.y});
+ if cell.x < data.cols - 3 && data.grid[cell.y * data.cols + cell.x + 2] array_add(*data.frontier, .{cell.x + 2, cell.y});
+ }
+ array_free(empty_neighbors);
+ } else {
+ data.is_generating = false;
+ break;
+ }
+ }
+ }
+ }
};
- widget.update = (widget: *Widget_State, dt: float) {};
widget.handle_input = handle_input;
widget.render = render;
widget.shutdown = (widget: *Widget_State) {
data := cast(*Maze_Data) widget.data;
+ ui_unregister_panel(*data.panel);
array_free(data.grid);
array_free(data.width_input);
array_free(data.height_input);
+ array_free(data.frontier);
free(data);
};
}
#scope_file
+generate_maze_callback :: (userdata: *void) {
+ data := cast(*Maze_Data) userdata;
+
+ // Fill with walls
+ for r: 0..data.rows-1 {
+ for c: 0..data.cols-1 {
+ data.grid[r * data.cols + c] = true;
+ }
+ }
+
+ // Pick start node (odd, odd)
+ start_r := 1;
+ start_c := 1;
+ if data.rows > 2 && data.cols > 2 {
+ start_r = cast(int)(random_get() % cast(u64)(data.rows / 2)) * 2 + 1;
+ start_c = cast(int)(random_get() % cast(u64)(data.cols / 2)) * 2 + 1;
+ }
+
+ data.grid[start_r * data.cols + start_c] = false;
+
+ array_reset(*data.frontier);
+ // Add neighbors of start node
+ if start_r >= 3 array_add(*data.frontier, .{start_c, start_r - 2});
+ if start_r < data.rows - 3 array_add(*data.frontier, .{start_c, start_r + 2});
+ if start_c >= 3 array_add(*data.frontier, .{start_c - 2, start_r});
+ if start_c < data.cols - 3 array_add(*data.frontier, .{start_c + 2, start_r});
+
+ data.is_generating = true;
+ data.time_since_last_step = 0;
+ data.generate_speed = 60.0; // steps per second
+}
+
apply_resize :: (data: *Maze_Data) {
+ data.is_generating = false;
+
+
w_str: string;
w_str.data = data.width_input.data;
w_str.count = data.width_input.count;
@@ -176,6 +261,7 @@ handle_input :: (widget: *Widget_State, occluded: bool) -> (occludes: bool) {
if data.panel.focus != 0 {
apply_resize(data);
data.panel.focus = 0;
+ ui_clear_focus();
}
}
diff --git a/src/main.jai b/src/main.jai
index 897cfb7..176f390 100644
--- a/src/main.jai
+++ b/src/main.jai
@@ -35,7 +35,6 @@ switch_app :: (new_app: *Widget_State) {
active_app = new_app;
}
-
main :: () {
window = create_window(window_width, window_height, "Jai Sandbox");
Simp.set_render_target(window);
diff --git a/src/ui_panel.jai b/src/ui_panel.jai
index cc0a2e2..c27b40d 100644
--- a/src/ui_panel.jai
+++ b/src/ui_panel.jai
@@ -9,6 +9,7 @@ UI_Element :: struct {
x0, y0, x1, y1: float;
label: string;
input_buf: *[..] u8;
+ text_filter: (char: u32) -> bool = null;
action: (userdata: *void) = null;
}
@@ -32,6 +33,7 @@ UI_Panel :: struct {
#scope_file
active_panels: [..] *UI_Panel;
text_edit: *[..] u8;
+text_filter: (char: u32) -> bool;
#scope_export
ui_register_panel :: (panel: *UI_Panel) {
@@ -47,6 +49,15 @@ ui_bring_to_front :: (panel: *UI_Panel) {
array_add(*active_panels, panel);
}
+ui_filter_for_nums :: (value: u32) -> bool {
+ return value >= #char "0" && value <= #char "9";
+}
+
+ui_clear_focus :: () {
+ text_edit = null;
+ text_filter = null;
+}
+
ui_handle_input :: (active_app: *void) -> (occludes: bool) {
occludes := false;
mouse_x, mouse_y, success := get_mouse_pointer_position(window, true);
@@ -83,6 +94,11 @@ ui_handle_input :: (active_app: *void) -> (occludes: bool) {
if text_edit {
dirty_text = true;
value := it.utf32;
+ if text_filter {
+ if !text_filter(value) {
+ continue;
+ }
+ }
str := Unicode.character_utf32_to_utf8(value);
for 0..str.count-1 {
@@ -161,6 +177,7 @@ ui_handle_input :: (active_app: *void) -> (occludes: bool) {
} else if elem.type == .TEXT_INPUT {
panel.focus = elem.id;
text_edit = elem.input_buf;
+ text_filter = elem.text_filter;
} else if elem.type == .BUTTON && mouse_released {
if elem.action elem.action(panel.userdata);
}
@@ -173,10 +190,15 @@ ui_handle_input :: (active_app: *void) -> (occludes: bool) {
if !hit_element {
panel.focus = 0;
- text_edit = null;
+ ui_clear_focus();
}
}
return true;
+ } else {
+ if mouse_btn {
+ panel.focus = 0;
+ ui_clear_focus();
+ }
}
}
return false;