start tictactoe, migrate data from benbot

This commit is contained in:
Ben Harris 2018-01-09 11:55:57 -05:00
parent 99215a91e4
commit eccd5680f4
11 changed files with 229 additions and 64 deletions

View File

@ -24,9 +24,9 @@ namespace dotbot.Commands
[Summary("start a game of hangman!")]
public async Task StartGame([Remainder] string secret)
{
await Context.Message.DeleteAsync();
var gameId = Context.Channel.Id;
_games.Add(gameId, new HangmanSession(secret));
await Context.Message.DeleteAsync();
await ReplyAsync($"{_games[gameId]}");
}
@ -41,11 +41,11 @@ namespace dotbot.Commands
{
var game = _games[gameId];
_games.Remove(gameId);
await ReplyAsync($"game stopped. the secret word was {game.SecretWord}");
await ReplyAsync($"game over. the secret word was {game.SecretWord}");
}
else
{
await ReplyAsync($"no game started in this channel...");
await ReplyAsync("no game running in this channel...");
}
}
}
@ -54,10 +54,15 @@ namespace dotbot.Commands
public class HangmanSession
{
internal string SecretWord;
internal IEnumerable<char> SecretWordLetters => SecretWord.ToCharArray().Distinct().OrderBy(a => a);
internal IEnumerable<char> SecretWordLetters
=> SecretWord.ToCharArray().Distinct().OrderBy(a => a);
public string ShowSecretWord
=> $"Word: {string.Join("", SecretWord.ToCharArray().Select(c => $"{(GuessedLetters.Contains(c) ? c : c == ' ' ? ' ' : '_')} "))}";
internal List<char> GuessedLetters;
private int Guesses;
public bool GameOver => SecretWordLetters.SequenceEqual(GuessedLetters.OrderBy(c => c)) && Guesses >= Hangman.Gallows.Length;
public bool GameOver
=> SecretWordLetters.SequenceEqual(GuessedLetters.OrderBy(c => c))
|| Guesses >= Hangman.Gallows.Length;
public HangmanSession(string secretWord)
{
@ -74,18 +79,12 @@ namespace dotbot.Commands
if (!SecretWordLetters.Contains(guess)) Guesses++;
return true;
}
public override string ToString()
{
var Incorrects = GuessedLetters.Except(SecretWordLetters);
return $"```{Hangman.Gallows[Guesses]}\n{ShowSecretWord()}\n\nGuessed Letters: {string.Join(' ', GuessedLetters)}\nIncorrect Letters: {string.Join(' ', Incorrects)}```";
}
=> $"```{Hangman.Gallows[Guesses]}\n{ShowSecretWord}\n\n"
+ $"Guessed Letters: {string.Join(' ', GuessedLetters)}\n"
+ $"Incorrect Letters: {string.Join(' ', GuessedLetters.Except(SecretWordLetters))}```";
public string ShowSecretWord()
{
var disp = SecretWord.ToCharArray().Select(c => $"{(GuessedLetters.Contains(c) ? c : '_')} ");
return $"Word: {string.Join("", disp)}";
}
}
}

66
Commands/TicTacToe.cs Executable file
View File

@ -0,0 +1,66 @@
using Discord.Commands;
using dotbot.Services;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Discord;
namespace dotbot.Commands
{
[Group("tic")]
public class TicTacToe : ModuleBase<SocketCommandContext>
{
public Dictionary<ulong, TicTacToeSession> _games;
public TicTacToe(TicTacToeService tic)
{
_games = tic._activeGames;
}
[Command]
[Priority(0)]
[Summary("start a game of tic tac toe!")]
public async Task StartGame([Summary("mention whom you would like to play with!")] IUser opponent)
{
var gameId = Context.Channel.Id;
_games.Add(gameId, new TicTacToeSession(opponent));
await ReplyAsync($"{_games[gameId]}");
}
[Command("stop")]
[Priority(1)]
[Summary("ends a tic tac toe session")]
public async Task StopGame()
{
var gameId = Context.Channel.Id;
if (_games.ContainsKey(gameId))
{
var game = _games[gameId];
_games.Remove(gameId);
await ReplyAsync("game over.");
}
else
{
await ReplyAsync("no game running in this channel...");
}
}
}
public class TicTacToeSession
{
public TicTacToeSession(IUser opponent)
{
}
public override string ToString()
=> $"tictactoe";
}
}

View File

@ -13,20 +13,20 @@ namespace dotbot.Commands
{
public string Uppers { get; set; }
public string Lowers { get; set; }
public string Nums { get; set; }
public string Digits { get; set; }
internal string ConvertChar(char c)
internal char ConvertChar(char c)
{
if (c >= '0' && c <= '9')
return $"{Nums[c - '0']} ";
return Digits[c - '0'];
else if (c >= 'a' && c <= 'z')
return $"{Lowers[c - 'a']} ";
return Lowers[c - 'a'];
else if (c >= 'A' && c <= 'Z')
return $"{Uppers[c - 'z']} ";
return "";
return Uppers[c - 'z'];
return ""[0];
}
internal string Convert(string msgtext) => string.Join("", msgtext.ToCharArray().Select(c => ConvertChar(c)));
internal string Convert(string msgtext) => string.Join("", msgtext.ToCharArray().Select(c => $"{ConvertChar(c)} "));
}
public static Dictionary<string, UnicodeFont> Fonts = new Dictionary<string, UnicodeFont>
@ -35,55 +35,55 @@ namespace dotbot.Commands
{
Uppers = "",
Lowers = "",
Nums = "",
Digits = "",
},
["mono"] = new UnicodeFont
{
Uppers = "𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉",
Lowers = "𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣",
Nums = "𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿",
Digits = "𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿",
},
["flipped"] = new UnicodeFont
{
Uppers = "ɐqɔpǝɟƃɥıɾʞןɯuodbɹsʇn𐌡ʍxʎz",
Lowers = "ɐqɔpǝɟƃɥıɾʞןɯuodbɹsʇnʌʍxʎz",
Nums = "0123456789",
Digits = "0123456789",
},
["reversed"] = new UnicodeFont
{
Uppers = "AdↃbƎꟻGHIJK⅃MᴎOꟼpᴙTUVWXYZ",
Lowers = "AdↄbɘꟻgHijklmᴎoqpᴙꙅTUvwxYz",
Nums = "0߁23456789",
Digits = "0߁23456789",
},
["cyrillic"] = new UnicodeFont
{
Uppers = "αв¢∂єƒﻭнιנкℓмησρ۹яѕтυνωχуչ",
Lowers = "αв¢∂єƒﻭнιנкℓмησρ۹яѕтυνωχуչ",
Nums = "0123456789",
Digits = "0123456789",
},
["slashed"] = new UnicodeFont
{
Uppers = "ȺɃȻĐɆFǤĦƗɈꝀŁMNØⱣꝖɌSŦᵾVWXɎƵ",
Lowers = "Ⱥƀȼđɇfǥħɨɉꝁłmnøᵽꝗɍsŧᵾvwxɏƶ",
Nums = "01ƻ3456789",
Digits = "01ƻ3456789",
},
["script"] = new UnicodeFont
{
Uppers = "𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩",
Lowers = "𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃",
Nums = "𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗",
Digits = "𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗",
},
["gothic"] = new UnicodeFont
{
Uppers = "𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅",
Lowers = "𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟",
Nums = "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡",
Digits = "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡",
},
["vaporwave"] = new UnicodeFont
{
Uppers = "",
Lowers = "",
Nums = "",
Digits = "",
},
};
@ -92,8 +92,8 @@ namespace dotbot.Commands
[Summary("block text!")]
public async Task BlockText([Remainder] string text)
{
var Nums = new string[] { ":zero:", ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" };
await ReplyAsync(string.Join("", text.ToCharArray().Select(c => Char.IsDigit(c) ? $"{Nums[c - '0']} " : Char.IsLetter(c) ? $":regional_indicator_{Char.ToLower(c)}: " : "")));
var Digits = new string[] { ":zero:", ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" };
await ReplyAsync(string.Join("", text.ToCharArray().Select(c => Char.IsDigit(c) ? $"{Digits[c - '0']} " : Char.IsLetter(c) ? $":regional_indicator_{Char.ToLower(c)}: " : "")));
}
@ -101,7 +101,7 @@ namespace dotbot.Commands
[Alias("fontlist")]
public async Task ListFonts()
{
await base.ReplyAsync($"here are the available unicode fonts:```{string.Join(", ", Fonts.Keys)}```");
await ReplyAsync($"here are the available unicode fonts:```{string.Join(", ", Fonts.Keys)}```");
}
}

View File

@ -42,6 +42,7 @@ namespace dotbot
.AddSingleton<DotbotDb>()
.AddSingleton<PollService>()
.AddSingleton<HangmanService>()
.AddSingleton<TicTacToeService>()
.AddSingleton<Random>()
.AddSingleton(_config);

View File

@ -18,6 +18,7 @@ namespace dotbot.Services
private readonly IConfigurationRoot _config;
private readonly IServiceProvider _provider;
private readonly HangmanService _hangman;
private readonly TicTacToeService _tic;
private Dictionary<ulong, Poll> _polls;
public CommandHandlerService(
@ -27,8 +28,10 @@ namespace dotbot.Services
IServiceProvider provider,
DotbotDb db,
PollService polls,
HangmanService hangman
) {
HangmanService hangman,
TicTacToeService tic
)
{
_discord = discord;
_commands = commands;
_config = config;
@ -36,6 +39,7 @@ namespace dotbot.Services
_db = db;
_polls = polls.currentPolls;
_hangman = hangman;
_tic = tic;
_discord.MessageReceived += OnMessageReceivedAsync;
}
@ -45,7 +49,7 @@ namespace dotbot.Services
var msg = s as SocketUserMessage;
if (msg == null) return;
// Ignore self and other bots when checking commands
if (msg.Author.Id == _discord.CurrentUser.Id || msg.Author.IsBot) return;
if (msg.Author.Id == _discord.CurrentUser.Id || msg.Author.IsBot) return;
var context = new SocketCommandContext(_discord, msg);
@ -61,7 +65,7 @@ namespace dotbot.Services
if (msg.HasStringPrefix(_config["prefix"], ref argPos))
{ // check for other conditions
var key = msg.Content.Substring(_config["prefix"].Length).Split(' ').First();
if (_db.Defs.Any(d => d.Id == key))
{ // get def
await context.Channel.SendMessageAsync($"**{key}**: {_db.Defs.Find(key).Def}");
@ -70,8 +74,7 @@ namespace dotbot.Services
{ // get img
await context.Channel.TriggerTypingAsync();
await context.Message.DeleteAsync();
var img = _db.Images.Find(key);
await context.Channel.SendFileAsync($"UploadedImages/{img.FilePath}", $"{img.Id} by {context.User.Mention}");
await context.Channel.SendFileAsync($"UploadedImages/{_db.Images.Find(key).FilePath}", $"{key} by {context.User.Mention}");
}
else if (UnicodeFonts.Fonts.ContainsKey(key))
{ // convert font
@ -80,8 +83,8 @@ namespace dotbot.Services
}
}
else
{
// add poll options
{
// add poll options
var id = context.Channel.Id;
if (_polls.ContainsKey(id) && _polls[id].Owner == context.User && !_polls[id].IsOpen)
{
@ -90,29 +93,11 @@ namespace dotbot.Services
}
// handle hangman guess
if (_hangman._activeGames.ContainsKey(id))
{
var game = _hangman._activeGames[id];
if (msg.Content.Length != 1)
{
await context.Channel.SendMessageAsync($"send one letter at a time.\n\n{game}");
return;
}
await _hangman.HandleMove(msg, context);
// handle tictactoe games
await _tic.HandleMove(msg, context);
if (game.Guess(msg.Content[0]))
{ // proper guess
if (game.GameOver)
{
await context.Channel.SendMessageAsync($"game over!\n\n{game}");
return;
}
await context.Channel.SendMessageAsync($"{msg.Content[0]} guessed:\n\n{game}");
}
else
{
await context.Channel.SendMessageAsync($"letter already guessed... try again!\n\n{game}");
}
}
}
}
}

View File

@ -1,6 +1,9 @@
using Discord.WebSocket;
using dotbot.Commands;
using System.Collections.Generic;
using System;
using Discord.Commands;
using System.Threading.Tasks;
namespace dotbot.Services
{
@ -14,5 +17,35 @@ namespace dotbot.Services
_discord = discord;
_activeGames = new Dictionary<ulong, HangmanSession>();
}
internal async Task HandleMove(SocketUserMessage msg, SocketCommandContext context)
{
var id = context.Channel.Id;
if (_activeGames.ContainsKey(id))
{
var game = _activeGames[id];
if (msg.Content.Length != 1)
{
await context.Channel.SendMessageAsync($"send one letter at a time.\n\n{game}");
return;
}
if (game.Guess(msg.Content[0]))
{ // proper guess
if (game.GameOver)
{
await context.Channel.SendMessageAsync($"game over!\n\n{game}");
return;
}
await context.Channel.SendMessageAsync($"{msg.Content[0]} guessed:\n\n{game}");
}
else
{
await context.Channel.SendMessageAsync($"letter already guessed... try again!\n\n{game}");
}
}
}
}
}

33
Services/TicTacToeService.cs Executable file
View File

@ -0,0 +1,33 @@
using Discord.WebSocket;
using dotbot.Commands;
using System.Collections.Generic;
using System;
using Discord.Commands;
using System.Threading.Tasks;
namespace dotbot.Services
{
public class TicTacToeService
{
private DiscordSocketClient _discord;
public Dictionary<ulong, TicTacToeSession> _activeGames;
public TicTacToeService(DiscordSocketClient discord)
{
_discord = discord;
_activeGames = new Dictionary<ulong, TicTacToeSession>();
}
internal async Task HandleMove(SocketUserMessage msg, SocketCommandContext context)
{
var id = context.Channel.Id;
if (_activeGames.ContainsKey(id))
{
var game = _activeGames[id];
await context.Channel.SendMessageAsync("tictactoe move");
}
}
}
}

47
benbot_migrate.php Executable file
View File

@ -0,0 +1,47 @@
<?php
$db = new SQLite3("migration.db");
$cities = json_decode(file_get_contents("cities.json"));
$defs = json_decode(file_get_contents("defs.json"));
$emails = json_decode(file_get_contents("emails.json"));
$img_urls = json_decode(file_get_contents("img_urls.json"));
foreach ($emails as $key => $value) {
$query = "insert into Emails (Id, EmailAddress) values (:id, :email)";
$stmt = $db->prepare($query);
$stmt->bindValue(':id', $key);
$stmt->bindValue(':email', $value);
$stmt->execute();
}
foreach ($cities as $key => $value) {
$query = "insert into UserLocations (Id, City, CityId, Lat, Lng, TimeZone) values (:id, :city, :cityid, :lat, :lng, :timezone)";
$stmt = $db->prepare($query);
$stmt->bindValue(':id', $key);
$stmt->bindValue(':city', $value->city);
$stmt->bindValue(':cityid', $value->id);
$stmt->bindValue(':lat', $value->lat);
$stmt->bindValue(':lng', $value->lon);
$stmt->bindValue(':timezone', $value->timezone);
$stmt->execute();
}
foreach ($defs as $key => $value) {
$query = "insert into Defs (Id, Def) values (:id, :def)";
$stmt = $db->prepare($query);
$stmt->bindValue(':id', $key);
$stmt->bindValue(':def', $value);
$stmt->execute();
}
foreach ($img_urls as $key => $value) {
$query = "insert into Images (Id, FilePath) values (:id, :filepath)";
$stmt = $db->prepare($query);
$stmt->bindValue(':id', $key);
$stmt->bindValue(':filepath', $value);
$stmt->execute();
}

View File

@ -13,6 +13,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="NodaTime" Version="2.2.3" />
<PackageReference Include="WrapYouTubeDl" Version="1.0.4" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />

BIN
ytdl-bin/ffmpeg.exe Executable file

Binary file not shown.

BIN
ytdl-bin/youtube-dl.exe Executable file

Binary file not shown.