]> git.localhorst.tv Git - alttp.git/blob - app/Models/Channel.php
exclude 0 scores from leaderboard calculation
[alttp.git] / app / Models / Channel.php
1 <?php
2
3 namespace App\Models;
4
5 use Illuminate\Broadcasting\Channel as PublicChannel;
6 use Illuminate\Broadcasting\PrivateChannel;
7 use Illuminate\Database\Eloquent\BroadcastsEvents;
8 use Illuminate\Database\Eloquent\Factories\HasFactory;
9 use Illuminate\Database\Eloquent\Model;
10 use Illuminate\Support\Arr;
11
12 class Channel extends Model {
13
14         use BroadcastsEvents;
15         use HasFactory;
16
17         public function broadcastOn($event) {
18                 $channels = [
19                         new PrivateChannel('Channel.'.$this->id),
20                 ];
21                 if (!empty($this->access_key)) {
22                         $channels[] = new PublicChannel('ChannelKey.'.$this->access_key);
23                 }
24                 return $channels;
25         }
26
27         public function getCurrentEpisode() {
28                 return $this->episodes()
29                         ->where('start', '<', now()->subMinutes(10))
30                         ->orderBy('start', 'DESC')
31                         ->first();
32         }
33
34         public function getGuessingLeaderboard() {
35                 $query = $this->winners()
36                         ->selectRaw('(select t2.uname from guessing_winners t2 where t2.uid = guessing_winners.uid order by created_at desc limit 1) as name, sum(score) as score')
37                         ->where('score', '!=', 0)
38                         ->groupBy('uid')
39                         ->orderBy('score', 'desc')
40                         ->limit(10);
41                 $type = $this->getGuessingSetting('leaderboard_type', 'all');
42                 if ($type == 'month') {
43                         $query->where('created_at', '>=', now()->startOfMonth());
44                 } else if ($type == 'year') {
45                         $query->where('created_at', '>=', now()->startOfYear());
46                 } else if (is_numeric($type)) {
47                         $query->where('created_at', '>=', now()->sub($type, 'days'));
48                 }
49                 return $query->get();
50         }
51
52         public function hasActiveGuessing() {
53                 return !is_null($this->guessing_start);
54         }
55
56         public function isAcceptingGuesses() {
57                 return !is_null($this->guessing_start) && is_null($this->guessing_end);
58         }
59
60         public function startGuessing($type) {
61                 $this->guessing_type = $type;
62                 $this->guessing_start = now();
63                 $this->save();
64         }
65
66         public function stopGuessing() {
67                 $this->guessing_end = now();
68                 $this->save();
69         }
70
71         public function getGuessingSetting($name, $default = null) {
72                 if (empty($this->guessing_settings) ||
73                         empty($this->guessing_type) ||
74                         !array_key_exists($this->guessing_type, $this->guessing_settings) ||
75                         !array_key_exists($name, $this->guessing_settings[$this->guessing_type])
76                 ) {
77                         return $default;
78                 }
79                 return $this->guessing_settings[$this->guessing_type][$name];
80         }
81
82         public function solveGuessing($solution) {
83                 $start = $this->guessing_start;
84                 $end = is_null($this->guessing_end) ? now() : $this->guessing_end;
85                 $guesses = $this->guesses()->whereBetween('created_at', [$start, $end])->orderBy('created_at', 'ASC')->get();
86                 $unique_guesses = [];
87                 foreach ($guesses as $guess) {
88                         $unique_guesses[$guess->uid] = $guess;
89                 }
90                 $candidates = [];
91                 foreach ($unique_guesses as $guess) {
92                         if ($guess->guess == $solution) {
93                                 $candidates[] = $guess;
94                         }
95                 }
96                 if (empty($candidates) && is_numeric($solution)) {
97                         $min_distance = null;
98                         foreach ($unique_guesses as $guess) {
99                                 $distance = abs(intval($guess->guess) - intval($solution));
100                                 if (is_null($min_distance) || $distance == $min_distance) {
101                                         $candidates[] = $guess;
102                                         if (is_null($min_distance)) {
103                                                 $min_distance = $distance;
104                                         }
105                                 } else if ($distance < $min_distance) {
106                                         $candidates = [$guess];
107                                         $min_distance = $distance;
108                                 }
109                         }
110                 }
111                 $winners = [];
112                 $first = true;
113                 foreach ($candidates as $candidate) {
114                         $score = $this->scoreGuessing($solution, $candidate->guess, $first);
115                         $winner = new GuessingWinner();
116                         $winner->channel()->associate($this);
117                         $winner->pod = $start;
118                         $winner->uid = $candidate->uid;
119                         $winner->uname = $candidate->uname;
120                         $winner->guess = $candidate->guess;
121                         $winner->solution = $solution;
122                         $winner->score = $score;
123                         $winner->save();
124                         $winners[] = $winner;
125                         $first = false;
126                 }
127                 return $winners;
128         }
129
130         public function clearGuessing() {
131                 $this->guessing_start = null;
132                 $this->guessing_end = null;
133                 $this->save();
134         }
135
136         public function transformGuess($original) {
137                 $transformed = trim($original);
138                 if ($this->guessing_type == 'gtbk') {
139                         $transformed = str_replace(['roodyo1Gtbigkey'], ['2'], $transformed);
140                         $transformed = str_ireplace(['vier 4Head'], ['4'], $transformed);
141                 }
142                 return $transformed;
143         }
144
145         public function registerGuess($uid, $uname, $guess) {
146                 $model = new GuessingGuess();
147                 $model->channel()->associate($this);
148                 $model->uid = $uid;
149                 $model->uname = $uname;
150                 $model->guess = $this->transformGuess($guess);
151                 $model->save();
152         }
153
154         public function scoreGuessing($solution, $guess, $first) {
155                 $transformed = $this->transformGuess($guess);
156                 if ($transformed == $solution) {
157                         if ($first) {
158                                 return $this->getGuessingSetting('points_exact_first', 1);
159                         }
160                         return $this->getGuessingSetting('points_exact_other', 1);
161                 }
162                 $distance = abs(intval($transformed) - intval($solution));
163                 if ($distance <= $this->getGuessingSetting('points_close_max', 3)) {
164                         if ($first) {
165                                 return $this->getGuessingSetting('points_close_first', 1);
166                         }
167                         return $this->getGuessingSetting('points_close_other', 1);
168                 }
169                 return 0;
170         }
171
172         public function isValidGuess($solution) {
173                 $transformed = $this->transformGuess($solution);
174                 if ($this->guessing_type == 'gtbk') {
175                         $int_solution = intval($transformed);
176                         return is_numeric($transformed) && $int_solution > 0 && $int_solution < 23;
177                 }
178                 return false;
179         }
180
181         public function listGuessingWinners($winners) {
182                 $names = [];
183                 $distance = 0;
184                 foreach ($winners as $winner) {
185                         if ($winner->score > 0) {
186                                 $names[] = $winner->uname;
187                                 $distance = abs(intval($winner->guess) - intval($winner->solution));
188                         }
189                 }
190                 $msg = '';
191                 if (empty($names)) {
192                         $msg = $this->getGuessingSetting('no_winners_message');
193                 } else {
194                         $msg = $this->getGuessingSetting($distance ? 'close_winners_message' : 'winners_message', $this->getGuessingSetting('winners_message'));
195                         $msg = str_replace(['{distance}', '{names}'], [$distance, $this->listAnd($names)], $msg);
196                 }
197                 return $msg;
198         }
199
200         public function listAnd($entries) {
201                 $lang = empty($this->languages) ? 'en' : $this->languages[0];
202                 if ($lang == 'de') {
203                         return Arr::join($entries, ', ', ' und ');
204                 }
205                 return Arr::join($entries, ', ', ' and ');
206         }
207
208         public function crews() {
209                 return $this->hasMany(ChannelCrew::class);
210         }
211
212         public function episodes() {
213                 return $this->belongsToMany(Episode::class)
214                         ->using(Restream::class)
215                         ->withPivot('accept_comms', 'accept_tracker');
216         }
217
218         public function guesses() {
219                 return $this->hasMany(GuessingGuess::class);
220         }
221
222         public function organization() {
223                 return $this->belongsTo(Organization::class);
224         }
225
226         public function winners() {
227                 return $this->hasMany(GuessingWinner::class);
228         }
229
230         protected $casts = [
231                 'chat' => 'boolean',
232                 'chat_commands' => 'array',
233                 'chat_settings' => 'array',
234                 'guessing_end' => 'datetime',
235                 'guessing_settings' => 'array',
236                 'guessing_start' => 'datetime',
237                 'languages' => 'array',
238                 'join' => 'boolean',
239         ];
240
241         protected $hidden = [
242                 'access_key',
243                 'chat',
244                 'chat_commands',
245                 'chat_settings',
246                 'created_at',
247                 'ext_id',
248                 'guessing_settings',
249                 'join',
250                 'twitch_chat',
251                 'updated_at',
252         ];
253
254 }