From 1c847edd065dbc2a8ba79f654f11b7022c8a284b Mon Sep 17 00:00:00 2001 From: Joe Cowman Date: Fri, 3 Aug 2018 09:25:38 -0700 Subject: [PATCH 1/5] Started events guide [ci skip] --- docs/overview/events.md | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 docs/overview/events.md diff --git a/docs/overview/events.md b/docs/overview/events.md new file mode 100644 index 0000000..7b11eca --- /dev/null +++ b/docs/overview/events.md @@ -0,0 +1,46 @@ +# Events + +A game where eveything stays the same isn't much of a game. Therefore, **Regal** provides *events*. + +An *event* can be thought of as any change that occurs within your game. Any time the state of your game changes, it is contained in an event. This guide will help you use events in your game. + +## Event Functions + +The *Event Function* is one of **Regal**'s central ideas. + +An *Event Function* takes a `GameInstance` as an argument and modifies it. Its contract looks like this: + +```ts +type EventFunction = (game: GameInstance) => void; +``` + +Every *event* is actually an `EventFunction`. + +## Your First Event + +A very basic event might look like this: + +```ts +const greet: EventFunction = (game: GameInstance) => { + game.output.write("Hello, world!"); +} +``` + +This event takes in a `GameInstance` and writes "Hello, world!" to its output. We could call this event just like a function; all we need to do is pass in a `GameInstance`: + +```ts +const myGame = new GameInstance(); +greet(myGame); // Adds "Hello, world!" to myGame's output +``` + +## The `on` Function + +TODO: Write description here + +```ts +const greet = on("GREET", game => { + game.output.write("Hello, world!"); +}); +``` + +The `on` function used here is a helper function that generates an `EventFunction` \ No newline at end of file From e8fff9c0568bfc4c49dcb5e083092d33fd529081 Mon Sep 17 00:00:00 2001 From: Joe Cowman Date: Sun, 5 Aug 2018 19:09:42 -0700 Subject: [PATCH 2/5] Updated events docs --- docs/overview/events.md | 146 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 141 insertions(+), 5 deletions(-) diff --git a/docs/overview/events.md b/docs/overview/events.md index 7b11eca..ea1423c 100644 --- a/docs/overview/events.md +++ b/docs/overview/events.md @@ -8,10 +8,12 @@ An *event* can be thought of as any change that occurs within your game. Any tim The *Event Function* is one of **Regal**'s central ideas. -An *Event Function* takes a `GameInstance` as an argument and modifies it. Its contract looks like this: +An *Event Function* takes a `GameInstance` as an argument, modifies it, and returns any resulting `EventFunction`s. + +An `EventFunction`'s contract looks like this: ```ts -type EventFunction = (game: GameInstance) => void; +type EventFunction = (game: GameInstance) => EventFunction; ``` Every *event* is actually an `EventFunction`. @@ -23,10 +25,13 @@ A very basic event might look like this: ```ts const greet: EventFunction = (game: GameInstance) => { game.output.write("Hello, world!"); + return noop; } ``` -This event takes in a `GameInstance` and writes "Hello, world!" to its output. We could call this event just like a function; all we need to do is pass in a `GameInstance`: +This event takes in a `GameInstance` and writes "Hello, world!" to its output. `noop` is a special type of `EventFunction` that stands for *no operation*. It simply tells the event executor that there are no more events. + +We could call this event just like a function; all we need to do is pass in a `GameInstance`: ```ts const myGame = new GameInstance(); @@ -35,12 +40,143 @@ greet(myGame); // Adds "Hello, world!" to myGame's output ## The `on` Function -TODO: Write description here +Although that's a perfectly valid use of an `EventFunction`, **Regal** offers a function called `on` that wraps your events with some useful metadata. + +```ts +on(eventName: string, eventFunc: EventFunction): EventFunction; +``` + +The event from our previous example could be written with `on` like this: ```ts const greet = on("GREET", game => { game.output.write("Hello, world!"); + return noop; }); ``` -The `on` function used here is a helper function that generates an `EventFunction` \ No newline at end of file +The resulting `EventFunction` is called exactly like our previous example, as well. + +```ts +const myGame = new GameInstance(); +greet(myGame); // Adds "Hello, world!" to myGame's output +``` + +However, this stores some helpful metadata in `game.events.list`. + +```ts +myGame.events.list === [ + { + id: 1, // Auto-generated event ID + name: "GREET", + output: [ + "Hello, world!" + ] + } +]; +``` + +This is just a small sample of what is added when you track your events with `on`. It is highly recommended that you build your events this way. + +## Partially Applied Events + +It often happens that you'll want the effects of an event to be configurable whenever the event is used. This is done through a technique called **partial application**, in which the arguments of a function are applied at different times. + +For example, we could use partial application to create a generic version of our `greet` event: + +```ts +const greet = (name: string) => + on("GREET", game => { + game.output.write(`Hello, ${name}!`); + return noop; + }); +``` + +Now, `greet` can take any `name` and will generate an `EventFunction` using that argument. + +```ts +const myGame = new GameInstance(); +greet("Regal")(myGame); // Create an EventFunction where name = "Regal" + +myGame.events.list === [ + { + id: 1, + name: "GREET", + output: [ + "Hello, Regal!" // Our custom output + ] + } +]; +``` + +For even more fun, we can use **string templating** to let the event's name reflect its unique arguments! + +```ts +const greet = (name: string) => + on(`GREET <${name}>`, game => { + game.output.write(`Hello, ${name}!`); + return noop; + }); + +const myGame = new GameInstance(); +greet("Regal")(myGame); + +myGame.events.list === [ + { + id: 1, + name: "GREET ", // Unique name! + output: [ + "Hello, Regal!" + ] + } +]; +``` + +## Causing Additional Events + +```ts +const morning = on("MORNING", event => { + game.output.write("Have a great day!"); + return noop; +}); + +const afternoon = on("AFTERNOON", event => { + game.output.write("Keep it up!"); + return noop; +}); + +const motivate = (date: Date) => + on("MOTIVATE", game => { + return (date.getHours() < 12) ? morning : afternoon; + }); + +const myGame = new GameInstance(); +const myDate = new Date("August 5, 2018 10:15:00"); + +motivate(myDate)(myGame); +myGame.events.list === [ + { + id: 2, + causedBy: 1, + name: "MORNING", + output: [ + "Have a great day!" + ] + }, + { + id: 1, + name: "MOTIVATE", + caused: [ + 2 + ] + } +] +``` + +## Causing Multiple Events + +### Using `pipe` + +### Using `queue` + +## Tracking Agent Changes by Event \ No newline at end of file From fd81d37fdd5ac445b0c9041cbd2b14cf2242cd16 Mon Sep 17 00:00:00 2001 From: Joe Cowman Date: Sun, 5 Aug 2018 22:03:43 -0700 Subject: [PATCH 3/5] Improved events document <3 --- docs/overview/events.md | 204 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 197 insertions(+), 7 deletions(-) diff --git a/docs/overview/events.md b/docs/overview/events.md index ea1423c..f566037 100644 --- a/docs/overview/events.md +++ b/docs/overview/events.md @@ -8,7 +8,7 @@ An *event* can be thought of as any change that occurs within your game. Any tim The *Event Function* is one of **Regal**'s central ideas. -An *Event Function* takes a `GameInstance` as an argument, modifies it, and returns any resulting `EventFunction`s. +An `EventFunction` takes a `GameInstance` as an argument, modifies it, and returns any resulting `EventFunction`s. An `EventFunction`'s contract looks like this: @@ -80,7 +80,7 @@ This is just a small sample of what is added when you track your events with `on ## Partially Applied Events -It often happens that you'll want the effects of an event to be configurable whenever the event is used. This is done through a technique called **partial application**, in which the arguments of a function are applied at different times. +Often, you'll want the effects of an event to be configurable whenever the event is used. This is done through a technique called **partial application**, in which the arguments of a function are applied at different times. For example, we could use partial application to create a generic version of our `greet` event: @@ -96,7 +96,7 @@ Now, `greet` can take any `name` and will generate an `EventFunction` using that ```ts const myGame = new GameInstance(); -greet("Regal")(myGame); // Create an EventFunction where name = "Regal" +greet("Regal")(myGame); // Create an EventFunction where name == "Regal" myGame.events.list === [ { @@ -109,7 +109,7 @@ myGame.events.list === [ ]; ``` -For even more fun, we can use **string templating** to let the event's name reflect its unique arguments! +For even more fun, we can use **string templating** to let the event's name reflect its unique arguments. ```ts const greet = (name: string) => @@ -134,6 +134,10 @@ myGame.events.list === [ ## Causing Additional Events +As stated earlier, an `EventFunction` can return another `EventFunction`. This tells the event executor that another event should be executed on the game instance. + +Here is an example of an `EventFunction` causing another: + ```ts const morning = on("MORNING", event => { game.output.write("Have a great day!"); @@ -149,7 +153,11 @@ const motivate = (date: Date) => on("MOTIVATE", game => { return (date.getHours() < 12) ? morning : afternoon; }); +``` + +When the `motivate` event is executed, it checks the value of its `date` argument to determine which event should be caused next. +```ts const myGame = new GameInstance(); const myDate = new Date("August 5, 2018 10:15:00"); @@ -157,7 +165,7 @@ motivate(myDate)(myGame); myGame.events.list === [ { id: 2, - causedBy: 1, + causedBy: 1, // MOTIVATE (1) caused MORNING (2) name: "MORNING", output: [ "Have a great day!" @@ -175,8 +183,190 @@ myGame.events.list === [ ## Causing Multiple Events -### Using `pipe` +It's also possible to have one `EventFunction` cause multiple events. + +### Immediate Execution with `EventFunction.then` + +To have one event be executed immediately after another, use the `.then` method. This is useful in situations where multiple events should be executed in direct sequence. + +To demonstrate, here's an example of a player crafting a sword. When the `makeSword` event is executed, the sword is immediately added to the player's inventory (`addItemToInventory`) and the player learns the blacksmithing skill (`learnSkill`). + +```ts +const learnSkill = (name: string, skill: string) => + on(`LEARN SKILL <${skill}>`, game => { + game.output.write(`${name} learned ${skill}!`); + return noop; + }); + +const addItemToInventory = (name: string, item: string) => + on(`ADD ITEM <${item}>`, game => { + game.output.write(`Added ${item} to ${name}'s inventory.`); + return noop; + }); + +const makeSword = (name: string) => + on(`MAKE SWORD`, game => { + game.output.write(`${name} made a sword!`); + return learnSkill(name, "Blacksmithing") + .then(addItemToInventory(name, "Sword")); + }); +``` + +`GameInstance.events.list` contains the list of events that occurred, in reverse chronological order. + +```ts +const myGame = new GameInstance(); + +makeSword("King Arthur")(myGame); +myGame.events.list === [ + { + id: 3, + causedBy: 1, + name: "LEARN SKILL ", + output: [ + "King Arthur learned Blacksmithing!" + ] + }, + { + id: 2, + causedBy: 1, + name: "ADD ITEM ", + output: [ + "Added Sword to King Arthur's inventory." + ] + }, + { + id: 1, + name: "MAKE SWORD", + output: [ + "King Arthur made a sword!" + ] + caused: [ + 2, 3 + ] + } +] +``` + +### Delayed Execution with `enqueue` + +To schedule an event to be executed after all of the immediate events are finished, use the `enqueue` function. This is useful in situations where you have multiple series of events, and you want each series to execute their events in the same "round." + +This is best illustrated with an example: + +```ts +const hitGround = (item: string) => + on("HIT GROUND ", game => { + game.output.write(`${item} hits the ground. Thud!`); + return noop; + }); + +const fall = (item: string) => + on("FALL ", game => { + game.output.write(`${item} falls.`); + return enqueue(hitGround(item)); + }); + +const drop = (items: string[]) => + on("DROP ITEMS", game => { + let queue = enqueue(); + for (let item in items) { + queue = queue.enqueue(fall(item)); + } + return queue; + }); +``` + +We'll walk through each line, starting from the `drop` function. + +```ts +const drop = (items: string[]) => + on("DROP ITEMS", game => { +``` + +Our top-level event function `drop` takes in a list of items that are being dropped. + +```ts + let queue = enqueue(); +``` + +The `enqueue` function takes zero or more `EventFunction`s as arguments, which it uses to build another `EventFunction` called the event queue. Creating an empty queue has no effect; it simply provides us a reference to add additional events shortly. + +```ts + for (let item in items) { + queue = queue.enqueue(fall(item)); + } +``` + +In addition to being a standalone function, `enqueue` is also a method of `EventFunction`. Calling `EventFunction.enqueue` returns a new `EventFunction` which contains the event queue of all previously enqueued events and the new event(s). + +The previous two code blocks could be simplified using JavaScript's `map` function like so: + +```ts +const queue = enqueue(items.map(item => fall(item))); +``` + +Finally, we return the event queue, which is itself an `EventFunction`. + +```ts + return queue; + }); +``` + +The `fall` event is simpler: + +```ts +const fall = (item: string) => + on("FALL ", game => { + game.output.write(`${item} falls.`); + return enqueue(hitGround(item)); + }); +``` + +For each item that was dropped, we write an output message describing that item falling, and then we `enqueue` an event for that item hitting the ground. + +```ts +const hitGround = (item: string) => + on("HIT GROUND ", game => { + game.output.write(`${item} hits the ground. Thud!`); + return noop; + }); +``` + +All said and done, an execution of this group of events would look something like this: + +```ts +const myGame = new GameInstance(); +const items = ["Hat", "Duck", "Spoon"]; + +drop(items)(myGame); + +// myGame's output would print in this order: +// Hat falls. +// Duck falls. +// Spoon falls. +// Hat hits the ground. Thud! +// Duck hits the ground. Thud! +// Spoon hits the ground. Thud! +``` + +If you're still confused about the differene between `then` and `enqueue`, here is what the output would have been if `then` was used instead: + +```ts +// Hat falls. +// Hat hits the ground. Thud! +// Duck falls. +// Duck hits the ground. Thud! +// Spoon falls. +// Spoon hits the ground. Thud! +``` + +Remember, `enqueue` is useful for situations where you have multiple series of events, like our [`fall` -> `hitGround`] series, and you want each series to execute their individual events in the same "round." We didn't want `hat` to `fall` and then `hitGround`, *then* `duck` to `fall` and then `hitGround`, etc. We wanted all of the items to fall together, and then all of them to hit the ground together. + +If you wanted the opposite, (say you wanted `hat` to be done with hitting the ground before `duck` fell), then you would use `then`. + +`then` is for immediate execution; `enqueue` is for delayed exeuction. -### Using `queue` +Final note: instead of writing `enqueue`, you can use the shorthand `nq`. ## Tracking Agent Changes by Event \ No newline at end of file From 132fa46c6f9873884002f272fbbc112cc748a900 Mon Sep 17 00:00:00 2001 From: Joe Cowman Date: Mon, 6 Aug 2018 15:20:03 -0500 Subject: [PATCH 4/5] Revised events guide --- docs/overview/events.md | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/docs/overview/events.md b/docs/overview/events.md index f566037..2d2240b 100644 --- a/docs/overview/events.md +++ b/docs/overview/events.md @@ -8,7 +8,7 @@ An *event* can be thought of as any change that occurs within your game. Any tim The *Event Function* is one of **Regal**'s central ideas. -An `EventFunction` takes a `GameInstance` as an argument, modifies it, and returns any resulting `EventFunction`s. +An `EventFunction` takes a `GameInstance` as an argument, modifies it, and returns the next `EventFunction` to be executed. An `EventFunction`'s contract looks like this: @@ -16,7 +16,7 @@ An `EventFunction`'s contract looks like this: type EventFunction = (game: GameInstance) => EventFunction; ``` -Every *event* is actually an `EventFunction`. +Every *event* in a Regal game is actually an `EventFunction`. ## Your First Event @@ -40,7 +40,7 @@ greet(myGame); // Adds "Hello, world!" to myGame's output ## The `on` Function -Although that's a perfectly valid use of an `EventFunction`, **Regal** offers a function called `on` that wraps your events with some useful metadata. +Although that's a perfectly valid use of an `EventFunction`, **Regal** offers a function called `on` that wraps your events with some useful metadata and make executing multiple events easier. ```ts on(eventName: string, eventFunc: EventFunction): EventFunction; @@ -62,7 +62,7 @@ const myGame = new GameInstance(); greet(myGame); // Adds "Hello, world!" to myGame's output ``` -However, this stores some helpful metadata in `game.events.list`. +Since we used `on`, some useful metadata was stored in `game.events.list`. ```ts myGame.events.list === [ @@ -76,7 +76,7 @@ myGame.events.list === [ ]; ``` -This is just a small sample of what is added when you track your events with `on`. It is highly recommended that you build your events this way. +This is just a small sample of what is gained when you track your events with `on`. It is highly recommended that you build your events this way. ## Partially Applied Events @@ -96,7 +96,7 @@ Now, `greet` can take any `name` and will generate an `EventFunction` using that ```ts const myGame = new GameInstance(); -greet("Regal")(myGame); // Create an EventFunction where name == "Regal" +greet("Regal")(myGame); // Create an EventFunction where name == "Regal", then execute it myGame.events.list === [ { @@ -134,7 +134,7 @@ myGame.events.list === [ ## Causing Additional Events -As stated earlier, an `EventFunction` can return another `EventFunction`. This tells the event executor that another event should be executed on the game instance. +As stated earlier, an `EventFunction` returns another `EventFunction`. This tells the event executor that another event should be executed on the game instance. Here is an example of an `EventFunction` causing another: @@ -155,7 +155,7 @@ const motivate = (date: Date) => }); ``` -When the `motivate` event is executed, it checks the value of its `date` argument to determine which event should be caused next. +When the `motivate` event is executed, it checks the value of its `date` argument to determine which event should be executed next. ```ts const myGame = new GameInstance(); @@ -185,9 +185,11 @@ myGame.events.list === [ It's also possible to have one `EventFunction` cause multiple events. +There are two ways this can be done: immediately with `EventFunction.then` and delayed with `enqueue`. + ### Immediate Execution with `EventFunction.then` -To have one event be executed immediately after another, use the `.then` method. This is useful in situations where multiple events should be executed in direct sequence. +To have one event be executed immediately after another, use the `EventFunction.then` method. This is useful in situations where multiple events should be executed in direct sequence. To demonstrate, here's an example of a player crafting a sword. When the `makeSword` event is executed, the sword is immediately added to the player's inventory (`addItemToInventory`) and the player learns the blacksmithing skill (`learnSkill`). @@ -290,7 +292,7 @@ Our top-level event function `drop` takes in a list of items that are being drop let queue = enqueue(); ``` -The `enqueue` function takes zero or more `EventFunction`s as arguments, which it uses to build another `EventFunction` called the event queue. Creating an empty queue has no effect; it simply provides us a reference to add additional events shortly. +The `enqueue` function takes zero or more `EventFunction`s as arguments, which it uses to build a special type of `EventFunction` called an `EventQueue`. Creating an empty queue has no effect; it simply provides us a reference to add additional events shortly. ```ts for (let item in items) { @@ -298,7 +300,7 @@ The `enqueue` function takes zero or more `EventFunction`s as arguments, which i } ``` -In addition to being a standalone function, `enqueue` is also a method of `EventFunction`. Calling `EventFunction.enqueue` returns a new `EventFunction` which contains the event queue of all previously enqueued events and the new event(s). +In addition to being a standalone function, `enqueue` is also a method of `EventQueue`. Calling `EventQueue.enqueue` returns a new `EventQueue` which contains the all previously enqueued events plus the new event(s). The previous two code blocks could be simplified using JavaScript's `map` function like so: @@ -306,7 +308,7 @@ The previous two code blocks could be simplified using JavaScript's `map` functi const queue = enqueue(items.map(item => fall(item))); ``` -Finally, we return the event queue, which is itself an `EventFunction`. +Finally, we return the event queue. ```ts return queue; @@ -361,12 +363,21 @@ If you're still confused about the differene between `then` and `enqueue`, here // Spoon hits the ground. Thud! ``` -Remember, `enqueue` is useful for situations where you have multiple series of events, like our [`fall` -> `hitGround`] series, and you want each series to execute their individual events in the same "round." We didn't want `hat` to `fall` and then `hitGround`, *then* `duck` to `fall` and then `hitGround`, etc. We wanted all of the items to fall together, and then all of them to hit the ground together. +Remember, `enqueue` is useful for situations where you have multiple series of events, like our [`fall` -> `hitGround`] series, and you want each series to execute their alike events in the same "round." We didn't want `hat` to `fall` and then `hitGround`, *then* `duck` to `fall` and then `hitGround`, etc; we wanted *all of the items to fall together*, and then all of them to hit the ground together. If you wanted the opposite, (say you wanted `hat` to be done with hitting the ground before `duck` fell), then you would use `then`. `then` is for immediate execution; `enqueue` is for delayed exeuction. -Final note: instead of writing `enqueue`, you can use the shorthand `nq`. +Events are added to the end of the event queue as soon as the `EventQueue` function is executed. + +#### `enqueue` Shorthand + +If you'd like, you can use the shorthand `nq` instead of writing `enqueue`. We're all about efficiency. :+1: + +```ts +const queue = nq([event1, event2]); +queue.nq(event3); +``` ## Tracking Agent Changes by Event \ No newline at end of file From d3be075a2032cd0072b711950e5cf5cb89ff4b19 Mon Sep 17 00:00:00 2001 From: Joe Cowman Date: Mon, 6 Aug 2018 15:33:59 -0500 Subject: [PATCH 5/5] Finished events guide --- docs/overview/events.md | 70 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/docs/overview/events.md b/docs/overview/events.md index 2d2240b..42e5518 100644 --- a/docs/overview/events.md +++ b/docs/overview/events.md @@ -62,10 +62,10 @@ const myGame = new GameInstance(); greet(myGame); // Adds "Hello, world!" to myGame's output ``` -Since we used `on`, some useful metadata was stored in `game.events.list`. +Since we used `on`, some useful metadata was stored in `game.events.history`. ```ts -myGame.events.list === [ +myGame.events.history === [ { id: 1, // Auto-generated event ID name: "GREET", @@ -98,7 +98,7 @@ Now, `greet` can take any `name` and will generate an `EventFunction` using that const myGame = new GameInstance(); greet("Regal")(myGame); // Create an EventFunction where name == "Regal", then execute it -myGame.events.list === [ +myGame.events.history === [ { id: 1, name: "GREET", @@ -121,7 +121,7 @@ const greet = (name: string) => const myGame = new GameInstance(); greet("Regal")(myGame); -myGame.events.list === [ +myGame.events.history === [ { id: 1, name: "GREET ", // Unique name! @@ -162,7 +162,7 @@ const myGame = new GameInstance(); const myDate = new Date("August 5, 2018 10:15:00"); motivate(myDate)(myGame); -myGame.events.list === [ +myGame.events.history === [ { id: 2, causedBy: 1, // MOTIVATE (1) caused MORNING (2) @@ -214,13 +214,13 @@ const makeSword = (name: string) => }); ``` -`GameInstance.events.list` contains the list of events that occurred, in reverse chronological order. +`GameInstance.events.history` contains the list of events that occurred, in reverse chronological order. ```ts const myGame = new GameInstance(); makeSword("King Arthur")(myGame); -myGame.events.list === [ +myGame.events.history === [ { id: 3, causedBy: 1, @@ -380,4 +380,58 @@ const queue = nq([event1, event2]); queue.nq(event3); ``` -## Tracking Agent Changes by Event \ No newline at end of file +## Tracking Agent Changes by Event + +In addition to what we've covered, `GameInstance.events.history` tracks all changes made to registered agents over the course of each event as well. + +Here's an example using a basic agent: + +```ts +class Dummy extends Agent { + constructor(public name: string, public health: number) { + super(); + } +} +``` + +And an event that modifies the agent: + +```ts +const heal = (target: Dummy, amount: number) => + on("HEAL", game => { + target.health += amount; + + game.output.write(`Healed ${target.name} by ${amount}. Health is now ${target.health}.`); + + return noop; + }); +``` + +When `heal` is executed, we see a log of the changes reflected in `GameInstance.events.history`. + +```ts +const myGame = new GameInstance(); +const dummy = new Dummy("Lars", 10); + +heal(dummy, 15)(game); + +game.events.history === [ + { + id: 1, + name: "HEAL", + output: [ + "Healed Lars by 15. Health is now 25." + ], + changes: [ + { + agentId: 1, + op: "MODIFIED", + init: 10, + final: 25, + property: "health" + } + ] + }, +]; +``` +(For more information on agents, see the agent guide.) \ No newline at end of file