5 use Illuminate\Support\Facades\Storage;
9 public function addMessage($msg) {
10 $tokens = $this->tokenize($msg->text_content);
11 if (empty($tokens)) return;
13 foreach ($tokens as $num => $token) {
15 $this->addTransition([], $token);
17 $start = max(0, $num - $this->size);
19 for ($i = $start; $i < $end; ++$i) {
20 $this->addTransition(array_slice($tokens, $i, $end - $i), $token);
21 if ($end - $i < 3) break;
27 public function compile() {
28 foreach ($this->transitions as $key => $value) {
29 $this->transitions[$key] = $this->index($this->transitions[$key]);
30 if (empty($this->transitions[$key])) {
31 unset($this->transitions[$key]);
36 public function generate($limit = 100) {
39 while (strlen($generated) < $limit) {
40 $next = $this->randomNext($tokens);
41 if (empty($next)) break;
48 public function saveAs($name) {
50 'size' => $this->size,
51 'transitions' => $this->transitions,
53 Storage::disk('chatlib')->put($name.'.json', json_encode($data));
56 public function loadFrom($name) {
57 $data = json_decode(Storage::disk('chatlib')->get($name.'.json'), true);
58 $this->size = $data['size'];
59 $this->transitions = $data['transitions'];
62 private function index($arr) {
65 foreach ($arr as $key => $entry) {
66 $weight = $entry['count'];
67 if ($weight == 1) continue;
71 if (is_array(end($entry['examples']))) {
73 $examples = $entry['examples'];
76 foreach ($entry['examples'] as $example => $subweight) {
78 $subsum += $subweight * $subweight;
79 $examples[] = [$example, $sublower, $subsum];
82 $result[] = [$key, $lower, $sum, $examples];
87 private function randomNext($tokens) {
88 $cnt = count($tokens);
89 for ($size = min($this->size, $cnt); $size >= 0; --$size) {
90 $cmb = $this->generalize(array_slice($tokens, $cnt - $size, $size));
91 if (isset($this->transitions[$cmb])) {
92 $pick = $this->pick($this->transitions[$cmb]);
93 if (!is_null($pick)) {
94 return $this->exampleOf($pick);
101 private function pick($options) {
102 if (empty($options)) return null;
103 $max = end($options)[2];
104 $num = random_int(0, $max);
106 $max_index = count($options) - 1;
107 while ($min_index < $max_index) {
108 $cur_index = intval(($min_index + $max_index) / 2);
109 $cur_low = $options[$cur_index][1];
110 $cur_high = $options[$cur_index][2];
111 if ($cur_low > $num) {
112 $max_index = $cur_index;
113 } else if ($cur_high < $num) {
114 $min_index = $cur_index + 1;
116 $min_index = $cur_index;
120 return $options[$min_index];
123 private function addTransition($state, $next) {
124 $cmb = $this->generalize($state);
125 if (!isset($this->transitions[$cmb])) {
126 $this->transitions[$cmb] = [];
128 $this->increment($this->transitions[$cmb], $next);
131 private function increment(&$which, $token) {
132 $generalized = $this->generalize([$token]);
133 if (!isset($which[$generalized])) {
134 $which[$generalized] = [
138 $which[$generalized]['examples'][$token] = 1;
140 ++$which[$generalized]['count'];
141 if (!isset($which[$generalized]['examples'][$token])) {
142 $which[$generalized]['examples'][$token] = 1;
144 ++$which[$generalized]['examples'][$token];
149 private function tokenize($str) {
150 return array_values(array_filter(preg_split('/\b/u', $str), function($token) {
151 if (empty($token)) return false;
152 if (preg_match('/cheer\d+/u', strtolower($token))) return false;
157 private function generalize($tokens) {
159 foreach ($tokens as $token) {
160 $replaced = preg_replace('/\d+/', '0', $token);
161 $replaced = strtolower($replaced);
162 $str .= empty($replaced) ? $token : $replaced;
167 private function exampleOf($pick) {
168 $example = $this->pick($pick[3]);
173 private $transitions = [];