2017-07-17 19:42:36 +00:00
using System ;
2017-05-27 08:19:27 +00:00
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using System.Threading.Tasks ;
2017-07-17 19:42:36 +00:00
using Discord ;
using Discord.WebSocket ;
using NadekoBot.Common ;
using NadekoBot.Extensions ;
using NadekoBot.Modules.Games.Services ;
using NLog ;
2017-05-27 08:19:27 +00:00
2017-07-17 19:42:36 +00:00
namespace NadekoBot.Modules.Games.Common
2017-05-27 08:19:27 +00:00
{
public class TypingGame
{
public const float WORD_VALUE = 4.5f ;
public ITextChannel Channel { get ; }
public string CurrentSentence { get ; private set ; }
public bool IsActive { get ; private set ; }
private readonly Stopwatch sw ;
private readonly List < ulong > finishedUserIds ;
2017-06-19 13:42:10 +00:00
private readonly DiscordSocketClient _client ;
2017-05-27 08:19:27 +00:00
private readonly GamesService _games ;
2017-05-30 04:54:59 +00:00
private readonly string _prefix ;
2017-05-27 08:19:27 +00:00
private Logger _log { get ; }
2017-06-19 13:42:10 +00:00
public TypingGame ( GamesService games , DiscordSocketClient client , ITextChannel channel , string prefix ) //kek@prefix
2017-05-27 08:19:27 +00:00
{
_log = LogManager . GetCurrentClassLogger ( ) ;
_games = games ;
_client = client ;
2017-05-30 04:54:59 +00:00
_prefix = prefix ;
2017-05-27 08:19:27 +00:00
this . Channel = channel ;
IsActive = false ;
sw = new Stopwatch ( ) ;
finishedUserIds = new List < ulong > ( ) ;
}
public async Task < bool > Stop ( )
{
if ( ! IsActive ) return false ;
_client . MessageReceived - = AnswerReceived ;
finishedUserIds . Clear ( ) ;
IsActive = false ;
sw . Stop ( ) ;
sw . Reset ( ) ;
try { await Channel . SendConfirmAsync ( "Typing contest stopped." ) . ConfigureAwait ( false ) ; } catch ( Exception ex ) { _log . Warn ( ex ) ; }
return true ;
}
public async Task Start ( )
{
if ( IsActive ) return ; // can't start running game
IsActive = true ;
CurrentSentence = GetRandomSentence ( ) ;
var i = ( int ) ( CurrentSentence . Length / WORD_VALUE * 1.7f ) ;
try
{
await Channel . SendConfirmAsync ( $@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can." ) . ConfigureAwait ( false ) ;
var msg = await Channel . SendMessageAsync ( "Starting new typing contest in **3**..." ) . ConfigureAwait ( false ) ;
await Task . Delay ( 1000 ) . ConfigureAwait ( false ) ;
try
{
await msg . ModifyAsync ( m = > m . Content = "Starting new typing contest in **2**..." ) . ConfigureAwait ( false ) ;
await Task . Delay ( 1000 ) . ConfigureAwait ( false ) ;
await msg . ModifyAsync ( m = > m . Content = "Starting new typing contest in **1**..." ) . ConfigureAwait ( false ) ;
await Task . Delay ( 1000 ) . ConfigureAwait ( false ) ;
}
catch ( Exception ex ) { _log . Warn ( ex ) ; }
await msg . ModifyAsync ( m = > m . Content = Format . Bold ( Format . Sanitize ( CurrentSentence . Replace ( " " , " \x200B" ) ) . SanitizeMentions ( ) ) ) . ConfigureAwait ( false ) ;
sw . Start ( ) ;
HandleAnswers ( ) ;
while ( i > 0 )
{
await Task . Delay ( 1000 ) . ConfigureAwait ( false ) ;
i - - ;
if ( ! IsActive )
return ;
}
}
catch { }
finally
{
await Stop ( ) . ConfigureAwait ( false ) ;
}
}
public string GetRandomSentence ( )
{
if ( _games . TypingArticles . Any ( ) )
return _games . TypingArticles [ new NadekoRandom ( ) . Next ( 0 , _games . TypingArticles . Count ) ] . Text ;
else
2017-05-30 04:54:59 +00:00
return $"No typing articles found. Use {_prefix}typeadd command to add a new article for typing." ;
2017-05-27 08:19:27 +00:00
}
private void HandleAnswers ( )
{
_client . MessageReceived + = AnswerReceived ;
}
2017-06-15 23:55:14 +00:00
private Task AnswerReceived ( SocketMessage imsg )
2017-05-27 08:19:27 +00:00
{
2017-06-05 22:46:58 +00:00
var _ = Task . Run ( async ( ) = >
2017-05-27 08:19:27 +00:00
{
2017-06-05 22:46:58 +00:00
try
{
if ( imsg . Author . IsBot )
return ;
var msg = imsg as SocketUserMessage ;
if ( msg = = null )
return ;
2017-05-27 08:19:27 +00:00
2017-06-05 22:46:58 +00:00
if ( this . Channel = = null | | this . Channel . Id ! = msg . Channel . Id ) return ;
2017-05-27 08:19:27 +00:00
2017-06-05 22:46:58 +00:00
var guess = msg . Content ;
2017-05-27 08:19:27 +00:00
2017-06-05 22:46:58 +00:00
var distance = CurrentSentence . LevenshteinDistance ( guess ) ;
var decision = Judge ( distance , guess . Length ) ;
if ( decision & & ! finishedUserIds . Contains ( msg . Author . Id ) )
2017-05-27 08:19:27 +00:00
{
2017-06-05 22:46:58 +00:00
var elapsed = sw . Elapsed ;
var wpm = CurrentSentence . Length / WORD_VALUE / elapsed . TotalSeconds * 60 ;
finishedUserIds . Add ( msg . Author . Id ) ;
await this . Channel . EmbedAsync ( new EmbedBuilder ( ) . WithOkColor ( )
. WithTitle ( $"{msg.Author} finished the race!" )
. AddField ( efb = > efb . WithName ( "Place" ) . WithValue ( $"#{finishedUserIds.Count}" ) . WithIsInline ( true ) )
. AddField ( efb = > efb . WithName ( "WPM" ) . WithValue ( $"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*" ) . WithIsInline ( true ) )
. AddField ( efb = > efb . WithName ( "Errors" ) . WithValue ( distance . ToString ( ) ) . WithIsInline ( true ) ) )
. ConfigureAwait ( false ) ;
if ( finishedUserIds . Count % 4 = = 0 )
{
await this . Channel . SendConfirmAsync ( $":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \ x200B ")).SanitizeMentions()}**" ) . ConfigureAwait ( false ) ;
}
2017-05-27 08:19:27 +00:00
}
}
2017-06-05 22:46:58 +00:00
catch ( Exception ex ) { _log . Warn ( ex ) ; }
} ) ;
2017-06-15 23:55:14 +00:00
return Task . CompletedTask ;
2017-05-27 08:19:27 +00:00
}
private bool Judge ( int errors , int textLength ) = > errors < = textLength / 25 ;
}
}