From 87c09f1e27759b1aab074640e5621c58985db88f Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Fri, 3 Jan 2025 18:17:18 +0200 Subject: [PATCH] add creation of sub goals through requirements --- api/root.zig | 1 + api/schemas/simple_item.zig | 16 ++++++++-- cli/main.zig | 2 +- lib/craft_goal.zig | 35 +++++++++++++------- lib/gather_goal.zig | 9 ++++++ lib/root.zig | 64 +++++++++++++++++++++++++++++++++++-- 6 files changed, 109 insertions(+), 18 deletions(-) diff --git a/api/root.zig b/api/root.zig index af9f777..665e336 100644 --- a/api/root.zig +++ b/api/root.zig @@ -23,6 +23,7 @@ pub const Equipment = @import("./schemas/equipment.zig"); pub const Craft = @import("./schemas/craft.zig"); pub const Resource = @import("./schemas/resource.zig"); pub const MoveResult = @import("./schemas/move_result.zig"); +pub const SimpleItem = @import("./schemas/simple_item.zig"); const SkillUsageResult = @import("./schemas/skill_usage_result.zig"); pub const GatherResult = SkillUsageResult; pub const CraftResult = SkillUsageResult; diff --git a/api/schemas/simple_item.zig b/api/schemas/simple_item.zig index 8258556..b69fe8c 100644 --- a/api/schemas/simple_item.zig +++ b/api/schemas/simple_item.zig @@ -30,10 +30,10 @@ pub fn BoundedArray(comptime slot_count: u32) type { const Items = std.BoundedArray(SimpleItem, slot_count); return struct { - items: Items, + items: Items = .{ .len = 0 }, pub fn init() @This() { - return @This(){ .items = Items.init(0) catch unreachable }; + return @This(){}; } pub fn parse(store: *Store, slots_array: std.json.Array) !@This() { @@ -89,6 +89,16 @@ pub fn BoundedArray(comptime slot_count: u32) type { } } + pub fn addAssumeCapacity(self: *@This(), id: Store.Id, quantity: u64) void { + if (quantity == 0) return; + + if (self.findSlot(id)) |slot| { + slot.quantity += quantity; + } else { + self.items.appendAssumeCapacity(SimpleItem.init(id, quantity)); + } + } + pub fn addSlice(self: *@This(), items: []const SimpleItem) void { for (items) |item| { self.add(item.id, item.quantity); @@ -117,7 +127,7 @@ pub fn BoundedArray(comptime slot_count: u32) type { return count; } - pub fn slice(self: *@This()) []SimpleItem { + pub fn slice(self: @This()) []const SimpleItem { return self.items.slice(); } diff --git a/cli/main.zig b/cli/main.zig index 3d953c3..9f2f261 100644 --- a/cli/main.zig +++ b/cli/main.zig @@ -72,7 +72,7 @@ pub fn main() !void { _ = try artificer.appendGoal(Artificer.Goal{ .craft = .{ .item = store.items.getId("copper").?, - .quantity = 3 + .quantity = 2 } }); diff --git a/lib/craft_goal.zig b/lib/craft_goal.zig index debf948..ab80f37 100644 --- a/lib/craft_goal.zig +++ b/lib/craft_goal.zig @@ -1,12 +1,20 @@ // zig fmt: off const Api = @import("artifacts-api"); const Artificer = @import("./root.zig"); +const Requirements = Artificer.Requirements; const Goal = @This(); item: Api.Store.Id, quantity: u64, +fn getCraftMultiples(self: Goal, craft: Api.Craft) u64 { + return @intFromFloat(@ceil( + @as(f32, @floatFromInt(self.quantity)) / + @as(f32, @floatFromInt(craft.quantity)) + )); +} + pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void { const store = artificer.server.store; const character = store.characters.get(artificer.character).?; @@ -24,18 +32,6 @@ pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void return error.SkillTooLow; } - const craft_multiples: u64 = @intFromFloat(@ceil( - @as(f32, @floatFromInt(self.quantity)) / - @as(f32, @floatFromInt(craft.quantity)) - )); - - for (craft.items.items.slice()) |craft_item| { - const inventory_item_quantity = character.inventory.getQuantity(craft_item.id); - if (inventory_item_quantity < craft_item.quantity * craft_multiples) { - return error.NotEnoughItems; - } - } - const workshop_position = artificer.findNearestWorkstation(craft.skill).?; if (!workshop_position.eql(character.position)) { artificer.queued_actions.appendAssumeCapacity(.{ @@ -51,6 +47,21 @@ pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) !void }); } +pub fn requirements(self: Goal, artificer: *Artificer) Artificer.Requirements { + var reqs: Artificer.Requirements = .{}; + + const store = artificer.server.store; + const item = store.items.get(self.item).?; + const craft = item.craft.?; + const craft_multiples = self.getCraftMultiples(craft); + + for (craft.items.slice()) |craft_item| { + reqs.items.addAssumeCapacity(craft_item.id, craft_item.quantity * craft_multiples); + } + + return reqs; +} + pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void { _ = goal_id; diff --git a/lib/gather_goal.zig b/lib/gather_goal.zig index 36c65fe..712003d 100644 --- a/lib/gather_goal.zig +++ b/lib/gather_goal.zig @@ -34,6 +34,15 @@ pub fn tick(self: *Goal, goal_id: Artificer.GoalId, artificer: *Artificer) void }); } +pub fn requirements(self: Goal, artificer: *Artificer) Artificer.Requirements { + _ = self; + _ = artificer; + + const reqs: Artificer.Requirements = .{}; + // TODO: add skill requirement + return reqs; +} + pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void { _ = goal_id; diff --git a/lib/root.zig b/lib/root.zig index 3b4c98d..e8bb45f 100644 --- a/lib/root.zig +++ b/lib/root.zig @@ -16,7 +16,11 @@ pub const GoalId = packed struct { const Index = u11; generation: Generation, - index: Index + index: Index, + + pub fn eql(self: GoalId, other: GoalId) bool { + return self.index == other.index and self.generation == other.generation; + } }; const max_goals = std.math.maxInt(GoalId.Index); @@ -35,6 +39,13 @@ pub const Goal = union(enum) { } } + pub fn requirements(self: Goal, artificer: *Artificer) Requirements { + return switch (self) { + .gather => |gather| gather.requirements(artificer), + .craft => |craft| craft.requirements(artificer), + }; + } + pub fn onActionCompleted(self: *Goal, goal_id: Artificer.GoalId, result: Artificer.ActionResult) void { switch (self.*) { .gather => |*gather| gather.onActionCompleted(goal_id, result), @@ -45,6 +56,7 @@ pub const Goal = union(enum) { const GoalSlot = struct { generation: GoalId.Generation = 0, + parent_goal: ?GoalId = null, goal: ?Goal = null, }; @@ -68,6 +80,16 @@ const ActionSlot = struct { action: Action, }; +pub const Requirements = struct { + pub const Items = Api.SimpleItem.BoundedArray(blk: { + var max: usize = 0; + max = @max(max, Api.Craft.max_items); + break :blk max; + }); + + items: Items = .{} +}; + const QueuedActions = std.ArrayListUnmanaged(ActionSlot); server: *Api.Server, @@ -256,8 +278,19 @@ fn getGoalCount(self: *Artificer) u32 { return count; } +fn hasSubGoals(self: *Artificer, goal_id: GoalId) bool { + for (self.goal_slots) |goal_slot| { + if (goal_slot.goal != null and goal_slot.parent_goal != null and goal_slot.parent_goal.?.eql(goal_id)) { + return true; + } + } + + return false; +} + pub fn tick(self: *Artificer) !void { const store = self.server.store; + const character = store.characters.get(self.character).?; if (self.queued_actions.items.len > 0) { const expires_in = self.timeUntilCooldownExpires(); @@ -265,7 +298,6 @@ pub fn tick(self: *Artificer) !void { return; } - const character = store.characters.get(self.character).?; const action_slot = self.queued_actions.orderedRemove(0); const action_result = switch (action_slot.action) { .move => |position| ActionResult{ @@ -297,6 +329,34 @@ pub fn tick(self: *Artificer) !void { .generation = goal_slot.generation }; + if (self.hasSubGoals(goal_id)) { + continue; + } + + const reqs = goal.requirements(self); + for (reqs.items.slice()) |req_item| { + const inventory_quantity = character.inventory.getQuantity(req_item.id); + if (inventory_quantity < req_item.quantity) { + if (self.findBestResourceWithItem(req_item.id) != null) { + const subgoal_id = try self.appendGoal(.{ + .gather = .{ + .item = req_item.id, + .quantity = req_item.quantity - inventory_quantity, + } + }); + + const subgoal = self.getGoal(subgoal_id).?; + subgoal.parent_goal = goal_id; + } else { + @panic("Not all requirements were handled"); + } + } + } + + if (self.hasSubGoals(goal_id)) { + continue; + } + try goal.tick(goal_id, self); } }