Update shiritori to work with kanji - only allow JP
This commit is contained in:
parent
cdccc2b85e
commit
4275f004ac
|
@ -1,6 +1,7 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
class Games(commands.Cog):
|
class Games(commands.Cog):
|
||||||
|
@ -8,9 +9,9 @@ class Games(commands.Cog):
|
||||||
self.running_games = collections.defaultdict(dict)
|
self.running_games = collections.defaultdict(dict)
|
||||||
|
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
# @utils.can_run(send_messages=True)
|
|
||||||
@commands.is_owner()
|
@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):
|
async def shiritori(self, ctx, *, start_word):
|
||||||
"""
|
"""
|
||||||
Starts a game of Shiritori, in which the last letter of the last word given
|
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 kana ん cannot be used, as no word in Japanese starts with this
|
||||||
The word used cannot be a previously given word
|
The word used cannot be a previously given word
|
||||||
"""
|
"""
|
||||||
|
game = self.running_games["shiritori"].get(ctx.channel.id)
|
||||||
|
|
||||||
def grab_letter(word, last=True):
|
def grab_letter(readings, last=True):
|
||||||
iterator = reversed(word) if last else iter(word)
|
readings = [reversed(word) if last else iter(word) for word in readings]
|
||||||
|
|
||||||
for char in iterator:
|
letters = []
|
||||||
|
|
||||||
|
for reading in readings:
|
||||||
|
for char in reading:
|
||||||
if char.isalpha():
|
if char.isalpha():
|
||||||
return char
|
letters.append(char)
|
||||||
|
|
||||||
def check(message):
|
def check(message):
|
||||||
# Ensure it's in the same channel
|
# 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
|
# 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:
|
elif last_author is not None and message.author == last_author:
|
||||||
return False
|
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:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Setup the info needed for the game
|
# Setup the info needed for the game
|
||||||
message = ctx.message
|
message = ctx.message
|
||||||
message.content = start_word
|
message.content = start_word
|
||||||
last_letter = None
|
last_letters = None
|
||||||
words_used = []
|
words_used = []
|
||||||
|
|
||||||
# Ensure only one game is happening at once
|
# Ensure only one game is happening at once
|
||||||
game = self.running_games["shiritori"].get(ctx.channel.id)
|
if game is not None:
|
||||||
if game:
|
if ctx.author not in game["players"]:
|
||||||
return await ctx.send("Only one game per channel!")
|
game["players"].append(ctx.author)
|
||||||
self.running_games["shiritori"][ctx.channel.id] = True
|
await ctx.message.add_reaction("✅")
|
||||||
|
else:
|
||||||
|
await ctx.message.add_reaction("✅")
|
||||||
|
else:
|
||||||
|
self.running_games["shiritori"][ctx.channel.id] = {"players": []}
|
||||||
|
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
f"Shiritori game started! First word is `{start_word}`, any responses after this"
|
f"Shiritori game started! First word is `{start_word}`, any responses after this"
|
||||||
|
@ -60,32 +76,38 @@ class Games(commands.Cog):
|
||||||
)
|
)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
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
|
# Grab the first letter of this new word and check it
|
||||||
first_letter = grab_letter(message.content, last=False)
|
first_letters = grab_letter(readings, last=False)
|
||||||
# Include extra check for if this is the first word
|
# Include extra check for if this is the first word
|
||||||
if words_used and first_letter != last_letter:
|
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
|
break
|
||||||
# Now set the "last" information, to start checking if it's correct
|
# Now set the "last" information, to start checking if it's correct
|
||||||
last_word = message.content
|
last_words = readings
|
||||||
last_letter = grab_letter(last_word)
|
last_letters = grab_letter(last_words)
|
||||||
last_author = message.author
|
last_author = message.author
|
||||||
|
|
||||||
# Extra check for the japanese version, ん cannot be used
|
# Extra check for the japanese version, ん cannot be used
|
||||||
if last_letter in ("ん", "ン"):
|
if any(char in last_letters for char in ("ん", "ン")):
|
||||||
break
|
break
|
||||||
# Cannot reuuse words; though make sure this doesn't get caught on the very first usage
|
# 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
|
break
|
||||||
|
|
||||||
# If we're here, then the last letter used was valid
|
# 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("✅")
|
await message.add_reaction("✅")
|
||||||
message = await ctx.bot.wait_for("message", check=check)
|
message = await ctx.bot.wait_for("message", check=check)
|
||||||
|
|
||||||
# If we're here, game over, someone messed up
|
# If we're here, game over, someone messed up
|
||||||
self.running_games["shiritori"][ctx.channel.id] = False
|
self.running_games["shiritori"][ctx.channel.id] = False
|
||||||
await message.add_reaction("❌")
|
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!")
|
await ctx.send("Wrong! ん cannot be used as the last kana!")
|
||||||
else:
|
else:
|
||||||
await ctx.send(f"Wrong! {message.author.mention} is a loser!")
|
await ctx.send(f"Wrong! {message.author.mention} is a loser!")
|
||||||
|
|
|
@ -12,6 +12,29 @@ def channel_is_nsfw(channel):
|
||||||
return isinstance(channel, discord.DMChannel) or channel.is_nsfw()
|
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):
|
async def download_image(url):
|
||||||
"""Returns a file-like object based on the URL provided"""
|
"""Returns a file-like object based on the URL provided"""
|
||||||
# Simply read the image, to get the bytes
|
# Simply read the image, to get the bytes
|
||||||
|
@ -74,7 +97,7 @@ async def request(
|
||||||
# If an invalid attribute was requested, return None
|
# If an invalid attribute was requested, return None
|
||||||
return None
|
return None
|
||||||
# If an error was hit other than the one we want to catch, try again
|
# If an error was hit other than the one we want to catch, try again
|
||||||
except:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue