Update shiritori to work with kanji - only allow JP

This commit is contained in:
Dan Hess 2021-03-25 16:03:58 -08:00
parent cdccc2b85e
commit 4275f004ac
2 changed files with 68 additions and 23 deletions

View File

@ -1,6 +1,7 @@
from discord.ext import commands
import collections
import utils
class Games(commands.Cog):
@ -8,9 +9,9 @@ class Games(commands.Cog):
self.running_games = collections.defaultdict(dict)
@commands.guild_only()
# @utils.can_run(send_messages=True)
@commands.is_owner()
@commands.command(aliases=["word_chain", "しりとり"])
@commands.command(aliases=["word_chain", "しりとり", "シリトリ"])
@commands.max_concurrency(1, per=commands.BucketType.channel)
async def shiritori(self, ctx, *, start_word):
"""
Starts a game of Shiritori, in which the last letter of the last word given
@ -21,13 +22,17 @@ class Games(commands.Cog):
The kana cannot be used, as no word in Japanese starts with this
The word used cannot be a previously given word
"""
game = self.running_games["shiritori"].get(ctx.channel.id)
def grab_letter(word, last=True):
iterator = reversed(word) if last else iter(word)
def grab_letter(readings, last=True):
readings = [reversed(word) if last else iter(word) for word in readings]
for char in iterator:
if char.isalpha():
return char
letters = []
for reading in readings:
for char in reading:
if char.isalpha():
letters.append(char)
def check(message):
# Ensure it's in the same channel
@ -39,20 +44,31 @@ class Games(commands.Cog):
# The last person who gave a message cannot be the next one as well
elif last_author is not None and message.author == last_author:
return False
# If no game is running then how did this even happen!?
elif game is None:
return False
# If the author is not a player
elif message.author not in game["players"]:
return False
# Otherwise we good
else:
return True
# Setup the info needed for the game
message = ctx.message
message.content = start_word
last_letter = None
last_letters = None
words_used = []
# Ensure only one game is happening at once
game = self.running_games["shiritori"].get(ctx.channel.id)
if game:
return await ctx.send("Only one game per channel!")
self.running_games["shiritori"][ctx.channel.id] = True
if game is not None:
if ctx.author not in game["players"]:
game["players"].append(ctx.author)
await ctx.message.add_reaction("")
else:
await ctx.message.add_reaction("")
else:
self.running_games["shiritori"][ctx.channel.id] = {"players": []}
await ctx.send(
f"Shiritori game started! First word is `{start_word}`, any responses after this"
@ -60,32 +76,38 @@ class Games(commands.Cog):
)
while True:
# Grab the first letter of this new word and check it
first_letter = grab_letter(message.content, last=False)
# Include extra check for if this is the first word
if words_used and first_letter != last_letter:
is_noun, readings = await utils.readings_for_word(message.content)
# Only nouns can be used
if not is_noun:
break
# Grab the first letter of this new word and check it
first_letters = grab_letter(readings, last=False)
# Include extra check for if this is the first word
if words_used:
# Check if there's a match between first and last letters
if not any(char in first_letters for char in last_letters):
break
# Now set the "last" information, to start checking if it's correct
last_word = message.content
last_letter = grab_letter(last_word)
last_words = readings
last_letters = grab_letter(last_words)
last_author = message.author
# Extra check for the japanese version, ん cannot be used
if last_letter in ("", ""):
if any(char in last_letters for char in ("", "")):
break
# Cannot reuuse words; though make sure this doesn't get caught on the very first usage
if last_word in words_used and len(words_used) >= 1:
if any(word in words_used for word in last_words) and len(words_used) >= 1:
break
# If we're here, then the last letter used was valid
words_used.append(last_word)
words_used.extend(last_words)
await message.add_reaction("")
message = await ctx.bot.wait_for("message", check=check)
# If we're here, game over, someone messed up
self.running_games["shiritori"][ctx.channel.id] = False
await message.add_reaction("")
if last_letter in ("", ""):
if any(char in last_letters for char in ("", "")):
await ctx.send("Wrong! ん cannot be used as the last kana!")
else:
await ctx.send(f"Wrong! {message.author.mention} is a loser!")

View File

@ -12,6 +12,29 @@ def channel_is_nsfw(channel):
return isinstance(channel, discord.DMChannel) or channel.is_nsfw()
async def readings_for_word(word):
"""Returns a tuple, the first element representing if this word is a noun or not,
the second being all the readings for this word"""
data = await request(
"https://jisho.org/api/v1/search/words", payload={"keyword": word}
)
is_noun = False
readings = []
for piece in data["data"]:
# Jisho returns parts of words too, we don't want those for this use case
if piece["slug"] != word:
continue
# Provide the readings for this word
readings.extend([r["reading"] for r in piece["japanese"] if r["word"] == word])
for sense in piece["senses"]:
if "Noun" in sense["parts_of_speech"]:
is_noun = True
return is_noun, readings
async def download_image(url):
"""Returns a file-like object based on the URL provided"""
# Simply read the image, to get the bytes
@ -74,7 +97,7 @@ async def request(
# If an invalid attribute was requested, return None
return None
# If an error was hit other than the one we want to catch, try again
except:
except Exception:
continue