5 use Illuminate\Support\Facades\Storage;
9 public function addMessage(ChatLog $msg) {
10 $this->addText($msg->text_content);
13 public function addText($text) {
14 $tokens = $this->tokenize($text);
15 if (empty($tokens)) return;
17 foreach ($tokens as $num => $token) {
19 $this->addTransition([], $token);
21 $start = max(0, $num - $this->size - 1);
23 for ($i = $start; $i < $end; ++$i) {
24 $this->addTransition(array_slice($tokens, $i, $end - $i), $token);
25 if ($end - $i < 4) break;
31 public function compile() {
32 foreach ($this->transitions as $key => $value) {
33 $this->transitions[$key] = $this->index($this->transitions[$key]);
34 if (empty($this->transitions[$key])) {
35 unset($this->transitions[$key]);
40 public function generate($limit = 100) {
43 while (strlen($generated) < $limit) {
44 $next = $this->randomNext($tokens);
45 if ($next === '') break;
52 public function saveAs($name) {
54 'size' => $this->size,
55 'transitions' => $this->transitions,
57 Storage::disk('chatlib')->put($name.'.json', json_encode($data));
60 public function loadFrom($name) {
61 $data = json_decode(Storage::disk('chatlib')->get($name.'.json'), true);
62 $this->size = $data['size'];
63 $this->transitions = $data['transitions'];
66 private function index($arr) {
69 foreach ($arr as $key => $entry) {
70 $weight = $entry['count'];
71 if ($weight == 1) continue;
75 if (is_array(end($entry['examples']))) {
77 $examples = $entry['examples'];
80 foreach ($entry['examples'] as $example => $subweight) {
82 $subsum += $subweight;
83 $examples[] = [$example, $sublower, $subsum];
86 $result[] = [$key, $lower, $sum, $examples];
91 private function randomNext($tokens) {
92 $cnt = count($tokens);
93 for ($size = min($this->size, $cnt); $size > 0; --$size) {
94 $cmb = $this->generalize(array_slice($tokens, -$size));
95 if (isset($this->transitions[$cmb])) {
96 $pick = $this->pick($this->transitions[$cmb]);
97 if (!is_null($pick)) {
98 return $this->exampleOf($pick);
105 private function pick($options) {
106 if (empty($options)) return null;
107 $max = end($options)[2];
108 $num = random_int(0, $max);
110 $max_index = count($options) - 1;
111 while ($min_index < $max_index) {
112 $cur_index = intval(($min_index + $max_index) / 2);
113 $cur_low = $options[$cur_index][1];
114 $cur_high = $options[$cur_index][2];
115 if ($cur_low > $num) {
116 $max_index = $cur_index;
117 } else if ($cur_high < $num) {
118 $min_index = $cur_index + 1;
120 $min_index = $cur_index;
124 return $options[$min_index];
127 private function addTransition($state, $next) {
128 $cmb = $this->generalize($state);
129 if (!isset($this->transitions[$cmb])) {
130 $this->transitions[$cmb] = [];
132 $this->increment($this->transitions[$cmb], $next);
135 private function increment(&$which, $token) {
136 $generalized = $this->generalize([$token]);
137 if (!isset($which[$generalized])) {
138 $which[$generalized] = [
142 $which[$generalized]['examples'][$token] = 1;
144 ++$which[$generalized]['count'];
145 if (!isset($which[$generalized]['examples'][$token])) {
146 $which[$generalized]['examples'][$token] = 1;
148 ++$which[$generalized]['examples'][$token];
153 private function tokenize($str) {
154 return array_values(array_filter(preg_split('/\b/u', $str), function($token) {
155 if (empty($token)) return false;
156 if (preg_match('/cheer\d+/u', strtolower($token))) return false;
161 private function generalize($tokens) {
163 foreach ($tokens as $token) {
164 $replaced = preg_replace('/\d+/', '0', $token);
165 $replaced = strtolower($replaced);
171 private function exampleOf($pick) {
172 $example = $this->pick($pick[3]);
177 private $transitions = [];