From 05b7b28f5c9eb51b7fda80d435de1f405dae3b08 Mon Sep 17 00:00:00 2001 From: Marto Date: Mon, 27 Apr 2026 22:24:06 +0200 Subject: [PATCH] Replaced all u32 instances with uint, implemented road length, implemented basic pathing checks/algorithms, implemented entities id display for easier debugging --- src/common/structures.odin | 4 +-- src/draw.odin | 44 ++++++++++++++++++++++++++++++--- src/infrastructure/node.odin | 6 ++--- src/infrastructure/road.odin | 8 +++--- src/infrastructure_helpers.odin | 44 +++++++++++++++++++++------------ src/input.odin | 4 ++- src/pathfinding.odin | 27 ++++++++++++-------- src/simulator.odin | 22 +++++++++-------- src/vehicles/car.odin | 8 +++--- 9 files changed, 114 insertions(+), 53 deletions(-) diff --git a/src/common/structures.odin b/src/common/structures.odin index afa5ef5..11af7d0 100644 --- a/src/common/structures.odin +++ b/src/common/structures.odin @@ -5,7 +5,7 @@ import rl "vendor:raylib" // Stores data about intersections Intersection_Data :: struct { // Index of the road that is intersected - road: u32, + road: uint, // The exact point of intersection point: rl.Vector2, } @@ -26,5 +26,5 @@ Car_Position :: struct { // Tracks which infrastructure the vehicle occupies type: Infrastructure, // Tracks the reference - ref: u32, + ref: uint, } \ No newline at end of file diff --git a/src/draw.odin b/src/draw.odin index 7cc02af..715c6d6 100644 --- a/src/draw.odin +++ b/src/draw.odin @@ -1,6 +1,7 @@ package main import "core:fmt" +import sc "core:strconv" import rl "vendor:raylib" import "common" @@ -14,17 +15,18 @@ draw :: proc(self: ^Simulator, pos: rl.Vector2) { draw_cars(self) draw_temp_road(self, pos) + if self.display_entity_data do draw_entity_data(self) draw_ui(self) } @(private="file") draw_roads :: proc(self: ^Simulator) { - for &road, index in self.roads { + for road, index in self.roads { start := road.nodes[0] end := road.nodes[1] road_colour: rl.Color - if road, ok := self.highlighted_road.?; ok && road == u32(index) && self.delete_mode { + if road, ok := self.highlighted_road.?; ok && road == uint(index) && self.delete_mode { road_colour = common.ROAD_HIGHLIGHT_COLOUR } else do road_colour = common.ROAD_COLOUR @@ -34,7 +36,7 @@ draw_roads :: proc(self: ^Simulator) { @(private="file") draw_nodes :: proc(self: ^Simulator) { - for &node in self.nodes { + for node in self.nodes { // draws the snapping radius if key is held down if self.show_details do rl.DrawCircleV(node.pos, common.NODE_SNAP_RADIUS, common.NODE_SNAP_COLOUR) // draws the node @@ -44,7 +46,7 @@ draw_nodes :: proc(self: ^Simulator) { @(private="file") draw_cars :: proc(self: ^Simulator) { - for &car in self.cars { + for car in self.cars { ref := car.pos.ref // TODO fix in the future // let's fix it by tracking length of the road and @@ -78,4 +80,38 @@ draw_temp_road :: proc(self: ^Simulator, pos: rl.Vector2) { draw_ui :: proc(self: ^Simulator) { entity_count := fmt.ctprintf("Nodes: %d, Roads: %d, Cars: %d", len(self.nodes), len(self.roads), len(self.cars)) rl.DrawText(entity_count, i32(len(entity_count)), common.HEIGHT - common.TEXT_SIZE, common.TEXT_SIZE, common.TEXT_COLOUR) +} + +// Draws ID's on top of all entities (roads, nodes, cars, etc.) +@(private="file") +draw_entity_data :: proc(self: ^Simulator) { + colour := rl.ORANGE + + for node, index in self.nodes { + radius_diff := f32(common.NODE_RADIUS / 2) + + actual_pos: rl.Vector2 = { + node.pos.x - radius_diff, + node.pos.y - radius_diff, + } + + id := fmt.caprintf("%d", index) + rl.DrawText(id, i32(actual_pos.x), i32(actual_pos.y), common.TEXT_SIZE, colour) + } + + // todo fix in the future + for road, index in self.roads { + id := fmt.caprintf("%d", index) + + start := self.nodes[road.nodes[0]] + end := self.nodes[road.nodes[1]] + + // calculate the appropriate coordiante + // first get the leftmost node + leftmost := start.pos.x <= end.pos.x ? start : end + + rl.DrawText(id, i32(leftmost.pos.x + road.length / 2), i32((start.pos.y + end.pos.y) / 2), common.TEXT_SIZE, colour) + } + + // todo for cars } \ No newline at end of file diff --git a/src/infrastructure/node.odin b/src/infrastructure/node.odin index a54d7d6..8af6106 100644 --- a/src/infrastructure/node.odin +++ b/src/infrastructure/node.odin @@ -10,7 +10,7 @@ Node :: struct { pos: rl.Vector2, // All of the roads that are connected to the node itself; // Stores the index of the Road object that is stored within Simulator struct in roads dynamic array - roads: [dynamic]u32, + roads: [dynamic]uint, } // Constructor @@ -37,7 +37,7 @@ node_within_snapping_radius :: proc(self: ^Node, pos: rl.Vector2) -> bool { } // Tries to remove the road reference from the node; returns false if failed -node_unreference_road :: proc(self: ^Node, road_to_unref: u32) -> bool { +node_unreference_road :: proc(self: ^Node, road_to_unref: uint) -> bool { for i in 0.. bool { } // Attempts to update the existing road references with new one; returns false if it can't find the old reference -node_update_road_reference :: proc(self: ^Node, old_ref: u32, new_ref: u32) -> bool { +node_update_road_reference :: proc(self: ^Node, old_ref: uint, new_ref: uint) -> bool { for &road in self.roads { if road != old_ref do continue diff --git a/src/infrastructure/road.odin b/src/infrastructure/road.odin index f180f8d..808307d 100644 --- a/src/infrastructure/road.odin +++ b/src/infrastructure/road.odin @@ -4,20 +4,22 @@ import "../common" Road :: struct { // Index to nodes that limit the road - nodes: [2]u32, + nodes: [2]uint, speed_limit: u8, + length: f32, } // Road Initialisation -road_init :: proc(start: u32, end: u32) -> Road { +road_init :: proc(start: uint, end: uint, calculated_length: f32) -> Road { return { nodes = {start, end}, speed_limit = common.DEFAULT_SPEED_LIMIT, + length = calculated_length } } // Updates existing node reference to a new one; returns false if old ref was not found -road_update_node_reference :: proc(self: ^Road, old_ref: u32, new_ref: u32) -> bool { +road_update_node_reference :: proc(self: ^Road, old_ref: uint, new_ref: uint) -> bool { for &node in self.nodes { if node != old_ref do continue diff --git a/src/infrastructure_helpers.odin b/src/infrastructure_helpers.odin index ae8a85f..e759862 100644 --- a/src/infrastructure_helpers.odin +++ b/src/infrastructure_helpers.odin @@ -1,5 +1,6 @@ package main +import "core:math" import rl "vendor:raylib" import "core:math/rand" @@ -9,9 +10,9 @@ import v "vehicles" // This function only returns the index to the node or if it doesn't exist bool in the tuple is false @private -get_node_index_if_exists :: proc(self: ^Simulator, pos: rl.Vector2) -> (u32, bool) { +get_node_index_if_exists :: proc(self: ^Simulator, pos: rl.Vector2) -> (uint, bool) { for &node, index in self.nodes { - if inf.node_within_snapping_radius(&node, pos) do return u32(index), true + if inf.node_within_snapping_radius(&node, pos) do return uint(index), true } return 0, false @@ -20,19 +21,19 @@ get_node_index_if_exists :: proc(self: ^Simulator, pos: rl.Vector2) -> (u32, boo // Given position, the function will attempt the return the pointer to the node in near vicinity, // or if unsuccesful manually creating the node based on the position in the list and then returning the pointer to it @private -get_node_or_new :: proc(self: ^Simulator, pos: rl.Vector2) -> u32 { +get_node_or_new :: proc(self: ^Simulator, pos: rl.Vector2) -> uint { if node, ok := get_node_index_if_exists(self, pos); ok do return node node := inf.node_init(pos) append(&self.nodes, node) - return u32(len(self.nodes) - 1) + return uint(len(self.nodes) - 1) } // Attempts to update node reference to the road; // Returns false if the old reference doesn't exist @private -update_node_reference :: proc(self: ^Simulator, road_to_update: u32, old_ref: u32, new_ref: u32) -> bool { +update_node_reference :: proc(self: ^Simulator, road_to_update: uint, old_ref: uint, new_ref: uint) -> bool { road := &self.roads[road_to_update] for i in 0.. ([2]u32, bool) { - mlen: u32 +delete_entity :: proc(self: ^Simulator, entity_index: uint, type: common.Entity) -> ([2]uint, bool) { + mlen: uint // Stores data about old and new index in case the deleted index is not last, meaning the swap occurs - index_change: [2]u32 + index_change: [2]uint // Tracks whether the removal of node/road will cause a swap in the (dynamic) array // and thus forcing the pre-swapped reference to be updated swap_made: bool switch type { case .Node: - mlen = u32(len(self.nodes)) + mlen = uint(len(self.nodes)) case .Road: - mlen = u32(len(self.roads)) + mlen = uint(len(self.roads)) case .Car: - mlen = u32(len(self.cars)) + mlen = uint(len(self.cars)) } last := mlen - 1 @@ -81,7 +82,7 @@ delete_entity :: proc(self: ^Simulator, entity_index: u32, type: common.Entity) pos := self.cars[i].pos if pos.type != .Node || pos.ref != entity_index do continue - delete_entity(self, u32(i), .Car) + delete_entity(self, uint(i), .Car) } unordered_remove(&self.nodes, entity_index) @@ -96,7 +97,7 @@ delete_entity :: proc(self: ^Simulator, entity_index: u32, type: common.Entity) pos := self.cars[i].pos if pos.type != .Road || pos.ref != entity_index do continue - delete_entity(self, u32(i), .Car) + delete_entity(self, uint(i), .Car) } unordered_remove(&self.roads, entity_index) @@ -115,8 +116,8 @@ delete_entity :: proc(self: ^Simulator, entity_index: u32, type: common.Entity) } // Returns a random node that has no cars on it -get_free_node :: proc(self: ^Simulator) -> Maybe(u32) { - car_occupied_nodes: [dynamic]u32 +get_free_node :: proc(self: ^Simulator) -> Maybe(uint) { + car_occupied_nodes: [dynamic]uint for car in self.cars { if car.pos.type != .Node do continue @@ -127,8 +128,19 @@ get_free_node :: proc(self: ^Simulator) -> Maybe(u32) { if len(car_occupied_nodes) == len(self.nodes) do return nil for { - node := rand.uint32_max(u32(len(self.nodes))) + node := rand.uint_max(uint(len(self.nodes))) if !common.list_contains(car_occupied_nodes[:], node) do return node } +} + +calculate_road_length :: proc(self: ^Simulator, start: uint, end: uint) -> f32 { + start_pos := self.nodes[start].pos + end_pos := self.nodes[end].pos + + x_diff := end_pos.x - start_pos.x + y_diff := end_pos.y - start_pos.y + len := math.sqrt(x_diff * x_diff - y_diff * y_diff) + + return len } \ No newline at end of file diff --git a/src/input.odin b/src/input.odin index 57c56ca..192b84f 100644 --- a/src/input.odin +++ b/src/input.odin @@ -18,6 +18,8 @@ handle_keyboard_input :: proc(self: ^Simulator) { self.show_details = rl.IsKeyDown(.LEFT_ALT) self.auto_continue = rl.IsKeyDown(.LEFT_CONTROL) self.delete_mode = rl.IsKeyDown(.LEFT_SHIFT) + + if rl.IsKeyReleased(.TAB) do self.display_entity_data = !self.display_entity_data if rl.IsKeyReleased(.C) { self.temp_node = nil @@ -32,7 +34,7 @@ handle_keyboard_input :: proc(self: ^Simulator) { car := v.car_init(node_id, self.nodes[:]) set_car_route(self, &car) append(&self.cars, car) - v.car_print_route(u32(len(self.cars)) - 1, &car) + v.car_print_route(uint(len(self.cars)) - 1, &car) } } diff --git a/src/pathfinding.odin b/src/pathfinding.odin index 64f2ce6..4011d84 100644 --- a/src/pathfinding.odin +++ b/src/pathfinding.odin @@ -7,15 +7,22 @@ import inf "infrastructure" import v "vehicles" // Returns path to destination node => road => node -get_path_to_destination :: proc(self: ^Simulator, source: u32, destination: u32) -> []u32 { - source_node := self.nodes[source] - destination_node := self.nodes[destination] +get_path_to_destination :: proc(self: ^Simulator, node_to_search: uint, destination: uint, nodes_to_ignore: ^[dynamic]uint) -> []uint { + if !self.nodes[node_to_search].enabled || common.list_contains(nodes_to_ignore[:], node_to_search) do return {} + append(nodes_to_ignore, node_to_search) - return nil + nodes: []uint + + // TODO!!! + // if node_to_search == destination do nodes + + + + return nodes } // Returns if path is reachable from node => destination -get_destination_reachable :: proc(self: ^Simulator, node_to_search: u32, destination: u32, nodes_to_ignore: ^[dynamic]u32) -> bool { +get_destination_reachable :: proc(self: ^Simulator, node_to_search: uint, destination: uint, nodes_to_ignore: ^[dynamic]uint) -> bool { if !self.nodes[node_to_search].enabled || common.list_contains(nodes_to_ignore[:], node_to_search) do return false append(nodes_to_ignore, node_to_search) @@ -28,9 +35,9 @@ get_destination_reachable :: proc(self: ^Simulator, node_to_search: u32, destina } @(private="file") -get_neighbouring_nodes :: proc(self: ^Simulator, node_index: u32) -> []u32 { +get_neighbouring_nodes :: proc(self: ^Simulator, node_index: uint) -> []uint { node := self.nodes[node_index] - neighbour_nodes := make([dynamic]u32, 0, len(node.roads)) + neighbour_nodes := make([dynamic]uint, 0, len(node.roads)) for road_index in node.roads { road := self.roads[road_index] @@ -47,13 +54,13 @@ get_neighbouring_nodes :: proc(self: ^Simulator, node_index: u32) -> []u32 { set_car_route :: proc(self: ^Simulator, car: ^v.Car) { destination_reachable := false - destination: u32 + destination: uint for !destination_reachable { - ignored_nodes: [dynamic]u32 + ignored_nodes: [dynamic]uint for { - destination = rand.uint32_max(u32(len(self.nodes))) + destination = rand.uint_max(uint(len(self.nodes))) if car.pos.type != .Node || car.pos.ref != destination do break } diff --git a/src/simulator.odin b/src/simulator.odin index d658d27..04f363d 100644 --- a/src/simulator.odin +++ b/src/simulator.odin @@ -15,15 +15,17 @@ Simulator :: struct { // Stores all cars cars: [dynamic]v.Car, // Tracks the temporary node location - temp_node: Maybe(u32), + temp_node: Maybe(uint), // Tracks the selected road - highlighted_road: Maybe(u32), + highlighted_road: Maybe(uint), // Tracks whether the user wishes to see node's snapping radius show_details: bool, // Tracks whether after placing a road new one will start being placed auto_continue: bool, // Tracks whether the delete mode is activated delete_mode: bool, + // Tracks whether entity IDs should be displayed + display_entity_data: bool, } // Destructor @@ -66,7 +68,7 @@ create_road :: proc(self: ^Simulator, pos: rl.Vector2) { // Returns data about roads that intersect the given 2 nodes (points) @(private="file") -get_intersecting_roads :: proc(self: ^Simulator, start: u32, end: u32) -> []common.Intersection_Data { +get_intersecting_roads :: proc(self: ^Simulator, start: uint, end: uint) -> []common.Intersection_Data { intersections: [dynamic]common.Intersection_Data collision_point: rl.Vector2 @@ -78,7 +80,7 @@ get_intersecting_roads :: proc(self: ^Simulator, start: u32, end: u32) -> []comm // Save the collision info data := common.Intersection_Data { - road = u32(index), + road = uint(index), point = collision_point } @@ -99,7 +101,7 @@ get_intersecting_roads :: proc(self: ^Simulator, start: u32, end: u32) -> []comm // Given intersection data, the function splits all existing roads and adds new nodes on intersections @(private="file") -split_roads_by_points :: proc(self: ^Simulator, intersections: []common.Intersection_Data, start: u32, end: u32) { +split_roads_by_points :: proc(self: ^Simulator, intersections: []common.Intersection_Data, start: uint, end: uint) { if len(intersections) == 0 { add_road(self, start, end) return @@ -142,18 +144,18 @@ split_roads_by_points :: proc(self: ^Simulator, intersections: []common.Intersec // Adds a new road into roads array, start and end are indexes of existing nodes @(private="file") -add_road :: proc(self: ^Simulator, start: u32, end: u32) { - road := inf.road_init(start, end) +add_road :: proc(self: ^Simulator, start: uint, end: uint) { + road := inf.road_init(start, end, calculate_road_length(self, start, end)) append(&self.roads, road) - road_index := u32(len(self.roads) - 1) + road_index := uint(len(self.roads) - 1) append(&self.nodes[start].roads, road_index) append(&self.nodes[end].roads, road_index) } // Deletes the road which index was sent in, alongside deleting references of said road and removal of nodes if that road was their only connection @private -delete_road :: proc(self: ^Simulator, road_to_delete: u32) { +delete_road :: proc(self: ^Simulator, road_to_delete: uint) { // First we need to unreference this road from surrounding nodes and then delete those nodes IF this was the last road connection road := self.roads[road_to_delete] // Pointers to the nodes bordering the road we wish to delete @@ -188,7 +190,7 @@ update_highlighted_road :: proc(self: ^Simulator, pos: rl.Vector2) { if !rl.CheckCollisionPointLine(pos, start_node.pos, end_node.pos, common.ROAD_SIZE) do continue - self.highlighted_road = u32(index) + self.highlighted_road = uint(index) return } diff --git a/src/vehicles/car.odin b/src/vehicles/car.odin index 6dacba5..12a8564 100644 --- a/src/vehicles/car.odin +++ b/src/vehicles/car.odin @@ -18,14 +18,14 @@ Car :: struct { // Car's current node/road pos: common.Car_Position, // Car's destination node - destination: Maybe(u32), + destination: Maybe(uint), // tracks absolute pos (within canvas) absolute_pos: rl.Vector2, } // Constructor -car_init :: proc(spawn_node: u32, nodes: []inf.Node) -> Car { +car_init :: proc(spawn_node: uint, nodes: []inf.Node) -> Car { return { fuel_level = common.FUEL_MAX, max_speed = common.CAR_MAX_SPEED, @@ -38,13 +38,13 @@ car_init :: proc(spawn_node: u32, nodes: []inf.Node) -> Car { } // Updates (origin and destination) node reference -car_update_node_reference :: proc(self: ^Car, old_ref: u32, new_ref: u32) { +car_update_node_reference :: proc(self: ^Car, old_ref: uint, new_ref: uint) { if self.pos.type == .Node && self.pos.ref == old_ref do self.pos.ref = new_ref if self.destination == old_ref do self.destination = new_ref } // Prints car's route -car_print_route :: proc(id: Maybe(u32) = nil, self: ^Car) { +car_print_route :: proc(id: Maybe(uint) = nil, self: ^Car) { val, ok := self.destination.? destination := ok ? fmt.aprintf("N%d", val) : "/" source_type := self.pos.type == .Node ? 'N' : 'R'