<?php
function parseRollCode($rollCode) {
$cleanRollCode = preg_replace('/\s+/', '', strtolower($rollCode));
$intro = "<strong>Dice Roll:</strong> {$cleanRollCode}";
$error = "Invalid Roll Code";
$result = [
'input' => $rollCode,
'total' => 0,
'detailText' => $intro
];
/*
$out...
[0] => roll tokens (optional leading sign)
[1] => optional signs
[2] => number&type die segments
[3] => reroll threshold segments
[4] => keep setting segments
[5] => constant segments
*/
if (!preg_match_all('~\G(^-?|[+-])(?:(\d+d\d+)(r\d+)?([v^]\d+)?|(\d+))(?=[+-]|$)~x', $cleanRollCode, $out)) {
$result['detailText'] .= "\n{$error}";
return $result;
}
foreach ($out[0] as $index => $segment) {
$result['detailText'] .= "\n<strong>{$segment} Results:</strong>";
if (!empty($out[5][$index])) {
$result['detailText'] .= ' (subtotal: ' . ((int)$segment) . ')';
$result['total'] += $segment;
} else {
// dice count & die type
[$diceCount, $dieType] = explode('d', $out[2][$index], 2);
// positive or negative arithmetic
$signFactor = (!empty($out[1][$index][0]) && $out[1][$index][0] == '-' ? -1 : 1);
// reroll threshold
$rerollThreshold = (int)ltrim($out[3][$index] ?? 0, 'r');
if ($rerollThreshold >= $dieType) {
return ['input' => $rollCode, 'total' => 0, 'detailText' => "{$intro}\n{$error}"];
}
$rollResults = [];
for ($r = 0; $r < $diceCount; ++$r) {
$rollResults[] = rand($rerollThreshold + 1, $dieType);
}
// keep settings
if (!empty($out[4][$index])) {
$keepCount = ltrim($out[4][$index], '^v');
if ($keepCount > $diceCount) {
return ['input' => $rollCode, 'total' => 0, 'detailText' => "{$intro}\n{$error}"];
}
if ($out[4][$index][0] == '^') {
arsort($rollResults);
} else {
asort($rollResults);
}
$keep = array_slice($rollResults, 0, $keepCount, true);
$subtotal = array_sum($keep) * $signFactor;
for ($i = 0, $len = count($rollResults); $i < $len; ++$i) {
$result['detailText'] .= ' ' . ($keep[$i] ?? "<s>{$rollResults[$i]}</s>");
}
$result['detailText'] .= " (subtotal: {$subtotal})";
$result['total'] += $subtotal;
} else {
$subtotal = array_sum($rollResults) * $signFactor;
$result['detailText'] .= " " . implode(" ", $rollResults) . " (subtotal: {$subtotal})";
$result['total'] += $subtotal;
}
}
}
return $result;
}
$rolls = ["1 d2 0", "-1d20+5", "d20", "2d20^1", "4d6r1^3", "5d20^3", "5d8v2", "2d20v1", "5+3d4", "3d20^1",
"3d6r1 + 1d4r2", "-1d20", "1d20+5", "1d20 + 5", "1d-20", "1d20+1d6", "5d20-3d6", "1d6r2", "5d6r-6", "3d20^1v1",
"1d20r30", "1d20r20", "1d20r19", "1d20v2", "1d20v1", "1d20^2", "1d20+5d8r2^3-5-1d6+3d4+2-4+6-8"];
foreach ($rolls as $roll) {
$rollResult = parseRollCode($roll);
echo "{$rollResult['detailText']}\n<strong>Total:</strong> {$rollResult['total']}\n---\n";
}
preferences:
24.68 ms | 415 KiB | 5 Q