-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 83d8fb3
Showing
40 changed files
with
19,783 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Sphinx build info version 1 | ||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. | ||
config: 298faf75c1ac42b6054d9cd9861cb66a | ||
tags: d77d1c0d9ca2f4c8421862c7c5a0d620 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
hide-toc: true | ||
--- | ||
|
||
```{include} ../CHANGELOG.md | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# bolt-expressions | ||
|
||
```{toctree} | ||
:hidden: | ||
|
||
tutorial | ||
changelog | ||
``` | ||
|
||
```{toctree} | ||
:caption: Project links | ||
:hidden: | ||
|
||
GitHub repository <https://github.com/rx-modules/bolt-expressions> | ||
PyPI package <https://pypi.org/project/bolt-expressions> | ||
``` | ||
|
||
```{eval-rst} | ||
{include} ../README.md | ||
:start-line: 10 | ||
:end-line: -3 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
--- | ||
hide-toc: true | ||
--- | ||
|
||
# Tutorial | ||
|
||
`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: | ||
- bolt | ||
- 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 | ||
``` | ||
|
||
<details> | ||
|
||
<summary>Generated Commands</summary> | ||
|
||
```mcfunction | ||
scoreboard players operation $i0 bolt.expr.temp = current_id entity.id | ||
scoreboard players operation $i0 bolt.expr.temp /= $200 bolt.expr.const | ||
scoreboard players add $i0 bolt.expr.temp 86400 | ||
scoreboard players operation @s math *= $i0 bolt.expr.temp | ||
|
||
execute store result score @a[sort=random, limit=1] math run data get entity @s SelectedSlot 1 | ||
scoreboard players operation @a[sort=random, limit=1] math *= $9 bolt.expr.const | ||
scoreboard players operation @a[sort=random, limit=1] math /= $5 bolt.expr.const | ||
execute store result score $i3 bolt.expr.temp run data get entity @s Health 1 | ||
scoreboard players operation @a[sort=random, limit=1] math += $i3 bolt.expr.temp | ||
``` | ||
|
||
</details> | ||
|
||
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"] | ||
} | ||
value = damage["value"] | ||
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 * input.armor) / 5, atf) # still local variable | ||
output = value * (250 - (min(200, maxxed))) / 25 # ✨ special behavior | generates commands ✨ | ||
``` | ||
|
||
<details> | ||
|
||
<summary>Generated Commands</summary> | ||
|
||
```mcfunction | ||
scoreboard players set $i0 bolt.expr.temp 10 | ||
scoreboard players operation $i0 bolt.expr.temp *= armor damage | ||
scoreboard players operation $i0 bolt.expr.temp /= $5 bolt.expr.const | ||
scoreboard players set $i2 bolt.expr.temp 10 | ||
scoreboard players operation $i2 bolt.expr.temp *= armor damage | ||
scoreboard players set $i3 bolt.expr.temp 400 | ||
scoreboard players operation $i3 bolt.expr.temp *= armor damage | ||
scoreboard players set $i4 bolt.expr.temp 10 | ||
scoreboard players operation $i4 bolt.expr.temp *= toughness damage | ||
scoreboard players add $i4 bolt.expr.temp 80 | ||
scoreboard players operation $i3 bolt.expr.temp /= $i4 bolt.expr.temp | ||
scoreboard players operation $i2 bolt.expr.temp -= $i3 bolt.expr.temp | ||
scoreboard players operation $i0 bolt.expr.temp > $i2 bolt.expr.temp | ||
scoreboard players set $i9 bolt.expr.temp 200 | ||
scoreboard players operation $i9 bolt.expr.temp < $i0 bolt.expr.temp | ||
scoreboard players set output damage 250 | ||
scoreboard players operation output damage -= $i9 bolt.expr.temp | ||
scoreboard players operation output damage *= value damage | ||
scoreboard players operation output damage /= $25 bolt.expr.const | ||
``` | ||
|
||
</details> | ||
|
||
## 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! | ||
``` | ||
|
||
*If you have any cool examples, please let us know! Just open an issue :)* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* _sphinx_javascript_frameworks_compat.js | ||
* ~~~~~~~~~~ | ||
* | ||
* Compatability shim for jQuery and underscores.js. | ||
* | ||
* WILL BE REMOVED IN Sphinx 6.0 | ||
* xref RemovedInSphinx60Warning | ||
* | ||
*/ | ||
|
||
/** | ||
* select a different prefix for underscore | ||
*/ | ||
$u = _.noConflict(); | ||
|
||
|
||
/** | ||
* small helper function to urldecode strings | ||
* | ||
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL | ||
*/ | ||
jQuery.urldecode = function(x) { | ||
if (!x) { | ||
return x | ||
} | ||
return decodeURIComponent(x.replace(/\+/g, ' ')); | ||
}; | ||
|
||
/** | ||
* small helper function to urlencode strings | ||
*/ | ||
jQuery.urlencode = encodeURIComponent; | ||
|
||
/** | ||
* This function returns the parsed url parameters of the | ||
* current request. Multiple values per key are supported, | ||
* it will always return arrays of strings for the value parts. | ||
*/ | ||
jQuery.getQueryParameters = function(s) { | ||
if (typeof s === 'undefined') | ||
s = document.location.search; | ||
var parts = s.substr(s.indexOf('?') + 1).split('&'); | ||
var result = {}; | ||
for (var i = 0; i < parts.length; i++) { | ||
var tmp = parts[i].split('=', 2); | ||
var key = jQuery.urldecode(tmp[0]); | ||
var value = jQuery.urldecode(tmp[1]); | ||
if (key in result) | ||
result[key].push(value); | ||
else | ||
result[key] = [value]; | ||
} | ||
return result; | ||
}; | ||
|
||
/** | ||
* highlight a given string on a jquery object by wrapping it in | ||
* span elements with the given class name. | ||
*/ | ||
jQuery.fn.highlightText = function(text, className) { | ||
function highlight(node, addItems) { | ||
if (node.nodeType === 3) { | ||
var val = node.nodeValue; | ||
var pos = val.toLowerCase().indexOf(text); | ||
if (pos >= 0 && | ||
!jQuery(node.parentNode).hasClass(className) && | ||
!jQuery(node.parentNode).hasClass("nohighlight")) { | ||
var span; | ||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); | ||
if (isInSVG) { | ||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); | ||
} else { | ||
span = document.createElement("span"); | ||
span.className = className; | ||
} | ||
span.appendChild(document.createTextNode(val.substr(pos, text.length))); | ||
node.parentNode.insertBefore(span, node.parentNode.insertBefore( | ||
document.createTextNode(val.substr(pos + text.length)), | ||
node.nextSibling)); | ||
node.nodeValue = val.substr(0, pos); | ||
if (isInSVG) { | ||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | ||
var bbox = node.parentElement.getBBox(); | ||
rect.x.baseVal.value = bbox.x; | ||
rect.y.baseVal.value = bbox.y; | ||
rect.width.baseVal.value = bbox.width; | ||
rect.height.baseVal.value = bbox.height; | ||
rect.setAttribute('class', className); | ||
addItems.push({ | ||
"parent": node.parentNode, | ||
"target": rect}); | ||
} | ||
} | ||
} | ||
else if (!jQuery(node).is("button, select, textarea")) { | ||
jQuery.each(node.childNodes, function() { | ||
highlight(this, addItems); | ||
}); | ||
} | ||
} | ||
var addItems = []; | ||
var result = this.each(function() { | ||
highlight(this, addItems); | ||
}); | ||
for (var i = 0; i < addItems.length; ++i) { | ||
jQuery(addItems[i].parent).before(addItems[i].target); | ||
} | ||
return result; | ||
}; | ||
|
||
/* | ||
* backward compatibility for jQuery.browser | ||
* This will be supported until firefox bug is fixed. | ||
*/ | ||
if (!jQuery.browser) { | ||
jQuery.uaMatch = function(ua) { | ||
ua = ua.toLowerCase(); | ||
|
||
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || | ||
/(webkit)[ \/]([\w.]+)/.exec(ua) || | ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || | ||
/(msie) ([\w.]+)/.exec(ua) || | ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || | ||
[]; | ||
|
||
return { | ||
browser: match[ 1 ] || "", | ||
version: match[ 2 ] || "0" | ||
}; | ||
}; | ||
jQuery.browser = {}; | ||
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; | ||
} |
Oops, something went wrong.