|
1 use crate::{ |
|
2 protocol::messages::{ |
|
3 server_chat, |
|
4 add_flags, remove_flags, |
|
5 HWProtocolMessage::{self, Rnd}, |
|
6 HWServerMessage::{self, *}, |
|
7 ProtocolFlags as Flags, |
|
8 }, |
|
9 core::{ |
|
10 client::HWClient, |
|
11 server::HWServer, |
|
12 types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType}, |
|
13 room::HWRoom, |
|
14 }, |
|
15 utils::to_engine_msg, |
|
16 }; |
|
17 |
|
18 use super::Response; |
|
19 |
|
20 use crate::core::types::RoomConfig; |
|
21 use rand::{self, seq::SliceRandom, thread_rng, Rng}; |
|
22 use std::{iter::once, mem::replace}; |
|
23 |
|
24 pub fn rnd_reply(options: &[String]) -> HWServerMessage { |
|
25 let mut rng = thread_rng(); |
|
26 |
|
27 let reply = if options.is_empty() { |
|
28 (*&["heads", "tails"].choose(&mut rng).unwrap()).to_string() |
|
29 } else { |
|
30 options.choose(&mut rng).unwrap().clone() |
|
31 }; |
|
32 |
|
33 ChatMsg { |
|
34 nick: "[random]".to_string(), |
|
35 msg: reply, |
|
36 } |
|
37 } |
|
38 |
|
39 pub fn join_lobby(server: &mut HWServer, response: &mut Response) { |
|
40 let client_id = response.client_id(); |
|
41 |
|
42 let client = &server.clients[client_id]; |
|
43 let nick = vec![client.nick.clone()]; |
|
44 let mut flags = vec![]; |
|
45 if client.is_registered() { |
|
46 flags.push(Flags::Registered) |
|
47 } |
|
48 if client.is_admin() { |
|
49 flags.push(Flags::Admin) |
|
50 } |
|
51 if client.is_contributor() { |
|
52 flags.push(Flags::Contributor) |
|
53 } |
|
54 |
|
55 let all_nicks: Vec<_> = server.collect_nicks(|_| true); |
|
56 |
|
57 let mut flag_selectors = [ |
|
58 ( |
|
59 Flags::Registered, |
|
60 server.collect_nicks(|(_, c)| c.is_registered()), |
|
61 ), |
|
62 (Flags::Admin, server.collect_nicks(|(_, c)| c.is_admin())), |
|
63 ( |
|
64 Flags::Contributor, |
|
65 server.collect_nicks(|(_, c)| c.is_contributor()), |
|
66 ), |
|
67 ( |
|
68 Flags::InRoom, |
|
69 server.collect_nicks(|(_, c)| c.room_id.is_some()), |
|
70 ), |
|
71 ]; |
|
72 |
|
73 let server_msg = ServerMessage(server.get_greetings(client_id).to_string()); |
|
74 |
|
75 let rooms_msg = Rooms( |
|
76 server |
|
77 .rooms |
|
78 .iter() |
|
79 .filter(|(_, r)| r.protocol_number == client.protocol_number) |
|
80 .flat_map(|(_, r)| r.info(r.master_id.map(|id| &server.clients[id]))) |
|
81 .collect(), |
|
82 ); |
|
83 |
|
84 response.add(LobbyJoined(nick).send_all().but_self()); |
|
85 response.add( |
|
86 ClientFlags(add_flags(&flags), all_nicks.clone()) |
|
87 .send_all() |
|
88 .but_self(), |
|
89 ); |
|
90 |
|
91 response.add(LobbyJoined(all_nicks).send_self()); |
|
92 for (flag, nicks) in &mut flag_selectors { |
|
93 if !nicks.is_empty() { |
|
94 response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self()); |
|
95 } |
|
96 } |
|
97 |
|
98 response.add(server_msg.send_self()); |
|
99 response.add(rooms_msg.send_self()); |
|
100 } |
|
101 |
|
102 pub fn remove_teams( |
|
103 room: &mut HWRoom, |
|
104 team_names: Vec<String>, |
|
105 is_in_game: bool, |
|
106 response: &mut Response, |
|
107 ) { |
|
108 if let Some(ref mut info) = room.game_info { |
|
109 for team_name in &team_names { |
|
110 info.left_teams.push(team_name.clone()); |
|
111 |
|
112 if is_in_game { |
|
113 let msg = once(b'F').chain(team_name.bytes()); |
|
114 response.add( |
|
115 ForwardEngineMessage(vec![to_engine_msg(msg)]) |
|
116 .send_all() |
|
117 .in_room(room.id) |
|
118 .but_self(), |
|
119 ); |
|
120 |
|
121 info.teams_in_game -= 1; |
|
122 |
|
123 let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes())); |
|
124 if let Some(m) = &info.sync_msg { |
|
125 info.msg_log.push(m.clone()); |
|
126 info.sync_msg = None |
|
127 } |
|
128 info.msg_log.push(remove_msg.clone()); |
|
129 |
|
130 response.add( |
|
131 ForwardEngineMessage(vec![remove_msg]) |
|
132 .send_all() |
|
133 .in_room(room.id) |
|
134 .but_self(), |
|
135 ); |
|
136 } |
|
137 } |
|
138 } |
|
139 |
|
140 for team_name in team_names { |
|
141 room.remove_team(&team_name); |
|
142 response.add(TeamRemove(team_name).send_all().in_room(room.id)); |
|
143 } |
|
144 } |
|
145 |
|
146 fn remove_client_from_room( |
|
147 client: &mut HWClient, |
|
148 room: &mut HWRoom, |
|
149 response: &mut Response, |
|
150 msg: &str, |
|
151 ) { |
|
152 room.players_number -= 1; |
|
153 if room.players_number > 0 || room.is_fixed() { |
|
154 if client.is_ready() && room.ready_players_number > 0 { |
|
155 room.ready_players_number -= 1; |
|
156 } |
|
157 |
|
158 let team_names: Vec<_> = room |
|
159 .client_teams(client.id) |
|
160 .map(|t| t.name.clone()) |
|
161 .collect(); |
|
162 remove_teams(room, team_names, client.is_in_game(), response); |
|
163 |
|
164 if room.players_number > 0 { |
|
165 response.add( |
|
166 RoomLeft(client.nick.clone(), msg.to_string()) |
|
167 .send_all() |
|
168 .in_room(room.id) |
|
169 .but_self(), |
|
170 ); |
|
171 } |
|
172 |
|
173 if client.is_master() && !room.is_fixed() { |
|
174 client.set_is_master(false); |
|
175 response.add( |
|
176 ClientFlags( |
|
177 remove_flags(&[Flags::RoomMaster]), |
|
178 vec![client.nick.clone()], |
|
179 ) |
|
180 .send_all() |
|
181 .in_room(room.id), |
|
182 ); |
|
183 room.master_id = None; |
|
184 } |
|
185 } |
|
186 |
|
187 client.room_id = None; |
|
188 |
|
189 let update_msg = if room.players_number == 0 && !room.is_fixed() { |
|
190 RoomRemove(room.name.clone()) |
|
191 } else { |
|
192 RoomUpdated(room.name.clone(), room.info(Some(&client))) |
|
193 }; |
|
194 response.add(update_msg.send_all().with_protocol(room.protocol_number)); |
|
195 |
|
196 response.add(ClientFlags(remove_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_all()); |
|
197 } |
|
198 |
|
199 pub fn change_master( |
|
200 server: &mut HWServer, |
|
201 room_id: RoomId, |
|
202 new_master_id: ClientId, |
|
203 response: &mut Response, |
|
204 ) { |
|
205 let room = &mut server.rooms[room_id]; |
|
206 if let Some(master_id) = room.master_id { |
|
207 server.clients[master_id].set_is_master(false); |
|
208 response.add( |
|
209 ClientFlags( |
|
210 remove_flags(&[Flags::RoomMaster]), |
|
211 vec![server.clients[master_id].nick.clone()], |
|
212 ) |
|
213 .send_all() |
|
214 .in_room(room_id), |
|
215 ) |
|
216 } |
|
217 |
|
218 room.master_id = Some(new_master_id); |
|
219 server.clients[new_master_id].set_is_master(true); |
|
220 |
|
221 response.add( |
|
222 ClientFlags( |
|
223 add_flags(&[Flags::RoomMaster]), |
|
224 vec![server.clients[new_master_id].nick.clone()], |
|
225 ) |
|
226 .send_all() |
|
227 .in_room(room_id), |
|
228 ); |
|
229 } |
|
230 |
|
231 pub fn enter_room( |
|
232 server: &mut HWServer, |
|
233 client_id: ClientId, |
|
234 room_id: RoomId, |
|
235 response: &mut Response, |
|
236 ) { |
|
237 let nick = server.clients[client_id].nick.clone(); |
|
238 server.move_to_room(client_id, room_id); |
|
239 |
|
240 response.add(RoomJoined(vec![nick.clone()]).send_all().in_room(room_id)); |
|
241 response.add(ClientFlags(add_flags(&[Flags::InRoom]), vec![nick]).send_all()); |
|
242 let nicks = server.collect_nicks(|(_, c)| c.room_id == Some(room_id)); |
|
243 response.add(RoomJoined(nicks).send_self()); |
|
244 |
|
245 get_room_teams(server, room_id, client_id, response); |
|
246 |
|
247 let room = &server.rooms[room_id]; |
|
248 get_room_config(room, client_id, response); |
|
249 |
|
250 let mut flag_selectors = [ |
|
251 ( |
|
252 Flags::RoomMaster, |
|
253 server.collect_nicks(|(_, c)| c.is_master()), |
|
254 ), |
|
255 (Flags::Ready, server.collect_nicks(|(_, c)| c.is_ready())), |
|
256 (Flags::InGame, server.collect_nicks(|(_, c)| c.is_in_game())), |
|
257 ]; |
|
258 |
|
259 for (flag, nicks) in &mut flag_selectors { |
|
260 response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self()); |
|
261 } |
|
262 |
|
263 if !room.greeting.is_empty() { |
|
264 response.add( |
|
265 ChatMsg { |
|
266 nick: "[greeting]".to_string(), |
|
267 msg: room.greeting.clone(), |
|
268 } |
|
269 .send_self(), |
|
270 ); |
|
271 } |
|
272 } |
|
273 |
|
274 pub fn exit_room(server: &mut HWServer, client_id: ClientId, response: &mut Response, msg: &str) { |
|
275 let client = &mut server.clients[client_id]; |
|
276 |
|
277 if let Some(room_id) = client.room_id { |
|
278 let room = &mut server.rooms[room_id]; |
|
279 |
|
280 remove_client_from_room(client, room, response, msg); |
|
281 |
|
282 if !room.is_fixed() { |
|
283 if room.players_number == 0 { |
|
284 server.rooms.remove(room_id); |
|
285 } else if room.master_id == None { |
|
286 let new_master_id = server.room_clients(room_id).next(); |
|
287 if let Some(new_master_id) = new_master_id { |
|
288 let new_master_nick = server.clients[new_master_id].nick.clone(); |
|
289 let room = &mut server.rooms[room_id]; |
|
290 room.master_id = Some(new_master_id); |
|
291 server.clients[new_master_id].set_is_master(true); |
|
292 |
|
293 if room.protocol_number < 42 { |
|
294 room.name = new_master_nick.clone(); |
|
295 } |
|
296 |
|
297 room.set_join_restriction(false); |
|
298 room.set_team_add_restriction(false); |
|
299 room.set_unregistered_players_restriction(true); |
|
300 |
|
301 response.add( |
|
302 ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick]) |
|
303 .send_all() |
|
304 .in_room(room.id), |
|
305 ); |
|
306 } |
|
307 } |
|
308 } |
|
309 } |
|
310 } |
|
311 |
|
312 pub fn remove_client(server: &mut HWServer, response: &mut Response, msg: String) { |
|
313 let client_id = response.client_id(); |
|
314 let client = &mut server.clients[client_id]; |
|
315 let nick = client.nick.clone(); |
|
316 |
|
317 exit_room(server, client_id, response, &msg); |
|
318 |
|
319 server.remove_client(client_id); |
|
320 |
|
321 response.add(LobbyLeft(nick, msg.to_string()).send_all()); |
|
322 response.add(Bye("User quit: ".to_string() + &msg).send_self()); |
|
323 response.remove_client(client_id); |
|
324 } |
|
325 |
|
326 pub fn get_room_update( |
|
327 room_name: Option<String>, |
|
328 room: &HWRoom, |
|
329 master: Option<&HWClient>, |
|
330 response: &mut Response, |
|
331 ) { |
|
332 let update_msg = RoomUpdated(room_name.unwrap_or(room.name.clone()), room.info(master)); |
|
333 response.add(update_msg.send_all().with_protocol(room.protocol_number)); |
|
334 } |
|
335 |
|
336 pub fn get_room_config_impl(config: &RoomConfig, to_client: ClientId, response: &mut Response) { |
|
337 response.add(ConfigEntry("FULLMAPCONFIG".to_string(), config.to_map_config()).send(to_client)); |
|
338 for cfg in config.to_game_config() { |
|
339 response.add(cfg.to_server_msg().send(to_client)); |
|
340 } |
|
341 } |
|
342 |
|
343 pub fn get_room_config(room: &HWRoom, to_client: ClientId, response: &mut Response) { |
|
344 get_room_config_impl(room.active_config(), to_client, response); |
|
345 } |
|
346 |
|
347 pub fn get_teams<'a, I>(teams: I, to_client: ClientId, response: &mut Response) |
|
348 where |
|
349 I: Iterator<Item = &'a TeamInfo>, |
|
350 { |
|
351 for team in teams { |
|
352 response.add(TeamAdd(team.to_protocol()).send(to_client)); |
|
353 response.add(TeamColor(team.name.clone(), team.color).send(to_client)); |
|
354 response.add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send(to_client)); |
|
355 } |
|
356 } |
|
357 |
|
358 pub fn get_room_teams( |
|
359 server: &HWServer, |
|
360 room_id: RoomId, |
|
361 to_client: ClientId, |
|
362 response: &mut Response, |
|
363 ) { |
|
364 let room = &server.rooms[room_id]; |
|
365 let current_teams = match room.game_info { |
|
366 Some(ref info) => &info.teams_at_start, |
|
367 None => &room.teams, |
|
368 }; |
|
369 |
|
370 get_teams(current_teams.iter().map(|(_, t)| t), to_client, response); |
|
371 } |
|
372 |
|
373 pub fn get_room_flags( |
|
374 server: &HWServer, |
|
375 room_id: RoomId, |
|
376 to_client: ClientId, |
|
377 response: &mut Response, |
|
378 ) { |
|
379 let room = &server.rooms[room_id]; |
|
380 if let Some(id) = room.master_id { |
|
381 response.add( |
|
382 ClientFlags( |
|
383 add_flags(&[Flags::RoomMaster]), |
|
384 vec![server.clients[id].nick.clone()], |
|
385 ) |
|
386 .send(to_client), |
|
387 ); |
|
388 } |
|
389 let nicks: Vec<_> = server |
|
390 .clients |
|
391 .iter() |
|
392 .filter(|(_, c)| c.room_id == Some(room_id) && c.is_ready()) |
|
393 .map(|(_, c)| c.nick.clone()) |
|
394 .collect(); |
|
395 if !nicks.is_empty() { |
|
396 response.add(ClientFlags(add_flags(&[Flags::Ready]), nicks).send(to_client)); |
|
397 } |
|
398 } |
|
399 |
|
400 pub fn apply_voting_result( |
|
401 server: &mut HWServer, |
|
402 room_id: RoomId, |
|
403 response: &mut Response, |
|
404 kind: VoteType, |
|
405 ) { |
|
406 match kind { |
|
407 VoteType::Kick(nick) => { |
|
408 if let Some(client) = server.find_client(&nick) { |
|
409 if client.room_id == Some(room_id) { |
|
410 let id = client.id; |
|
411 response.add(Kicked.send(id)); |
|
412 exit_room(server, id, response, "kicked"); |
|
413 } |
|
414 } |
|
415 } |
|
416 VoteType::Map(None) => (), |
|
417 VoteType::Map(Some(name)) => { |
|
418 if let Some(location) = server.rooms[room_id].load_config(&name) { |
|
419 response.add( |
|
420 server_chat(location.to_string()) |
|
421 .send_all() |
|
422 .in_room(room_id), |
|
423 ); |
|
424 let room = &server.rooms[room_id]; |
|
425 let room_master = if let Some(id) = room.master_id { |
|
426 Some(&server.clients[id]) |
|
427 } else { |
|
428 None |
|
429 }; |
|
430 get_room_update(None, room, room_master, response); |
|
431 |
|
432 for (_, client) in server.clients.iter() { |
|
433 if client.room_id == Some(room_id) { |
|
434 super::common::get_room_config(&server.rooms[room_id], client.id, response); |
|
435 } |
|
436 } |
|
437 } |
|
438 } |
|
439 VoteType::Pause => { |
|
440 if let Some(ref mut info) = server.rooms[room_id].game_info { |
|
441 info.is_paused = !info.is_paused; |
|
442 response.add( |
|
443 server_chat("Pause toggled.".to_string()) |
|
444 .send_all() |
|
445 .in_room(room_id), |
|
446 ); |
|
447 response.add( |
|
448 ForwardEngineMessage(vec![to_engine_msg(once(b'I'))]) |
|
449 .send_all() |
|
450 .in_room(room_id), |
|
451 ); |
|
452 } |
|
453 } |
|
454 VoteType::NewSeed => { |
|
455 let seed = thread_rng().gen_range(0, 1_000_000_000).to_string(); |
|
456 let cfg = GameCfg::Seed(seed); |
|
457 response.add(cfg.to_server_msg().send_all().in_room(room_id)); |
|
458 server.rooms[room_id].set_config(cfg); |
|
459 } |
|
460 VoteType::HedgehogsPerTeam(number) => { |
|
461 let r = &mut server.rooms[room_id]; |
|
462 let nicks = r.set_hedgehogs_number(number); |
|
463 |
|
464 response.extend( |
|
465 nicks |
|
466 .into_iter() |
|
467 .map(|n| HedgehogsNumber(n, number).send_all().in_room(room_id)), |
|
468 ); |
|
469 } |
|
470 } |
|
471 } |
|
472 |
|
473 fn add_vote(room: &mut HWRoom, response: &mut Response, vote: Vote) -> Option<bool> { |
|
474 let client_id = response.client_id; |
|
475 let mut result = None; |
|
476 |
|
477 if let Some(ref mut voting) = room.voting { |
|
478 if vote.is_forced || voting.votes.iter().all(|(id, _)| client_id != *id) { |
|
479 response.add(server_chat("Your vote has been counted.".to_string()).send_self()); |
|
480 voting.votes.push((client_id, vote.is_pro)); |
|
481 let i = voting.votes.iter(); |
|
482 let pro = i.clone().filter(|(_, v)| *v).count(); |
|
483 let contra = i.filter(|(_, v)| !*v).count(); |
|
484 let success_quota = voting.voters.len() / 2 + 1; |
|
485 if vote.is_forced && vote.is_pro || pro >= success_quota { |
|
486 result = Some(true); |
|
487 } else if vote.is_forced && !vote.is_pro || contra > voting.voters.len() - success_quota |
|
488 { |
|
489 result = Some(false); |
|
490 } |
|
491 } else { |
|
492 response.add(server_chat("You already have voted.".to_string()).send_self()); |
|
493 } |
|
494 } else { |
|
495 response.add(server_chat("There's no voting going on.".to_string()).send_self()); |
|
496 } |
|
497 |
|
498 result |
|
499 } |
|
500 |
|
501 pub fn submit_vote(server: &mut HWServer, vote: Vote, response: &mut Response) { |
|
502 let client_id = response.client_id; |
|
503 let client = &server.clients[client_id]; |
|
504 |
|
505 if let Some(room_id) = client.room_id { |
|
506 let room = &mut server.rooms[room_id]; |
|
507 |
|
508 if let Some(res) = add_vote(room, response, vote) { |
|
509 response.add( |
|
510 server_chat("Voting closed.".to_string()) |
|
511 .send_all() |
|
512 .in_room(room.id), |
|
513 ); |
|
514 let voting = replace(&mut room.voting, None).unwrap(); |
|
515 if res { |
|
516 apply_voting_result(server, room_id, response, voting.kind); |
|
517 } |
|
518 } |
|
519 } |
|
520 } |
|
521 |
|
522 pub fn start_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) { |
|
523 let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server |
|
524 .clients |
|
525 .iter() |
|
526 .map(|(id, c)| (id, c.nick.clone())) |
|
527 .unzip(); |
|
528 let room = &mut server.rooms[room_id]; |
|
529 |
|
530 if !room.has_multiple_clans() { |
|
531 response.add( |
|
532 Warning("The game can't be started with less than two clans!".to_string()).send_self(), |
|
533 ); |
|
534 } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number { |
|
535 response.add(Warning("Not all players are ready".to_string()).send_self()); |
|
536 } else if room.game_info.is_some() { |
|
537 response.add(Warning("The game is already in progress".to_string()).send_self()); |
|
538 } else { |
|
539 room.start_round(); |
|
540 for id in room_clients { |
|
541 let c = &mut server.clients[id]; |
|
542 c.set_is_in_game(true); |
|
543 c.team_indices = room.client_team_indices(c.id); |
|
544 } |
|
545 response.add(RunGame.send_all().in_room(room.id)); |
|
546 response.add( |
|
547 ClientFlags(add_flags(&[Flags::InGame]), room_nicks) |
|
548 .send_all() |
|
549 .in_room(room.id), |
|
550 ); |
|
551 |
|
552 let room_master = if let Some(id) = room.master_id { |
|
553 Some(&server.clients[id]) |
|
554 } else { |
|
555 None |
|
556 }; |
|
557 get_room_update(None, room, room_master, response); |
|
558 } |
|
559 } |
|
560 |
|
561 pub fn end_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) { |
|
562 let room = &mut server.rooms[room_id]; |
|
563 room.ready_players_number = 1; |
|
564 let room_master = if let Some(id) = room.master_id { |
|
565 Some(&server.clients[id]) |
|
566 } else { |
|
567 None |
|
568 }; |
|
569 get_room_update(None, room, room_master, response); |
|
570 response.add(RoundFinished.send_all().in_room(room_id)); |
|
571 |
|
572 if let Some(info) = replace(&mut room.game_info, None) { |
|
573 for (_, client) in server.clients.iter() { |
|
574 if client.room_id == Some(room_id) && client.is_joined_mid_game() { |
|
575 super::common::get_room_config(room, client.id, response); |
|
576 response.extend( |
|
577 info.left_teams |
|
578 .iter() |
|
579 .map(|name| TeamRemove(name.clone()).send(client.id)), |
|
580 ); |
|
581 } |
|
582 } |
|
583 } |
|
584 |
|
585 let nicks: Vec<_> = server |
|
586 .clients |
|
587 .iter_mut() |
|
588 .filter(|(_, c)| c.room_id == Some(room_id)) |
|
589 .map(|(_, c)| { |
|
590 c.set_is_ready(c.is_master()); |
|
591 c.set_is_joined_mid_game(false); |
|
592 c |
|
593 }) |
|
594 .filter_map(|c| { |
|
595 if !c.is_master() { |
|
596 Some(c.nick.clone()) |
|
597 } else { |
|
598 None |
|
599 } |
|
600 }) |
|
601 .collect(); |
|
602 |
|
603 if !nicks.is_empty() { |
|
604 let msg = if room.protocol_number < 38 { |
|
605 LegacyReady(false, nicks) |
|
606 } else { |
|
607 ClientFlags(remove_flags(&[Flags::Ready]), nicks) |
|
608 }; |
|
609 response.add(msg.send_all().in_room(room_id)); |
|
610 } |
|
611 } |
|
612 |
|
613 #[cfg(test)] |
|
614 mod tests { |
|
615 use super::*; |
|
616 use crate::protocol::messages::HWServerMessage::ChatMsg; |
|
617 use crate::server::actions::PendingMessage; |
|
618 |
|
619 fn reply2string(r: HWServerMessage) -> String { |
|
620 match r { |
|
621 ChatMsg { msg: p, .. } => String::from(p), |
|
622 _ => panic!("expected a ChatMsg"), |
|
623 } |
|
624 } |
|
625 |
|
626 fn run_handle_test(opts: Vec<String>) { |
|
627 let opts2 = opts.clone(); |
|
628 for opt in opts { |
|
629 while reply2string(rnd_reply(&opts2)) != opt {} |
|
630 } |
|
631 } |
|
632 |
|
633 /// This test terminates almost surely. |
|
634 #[test] |
|
635 fn test_handle_rnd_empty() { |
|
636 run_handle_test(vec![]) |
|
637 } |
|
638 |
|
639 /// This test terminates almost surely. |
|
640 #[test] |
|
641 fn test_handle_rnd_nonempty() { |
|
642 run_handle_test(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()]) |
|
643 } |
|
644 |
|
645 /// This test terminates almost surely (strong law of large numbers) |
|
646 #[test] |
|
647 fn test_distribution() { |
|
648 let eps = 0.000001; |
|
649 let lim = 0.5; |
|
650 let opts = vec![0.to_string(), 1.to_string()]; |
|
651 let mut ones = 0; |
|
652 let mut tries = 0; |
|
653 |
|
654 while tries < 1000 || ((ones as f64 / tries as f64) - lim).abs() >= eps { |
|
655 tries += 1; |
|
656 if reply2string(rnd_reply(&opts)) == 1.to_string() { |
|
657 ones += 1; |
|
658 } |
|
659 } |
|
660 } |
|
661 } |