Skip to content

Commit

Permalink
docs: added tutorial and cleaned README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
rx97 committed Mar 31, 2022
1 parent e292d99 commit b786052
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 22 deletions.
28 changes: 6 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,35 +59,19 @@ from bolt_expressions import Scoreboard, Storage
Any usage of the `bolt_expressions` package will require you to inject the current beet context into the API objects. Then, you can create an objective and start creating expressions.

```py
Objective = ctx.inject(Scoreboard)

math = Objective("math")
entity_id = Objective("entity_id")
math = Scoreboard.objective("math")
entity_id = Scoreboard.objective("entity_id")

math["@s"] += 10
```

## Features

The `Scoreboard` object provides a rich API for you to interact within your bolt landscape. Most numerical operations (such as `+`, `*`, `%`) will produce correct `scoreboard operation` commands for you to work with.
- Robust API supporting Scoreboards, Storage, Blocks, and Entities
- Provides an interface to manipulate large, complex mathematical expressions simplily
- Allows you to interopt custom variables with normal commands *(soon)*

```
CONSTANT = 60 * 60 * 24
math["@s"] *= (entity_id["current_id"] / 200) + CONSTANT
```

You can also utilize local variables to simplify readability with longer operations. This is due to the unique `__rebind__` operator provided only in the `bolt` context which allows us provide custom behavior with the `=` operation. We also have defined helper functions such as `min` and `max`, alongside `sqrt` and `**` (for `pow`).

```py
dmg_obj = Objective("damage")
damage = dmg_obj["damage"]
toughness = dmg_obj["toughness"]
armor = dmg_obj["armor"]

atf = (10 * armor - (400 * damage / (80 + 10 * toughness))) # local variable
maxxed = max((10 * armor) / 5, atf) # still local variable
damage = damage * (250 - (min(200, maxxed))) / 25 # calls __rebind__!
```
Checkout some examples over at our [docs](https://rx-modules.github.io/bolt-expressions/)!

## Contributing

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
```{toctree}
:hidden:
tutorial
changelog
```

Expand Down
115 changes: 115 additions & 0 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Intro

`bolt_expressions` provides some psuedo pointers which allow you to interopt between normal minecraft (or bolt) commands and our robust interface for generating expressions. The main endpoints for the logic lay in the specialized `Scoreboard` and `Data` objects.

Before utilizing `bolt_expressions` in a bolt script, make sure you *require* `bolt_expressions` in your beet config:

```yaml
name: my cool project
author: me (cool person)

data_pack:
load: [src]

require:
- mecha.bolt.contrib
- bolt_expressions

pipeline:
- mecha
```
Now, inside a valid bolt script, import from our library.
```py
from bolt_expressions import Scoreboard, Data

my_obj = Scoreboard.objective("my_obj")
my_obj["@s"] += 25
```

# Common Use Cases

The `Scoreboard` and `Data` objects provides you a *rich* API for you to interact within your bolt landscape. Most numerical operations (such as `+`, `*`, `%`) will produce sequential `scoreboard operation` commands for you to work with. Longer expressions will benefit from an optimization system which will help shorten the size of the resulting commands. Feel free to use various pointers together, our library will chunk through anything!

```py
math = Scoreboard.objective("math")
player = Data.entity("@s")

CONSTANT = 60 * 60 * 24
math["@s"] *= (entity_id["current_id"] / 200) + CONSTANT
math["@a[sort=random, limit=1]"] = player.Health + (player.SelectedSlot * 9) / 5
```

You can also utilize local variables to simplify readability with longer operations. This is due to the unique `__rebind__` operator provided only in the `bolt` context which allows us provide custom behavior with the `=` operation. We also have defined helper functions such as `min` and `max`, alongside `sqrt` and `**` (for `pow`).

```py
damage = Scoreboard.objective("damage")
input = {
"damage": damage["damage"],
"toughness" = damage["toughness"],
"armor" = damage["armor"]
}
output = damage["output]

# This example is split up onto 3 lines to help with readability 📖
# The library will consider this one long expression when optimizing 🔥
atf = (10 * input.armor - (400 * input.armor / (80 + 10 * input.toughness))) # python local variable
maxxed = max((10 * armor) / 5, atf) # still local variable
output = damage * (250 - (min(200, maxxed))) / 25 # ✨ special behavior | generates commands ✨
```

# Advanced Use Cases 🔥

This library sits very nicely in the `bolt` workspace as it can integrate with python functions. This leads naturally to a popular *callback* pattern which allows you to encapsulate repeated and popular patterns. Here are just a couple of examples to get you inspired ✨!

```py
# ./utils
from bolt_expressions import Scoreboard
import random as py_random

math = Scoreboard.objective("abc.math")
temp = Scoreboard.objective("abc.temp")

_LCG_CONSTANT = 1623164762
def randint(min, max, randomize_output=True):
math["#range"] = (max - min) + 1

math["#lcg"] *= math["#lcg.multiplier"]
math["#lcg"] += _LCG_CONSTANT

math["#output"] = math["#lcg"] % math["#range"] + min

if randomize_output:
output = math[f"#output_{hash((min, max, py_random()))}"]

# This is hacky, you could do this in a if statement.
# a) it just copies the `math["output"]` python reference
# b) it performs `__rebind__` and does a `scoreboard players operation`
output = math["output"]
return output

blocks = [...] # pretend this has every block in the game

# ./client
from ./utils import randint, blocks

choice = randint(0, len(blocks))

for node in generate_tree(blocks):
append function node.parent:
if node.partition(8):
if score f"{choice.scoreholder}" f"{choice.objective}" matches node.range function node.children
else:
if score f"{choice.scoreholder}" f"{choice.objective}" matches node.range setblock ~ ~ ~ node.value

# Note, in the future, you will be able to use `choice` directly with mecha commands
```

In this example, we wrote a wrapper for a tiny `randint` function which will generate random number generation (through a LCG). The beauty of an interface like so is that we don't have to worry about passing in scoreholders as arugments, instead, we just pass in numbers, and the function handles the arguments and outputs for us. This lets us write in a more *functional* style.

```{admonition} 🚧 In Construction 🚧
:class: warning

This section is still in progress. Stay tuned!
```

0 comments on commit b786052

Please sign in to comment.