User Guides

Rule Expression Format

Learn the Shogi expression format through practical examples.

What this page covers

This page explains the Shogi expression format that can be used in shogi.json or other supported config files.

A rule can be as small as one value

These are all valid rules:

220
'Disabled in this area'
true
$distance

What these mean:

  • 220 evaluates as a number
  • 'Disabled in this area' evaluates as a string
  • true evaluates as a boolean
  • $distance reads a variable provided by the active rule context

Use condition -> result for conditional rules

The most common pattern is:

condition_name(...) -> result_here

Examples:

is_dimension('minecraft:the_nether') -> 180
is_inventory_button -> cooldown_cost('inventory_button', '300s')
is_interdimensional -> failure('Interdimensional travel is disabled')

What happens:

  • if the condition matches, the part after -> runs
  • if the condition does not match, that rule does nothing
  • when you have a list of rules, rules are applied in order

Conditions: + means all, , means any

Use + when every condition must match:

is_dimension('minecraft:the_nether') + is_below_y(0) -> failure('Too dangerous here')

Use , when either condition may match:

is_dimension('minecraft:the_nether'), is_dimension('minecraft:the_end') -> 256

If you mix them, + is checked first:

is_dimension('minecraft:the_nether') + is_below_y(0), is_player_nearby(12) -> failure('Unsafe teleport target')

That reads like:

  • (in the Nether AND below Y 0) OR player is nearby

Use parentheses when you want to make grouping obvious:

is_dimension('minecraft:the_nether') + (is_below_y(0), is_below_y(16)) -> failure('Too low for this teleport')

! means “not”

You can negate a single condition:

!is_interdimensional -> damage_item(20)

Or a grouped condition:

!(is_dimension('minecraft:the_nether'), is_dimension('minecraft:the_end')) -> 220

Function calls

Most Shogi rules are built from effect or condition calls:

damage_item(80)
failure('You need more XP')
clamp($distance * 0.01, 0, 27)
if(condition = is_interdimensional, then = 27, else = $distance * 0.01)

Two useful patterns:

  • no-argument calls can be written without parentheses: is_inventory_button
  • calls with arguments use commas between arguments

Strings, numbers, and booleans

Strings

Use single quotes, since double quotes require escaping in most contexts:

'minecraft:the_nether'
'300s'

Numbers

Both integers and decimals are supported:

27
0.01
256

Booleans

Use lowercase:

true
false

Variables

Variables start with $:

$distance
$xp_cost

You can assign to a variable and use it later:

$xp_cost = if(condition = is_interdimensional, then = 27, else = $distance * 0.01)
$xp_cost = clamp($xp_cost, 0, 27)
$xp_cost

This is a common pattern for rules that need a few calculation steps.

Implicit effect calls from assigned variables

In aggregate-style configs such as rule lists, assigning to some variable names can also create a matching effect automatically.

Example:

$xp_points_cost = if(condition = is_interdimensional, then = 27, else = $distance * 0.01)
$xp_points_cost = clamp($xp_points_cost, 0, 27)

If the active scope has a single-parameter effect named xp_points_cost, Shogi treats this like you had also written:

xp_points_cost($xp_points_cost)

Why this is useful:

  • you can calculate a value in steps
  • keep the final variable name aligned with the property or cost you want to apply
  • avoid repeating the same variable name again in a final call

This only works when the variable name matches a real effect name that accepts one main value.

If there is no matching effect, the variable stays just a variable.

Calculations

You can use regular math operators inside expressions:

$distance * 0.01
($distance + 500) / 2
clamp($height - 8, 128, 320)

Operator order works the usual way:

  • * and / happen before + and -
  • parentheses override the default order

Examples:

1 + 2 * 3
(1 + 2) * 3

Named arguments

Some calls are clearer with named arguments:

if(condition = is_interdimensional, then = 27, else = 0)

This is also valid even if the named arguments are in a different order:

if(else = 0, then = 27, condition = is_interdimensional)

Do not mix named and positional arguments in the same call.

Use one style per call:

clamp($xp_cost, 0, 27)
if(condition = is_interdimensional, then = 27, else = 0)

Whitespace is flexible

These mean the same thing:

is_inventory_button -> cooldown_cost('inventory_button', '300s')
is_inventory_button->cooldown_cost('inventory_button','300s')

Extra spaces are fine. Clear spacing is still easier to maintain.

Common mistakes

  • Missing quotes around text values: write 'minecraft:the_nether', not minecraft:the_nether
  • Wrong arrow: use ->, not =>
  • Mixed argument styles: avoid foo(bar = 1, 2)
  • Unclosed parentheses or quotes
  • Wrong function name for the current mod integration
  • Wrong target key even when the rule itself is valid

A complete example

This pattern is typical for a property hook that calculates a value and applies extra effects:

$xp_points_cost = if(condition = is_interdimensional, then = 27, else = $distance * 0.01)
$xp_points_cost = clamp($xp_points_cost, 0, 27)
is_inventory_button -> cooldown_cost('inventory_button', '300s')
is_warp_stone -> damage_item(80)

Read it as:

  • calculate a base XP cost
  • clamp it into a safe range
  • implicitly apply xp_points_cost($xp_points_cost) because the variable name matches that effect
  • add a cooldown only for inventory-button teleports
  • add item damage only for warp-stone teleports

Next steps