diff --git a/README.md b/README.md index e15987d..f8ef670 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ You don't have to use composer to include EZDice in your project, but you can: ### roll($diceStr) -Parse **$diceStr** as dice notation then roll those dice. Returns *(int)* total of all rolls and modifiers, or *false* if none were found. +Parse **$diceStr** as dice notation, then roll those dice. Returns *(int)* total of all rolls and modifiers, or *false* if none were found. The parser is very forgiving, ignoring whitespace and anything else it doesn't recognise. It is also case-insensitive. Dice notation is briefly documented below. @@ -66,13 +66,17 @@ Returns a *(string)* representing the total of all modifiers in the last roll. I e.g. if you rolled `1d8+10+1d4-2` this method would return `+8`. +### strContainsDice($diceStr) + +Parse **$diceStr** and returns true if it contains at least one dice roll, otherwise returns false. Useful for verifying user input. Modifiers without dice don't count. + ## Dice Notation - Dice notation is in the form (number of dice)**d**(dice sides). e.g. `2d10`. - Additional dice can be chained with **+** and **-** operators. e.g. `2d10+1d6`. - Modifiers can also be specified. e.g. `2d10-5` - d% can be used as a shorthand for a percentile dice. `1d%` and `1d100` are equivalent. -- Append a roll with -L to drop the lowest dice in that group, or -H to drop the highest. Dropped dice are excluded from the total. e.g. `2d20-L` will roll 2 twenty sided dice and drop the lowest. +- Append a roll with -L to drop the lowest dice in that group, or -H to drop the highest. Dropped dice are excluded from the total. e.g. `2d20-L` will roll 2 twenty sided dice and drop the lowest. You can also specify a number of dice to drop e.g. `6d6-H3` will drop the highest 3 rolls. - No notation is currently provided for fudge dice. You can use `1d3-2` instead. - Whitespace, and anything else not recognised as a dice or a modifier, is treated like a **+** operator. e.g. `foo10 1d4bar1d4 5` is equivalent to `5+1d4+1d4+10`, or simply `2d4+15`. diff --git a/composer.json b/composer.json index 3acac27..5fbf558 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,9 @@ "rpg", "dice-notation", "die", - "dice-roller" + "dice-roller", + "roller", + "random" ], "require": { "php": ">=7.0" diff --git a/example.php b/example.php index 72740a5..8d1828a 100644 --- a/example.php +++ b/example.php @@ -3,6 +3,15 @@ require 'ezdice.php'; $ezd = new ezdice\EZDice(); -echo($ezd->roll('1d20+2d4-L+6').PHP_EOL); + +$diceStr = '1d20+2d4-L+6'; + +// Validating a dice string +if ($ezd->strContainsDice("$diceStr") { + echo "$diceStr contains dice!"; +}; + +// Rolling dice +echo($ezd->roll($diceStr).PHP_EOL); print_r($ezd->getDiceStates()); echo($ezd->getModifier()); diff --git a/ezdice.php b/ezdice.php index da45420..9dd6aa0 100644 --- a/ezdice.php +++ b/ezdice.php @@ -2,15 +2,28 @@ namespace ezdice; +/** + * EZDice, a dice rolling library + */ class EZDice { // Magic dice & modifier matching regex - private $re = '(?[\+-])?\s*(?\d+)(?:[dD](?(?:\d+|%))(?:-(?[LlHh]))?)?'; + private $re = '(?[\+-])?\s*(?\d+)(?:[dD](?(?:\d+|%))(?:-(?[LlHh])(?\d+)?)?)'; // Stores information on last roll private $total = 0; private $states = []; private $modifier = 0; + /** + * Parse **$diceStr** as dice notation, then roll those dice. + * + * The parser is very forgiving, ignoring whitespace and anything else it doesn't recognise. + * It is also case-insensitive. Dice notation is documented in README.md + * + * @param string $diceStr the string containing the dice rolls. + * + * @return int|false total of all rolls and modifiers, or false if none were found. + */ public function roll(string $diceStr) { // Reset result values @@ -26,7 +39,8 @@ public function roll(string $diceStr) } // Search for dice groups and modifiers - preg_match_all("/{$this->re}/", $diceStr, $matches, PREG_SET_ORDER, 0); + // The extra "?" at the end of the regex allows it to find modifiers too + preg_match_all("/{$this->re}?/", $diceStr, $matches, PREG_SET_ORDER, 0); // Returning false if no matches found if (sizeof($matches) == 0) return false; @@ -39,6 +53,18 @@ public function roll(string $diceStr) return $this->total; } + /** + * Parse **$diceStr** and determine if it contains at least one dice roll. + * + * @param string $diceStr the string which may contain dice rolls. + * + * @return bool true if $diceStr contains at least one dice roll, otherwise false. + */ + public function strContainsDice(string $diceStr) + { + return (preg_match_all("/{$this->re}/", $diceStr) > 0); + } + private function addState(int $sides, int $value, bool $dropped = false): void { $this->states[] = [ @@ -65,8 +91,8 @@ private function processGroup(array $group): void return; } - // Collect variant information from group - $variant = (isset($group['variant']) ? strtoupper($group['variant']) : null); + // Collect drop information from group + $drop = (isset($group['drop']) ? strtoupper($group['drop']) : null); // 'd%' can be used as shorthand for 'd100' $sides = $sides=="%" ? 100 : $sides; @@ -80,15 +106,18 @@ private function processGroup(array $group): void } // Dropping dice - if ($variant && $number > 1) { + if ($drop) { + $dropQuantity = min($group['dquantity'] ?? 1, $number); // Sort low to high sort($results, SORT_NUMERIC); // Reverse array if dropping lowest - if ($variant == 'L') { + if ($drop == 'L') { $results = array_reverse($results); } - $droppedResult = array_pop($results); - $this->addState($sides, $droppedResult, true); + for ($i=0; $i < $dropQuantity; $i++) { + $droppedResult = array_pop($results); + $this->addState($sides, $droppedResult, true); + } // Cosmetic re-shuffle of rest of dice shuffle($results); } @@ -101,21 +130,44 @@ private function processGroup(array $group): void } } + /** + * Generates the psudo-random number for dice rolls. + * + * @param int $max the highest number on the dice. Roll is 1 - $max (inclusive). + * + * @return int result of the roll. + */ protected function getRandomNumber(int $max): int { return mt_rand(1,$max); } + /** + * Get the total of the last roll. + * + * @return bool result of test. + */ public function getTotal(): int { return $this->total; } + /** + * Get the state of the dice after the last roll. + * + * @return array array that describes the state of the dice after the last roll. + */ public function getDiceStates(): array { return $this->states; } + /** + * Get the combined modifiers of the last roll. + * + * @return string representing the total of all modifiers in the last roll. If there were no modifiers, or they + * cancelled out, an empty string is returned. + */ public function getModifier(): string { if (!$this->modifier) return "";