Global usings and file scoped namespaces

This commit is contained in:
Kwoth
2021-12-19 05:14:11 +01:00
parent bc31dae965
commit ee33313519
548 changed files with 47528 additions and 49115 deletions

View File

@@ -1,72 +1,67 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using NadekoBot.Common;
using NadekoBot.Common.Yml;
using Serilog;
namespace NadekoBot.Modules.Games.Hangman
namespace NadekoBot.Modules.Games.Hangman;
public sealed class DefaultHangmanSource : IHangmanSource
{
public sealed class DefaultHangmanSource : IHangmanSource
private IReadOnlyDictionary<string, HangmanTerm[]> _terms = new Dictionary<string, HangmanTerm[]>();
private readonly Random _rng;
public DefaultHangmanSource()
{
private IReadOnlyDictionary<string, HangmanTerm[]> _terms = new Dictionary<string, HangmanTerm[]>();
private readonly Random _rng;
_rng = new NadekoRandom();
Reload();
}
public DefaultHangmanSource()
public void Reload()
{
if (!Directory.Exists("data/hangman"))
{
_rng = new NadekoRandom();
Reload();
Log.Error("Hangman game won't work. Folder 'data/hangman' is missing.");
return;
}
public void Reload()
var qs = new Dictionary<string, HangmanTerm[]>();
foreach (var file in Directory.EnumerateFiles("data/hangman/", "*.yml"))
{
if (!Directory.Exists("data/hangman"))
try
{
Log.Error("Hangman game won't work. Folder 'data/hangman' is missing.");
return;
var data = Yaml.Deserializer.Deserialize<HangmanTerm[]>(File.ReadAllText(file));
qs[Path.GetFileNameWithoutExtension(file).ToLowerInvariant()] = data;
}
var qs = new Dictionary<string, HangmanTerm[]>();
foreach (var file in Directory.EnumerateFiles("data/hangman/", "*.yml"))
catch (Exception ex)
{
try
{
var data = Yaml.Deserializer.Deserialize<HangmanTerm[]>(File.ReadAllText(file));
qs[Path.GetFileNameWithoutExtension(file).ToLowerInvariant()] = data;
}
catch (Exception ex)
{
Log.Error(ex, "Loading {HangmanFile} failed.", file);
}
Log.Error(ex, "Loading {HangmanFile} failed.", file);
}
}
_terms = qs;
_terms = qs;
Log.Information("Loaded {HangmanCategoryCount} hangman categories.", qs.Count);
}
Log.Information("Loaded {HangmanCategoryCount} hangman categories.", qs.Count);
}
public IReadOnlyCollection<string> GetCategories()
=> _terms.Keys.ToList();
public IReadOnlyCollection<string> GetCategories()
=> _terms.Keys.ToList();
public bool GetTerm(string? category, [NotNullWhen(true)] out HangmanTerm? term)
public bool GetTerm(string? category, [NotNullWhen(true)] out HangmanTerm? term)
{
if (category is null)
{
if (category is null)
{
var cats = GetCategories();
category = cats.ElementAt(_rng.Next(0, cats.Count));
}
if (_terms.TryGetValue(category, out var terms))
{
term = terms[_rng.Next(0, terms.Length)];
return true;
}
term = null;
return false;
var cats = GetCategories();
category = cats.ElementAt(_rng.Next(0, cats.Count));
}
if (_terms.TryGetValue(category, out var terms))
{
term = terms[_rng.Next(0, terms.Length)];
return true;
}
term = null;
return false;
}
}

View File

@@ -1,121 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AngleSharp.Text;
using AngleSharp.Text;
namespace NadekoBot.Modules.Games.Hangman
namespace NadekoBot.Modules.Games.Hangman;
public sealed class HangmanGame
{
public sealed class HangmanGame
public enum Phase { Running, Ended }
public enum GuessResult { NoAction, AlreadyTried, Incorrect, Guess, Win }
public record State(
int Errors,
Phase Phase,
string Word,
GuessResult GuessResult,
List<char> missedLetters,
string ImageUrl)
{
public enum Phase { Running, Ended }
public enum GuessResult { NoAction, AlreadyTried, Incorrect, Guess, Win }
public bool Failed => Errors > 5;
}
public record State(
int Errors,
Phase Phase,
string Word,
GuessResult GuessResult,
List<char> missedLetters,
string ImageUrl)
{
public bool Failed => Errors > 5;
}
private Phase CurrentPhase { get; set; }
private Phase CurrentPhase { get; set; }
private readonly HashSet<char> _incorrect = new();
private readonly HashSet<char> _correct = new();
private readonly HashSet<char> _remaining = new();
private readonly HashSet<char> _incorrect = new();
private readonly HashSet<char> _correct = new();
private readonly HashSet<char> _remaining = new();
private readonly string _word;
private readonly string _imageUrl;
private readonly string _word;
private readonly string _imageUrl;
public HangmanGame(HangmanTerm term)
{
_word = term.Word;
_imageUrl = term.ImageUrl;
public HangmanGame(HangmanTerm term)
{
_word = term.Word;
_imageUrl = term.ImageUrl;
_remaining = _word
.ToLowerInvariant()
.Where(x => x.IsLetter())
.Select(char.ToLowerInvariant)
.ToHashSet();
_remaining = _word
.ToLowerInvariant()
.Where(x => x.IsLetter())
.Select(char.ToLowerInvariant)
.ToHashSet();
}
public State GetState(GuessResult guessResult = GuessResult.NoAction)
=> new State(_incorrect.Count,
CurrentPhase,
CurrentPhase == Phase.Ended
? _word
: GetScrambledWord(),
guessResult,
_incorrect.ToList(),
CurrentPhase == Phase.Ended
? _imageUrl
: string.Empty);
private string GetScrambledWord()
{
Span<char> output = stackalloc char[_word.Length * 2];
for (var i = 0; i < _word.Length; i++)
{
var ch = _word[i];
if (ch == ' ')
output[i*2] = '';
if (!ch.IsLetter() || !_remaining.Contains(char.ToLowerInvariant(ch)))
output[i*2] = ch;
else
output[i*2] = '_';
output[i * 2 + 1] = ' ';
}
public State GetState(GuessResult guessResult = GuessResult.NoAction)
=> new State(_incorrect.Count,
CurrentPhase,
CurrentPhase == Phase.Ended
? _word
: GetScrambledWord(),
guessResult,
_incorrect.ToList(),
CurrentPhase == Phase.Ended
? _imageUrl
: string.Empty);
return new(output);
}
private string GetScrambledWord()
public State Guess(string guess)
{
if (CurrentPhase != Phase.Running)
return GetState(GuessResult.NoAction);
guess = guess.Trim();
if (guess.Length > 1)
{
Span<char> output = stackalloc char[_word.Length * 2];
for (var i = 0; i < _word.Length; i++)
{
var ch = _word[i];
if (ch == ' ')
output[i*2] = '';
if (!ch.IsLetter() || !_remaining.Contains(char.ToLowerInvariant(ch)))
output[i*2] = ch;
else
output[i*2] = '_';
output[i * 2 + 1] = ' ';
}
return new(output);
}
public State Guess(string guess)
{
if (CurrentPhase != Phase.Running)
return GetState(GuessResult.NoAction);
guess = guess.Trim();
if (guess.Length > 1)
{
if (guess.Equals(_word, StringComparison.InvariantCultureIgnoreCase))
{
CurrentPhase = Phase.Ended;
return GetState(GuessResult.Win);
}
return GetState(GuessResult.NoAction);
}
var charGuess = guess[0];
if (!char.IsLetter(charGuess))
return GetState(GuessResult.NoAction);
if (_incorrect.Contains(charGuess) || _correct.Contains(charGuess))
return GetState(GuessResult.AlreadyTried);
if (_remaining.Remove(charGuess))
{
if (_remaining.Count == 0)
{
CurrentPhase = Phase.Ended;
return GetState(GuessResult.Win);
}
return GetState(GuessResult.Guess);
}
_incorrect.Add(charGuess);
if (_incorrect.Count > 5)
if (guess.Equals(_word, StringComparison.InvariantCultureIgnoreCase))
{
CurrentPhase = Phase.Ended;
return GetState(GuessResult.Incorrect);
return GetState(GuessResult.Win);
}
return GetState(GuessResult.NoAction);
}
var charGuess = guess[0];
if (!char.IsLetter(charGuess))
return GetState(GuessResult.NoAction);
if (_incorrect.Contains(charGuess) || _correct.Contains(charGuess))
return GetState(GuessResult.AlreadyTried);
if (_remaining.Remove(charGuess))
{
if (_remaining.Count == 0)
{
CurrentPhase = Phase.Ended;
return GetState(GuessResult.Win);
}
return GetState(GuessResult.Guess);
}
_incorrect.Add(charGuess);
if (_incorrect.Count > 5)
{
CurrentPhase = Phase.Ended;
return GetState(GuessResult.Incorrect);
}
return GetState(GuessResult.Incorrect);
}
}

View File

@@ -1,7 +1,5 @@
#nullable enable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Discord;
@@ -11,137 +9,136 @@ using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Services;
using NadekoBot.Services;
namespace NadekoBot.Modules.Games.Hangman
namespace NadekoBot.Modules.Games.Hangman;
public sealed class HangmanService : IHangmanService, ILateExecutor
{
public sealed class HangmanService : IHangmanService, ILateExecutor
private readonly ConcurrentDictionary<ulong, HangmanGame> _hangmanGames = new();
private readonly IHangmanSource _source;
private readonly IEmbedBuilderService _eb;
private readonly GamesConfigService _gcs;
private readonly ICurrencyService _cs;
private readonly IMemoryCache _cdCache;
private readonly object _locker = new();
public HangmanService(IHangmanSource source, IEmbedBuilderService eb, GamesConfigService gcs,
ICurrencyService cs, IMemoryCache cdCache)
{
private readonly ConcurrentDictionary<ulong, HangmanGame> _hangmanGames = new();
private readonly IHangmanSource _source;
private readonly IEmbedBuilderService _eb;
private readonly GamesConfigService _gcs;
private readonly ICurrencyService _cs;
private readonly IMemoryCache _cdCache;
private readonly object _locker = new();
_source = source;
_eb = eb;
_gcs = gcs;
_cs = cs;
_cdCache = cdCache;
}
public HangmanService(IHangmanSource source, IEmbedBuilderService eb, GamesConfigService gcs,
ICurrencyService cs, IMemoryCache cdCache)
public bool StartHangman(
ulong channelId,
string? category,
[NotNullWhen(true)] out HangmanGame.State? state)
{
state = null;
if (!_source.GetTerm(category, out var term))
return false;
var game = new HangmanGame(term);
lock (_locker)
{
_source = source;
_eb = eb;
_gcs = gcs;
_cs = cs;
_cdCache = cdCache;
}
public bool StartHangman(
ulong channelId,
string? category,
[NotNullWhen(true)] out HangmanGame.State? state)
{
state = null;
if (!_source.GetTerm(category, out var term))
return false;
var game = new HangmanGame(term);
lock (_locker)
var hc = _hangmanGames.GetOrAdd(channelId, game);
if (hc == game)
{
var hc = _hangmanGames.GetOrAdd(channelId, game);
if (hc == game)
{
state = hc.GetState();
return true;
}
return false;
}
}
public ValueTask<bool> StopHangman(ulong channelId)
{
lock (_locker)
{
if (_hangmanGames.TryRemove(channelId, out var game))
{
return new(true);
}
state = hc.GetState();
return true;
}
return new(false);
}
public IReadOnlyCollection<string> GetHangmanTypes()
=> _source.GetCategories();
public async Task LateExecute(IGuild guild, IUserMessage msg)
{
if (_hangmanGames.ContainsKey(msg.Channel.Id))
{
if (string.IsNullOrWhiteSpace(msg.Content))
return;
if (_cdCache.TryGetValue(msg.Author.Id, out _))
return;
HangmanGame.State state;
long rew = 0;
lock (_locker)
{
if (!_hangmanGames.TryGetValue(msg.Channel.Id, out var game))
return;
state = game.Guess(msg.Content.ToLowerInvariant());
if (state.GuessResult == HangmanGame.GuessResult.NoAction)
return;
if (state.GuessResult == HangmanGame.GuessResult.Incorrect
|| state.GuessResult == HangmanGame.GuessResult.AlreadyTried)
{
_cdCache.Set(msg.Author.Id, string.Empty, new MemoryCacheEntryOptions()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3)
});
}
if (state.Phase == HangmanGame.Phase.Ended)
if (_hangmanGames.TryRemove(msg.Channel.Id, out _))
rew = _gcs.Data.Hangman.CurrencyReward;
}
if (rew > 0)
await _cs.AddAsync(msg.Author, "hangman win", rew, gamble: true);
await SendState((ITextChannel)msg.Channel, msg.Author, msg.Content, state);
}
}
private Task<IUserMessage> SendState(ITextChannel channel, IUser user, string content, HangmanGame.State state)
{
var embed = Games.HangmanCommands.GetEmbed(_eb, state);
if (state.GuessResult == HangmanGame.GuessResult.Guess)
embed.WithDescription($"{user} guessed the letter {content}!")
.WithOkColor();
else if (state.GuessResult == HangmanGame.GuessResult.Incorrect && state.Failed)
embed.WithDescription($"{user} Letter {content} doesn't exist! Game over!")
.WithErrorColor();
else if (state.GuessResult == HangmanGame.GuessResult.Incorrect)
embed.WithDescription($"{user} Letter {content} doesn't exist!")
.WithErrorColor();
else if (state.GuessResult == HangmanGame.GuessResult.AlreadyTried)
embed.WithDescription($"{user} Letter {content} has already been used.")
.WithPendingColor();
else if (state.GuessResult == HangmanGame.GuessResult.Win)
embed.WithDescription($"{user} won!")
.WithOkColor();
if (!string.IsNullOrWhiteSpace(state.ImageUrl)
&& Uri.IsWellFormedUriString(state.ImageUrl, UriKind.Absolute))
{
embed.WithImageUrl(state.ImageUrl);
}
return channel.EmbedAsync(embed);
return false;
}
}
public ValueTask<bool> StopHangman(ulong channelId)
{
lock (_locker)
{
if (_hangmanGames.TryRemove(channelId, out var game))
{
return new(true);
}
}
return new(false);
}
public IReadOnlyCollection<string> GetHangmanTypes()
=> _source.GetCategories();
public async Task LateExecute(IGuild guild, IUserMessage msg)
{
if (_hangmanGames.ContainsKey(msg.Channel.Id))
{
if (string.IsNullOrWhiteSpace(msg.Content))
return;
if (_cdCache.TryGetValue(msg.Author.Id, out _))
return;
HangmanGame.State state;
long rew = 0;
lock (_locker)
{
if (!_hangmanGames.TryGetValue(msg.Channel.Id, out var game))
return;
state = game.Guess(msg.Content.ToLowerInvariant());
if (state.GuessResult == HangmanGame.GuessResult.NoAction)
return;
if (state.GuessResult == HangmanGame.GuessResult.Incorrect
|| state.GuessResult == HangmanGame.GuessResult.AlreadyTried)
{
_cdCache.Set(msg.Author.Id, string.Empty, new MemoryCacheEntryOptions()
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3)
});
}
if (state.Phase == HangmanGame.Phase.Ended)
if (_hangmanGames.TryRemove(msg.Channel.Id, out _))
rew = _gcs.Data.Hangman.CurrencyReward;
}
if (rew > 0)
await _cs.AddAsync(msg.Author, "hangman win", rew, gamble: true);
await SendState((ITextChannel)msg.Channel, msg.Author, msg.Content, state);
}
}
private Task<IUserMessage> SendState(ITextChannel channel, IUser user, string content, HangmanGame.State state)
{
var embed = Games.HangmanCommands.GetEmbed(_eb, state);
if (state.GuessResult == HangmanGame.GuessResult.Guess)
embed.WithDescription($"{user} guessed the letter {content}!")
.WithOkColor();
else if (state.GuessResult == HangmanGame.GuessResult.Incorrect && state.Failed)
embed.WithDescription($"{user} Letter {content} doesn't exist! Game over!")
.WithErrorColor();
else if (state.GuessResult == HangmanGame.GuessResult.Incorrect)
embed.WithDescription($"{user} Letter {content} doesn't exist!")
.WithErrorColor();
else if (state.GuessResult == HangmanGame.GuessResult.AlreadyTried)
embed.WithDescription($"{user} Letter {content} has already been used.")
.WithPendingColor();
else if (state.GuessResult == HangmanGame.GuessResult.Win)
embed.WithDescription($"{user} won!")
.WithOkColor();
if (!string.IsNullOrWhiteSpace(state.ImageUrl)
&& Uri.IsWellFormedUriString(state.ImageUrl, UriKind.Absolute))
{
embed.WithImageUrl(state.ImageUrl);
}
return channel.EmbedAsync(embed);
}
}

View File

@@ -1,8 +1,7 @@
namespace NadekoBot.Modules.Games.Hangman
namespace NadekoBot.Modules.Games.Hangman;
public sealed class HangmanTerm
{
public sealed class HangmanTerm
{
public string Word { get; set; }
public string ImageUrl { get; set; }
}
}
public string Word { get; set; }
public string ImageUrl { get; set; }
}

View File

@@ -1,14 +1,12 @@
#nullable enable
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Hangman
namespace NadekoBot.Modules.Games.Hangman;
public interface IHangmanService
{
public interface IHangmanService
{
bool StartHangman(ulong channelId, string? category, [NotNullWhen(true)] out HangmanGame.State? hangmanController);
ValueTask<bool> StopHangman(ulong channelId);
IReadOnlyCollection<string> GetHangmanTypes();
}
bool StartHangman(ulong channelId, string? category, [NotNullWhen(true)] out HangmanGame.State? hangmanController);
ValueTask<bool> StopHangman(ulong channelId);
IReadOnlyCollection<string> GetHangmanTypes();
}

View File

@@ -1,14 +1,12 @@
#nullable enable
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using NadekoBot.Services;
namespace NadekoBot.Modules.Games.Hangman
namespace NadekoBot.Modules.Games.Hangman;
public interface IHangmanSource : INService
{
public interface IHangmanSource : INService
{
public IReadOnlyCollection<string> GetCategories();
public void Reload();
public bool GetTerm(string? category, [NotNullWhen(true)] out HangmanTerm? term);
}
public IReadOnlyCollection<string> GetCategories();
public void Reload();
public bool GetTerm(string? category, [NotNullWhen(true)] out HangmanTerm? term);
}