Trivia improved quite a bit. Questions changed. More questions to come in the future via api. closes #180

This commit is contained in:
Kwoth 2016-12-25 13:09:22 +01:00
parent 0565830583
commit 4ecc66a3fe
6 changed files with 125 additions and 22881 deletions

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Games.Trivia
private int QuestionDurationMiliseconds { get; } = 30000; private int QuestionDurationMiliseconds { get; } = 30000;
private int HintTimeoutMiliseconds { get; } = 6000; private int HintTimeoutMiliseconds { get; } = 6000;
public bool ShowHints { get; set; } = true; public bool ShowHints { get; } = true;
private CancellationTokenSource triviaCancelSource { get; set; } private CancellationTokenSource triviaCancelSource { get; set; }
public TriviaQuestion CurrentQuestion { get; private set; } public TriviaQuestion CurrentQuestion { get; private set; }
@ -35,95 +35,111 @@ namespace NadekoBot.Modules.Games.Trivia
public int WinRequirement { get; } = 10; public int WinRequirement { get; } = 10;
public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq = 10) public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
{ {
_log = LogManager.GetCurrentClassLogger(); this._log = LogManager.GetCurrentClassLogger();
ShowHints = showHints;
this.ShowHints = showHints;
this.guild = guild; this.guild = guild;
this.channel = channel; this.channel = channel;
WinRequirement = winReq; this.WinRequirement = winReq;
Task.Run(async () => { try { await StartGame().ConfigureAwait(false); } catch { } });
} }
private async Task StartGame() public async Task StartGame()
{ {
while (!ShouldStopGame) while (!ShouldStopGame)
{ {
// reset the cancellation source // reset the cancellation source
triviaCancelSource = new CancellationTokenSource(); triviaCancelSource = new CancellationTokenSource();
var token = triviaCancelSource.Token;
// load question // load question
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions); CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
if (CurrentQuestion == null) if (CurrentQuestion == null)
{ {
try { await channel.SendErrorAsync($":exclamation: Failed loading a trivia question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false);
await End().ConfigureAwait(false);
return; return;
} }
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
//sendquestion
try { await channel.SendConfirmAsync($":question: Question",$"**{CurrentQuestion.Question}**").ConfigureAwait(false); }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
break;
}
catch (Exception ex) { _log.Warn(ex); }
//receive messages
NadekoBot.Client.MessageReceived += PotentialGuess;
//allow people to guess
GameActive = true;
EmbedBuilder questionEmbed;
IUserMessage questionMessage;
try try
{ {
//hint questionEmbed = new EmbedBuilder().WithOkColor()
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false); .WithTitle("Trivia Game")
if (ShowHints) .AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category))
try { await channel.SendConfirmAsync($":exclamation: Hint", CurrentQuestion.GetHint()).ConfigureAwait(false); } .AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question));
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
break;
}
catch (Exception ex) { _log.Warn(ex); }
//timeout
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
questionMessage = await channel.EmbedAsync(questionEmbed.Build()).ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
return;
}
catch (Exception ex)
{
_log.Warn(ex);
await Task.Delay(2000).ConfigureAwait(false);
continue;
}
//receive messages
try
{
NadekoBot.Client.MessageReceived += PotentialGuess;
//allow people to guess
GameActive = true;
try
{
//hint
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
if (ShowHints)
try
{
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
.ConfigureAwait(false);
}
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
break;
}
catch (Exception ex) { _log.Warn(ex); }
//timeout
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
}
catch (TaskCanceledException) { } //means someone guessed the answer
}
finally
{
GameActive = false;
NadekoBot.Client.MessageReceived -= PotentialGuess;
} }
catch (TaskCanceledException) { } //means someone guessed the answer
GameActive = false;
if (!triviaCancelSource.IsCancellationRequested) if (!triviaCancelSource.IsCancellationRequested)
try { await channel.SendConfirmAsync($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
NadekoBot.Client.MessageReceived -= PotentialGuess;
// load next question if game is still running
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
try { NadekoBot.Client.MessageReceived -= PotentialGuess; } catch { }
GameActive = false;
await End().ConfigureAwait(false);
} }
public async Task End() public async Task EnsureStopped()
{ {
ShouldStopGame = true; ShouldStopGame = true;
TriviaGame throwaway;
Games.TriviaCommands.RunningTrivias.TryRemove(channel.Guild.Id, out throwaway); await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
try .WithAuthor(eab => eab.WithName("Trivia Game Ended"))
{ .WithTitle("Final Results")
await channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithDescription(GetLeaderboard())
.WithTitle("Leaderboard") .Build()).ConfigureAwait(false);
.WithDescription(GetLeaderboard())
.Build(), "Trivia game ended.").ConfigureAwait(false);
}
catch { }
} }
public async Task StopGame() public async Task StopGame()
{ {
if (!ShouldStopGame) var old = ShouldStopGame;
try { await channel.SendConfirmAsync(":exclamation: Trivia will stop after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
ShouldStopGame = true; ShouldStopGame = true;
if (!old)
try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
private Task PotentialGuess(IMessage imsg) private Task PotentialGuess(IMessage imsg)
@ -137,11 +153,11 @@ namespace NadekoBot.Modules.Games.Trivia
{ {
try try
{ {
if (!(umsg.Channel is IGuildChannel && umsg.Channel is ITextChannel)) return; var textChannel = umsg.Channel as ITextChannel;
if ((umsg.Channel as ITextChannel).Guild != guild) return; if (textChannel == null || textChannel.Guild != guild)
if (umsg.Author.Id == NadekoBot.Client.GetCurrentUser().Id) return; return;
var guildUser = umsg.Author as IGuildUser; var guildUser = (IGuildUser)umsg.Author;
var guess = false; var guess = false;
await _guessLock.WaitAsync().ConfigureAwait(false); await _guessLock.WaitAsync().ConfigureAwait(false);
@ -156,10 +172,15 @@ namespace NadekoBot.Modules.Games.Trivia
finally { _guessLock.Release(); } finally { _guessLock.Release(); }
if (!guess) return; if (!guess) return;
triviaCancelSource.Cancel(); triviaCancelSource.Cancel();
try { await channel.SendConfirmAsync($"☑️ {guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
if (Users[guildUser] != WinRequirement) return;
ShouldStopGame = true; if (Users[guildUser] == WinRequirement) {
await channel.SendConfirmAsync($":exclamation: We have a winner! It's {guildUser.Mention}.").ConfigureAwait(false); ShouldStopGame = true;
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
return;
}
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
}); });
@ -169,7 +190,7 @@ namespace NadekoBot.Modules.Games.Trivia
public string GetLeaderboard() public string GetLeaderboard()
{ {
if (Users.Count == 0) if (Users.Count == 0)
return ""; return "No results.";
var sb = new StringBuilder(); var sb = new StringBuilder();
@ -181,4 +202,4 @@ namespace NadekoBot.Modules.Games.Trivia
return sb.ToString(); return sb.ToString();
} }
} }
} }

View File

@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Games.Trivia
}; };
public static int maxStringLength = 22; public static int maxStringLength = 22;
public string Category; public string Category { get; set; }
public string Question; public string Question { get; set; }
public string Answer; public string Answer { get; set; }
public TriviaQuestion(string q, string a, string c) public TriviaQuestion(string q, string a, string c)
{ {
@ -79,9 +79,6 @@ namespace NadekoBot.Modules.Games.Trivia
return str; return str;
} }
public override string ToString() =>
"Question: **" + this.Question + "?**";
private static string Scramble(string word) private static string Scramble(string word)
{ {
var letters = word.ToArray(); var letters = word.ToArray();
@ -101,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia
if (letters[i] != ' ') if (letters[i] != ' ')
letters[i] = '_'; letters[i] = '_';
} }
return "`" + string.Join(" ", letters) + "`"; return string.Join(" ", letters);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -11,36 +12,31 @@ namespace NadekoBot.Modules.Games.Trivia
{ {
public class TriviaQuestionPool public class TriviaQuestionPool
{ {
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool(); private static TriviaQuestionPool _instance;
public ConcurrentHashSet<TriviaQuestion> pool = new ConcurrentHashSet<TriviaQuestion>(); public static TriviaQuestionPool Instance { get; } = _instance ?? (_instance = new TriviaQuestionPool());
private const string questionsFile = "data/trivia_questions.json";
private Random rng { get; } = new NadekoRandom(); private Random rng { get; } = new NadekoRandom();
private TriviaQuestion[] pool { get; }
static TriviaQuestionPool() { } static TriviaQuestionPool() { }
private TriviaQuestionPool() private TriviaQuestionPool()
{ {
Reload(); pool = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(questionsFile));
} }
public TriviaQuestion GetRandomQuestion(IEnumerable<TriviaQuestion> exclude) public TriviaQuestion GetRandomQuestion(HashSet<TriviaQuestion> exclude)
{ {
var list = pool.Except(exclude).ToList(); if (pool.Length == 0)
var rand = rng.Next(0, list.Count); return null;
return list[rand];
}
public void Reload() TriviaQuestion randomQuestion;
{ while (exclude.Contains(randomQuestion = pool[rng.Next(0, pool.Length)])) ;
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
foreach (var item in arr) return randomQuestion;
{
var tq = new TriviaQuestion(item["Question"].ToString().SanitizeMentions(), item["Answer"].ToString().SanitizeMentions(), item["Category"]?.ToString());
pool.Add(tq);
}
var r = new NadekoRandom();
pool = new ConcurrentHashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
} }
} }
} }

View File

@ -20,27 +20,30 @@ namespace NadekoBot.Modules.Games
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Trivia(IUserMessage umsg, params string[] args) public Task Trivia(IUserMessage umsg, [Remainder] string additionalArgs = "")
=> Trivia(umsg, 10, additionalArgs);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Trivia(IUserMessage umsg, int winReq = 10, [Remainder] string additionalArgs = "")
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
TriviaGame trivia; var showHints = !additionalArgs.Contains("nohint");
if (!RunningTrivias.TryGetValue(channel.Guild.Id, out trivia))
TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq);
if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
{ {
var showHints = !args.Contains("nohint"); try
var number = args.Select(s =>
{ {
int num; await trivia.StartGame().ConfigureAwait(false);
return new Tuple<bool, int>(int.TryParse(s, out num), num); }
}).Where(t => t.Item1).Select(t => t.Item2).FirstOrDefault(); finally
if (number < 0) {
return; RunningTrivias.TryRemove(channel.Guild.Id, out trivia);
var triviaGame = new TriviaGame(channel.Guild, (ITextChannel)umsg.Channel, showHints, number == 0 ? 10 : number); await trivia.EnsureStopped().ConfigureAwait(false);
if (RunningTrivias.TryAdd(channel.Guild.Id, triviaGame)) }
await channel.SendConfirmAsync($"**Trivia game started! {triviaGame.WinRequirement} points needed to win.**").ConfigureAwait(false); return;
else
await triviaGame.StopGame().ConfigureAwait(false);
return;
} }
await channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false); await channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long