Create a recorder
These objects record the goings-on of a room into an HTML file.
This commit is contained in:
parent
b33f6ca7df
commit
be13c8cb6e
8 changed files with 329 additions and 107 deletions
|
|
@ -1,17 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from random import choice
|
||||
from re import match
|
||||
from re import match, findall
|
||||
|
||||
from evennia import CmdSet, create_script
|
||||
from evennia.utils import delay, logger
|
||||
from evennia.commands.default.muxcommand import MuxCommand
|
||||
from evennia.contrib.rpg.rpsystem import send_emote
|
||||
from evennia.prototypes.spawner import spawn
|
||||
from evennia.utils.utils import int2str
|
||||
|
||||
from .command import Command
|
||||
from typeclasses.scripts import DonkeyHeadSpell
|
||||
from typeclasses.drinkables import Cocktail
|
||||
from utils.word_list import routput
|
||||
from utils.word_list import routput, pluralize
|
||||
|
||||
|
||||
class CmdFly(Command):
|
||||
|
|
@ -88,6 +90,98 @@ class CmdMagic(Command):
|
|||
wizard.spell_sequence(None, msgs, wizard.db.magic_delay or 3)
|
||||
|
||||
|
||||
class CmdMakeItem(Command):
|
||||
"""
|
||||
Create one or more items from thin air.
|
||||
|
||||
Usage:
|
||||
|
||||
make [ number ] name [ description ]
|
||||
|
||||
Where 'effects' is a statement of what happens after the magic erupts.
|
||||
Note that you can have multiple effects separated by two semicolons.
|
||||
|
||||
You can tailor the effects to your character with the following:
|
||||
|
||||
- |y$you()|n: Replaced by "you" and your name for others, like "old gnome"
|
||||
- |y$your()|n: Replaced by "your" and your possessive name for others, like "old gnome's"
|
||||
- |y$conj(verb)|n: Replaced by "verb" for you, and "verbs" for others.
|
||||
- |y$pron(you)|n: Replaced by "you" for you, but "he" or "she" for others.
|
||||
- |y$pron(your)|n: Replaced by "your" for you, but "his" or "her" for others.
|
||||
|
||||
While flexible, you would use this complex replacement in either
|
||||
|gnicks|n or as part of a standard magical prefix, by setting the
|
||||
property:
|
||||
|
||||
@set self/magic_msg = "$You() $conj(shake) $pron(your) necklace of bones!"
|
||||
"""
|
||||
key = "make"
|
||||
locks = "cmd:holds()"
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Allows the following phrases:
|
||||
|
||||
candy
|
||||
2 candy Piece of Turkish Delight.
|
||||
candy Piece of Turkish Delight.
|
||||
"turkish delight" Piece of Turkish Delight.
|
||||
4 "turkish delight" Piece of Turkish Delight.
|
||||
"""
|
||||
pattern = r'"(.*?)"|(\S+)'
|
||||
matches = findall(pattern, self.args.strip())
|
||||
|
||||
try:
|
||||
self.item_number = int(matches[0][0] or matches[0][1])
|
||||
self.item_name = matches[1][0] or matches[1][1]
|
||||
start = 2
|
||||
except ValueError:
|
||||
self.item_number = 1
|
||||
self.item_name = matches[0][0] or matches[0][1]
|
||||
start = 1
|
||||
|
||||
words = [m[0] or m[1] for m in matches[start:]]
|
||||
self.item_desc = " ".join(words)
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
Call the 'do_magic' method on the caller.
|
||||
"""
|
||||
wizard = self.caller
|
||||
msgs = wizard.db.make_msg or wizard.db.magic_msg
|
||||
if msgs:
|
||||
msgs = msgs.split(';;')
|
||||
else:
|
||||
msgs = [
|
||||
"$You() $conj(snap) $pron(your) fingers.",
|
||||
]
|
||||
|
||||
if self.item_number == 1:
|
||||
if match(r"^[aeiou]"):
|
||||
name = f"an {self.item_name}"
|
||||
else:
|
||||
name = f"a {self.item_name}"
|
||||
else:
|
||||
name = int2str(self.item_number) + " " + \
|
||||
pluralize(self.item_name)
|
||||
|
||||
# from evennia.utils.utils import int2str ; print(int2str("2"))
|
||||
msgs = msgs + [ name + " appear in $pron(your) hand." ]
|
||||
|
||||
wizard.spell_sequence(None, msgs, wizard.db.magic_delay or 3)
|
||||
|
||||
# FIXME So that the cat will eat it and it can't be littered:
|
||||
if not self.item_desc or self.item_desc == "":
|
||||
self.item_desc = f"Conjured by {wizard.get_name()}."
|
||||
|
||||
for _ in range(self.item_number):
|
||||
item = spawn({
|
||||
"typeclass": "typeclasses.consumables.Litterable",
|
||||
"key": self.item_name,
|
||||
"desc": self.item_desc
|
||||
})[0]
|
||||
item.location = wizard
|
||||
|
||||
class CmdSetWand(CmdSet):
|
||||
"""
|
||||
All wizard spells are tied to a 'wand' that might be flavored.
|
||||
|
|
@ -96,6 +190,7 @@ class CmdSetWand(CmdSet):
|
|||
super().at_cmdset_creation()
|
||||
self.add(CmdFly)
|
||||
self.add(CmdMagic)
|
||||
self.add(CmdMakeItem)
|
||||
|
||||
|
||||
class CmdMakeCocktail(MuxCommand):
|
||||
|
|
|
|||
48
transcripts/cozy-header-template.html
Normal file
48
transcripts/cozy-header-template.html
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Wyldwood Bar Transcript</title>
|
||||
<style type="text/css" media="screen">
|
||||
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Goudy+Bookletter+1911&family=Merienda:wght@300..900&display=swap');
|
||||
|
||||
html {
|
||||
background-color: #FCF5E5;
|
||||
color: #26201a;
|
||||
|
||||
font-family: "Goudy Bookletter 1911", serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 21px;
|
||||
line-height: 1.4em;
|
||||
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: auto;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
font-style: italic;
|
||||
}
|
||||
blockquote i {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.special {
|
||||
margin: 20px;
|
||||
padding: 2px 20px;
|
||||
border: 2px solid brown;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.special .title {
|
||||
font-variant-caps: small-caps;
|
||||
}
|
||||
|
||||
.heavy {
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Another Cozy Day</h1>
|
||||
|
|
@ -493,9 +493,15 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
|
|||
"""
|
||||
# All this does is add the character's gender to the message.
|
||||
# So $pron(you,op) will render "you" or "him"
|
||||
newmsg = sub(r"\$pron\((.*?),(.*?)\)", f"$pron(\\1,\\2 {self.db.gender})", message)
|
||||
newmsg = sub(r"\$pron\(([^\)]*?),([^\)]*?)\)",
|
||||
f"$pron(\\1,\\2 {self.db.gender})",
|
||||
message)
|
||||
|
||||
# While $pron(you) will render "you" and "he":
|
||||
newmsg = sub(r"\$pron\(([^,]*?)\)", f"$pron(\\1, {self.db.gender})", newmsg)
|
||||
newmsg = sub(r"\$pron\(([^,\)]*?)\)",
|
||||
f"$pron(\\1, {self.db.gender})",
|
||||
newmsg)
|
||||
|
||||
choose = choices(newmsg)
|
||||
logger.info(choose)
|
||||
self.location.msg_contents(f"{choose}", from_obj=self, exclude=exclude)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,12 @@ with a location in the game world (like Characters, Rooms, Exits).
|
|||
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from os import makedirs
|
||||
from os.path import dirname, exists
|
||||
from re import split, match, sub, IGNORECASE
|
||||
from random import randint
|
||||
from shutil import copyfile
|
||||
from random import randint, choice
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
|
@ -719,3 +723,116 @@ class Listener:
|
|||
|
||||
self.msg(f"You can't give '{gift}' to {receiver.key}... {gifts.keys()}")
|
||||
return None
|
||||
|
||||
class Recorder(Object):
|
||||
"""Mixin for recording the events in the current room.
|
||||
|
||||
Classes that include this mixin, need, in their msg()
|
||||
should call 'record_msg', for instance:
|
||||
|
||||
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||
self.record_msg(text, from_obj)
|
||||
|
||||
Objects should set the following properties:
|
||||
|
||||
@set scribe/transcripts_path = "transcripts/%Y-%m-%d.html"
|
||||
@set scribe/header_file = "transcripts/header-template.html"
|
||||
|
||||
The 'directory' is really just a prefix to a file that includes
|
||||
today's date.
|
||||
"""
|
||||
def capitalize_msg(self, text, msg_type=None):
|
||||
"""
|
||||
If text is lowercase, capitalize it.
|
||||
|
||||
Maybe prepend a 'The' to the front.
|
||||
"""
|
||||
logger.info(f"capitalize: {text} / {msg_type}")
|
||||
if msg_type and msg_type in ('traverse', 'teleport'):
|
||||
if match(r"^(\|[A-z])?[aeiou]", text):
|
||||
return "An " + text
|
||||
else:
|
||||
return "A " + text
|
||||
|
||||
# If the line is colored and lowercase, it probably assumes a
|
||||
# character's sdesc, so let's add a 'the' to the front:
|
||||
# Do we need to only do this on 'say'?
|
||||
elif match(r"^\|[mb][a-z]", text):
|
||||
return "The " + text
|
||||
elif match(r"^(\|[A-z])[a-z]", text) and len(text) > 2:
|
||||
return text[0:2] + text[2].upper() + text[3:]
|
||||
elif match(r"^[a-z]", text) and len(text) > 2:
|
||||
return text[0].upper() + text[1:]
|
||||
return text
|
||||
|
||||
def to_html(self, text, msg_type=None):
|
||||
"""
|
||||
Convert the 'text' to an HTML formatted string.
|
||||
"""
|
||||
# Yellow text should be italics:
|
||||
text = sub(r"\|y(.*?)\|n", '<i>\\1</i>', text)
|
||||
# Bold and tag the titles:
|
||||
text = sub(r"\|[cb](.*?)\|n", '<b class="title">\\1</b>', text)
|
||||
# Bold anything white:
|
||||
text = sub(r"\|w(.*?)\|n", '<b class="heavy">\\1</b>', text)
|
||||
# Remove the rest:
|
||||
text = sub(r"\|[A-z]", '', text)
|
||||
# Remove initial or final carriage returns:
|
||||
text = sub(r"^\n", '', text)
|
||||
text = sub(r"\n$", '', text)
|
||||
# Convert the carriage returns:
|
||||
text = sub(r"\n", ' <br/>\n', text)
|
||||
|
||||
if msg_type and msg_type == 'look':
|
||||
text = text[0].upper() + text[1:]
|
||||
return f"<div class=\"special\"><p>{text}</p></div>\n"
|
||||
if msg_type and msg_type == 'note':
|
||||
return f"<blockquote>{text}</blockquote>"
|
||||
return f"<p>{text}</p>\n"
|
||||
|
||||
|
||||
def find_actor(self, text, default=None):
|
||||
"""
|
||||
Extract the character performing the action from the text.
|
||||
"""
|
||||
if default:
|
||||
return default
|
||||
|
||||
m = match(r".*\|b(.*?)\|n.*", text)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
def record_msg(self, text, actor=None):
|
||||
msg_type = None
|
||||
|
||||
if isinstance(text, tuple):
|
||||
if text[1] and isinstance(text[1], dict):
|
||||
msg_type = text[1]['type']
|
||||
msg = self.capitalize_msg(text[0], msg_type)
|
||||
else:
|
||||
msg = self.capitalize_msg(str(text))
|
||||
|
||||
# Make things a little more interesting by substituting the
|
||||
# name...
|
||||
names = actor and actor.db.alt_names
|
||||
if names:
|
||||
logger.info("lets change this")
|
||||
msg = sub(f"^{actor.sdesc.get()}", choice(names),
|
||||
msg, flags=IGNORECASE)
|
||||
|
||||
actor = self.find_actor(msg, actor)
|
||||
|
||||
msg = self.to_html(msg, msg_type)
|
||||
filename = datetime.today().strftime(self.db.transcripts_path)
|
||||
|
||||
if not exists(filename):
|
||||
makedirs(dirname(filename), exist_ok=True)
|
||||
copyfile(self.db.header_file, filename)
|
||||
|
||||
with open(filename, "a") as myfile:
|
||||
myfile.write(msg)
|
||||
|
||||
# Follow-up Actions ...
|
||||
if match(r".* arrives .*", msg) and actor and \
|
||||
msg_type in ('traverse', 'teleport'):
|
||||
self.execute_cmd(f"look {actor}")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# Emacs environement
|
||||
# (setq python-shell-interpreter "/Users/howard/src/moss-n-puddles/.venv/bin/ipython")
|
||||
"""
|
||||
Pets
|
||||
|
||||
|
|
@ -16,7 +14,7 @@ from time import time
|
|||
import random
|
||||
|
||||
from evennia import TICKER_HANDLER
|
||||
from evennia.utils import logger
|
||||
from evennia.utils import logger, delay
|
||||
from evennia.utils.gametime import schedule
|
||||
from evennia.utils.search import search_object
|
||||
|
||||
|
|
@ -482,9 +480,10 @@ class WeeBeastie(Friendly, Familiar, Listener):
|
|||
"Override to return a string in response to message."
|
||||
owner = self.search("Dabbler")
|
||||
if owner:
|
||||
owner.announce_action(f"$Your() {name} purrs.")
|
||||
delay(3, owner.announce_action,
|
||||
f"$Your() {self.get_name()} purrs.")
|
||||
else:
|
||||
self.execute_cmd(f"emote /me purrs.")
|
||||
delay(3, self.execute_cmd, f"emote /me purrs.")
|
||||
|
||||
def feed(self, feeder, item=None):
|
||||
"""
|
||||
|
|
@ -492,9 +491,14 @@ class WeeBeastie(Friendly, Familiar, Listener):
|
|||
the character has, and go with that...
|
||||
"""
|
||||
# Categorize items that can be used to feed the beast:
|
||||
def is_flower(item):
|
||||
return (not item and feeder.has("yellow flower")) or \
|
||||
(item and item.key == 'yellow flower')
|
||||
def is_some(item, name):
|
||||
return (not item and feeder.has(name)) or \
|
||||
(item and item.key == name)
|
||||
|
||||
def is_edible(item):
|
||||
return is_some(item, "yellow flower") or \
|
||||
is_some(item, "candy") or \
|
||||
is_some(item, "turkish delight")
|
||||
|
||||
# Based on the reaction to the feeder, the adjectives may alter:
|
||||
noun = "The " + random.choice(["wee", "furry", "white", "adorable"]) + " beastie"
|
||||
|
|
@ -521,10 +525,11 @@ class WeeBeastie(Friendly, Familiar, Listener):
|
|||
"rubs its wee widdle head under $pron(you,op) chin in gratitude",
|
||||
])
|
||||
|
||||
if is_flower(item):
|
||||
msg = f"{noun} {how_sniff} sniffs $your() << hand holding a ^>> flower. It {how_eat} eats it, and {and_then}."
|
||||
edible = is_edible(item)
|
||||
if edible:
|
||||
msg = f"{noun} {how_sniff} sniffs $your() << hand holding a ^>> {edible}. It {how_eat} eats it, and {and_then}."
|
||||
self.adjust_character(feeder, 100)
|
||||
feeder.has('yellow flower').delete()
|
||||
edible.delete()
|
||||
else:
|
||||
msg = f"{noun} doesn't appear interested in anything you have."
|
||||
|
||||
|
|
@ -654,7 +659,6 @@ class BHB(Friendly):
|
|||
|
||||
if msg:
|
||||
feeder.announce_action(msg)
|
||||
# feeder.msg(msg)
|
||||
|
||||
def thrown_stick(self, thrower):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,16 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from os.path import exists
|
||||
from datetime import datetime
|
||||
from re import split, match, sub, IGNORECASE
|
||||
from shutil import copyfile
|
||||
|
||||
from evennia import CmdSet
|
||||
from evennia.utils import logger
|
||||
|
||||
from commands.command import Command
|
||||
from typeclasses.characters import Character
|
||||
from typeclasses.objects import Listener
|
||||
from typeclasses.objects import Listener, Recorder
|
||||
from utils.word_list import routput
|
||||
|
||||
|
||||
|
|
@ -101,7 +98,7 @@ class CmdSetShrubSay(CmdSet):
|
|||
self.add(CmdShrubSay)
|
||||
|
||||
|
||||
class Shrub(Puppet):
|
||||
class Shrub(Puppet, Recorder):
|
||||
"""
|
||||
The 'Shrub' has its own way of communicating.
|
||||
"""
|
||||
|
|
@ -145,89 +142,6 @@ class Shrub(Puppet):
|
|||
self.db.inside = f"The chalkboard reads: |w{new_text}|n"
|
||||
self.record_msg(self.db.inside)
|
||||
|
||||
def find_actor(self, text, default=None):
|
||||
"""
|
||||
Extract the character performing the action from the text.
|
||||
"""
|
||||
if default:
|
||||
return default
|
||||
|
||||
m = match(r".*\|b(.*?)\|n.*", text)
|
||||
if m:
|
||||
return m.group(1)
|
||||
|
||||
def capitalize_msg(self, text, msg_type=None):
|
||||
"""
|
||||
If text is lowercase, capitalize it.
|
||||
|
||||
Maybe prepend a 'The' to the front.
|
||||
"""
|
||||
logger.info(f"capitalize: {text} / {msg_type}")
|
||||
if msg_type and msg_type in ('traverse', 'teleport'):
|
||||
if match(r"^(\|[A-z])?[aeiou]", text):
|
||||
return "An " + text
|
||||
else:
|
||||
return "A " + text
|
||||
# If the line is colored and lowercase, it probably assumes a
|
||||
# character's sdesc, so let's add a 'the' to the front:
|
||||
# Do we need to only do this on 'say'?
|
||||
elif match(r"^\|[mb][a-z]", text):
|
||||
return "The " + text
|
||||
elif match(r"^(\|[A-z])[a-z]", text) and len(text) > 2:
|
||||
return text[0:2] + text[2].upper() + text[3:]
|
||||
elif match(r"^[a-z]", text) and len(text) > 2:
|
||||
return text[0].upper() + text[1:]
|
||||
return text
|
||||
|
||||
def to_html(self, text, msg_type=None):
|
||||
"""
|
||||
Convert the 'text' to an HTML formatted string.
|
||||
"""
|
||||
# Yellow text should be italics:
|
||||
text = sub(r"\|y(.*?)\|n", '<i>\\1</i>', text)
|
||||
# Bold and tag the titles:
|
||||
text = sub(r"\|[cb](.*?)\|n", '<b class="title">\\1</b>', text)
|
||||
# Bold anything white:
|
||||
text = sub(r"\|w(.*?)\|n", '<b class="heavy">\\1</b>', text)
|
||||
# Remove the rest:
|
||||
text = sub(r"\|[A-z]", '', text)
|
||||
# Remove initial or final carriage returns:
|
||||
text = sub(r"^\n", '', text)
|
||||
text = sub(r"\n$", '', text)
|
||||
# Convert the carriage returns:
|
||||
text = sub(r"\n", ' <br/>\n', text)
|
||||
|
||||
if msg_type and msg_type == 'look':
|
||||
text = text[0].upper() + text[1:]
|
||||
return f"<div class=\"special\"><p>{text}</p></div>\n"
|
||||
if msg_type and msg_type == 'note':
|
||||
return f"<blockquote>{text}</blockquote>"
|
||||
return f"<p>{text}</p>\n"
|
||||
|
||||
def record_msg(self, text, actor=None):
|
||||
msg_type = None
|
||||
|
||||
if isinstance(text, tuple):
|
||||
if text[1] and isinstance(text[1], dict):
|
||||
msg_type = text[1]['type']
|
||||
msg = self.capitalize_msg(text[0], msg_type)
|
||||
else:
|
||||
msg = self.capitalize_msg(str(text))
|
||||
|
||||
actor = self.find_actor(msg, actor)
|
||||
msg = self.to_html(msg, msg_type)
|
||||
filename = datetime.today().strftime('transcripts/%Y-%m-%d.html')
|
||||
if not exists(filename):
|
||||
copyfile("transcripts/header-template.html", filename)
|
||||
|
||||
with open(filename, "a") as myfile:
|
||||
myfile.write(msg)
|
||||
|
||||
# Follow-up Actions ...
|
||||
if match(r".* arrives .*", msg) and actor and \
|
||||
msg_type in ('traverse', 'teleport'):
|
||||
self.execute_cmd(f"look {actor}")
|
||||
|
||||
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||
"""
|
||||
Record everything that happens in the room for a transcript.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from commands.wizards import CmdSetWand
|
|||
from utils.word_list import routput, choices, paragraph
|
||||
from utils.scoring import Scores
|
||||
from typeclasses.consumables import Litterable
|
||||
from typeclasses.objects import Object
|
||||
from typeclasses.objects import Object, Recorder
|
||||
from typeclasses.scripts import KnockScript
|
||||
|
||||
|
||||
|
|
@ -936,7 +936,7 @@ class BagofJunk(Object):
|
|||
self.db.latest_item = item
|
||||
where = self.db._sdesc or self.name
|
||||
|
||||
msg = routput(f"$You() << $conj(scrounge) ^ $conj(rummage) ^ $conj(fish) ^ $conj(rifle) ^ $conj(put) $pron(your) hand >> << around ^ >> in $pron(your) {where}, and << $conj(pull) out ^ $conj(find) ^ $conj(stare) at >> |w{item}|n.")
|
||||
msg = routput(f"$You() << $conj(scrounge) ^ $conj(rummage) ^ $conj(fish) ^ $conj(rifle) ^ $conj(put) $pron(your) hand >> << around ^ >> in $pron(your) {where}, and << $conj(pull) out ^ $conj(find) ^ $conj(stare) at >>|w{item}|n.")
|
||||
owner.announce_action(msg)
|
||||
|
||||
def do_keep(self, keeper, description=None):
|
||||
|
|
@ -1142,3 +1142,16 @@ class GlobalAlarmClock(Object):
|
|||
if start:
|
||||
chan.msg(start)
|
||||
delay(seconds, chan.msg, finish)
|
||||
|
||||
|
||||
class Scribe(Recorder):
|
||||
"""
|
||||
Probably invisible script that records room events.
|
||||
|
||||
Set the following properties:
|
||||
|
||||
@set scribe/directory = "transcripts"
|
||||
@set scribe/header = "transcripts/header-template.html"
|
||||
"""
|
||||
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||
self.record_msg(text, from_obj)
|
||||
|
|
|
|||
|
|
@ -147,3 +147,28 @@ def choices(text, *substitutions):
|
|||
|
||||
if text:
|
||||
return routput(choice(text), *substitutions)
|
||||
|
||||
|
||||
def pluralize(noun):
|
||||
"""Convert a singular noun to its plural form."""
|
||||
# Basic pluralization rules
|
||||
|
||||
# Change 'y' to 'ies' if preceded by a consonant
|
||||
if noun.endswith('y') and noun[-2] not in 'aeiou':
|
||||
return noun[:-1] + 'ies'
|
||||
|
||||
# Add 'es' for words ending in 's', 'x', 'z', 'ch', or 'sh'
|
||||
elif noun.endswith('s') or noun.endswith('x') or \
|
||||
noun.endswith('z') or noun.endswith('ch') or \
|
||||
noun.endswith('sh'):
|
||||
return noun + 'es'
|
||||
|
||||
# Default case for regular nouns
|
||||
else:
|
||||
return noun + 's'
|
||||
|
||||
# print(pluralize("dog")) # Outputs: dogs
|
||||
# print(pluralize("candy")) # Outputs: candies
|
||||
# print(pluralize("box")) # Outputs: boxes
|
||||
# print(pluralize("bush")) # Outputs: bushes
|
||||
# print(pluralize("city")) # Outputs: cities
|
||||
|
|
|
|||
Loading…
Reference in a new issue