1<?php
2
3namespace TableRoller\Table;
4
5class BaseTable implements TableInterface
6{
7   protected array $options = [];
8   protected int $count;
9   protected int $upper_bound;
10   protected array $keys;
11
12   protected function pickOne() : int {
13      return $this->keys[rand(0, $this->count - 1)];
14   }
15
16   public function roll(int $times = 1) : array {
17      $result = [];
18
19      for ($i = 0; $i < $times; $i++) {
20         $result[] = $this->proceessResult($this->options[$this->pickOne()]);
21      }
22
23      return $result;
24   }
25
26   protected function setOptions(array $options) {
27
28      foreach ($options as $entry) {
29         if (preg_match('/^(\d+)\:(.*)$/', $entry, $match)) {
30            $this->options[(int)$match[1]] = trim($match[2]);
31         } elseif (preg_match('/^(\d+)\-(\d+)\:(.*)$/', $entry, $match)) {
32            foreach(range($match[1], $match[2]) as $key) {
33               $this->options[$key] = $match[3];
34            }
35         } else {
36            $this->options[] = $entry;
37         }
38      }
39
40	  $this->count = count($this->options);
41      $this->keys = array_keys($this->options);
42   }
43
44   public function rollOnce() : string {
45      return $this->proceessResult($this->options[$this->pickOne()]);
46   }
47
48   public function rollWithoutRepeats(int $times = 1) : array {
49      $result = [];
50      $used = [];
51
52      while (count($result) < $times) {
53         $pick = $this->pickOne();
54
55         if (!in_array($pick, $used)) {
56            $result[] = $this->proceessResult($this->options[$pick]);
57            $used[] = $pick;
58         }
59      }
60
61      return $result;
62   }
63
64   private function proceessResult(string $result) {
65
66      if (false !== strpos($result, '[')) {
67         $result = preg_replace_callback('/\[([^]]+)\]/', function($match) {
68            $options = explode('|', $match[1]);
69            shuffle($options);
70            return $options[0];
71         }, $result);
72
73      }
74
75      if (false !== strpos($result, '{')) {
76         $result = preg_replace_callback('/{(\d*)[Dd](\d+)(\+|\-)?(\d+)?}/', function($match) {
77			return $this->rollDie($match[0]);
78         }, $result);
79
80      }
81
82      return $result;
83   }
84
85   public function rollDie(string $formula) {
86      $formula = strtoupper(trim($formula));
87      preg_match('/(\d*)[Dd](\d+)(\+|\-)?(\d+)?/', $formula, $parts);
88      $rolls = empty($parts[1]) ? 1 : $parts[1];
89      $die = (int) $parts[2];
90
91      $mod = 0;
92      if (!empty($parts[3])) {
93         $mod = (int)($parts[3] . $parts[4]);
94      }
95      $result = 0;
96
97      for ($i = 0; $i < $rolls; $i++) {
98         $result += rand(1, $die);
99      }
100
101      $result += $mod;
102      return $result;
103   }
104
105   public function __invoke() : string {
106      return $this->rollOnce();
107   }
108
109   public function __toString() : string {
110      return $this->rollOnce();
111   }
112}