Trivia rewritten, small cleanup.
This commit is contained in:
parent
c721076147
commit
09bdaee91e
@ -89,8 +89,6 @@ namespace NadekoBot.Classes.Music {
|
|||||||
public Action OnResolving = null;
|
public Action OnResolving = null;
|
||||||
public Action<string> OnResolvingFailed = null;
|
public Action<string> OnResolvingFailed = null;
|
||||||
|
|
||||||
//todo maybe add remove, in order to create remove at position command
|
|
||||||
|
|
||||||
internal void Cancel() {
|
internal void Cancel() {
|
||||||
musicStreamer?.Cancel();
|
musicStreamer?.Cancel();
|
||||||
}
|
}
|
||||||
@ -150,7 +148,6 @@ namespace NadekoBot.Classes.Music {
|
|||||||
$"Length:{buffer.Length * 1.0f / 1.MB()}MB Status: {State}\n" +
|
$"Length:{buffer.Length * 1.0f / 1.MB()}MB Status: {State}\n" +
|
||||||
"--------------------------------\n";
|
"--------------------------------\n";
|
||||||
|
|
||||||
//todo app will crash if song is too long, should load only next 20-ish seconds
|
|
||||||
private async Task BufferSong() {
|
private async Task BufferSong() {
|
||||||
Console.WriteLine("Buffering...");
|
Console.WriteLine("Buffering...");
|
||||||
//start feeding the buffer
|
//start feeding the buffer
|
||||||
|
@ -1,366 +0,0 @@
|
|||||||
using Discord;
|
|
||||||
using Discord.Commands;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Timers;
|
|
||||||
using NadekoBot.Extensions;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
//github.com/micmorris contributed quite a bit to making trivia better!
|
|
||||||
namespace NadekoBot {
|
|
||||||
public class Trivia : DiscordCommand {
|
|
||||||
public static float HINT_TIME_SECONDS = 6;
|
|
||||||
|
|
||||||
public static ConcurrentDictionary<ulong, TriviaGame> runningTrivias;
|
|
||||||
|
|
||||||
public Trivia() : base() {
|
|
||||||
runningTrivias = new ConcurrentDictionary<ulong, TriviaGame>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TriviaGame StartNewGame(CommandEventArgs e) {
|
|
||||||
if (runningTrivias.ContainsKey(e.User.Server.Id))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var tg = new TriviaGame(e, NadekoBot.client);
|
|
||||||
runningTrivias.TryAdd(e.Server.Id, tg);
|
|
||||||
|
|
||||||
return tg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TriviaQuestion GetCurrentQuestion(ulong serverId) => runningTrivias[serverId].currentQuestion;
|
|
||||||
|
|
||||||
public override Func<CommandEventArgs, Task> DoFunc() => async e => {
|
|
||||||
TriviaGame tg;
|
|
||||||
if ((tg = StartNewGame(e)) != null) {
|
|
||||||
await e.Send("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
|
|
||||||
} else
|
|
||||||
await e.Send("Trivia game is already running on this server. The question is:\n**" + GetCurrentQuestion(e.Server.Id).Question + "**");
|
|
||||||
};
|
|
||||||
|
|
||||||
private Func<CommandEventArgs, Task> LbFunc() => async e => {
|
|
||||||
if (runningTrivias.ContainsKey(e.Server.Id)) {
|
|
||||||
var lb = runningTrivias[e.User.Server.Id].GetLeaderboard();
|
|
||||||
await e.Send(lb);
|
|
||||||
} else
|
|
||||||
await e.Send("Trivia game is not running on this server.");
|
|
||||||
};
|
|
||||||
|
|
||||||
private Func<CommandEventArgs, Task> RepeatFunc() => async e => {
|
|
||||||
if (runningTrivias.ContainsKey(e.Server.Id)) {
|
|
||||||
var lb = runningTrivias[e.User.Server.Id].GetLeaderboard();
|
|
||||||
await e.Send(lb);
|
|
||||||
} else
|
|
||||||
await e.Send("Trivia game is not running on this server.");
|
|
||||||
};
|
|
||||||
|
|
||||||
public override void Init(CommandGroupBuilder cgb) {
|
|
||||||
cgb.CreateCommand("t")
|
|
||||||
.Description("Starts a game of trivia.")
|
|
||||||
.Alias("-t")
|
|
||||||
.Do(DoFunc());
|
|
||||||
|
|
||||||
cgb.CreateCommand("tl")
|
|
||||||
.Description("Shows a current trivia leaderboard.")
|
|
||||||
.Alias("-tl")
|
|
||||||
.Alias("tlb")
|
|
||||||
.Alias("-tlb")
|
|
||||||
.Do(LbFunc());
|
|
||||||
|
|
||||||
cgb.CreateCommand("tq")
|
|
||||||
.Description("Quits current trivia after current question.")
|
|
||||||
.Alias("-tq")
|
|
||||||
.Do(QuitFunc());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Func<CommandEventArgs, Task> QuitFunc() => async e => {
|
|
||||||
if (runningTrivias.ContainsKey(e.Server.Id) && runningTrivias[e.Server.Id].ChannelId == e.Channel.Id) {
|
|
||||||
await e.Send("Trivia will stop after this question.");
|
|
||||||
runningTrivias[e.Server.Id].StopGame();
|
|
||||||
} else await e.Send("No trivias are running on this channel.");
|
|
||||||
};
|
|
||||||
|
|
||||||
internal static void FinishGame(TriviaGame triviaGame) {
|
|
||||||
TriviaGame throwaway;
|
|
||||||
runningTrivias.TryRemove(runningTrivias.Where(kvp => kvp.Value == triviaGame).First().Key,out throwaway);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TriviaGame {
|
|
||||||
|
|
||||||
private DiscordClient client;
|
|
||||||
private ulong _serverId;
|
|
||||||
private ulong _channellId;
|
|
||||||
|
|
||||||
public ulong ChannelId => _channellId;
|
|
||||||
|
|
||||||
private Dictionary<ulong, int> users;
|
|
||||||
|
|
||||||
public List<string> oldQuestions;
|
|
||||||
|
|
||||||
public TriviaQuestion currentQuestion = null;
|
|
||||||
|
|
||||||
private bool active = false;
|
|
||||||
|
|
||||||
//represents the min size to judge levDistance with
|
|
||||||
private List<Tuple<int, int>> strictness;
|
|
||||||
private int maxStringLength;
|
|
||||||
|
|
||||||
private Timer timeout;
|
|
||||||
private Stopwatch stopwatch;
|
|
||||||
private bool isQuit = false;
|
|
||||||
|
|
||||||
private System.Threading.CancellationTokenSource hintCancelSource;
|
|
||||||
|
|
||||||
public TriviaGame(CommandEventArgs starter, DiscordClient client) {
|
|
||||||
this.users = new Dictionary<ulong, int>();
|
|
||||||
this.client = client;
|
|
||||||
this._serverId = starter.Server.Id;
|
|
||||||
this._channellId = starter.Channel.Id;
|
|
||||||
|
|
||||||
oldQuestions = new List<string>();
|
|
||||||
client.MessageReceived += PotentialGuess;
|
|
||||||
|
|
||||||
strictness = new List<Tuple<int, int>>();
|
|
||||||
strictness.Add(new Tuple<int, int>(5, 0));
|
|
||||||
strictness.Add(new Tuple<int, int>(6, 1));
|
|
||||||
strictness.Add(new Tuple<int, int>(8, 2));
|
|
||||||
strictness.Add(new Tuple<int, int>(15, 3));
|
|
||||||
strictness.Add(new Tuple<int, int>(22, 4));
|
|
||||||
maxStringLength = 22;
|
|
||||||
|
|
||||||
timeout = new Timer();
|
|
||||||
timeout.Interval = 30000;
|
|
||||||
stopwatch = new Stopwatch();
|
|
||||||
timeout.Elapsed += (s, e) => { TimeUp(); };
|
|
||||||
|
|
||||||
hintCancelSource = new System.Threading.CancellationTokenSource();
|
|
||||||
TriviaQuestionsPool.Instance.Reload();
|
|
||||||
LoadNextRound();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void PotentialGuess(object sender, MessageEventArgs e) {
|
|
||||||
if (e.Server == null || e.Channel == null) return;
|
|
||||||
if (e.Server.Id != _serverId || !active)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (e.User.Id == client.CurrentUser.Id)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (e.Message.Text.ToLower().Equals("idfk")) {
|
|
||||||
GetHint(e.Channel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsAnswerCorrect(e.Message.Text.ToLower(), currentQuestion.Answer.ToLower())) {
|
|
||||||
active = false; //start pause between rounds
|
|
||||||
timeout.Enabled = false;
|
|
||||||
stopwatch.Stop();
|
|
||||||
|
|
||||||
if (!users.ContainsKey(e.User.Id))
|
|
||||||
users.Add(e.User.Id, 1);
|
|
||||||
else {
|
|
||||||
users[e.User.Id]++;
|
|
||||||
}
|
|
||||||
await e.Send(e.User.Mention + " Guessed it!\n The answer was: **" + currentQuestion.Answer + "**");
|
|
||||||
|
|
||||||
if (users[e.User.Id] >= 10) {
|
|
||||||
await e.Send(" We have a winner! It's " + e.User.Mention + "\n" + GetLeaderboard() + "\n To start a new game type '@NadekoBot t'");
|
|
||||||
FinishGame();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if it still didnt return, we can safely start another round :D
|
|
||||||
LoadNextRound();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsAnswerCorrect(string guess, string answer) {
|
|
||||||
if (guess.Equals(answer)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
guess = CleanString(guess);
|
|
||||||
answer = CleanString(answer);
|
|
||||||
if (guess.Equals(answer)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int levDistance = guess.LevenshteinDistance(answer);
|
|
||||||
return Judge(guess.Length, answer.Length, levDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool Judge(int guessLength, int answerLength, int levDistance) {
|
|
||||||
foreach (Tuple<int, int> level in strictness) {
|
|
||||||
if (guessLength <= level.Item1 || answerLength <= level.Item1) {
|
|
||||||
if (levDistance <= level.Item2)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string CleanString(string str) {
|
|
||||||
str = " " + str + " ";
|
|
||||||
str = Regex.Replace(str, "\\s+", " ");
|
|
||||||
str = Regex.Replace(str, "[^\\w\\d\\s]", "");
|
|
||||||
//Here's where custom modification can be done
|
|
||||||
str = Regex.Replace(str, "\\s(a|an|the|of|in|for|to|as|at|be)\\s", " ");
|
|
||||||
//End custom mod and cleanup whitespace
|
|
||||||
str = Regex.Replace(str, "^\\s+", "");
|
|
||||||
str = Regex.Replace(str, "\\s+$", "");
|
|
||||||
//Trim the really long answers
|
|
||||||
str = str.Length <= maxStringLength ? str : str.Substring(0, maxStringLength);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void GetHint(Channel ch) {
|
|
||||||
await ch.Send(currentQuestion.Answer.Scramble());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StopGame() {
|
|
||||||
isQuit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadNextRound() {
|
|
||||||
Channel ch = client.GetChannel(_channellId);
|
|
||||||
|
|
||||||
|
|
||||||
if (currentQuestion != null)
|
|
||||||
oldQuestions.Add(currentQuestion.Question);
|
|
||||||
|
|
||||||
currentQuestion = TriviaQuestionsPool.Instance.GetRandomQuestion(oldQuestions);
|
|
||||||
|
|
||||||
if (currentQuestion == null || isQuit) {
|
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
ch.Send("Trivia bot stopping. :\\\n" + GetLeaderboard());
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
FinishGame();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Timer t = new Timer();
|
|
||||||
t.Interval = 2500;
|
|
||||||
t.Enabled = true;
|
|
||||||
t.Elapsed += async (s, ev) => {
|
|
||||||
active = true;
|
|
||||||
await ch.Send(currentQuestion.ToString());
|
|
||||||
t.Enabled = false;
|
|
||||||
timeout.Enabled = true;//starting countdown of the next question
|
|
||||||
stopwatch.Reset();
|
|
||||||
stopwatch.Start();
|
|
||||||
try {
|
|
||||||
await Task.Delay((int)Trivia.HINT_TIME_SECONDS * 1000);
|
|
||||||
GetHint(ch);
|
|
||||||
} catch (Exception) { }
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void TimeUp() {
|
|
||||||
await client.GetChannel(_channellId)?.Send("**Time's up.**\nCorrect answer was: **" + currentQuestion.Answer + "**\n\n*[tq quits trivia][tl shows leaderboard][" + NadekoBot.botMention + " clr clears my messages]*");
|
|
||||||
LoadNextRound();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FinishGame() {
|
|
||||||
isQuit = true;
|
|
||||||
active = false;
|
|
||||||
client.MessageReceived -= PotentialGuess;
|
|
||||||
timeout.Enabled = false;
|
|
||||||
timeout.Dispose();
|
|
||||||
stopwatch.Stop();
|
|
||||||
stopwatch.Reset();
|
|
||||||
Trivia.FinishGame(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetLeaderboard() {
|
|
||||||
if (users.Count == 0)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
|
|
||||||
string str = "**Leaderboard:**\n-----------\n";
|
|
||||||
|
|
||||||
if (users.Count > 1)
|
|
||||||
users.OrderBy(kvp => kvp.Value);
|
|
||||||
|
|
||||||
foreach (var KeyValuePair in users) {
|
|
||||||
str += "**" + client.GetServer(_serverId).GetUser(KeyValuePair.Key).Name + "** has " + KeyValuePair.Value + (KeyValuePair.Value == 1 ? "point." : "points.") + Environment.NewLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TriviaQuestion {
|
|
||||||
public string Category;
|
|
||||||
public string Question;
|
|
||||||
public string Answer;
|
|
||||||
public TriviaQuestion(string q, string a) {
|
|
||||||
this.Question = q;
|
|
||||||
this.Answer = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() =>
|
|
||||||
this.Category == null ?
|
|
||||||
"--------**Q**--------\nQuestion: **" + this.Question + "?**" :
|
|
||||||
"--------Q--------\nCategory: " + this.Category + "\nQuestion: **" + this.Question + "?**";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TriviaQuestionsPool {
|
|
||||||
private static TriviaQuestionsPool instance = null;
|
|
||||||
|
|
||||||
public static TriviaQuestionsPool Instance {
|
|
||||||
get {
|
|
||||||
if (instance == null)
|
|
||||||
instance = new TriviaQuestionsPool();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
private set { instance = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TriviaQuestion> pool;
|
|
||||||
|
|
||||||
private Random _r;
|
|
||||||
|
|
||||||
public TriviaQuestionsPool() {
|
|
||||||
Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TriviaQuestion GetRandomQuestion(List<string> exclude) {
|
|
||||||
if (pool.Count == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
TriviaQuestion tq = pool[_r.Next(0, pool.Count)];
|
|
||||||
if (exclude.Count > 0 && exclude.Count < pool.Count) {
|
|
||||||
while (exclude.Contains(tq.Question)) {
|
|
||||||
tq = pool[_r.Next(0, pool.Count)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tq;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Reload() {
|
|
||||||
_r = new Random();
|
|
||||||
pool = new List<TriviaQuestion>();
|
|
||||||
JArray arr = JArray.Parse(File.ReadAllText("data/questions.txt"));
|
|
||||||
|
|
||||||
foreach (var item in arr) {
|
|
||||||
TriviaQuestion tq;
|
|
||||||
tq = new TriviaQuestion((string)item["Question"], (string)item["Answer"]);
|
|
||||||
|
|
||||||
if (item?["Category"] != null) {
|
|
||||||
tq.Category = item["Category"].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.Add(tq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
135
NadekoBot/Classes/Trivia/TriviaGame.cs
Normal file
135
NadekoBot/Classes/Trivia/TriviaGame.cs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Classes.Trivia {
|
||||||
|
class TriviaGame {
|
||||||
|
private readonly object _guessLock = new object();
|
||||||
|
|
||||||
|
private Server _server { get; }
|
||||||
|
private Channel _channel { get; }
|
||||||
|
|
||||||
|
private int QuestionDurationMiliseconds { get; } = 30000;
|
||||||
|
private int HintTimeoutMiliseconds { get; } = 6000;
|
||||||
|
private CancellationTokenSource triviaCancelSource { get; set; }
|
||||||
|
|
||||||
|
public TriviaQuestion CurrentQuestion { get; private set; }
|
||||||
|
public List<TriviaQuestion> oldQuestions { get; } = new List<TriviaQuestion>();
|
||||||
|
|
||||||
|
public ConcurrentDictionary<User, int> users { get; } = new ConcurrentDictionary<User, int>();
|
||||||
|
|
||||||
|
public bool GameActive { get; private set; } = false;
|
||||||
|
public bool ShouldStopGame { get; private set; }
|
||||||
|
|
||||||
|
public int WinRequirement { get; } = 3;
|
||||||
|
|
||||||
|
public TriviaGame(CommandEventArgs e) {
|
||||||
|
_server = e.Server;
|
||||||
|
_channel = e.Channel;
|
||||||
|
Task.Run(() => StartGame());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartGame() {
|
||||||
|
while (!ShouldStopGame) {
|
||||||
|
// reset the cancellation source
|
||||||
|
triviaCancelSource = new CancellationTokenSource();
|
||||||
|
// load question
|
||||||
|
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
||||||
|
if (CurrentQuestion == null) {
|
||||||
|
await _channel.SendMessage($":exclamation: Failed loading a trivia question");
|
||||||
|
End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
||||||
|
//sendquestion
|
||||||
|
await _channel.SendMessage($":question: **{CurrentQuestion.Question}**");
|
||||||
|
|
||||||
|
//receive messages
|
||||||
|
NadekoBot.client.MessageReceived += PotentialGuess;
|
||||||
|
|
||||||
|
//allow people to guess
|
||||||
|
GameActive = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
//hint
|
||||||
|
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token);
|
||||||
|
await _channel.SendMessage($":exclamation:**Hint:** {CurrentQuestion.GetHint()}");
|
||||||
|
|
||||||
|
//timeout
|
||||||
|
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (TaskCanceledException) {
|
||||||
|
Console.WriteLine("Trivia cancelled");
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
GameActive = false;
|
||||||
|
if (!triviaCancelSource.IsCancellationRequested)
|
||||||
|
await _channel.Send($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**");
|
||||||
|
NadekoBot.client.MessageReceived -= PotentialGuess;
|
||||||
|
}
|
||||||
|
// load next question if game is still running
|
||||||
|
await Task.Delay(2000);
|
||||||
|
}
|
||||||
|
End();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void End() {
|
||||||
|
_channel.SendMessage("**Trivia game ended**");
|
||||||
|
_channel.SendMessage(GetLeaderboard());
|
||||||
|
TriviaGame throwAwayValue;
|
||||||
|
Commands.Trivia.runningTrivias.TryRemove(_server, out throwAwayValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopGame() {
|
||||||
|
if (!ShouldStopGame)
|
||||||
|
_channel.SendMessage(":exclamation: Trivia will stop after this question.");
|
||||||
|
ShouldStopGame = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void PotentialGuess(object sender, MessageEventArgs e) {
|
||||||
|
if (e.Channel.IsPrivate) return;
|
||||||
|
if (e.Server != _server) return;
|
||||||
|
|
||||||
|
bool guess = false;
|
||||||
|
lock (_guessLock) {
|
||||||
|
if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested) {
|
||||||
|
users.TryAdd(e.User, 0); //add if not exists
|
||||||
|
users[e.User]++; //add 1 point to the winner
|
||||||
|
guess = true;
|
||||||
|
triviaCancelSource.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (guess) {
|
||||||
|
await _channel.SendMessage($"☑️ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**");
|
||||||
|
if (users[e.User] == WinRequirement) {
|
||||||
|
ShouldStopGame = true;
|
||||||
|
await _channel.Send($":exclamation: We have a winner! Its {e.User.Mention}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetLeaderboard() {
|
||||||
|
if (users.Count == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
string str = "**Leaderboard:**\n-----------\n";
|
||||||
|
|
||||||
|
if (users.Count > 1)
|
||||||
|
users.OrderBy(kvp => kvp.Value);
|
||||||
|
|
||||||
|
foreach (var kvp in users) {
|
||||||
|
str += $"**{kvp.Key.Name}** has {kvp.Value} points".ToString().SnPl(kvp.Value) + Environment.NewLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
78
NadekoBot/Classes/Trivia/TriviaQuestion.cs
Normal file
78
NadekoBot/Classes/Trivia/TriviaQuestion.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using NadekoBot.Extensions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
// THANKS @ShoMinamimoto for suggestions and coding help
|
||||||
|
namespace NadekoBot.Classes.Trivia {
|
||||||
|
public class TriviaQuestion {
|
||||||
|
//represents the min size to judge levDistance with
|
||||||
|
public static List<Tuple<int, int>> strictness = new List<Tuple<int, int>>() {
|
||||||
|
new Tuple<int, int>(6, 0),
|
||||||
|
new Tuple<int, int>(7, 1),
|
||||||
|
new Tuple<int, int>(12, 2),
|
||||||
|
new Tuple<int, int>(17, 3),
|
||||||
|
new Tuple<int, int>(22, 4),
|
||||||
|
};
|
||||||
|
public static int maxStringLength = 22;
|
||||||
|
|
||||||
|
public string Category;
|
||||||
|
public string Question;
|
||||||
|
public string Answer;
|
||||||
|
|
||||||
|
public TriviaQuestion(string q, string a, string c) {
|
||||||
|
this.Question = q;
|
||||||
|
this.Answer = a;
|
||||||
|
this.Category = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetHint() => Answer.Scramble();
|
||||||
|
|
||||||
|
public bool IsAnswerCorrect(string guess) {
|
||||||
|
guess = CleanGuess(guess);
|
||||||
|
if (Answer.Equals(guess)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Answer = CleanGuess(Answer);
|
||||||
|
guess = CleanGuess(guess);
|
||||||
|
if (Answer.Equals(guess)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int levDistance = Answer.LevenshteinDistance(guess);
|
||||||
|
return JudgeGuess(Answer.Length, guess.Length, levDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool JudgeGuess(int guessLength, int answerLength, int levDistance) {
|
||||||
|
foreach (Tuple<int, int> level in strictness) {
|
||||||
|
if (guessLength <= level.Item1 || answerLength <= level.Item1) {
|
||||||
|
if (levDistance <= level.Item2)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CleanGuess(string str) {
|
||||||
|
str = " " + str.ToLower() + " ";
|
||||||
|
str = Regex.Replace(str, "\\s+", " ");
|
||||||
|
str = Regex.Replace(str, "[^\\w\\d\\s]", "");
|
||||||
|
//Here's where custom modification can be done
|
||||||
|
str = Regex.Replace(str, "\\s(a|an|the|of|in|for|to|as|at|be)\\s", " ");
|
||||||
|
//End custom mod and cleanup whitespace
|
||||||
|
str = Regex.Replace(str, "^\\s+", "");
|
||||||
|
str = Regex.Replace(str, "\\s+$", "");
|
||||||
|
//Trim the really long answers
|
||||||
|
str = str.Length <= maxStringLength ? str : str.Substring(0, maxStringLength);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() =>
|
||||||
|
"Question: **" + this.Question + "?**";
|
||||||
|
}
|
||||||
|
}
|
38
NadekoBot/Classes/Trivia/TriviaQuestionPool.cs
Normal file
38
NadekoBot/Classes/Trivia/TriviaQuestionPool.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Classes.Trivia {
|
||||||
|
public class TriviaQuestionPool {
|
||||||
|
private static readonly TriviaQuestionPool _instance = new TriviaQuestionPool();
|
||||||
|
public static TriviaQuestionPool Instance => _instance;
|
||||||
|
|
||||||
|
public List<TriviaQuestion> pool = new List<TriviaQuestion>();
|
||||||
|
|
||||||
|
private Random _r { get; } = new Random();
|
||||||
|
|
||||||
|
static TriviaQuestionPool() { }
|
||||||
|
|
||||||
|
private TriviaQuestionPool() {
|
||||||
|
Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriviaQuestion GetRandomQuestion(List<TriviaQuestion> exclude) =>
|
||||||
|
pool.Except(exclude).OrderBy(x => _r.Next()).FirstOrDefault();
|
||||||
|
|
||||||
|
internal void Reload() {
|
||||||
|
JArray arr = JArray.Parse(File.ReadAllText("data/questions.txt"));
|
||||||
|
|
||||||
|
foreach (var item in arr) {
|
||||||
|
TriviaQuestion tq;
|
||||||
|
tq = new TriviaQuestion(item["Question"].ToString(), item["Answer"].ToString(),item["Category"]?.ToString());
|
||||||
|
|
||||||
|
pool.Add(tq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Parse;
|
using Parse;
|
||||||
|
|
||||||
namespace NadekoBot {
|
namespace NadekoBot.Commands {
|
||||||
|
|
||||||
public static class SentencesProvider {
|
public static class SentencesProvider {
|
||||||
internal static string GetRandomSentence() {
|
internal static string GetRandomSentence() {
|
||||||
@ -23,7 +23,6 @@ namespace NadekoBot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo add leniency and stuff
|
|
||||||
public class TypingGame {
|
public class TypingGame {
|
||||||
public static float WORD_VALUE { get; } = 4.5f;
|
public static float WORD_VALUE { get; } = 4.5f;
|
||||||
private Channel channel;
|
private Channel channel;
|
||||||
|
55
NadekoBot/Commands/TriviaCommand.cs
Normal file
55
NadekoBot/Commands/TriviaCommand.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Discord.Commands;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using Discord;
|
||||||
|
using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame;
|
||||||
|
|
||||||
|
namespace NadekoBot.Commands {
|
||||||
|
class Trivia : DiscordCommand {
|
||||||
|
public static ConcurrentDictionary<Server, TriviaGame> runningTrivias = new ConcurrentDictionary<Server, TriviaGame>();
|
||||||
|
|
||||||
|
public override Func<CommandEventArgs, Task> DoFunc() => async e => {
|
||||||
|
if (!runningTrivias.ContainsKey(e.Server)) {
|
||||||
|
runningTrivias.TryAdd(e.Server, new TriviaGame(e));
|
||||||
|
await e.Send("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
|
||||||
|
} else
|
||||||
|
await e.Send("Trivia game is already running on this server.\n" + runningTrivias[e.Server].CurrentQuestion);
|
||||||
|
};
|
||||||
|
|
||||||
|
public override void Init(CommandGroupBuilder cgb) {
|
||||||
|
cgb.CreateCommand("t")
|
||||||
|
.Description("Starts a game of trivia.")
|
||||||
|
.Alias("-t")
|
||||||
|
.Do(DoFunc());
|
||||||
|
|
||||||
|
cgb.CreateCommand("tl")
|
||||||
|
.Description("Shows a current trivia leaderboard.")
|
||||||
|
.Alias("-tl")
|
||||||
|
.Alias("tlb")
|
||||||
|
.Alias("-tlb")
|
||||||
|
.Do(async e=> {
|
||||||
|
if (runningTrivias.ContainsKey(e.Server))
|
||||||
|
await e.Send(runningTrivias[e.Server].GetLeaderboard());
|
||||||
|
else
|
||||||
|
await e.Send("No trivia is running on this server.");
|
||||||
|
});
|
||||||
|
|
||||||
|
cgb.CreateCommand("tq")
|
||||||
|
.Description("Quits current trivia after current question.")
|
||||||
|
.Alias("-tq")
|
||||||
|
.Do(async e=> {
|
||||||
|
if (runningTrivias.ContainsKey(e.Server)) {
|
||||||
|
runningTrivias[e.Server].StopGame();
|
||||||
|
TriviaGame throwaway;
|
||||||
|
runningTrivias.TryRemove(e.Server, out throwaway);
|
||||||
|
} else
|
||||||
|
await e.Send("No trivia is running on this server.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,9 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Discord.Modules;
|
using Discord.Modules;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Commands;
|
||||||
|
//🃏
|
||||||
|
//🏁
|
||||||
namespace NadekoBot.Modules
|
namespace NadekoBot.Modules
|
||||||
{
|
{
|
||||||
class Games : DiscordModule
|
class Games : DiscordModule
|
||||||
|
@ -136,14 +136,17 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Classes\Music\MusicControls.cs" />
|
<Compile Include="Classes\Music\MusicControls.cs" />
|
||||||
<Compile Include="Classes\Music\StreamRequest.cs" />
|
<Compile Include="Classes\Music\StreamRequest.cs" />
|
||||||
|
<Compile Include="Commands\TriviaCommand.cs" />
|
||||||
<Compile Include="Classes\SParser.cs" />
|
<Compile Include="Classes\SParser.cs" />
|
||||||
|
<Compile Include="Classes\Trivia\TriviaGame.cs" />
|
||||||
|
<Compile Include="Classes\Trivia\TriviaQuestion.cs" />
|
||||||
|
<Compile Include="Classes\Trivia\TriviaQuestionPool.cs" />
|
||||||
<Compile Include="Commands\RequestsCommand.cs" />
|
<Compile Include="Commands\RequestsCommand.cs" />
|
||||||
<Compile Include="Commands\ServerGreetCommand.cs" />
|
<Compile Include="Commands\ServerGreetCommand.cs" />
|
||||||
<Compile Include="Commands\SpeedTyping.cs" />
|
<Compile Include="Commands\SpeedTyping.cs" />
|
||||||
<Compile Include="Classes\_JSONModels.cs" />
|
<Compile Include="Classes\_JSONModels.cs" />
|
||||||
<Compile Include="Classes\Cards.cs" />
|
<Compile Include="Classes\Cards.cs" />
|
||||||
<Compile Include="Classes\Extensions.cs" />
|
<Compile Include="Classes\Extensions.cs" />
|
||||||
<Compile Include="Classes\Trivia.cs" />
|
|
||||||
<Compile Include="Commands\CopyCommand.cs" />
|
<Compile Include="Commands\CopyCommand.cs" />
|
||||||
<Compile Include="Commands\DiceRollCommand.cs" />
|
<Compile Include="Commands\DiceRollCommand.cs" />
|
||||||
<Compile Include="Commands\DiscordCommand.cs" />
|
<Compile Include="Commands\DiscordCommand.cs" />
|
||||||
|
Loading…
Reference in New Issue
Block a user