Simple Hangman game
This commit is contained in:
parent
70f3177eaf
commit
35df37c418
206
src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs
Normal file
206
src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
using Discord;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Services;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||||
|
{
|
||||||
|
public class HangmanModel
|
||||||
|
{
|
||||||
|
public List<HangmanObject> All { get; set; }
|
||||||
|
public List<HangmanObject> Animals { get; set; }
|
||||||
|
public List<HangmanObject> Countries { get; set; }
|
||||||
|
public List<HangmanObject> Movies { get; set; }
|
||||||
|
public List<HangmanObject> Things { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HangmanTermPool
|
||||||
|
{
|
||||||
|
public enum HangmanTermType
|
||||||
|
{
|
||||||
|
All,
|
||||||
|
Animals,
|
||||||
|
Countries,
|
||||||
|
Movies,
|
||||||
|
Things
|
||||||
|
}
|
||||||
|
|
||||||
|
const string termsPath = "data/hangman.json";
|
||||||
|
public static HangmanModel data { get; }
|
||||||
|
static HangmanTermPool()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
data = JsonConvert.DeserializeObject<HangmanModel>(File.ReadAllText(termsPath));
|
||||||
|
data.All = data.Animals.Concat(data.Countries)
|
||||||
|
.Concat(data.Movies)
|
||||||
|
.Concat(data.Things)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HangmanObject GetTerm(HangmanTermType type)
|
||||||
|
{
|
||||||
|
var rng = new NadekoRandom();
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case HangmanTermType.Animals:
|
||||||
|
return data.Animals[rng.Next(0, data.Animals.Count)];
|
||||||
|
case HangmanTermType.Countries:
|
||||||
|
return data.Countries[rng.Next(0, data.Countries.Count)];
|
||||||
|
case HangmanTermType.Movies:
|
||||||
|
return data.Movies[rng.Next(0, data.Movies.Count)];
|
||||||
|
case HangmanTermType.Things:
|
||||||
|
return data.Things[rng.Next(0, data.Things.Count)];
|
||||||
|
default:
|
||||||
|
return data.All[rng.Next(0, data.All.Count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HangmanGame
|
||||||
|
{
|
||||||
|
public IMessageChannel GameChannel { get; }
|
||||||
|
public HashSet<char> Guesses { get; } = new HashSet<char>();
|
||||||
|
public HangmanObject Term { get; private set; }
|
||||||
|
public uint Errors { get; private set; } = 0;
|
||||||
|
public uint MaxErrors { get; } = 6;
|
||||||
|
public uint MessagesSinceLastPost { get; private set; } = 0;
|
||||||
|
public string ScrambledWord => "`" + String.Concat(Term.Word.Select(c =>
|
||||||
|
{
|
||||||
|
if (!(char.IsLetter(c) || char.IsDigit(c)))
|
||||||
|
return $" {c}";
|
||||||
|
|
||||||
|
c = char.ToUpperInvariant(c);
|
||||||
|
|
||||||
|
if (c == ' ')
|
||||||
|
return " ";
|
||||||
|
return Guesses.Contains(c) ? $" {c}" : " _";
|
||||||
|
})) + "`";
|
||||||
|
|
||||||
|
public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant()
|
||||||
|
.Where(c => char.IsLetter(c) || char.IsDigit(c)));
|
||||||
|
|
||||||
|
public HangmanTermPool.HangmanTermType TermType { get; }
|
||||||
|
|
||||||
|
public event Action<HangmanGame> OnEnded;
|
||||||
|
|
||||||
|
public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type)
|
||||||
|
{
|
||||||
|
this.GameChannel = channel;
|
||||||
|
this.TermType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
this.Term = HangmanTermPool.GetTerm(TermType);
|
||||||
|
// start listening for answers when game starts
|
||||||
|
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task End()
|
||||||
|
{
|
||||||
|
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||||
|
OnEnded(this);
|
||||||
|
var toSend = "Game ended. You **" + (Errors >= MaxErrors ? "LOSE" : "WIN") + "**!\n" + GetHangman();
|
||||||
|
var embed = new EmbedBuilder().WithTitle("Hangman Game")
|
||||||
|
.WithDescription(toSend)
|
||||||
|
.AddField(efb => efb.WithName("It was").WithValue(Term.Word))
|
||||||
|
.WithImage(eib => eib.WithUrl(Term.ImageUrl));
|
||||||
|
if (Errors >= MaxErrors)
|
||||||
|
await GameChannel.EmbedAsync(embed.WithColor(NadekoBot.ErrorColor).Build()).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await GameChannel.EmbedAsync(embed.WithColor(NadekoBot.OkColor).Build()).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task PotentialGuess(IMessage msg)
|
||||||
|
{
|
||||||
|
if (msg.Channel != GameChannel)
|
||||||
|
return Task.CompletedTask; // message's channel has to be the same as game's
|
||||||
|
if (msg.Content.Length != 1) // message must be 1 char long
|
||||||
|
{
|
||||||
|
if (++MessagesSinceLastPost > 10)
|
||||||
|
{
|
||||||
|
MessagesSinceLastPost = 0;
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try { await GameChannel.SendConfirmAsync("Hangman Game", ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false); } catch { }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(char.IsLetter(msg.Content[0]) || char.IsDigit(msg.Content[0])))// and a letter or a digit
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
var guess = char.ToUpperInvariant(msg.Content[0]);
|
||||||
|
// todo hmmmm
|
||||||
|
// how do i want to limit the users on guessing?
|
||||||
|
// one guess every 5 seconds if wrong?
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Guesses.Contains(guess))
|
||||||
|
{
|
||||||
|
++Errors;
|
||||||
|
if (Errors < MaxErrors)
|
||||||
|
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await End().ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Guesses.Add(guess);
|
||||||
|
|
||||||
|
if (Term.Word.ToUpperInvariant().Contains(guess))
|
||||||
|
{
|
||||||
|
if (GuessedAll)
|
||||||
|
{
|
||||||
|
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { }
|
||||||
|
|
||||||
|
await End().ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false); } catch { }
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++Errors;
|
||||||
|
if (Errors < MaxErrors)
|
||||||
|
await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman()).ConfigureAwait(false);
|
||||||
|
else
|
||||||
|
await End().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetHangman()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
$@"\_\_\_\_\_\_\_\_\_
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
{(Errors > 0 ? "😲" : " ")} |
|
||||||
|
{(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} |
|
||||||
|
{(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} |
|
||||||
|
/-\";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Games.Commands.Hangman
|
||||||
|
{
|
||||||
|
public class HangmanObject
|
||||||
|
{
|
||||||
|
public string Word { get; set; }
|
||||||
|
public string ImageUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
66
src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs
Normal file
66
src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.Commands;
|
||||||
|
using NadekoBot.Attributes;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Modules.Games.Commands.Hangman;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Games
|
||||||
|
{
|
||||||
|
public partial class Games
|
||||||
|
{
|
||||||
|
|
||||||
|
[Group]
|
||||||
|
public class HangmanCommands
|
||||||
|
{
|
||||||
|
private static Logger _log { get; }
|
||||||
|
|
||||||
|
//channelId, game
|
||||||
|
public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>();
|
||||||
|
|
||||||
|
static HangmanCommands()
|
||||||
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
string typesStr { get; } = "";
|
||||||
|
public HangmanCommands()
|
||||||
|
{
|
||||||
|
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", Enum.GetNames(typeof(HangmanTermPool.HangmanTermType)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task Hangmanlist(IUserMessage imsg)
|
||||||
|
{
|
||||||
|
await imsg.Channel.SendConfirmAsync(typesStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
|
public async Task Hangman(IUserMessage imsg, HangmanTermPool.HangmanTermType type = HangmanTermPool.HangmanTermType.All)
|
||||||
|
{
|
||||||
|
var hm = new HangmanGame(imsg.Channel, type);
|
||||||
|
|
||||||
|
if (!HangmanGames.TryAdd(imsg.Channel.Id, hm))
|
||||||
|
{
|
||||||
|
await imsg.Channel.SendErrorAsync("Hangman game already running on this channel.").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hm.OnEnded += (g) =>
|
||||||
|
{
|
||||||
|
HangmanGame throwaway;
|
||||||
|
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
|
||||||
|
};
|
||||||
|
hm.Start();
|
||||||
|
|
||||||
|
await imsg.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ using NadekoBot.TypeReaders;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using NadekoBot.Modules.Music;
|
using NadekoBot.Modules.Music;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using NadekoBot.Modules.Games.Commands.Hangman;
|
||||||
|
|
||||||
namespace NadekoBot
|
namespace NadekoBot
|
||||||
{
|
{
|
||||||
|
54
src/NadekoBot/Resources/CommandStrings.Designer.cs
generated
54
src/NadekoBot/Resources/CommandStrings.Designer.cs
generated
@ -2840,6 +2840,60 @@ namespace NadekoBot.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to hangman.
|
||||||
|
/// </summary>
|
||||||
|
public static string hangman_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("hangman_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Starts a game of hangman in the channel. Use `{0}hangmanlist` to see a list of available term types. Defaults to 'all'..
|
||||||
|
/// </summary>
|
||||||
|
public static string hangman_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("hangman_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0}hangman` or `{0}hangman movies`.
|
||||||
|
/// </summary>
|
||||||
|
public static string hangman_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("hangman_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to hangmanlist.
|
||||||
|
/// </summary>
|
||||||
|
public static string hangmanlist_cmd {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("hangmanlist_cmd", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Shows a list of hangman term types..
|
||||||
|
/// </summary>
|
||||||
|
public static string hangmanlist_desc {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("hangmanlist_desc", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to `{0} hangmanlist`.
|
||||||
|
/// </summary>
|
||||||
|
public static string hangmanlist_usage {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("hangmanlist_usage", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to #.
|
/// Looks up a localized string similar to #.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2745,4 +2745,22 @@
|
|||||||
<data name="type_usage" xml:space="preserve">
|
<data name="type_usage" xml:space="preserve">
|
||||||
<value>`{0}type @someone`</value>
|
<value>`{0}type @someone`</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="hangmanlist_cmd" xml:space="preserve">
|
||||||
|
<value>hangmanlist</value>
|
||||||
|
</data>
|
||||||
|
<data name="hangmanlist_desc" xml:space="preserve">
|
||||||
|
<value>Shows a list of hangman term types.</value>
|
||||||
|
</data>
|
||||||
|
<data name="hangmanlist_usage" xml:space="preserve">
|
||||||
|
<value>`{0} hangmanlist`</value>
|
||||||
|
</data>
|
||||||
|
<data name="hangman_cmd" xml:space="preserve">
|
||||||
|
<value>hangman</value>
|
||||||
|
</data>
|
||||||
|
<data name="hangman_desc" xml:space="preserve">
|
||||||
|
<value>Starts a game of hangman in the channel. Use `{0}hangmanlist` to see a list of available term types. Defaults to 'all'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="hangman_usage" xml:space="preserve">
|
||||||
|
<value>`{0}hangman` or `{0}hangman movies`</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
3118
src/NadekoBot/data/hangman.json
Normal file
3118
src/NadekoBot/data/hangman.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user