From be44c9c4897eeadd265cb0036aa71c0c4e7c3e9b Mon Sep 17 00:00:00 2001 From: Jeremy Saklad Date: Fri, 13 Aug 2021 09:45:27 -0500 Subject: [PATCH] Add blacklist parameter to CLI Blacklisting uses a dedicated action to parse arguments into enumeration members. Choices are not provided for this option, as it would utterly overwhelm the help interface. This means that errors may not be as helpful, however. The README has been updated with a small section on blacklisting, how to do it, and why you may wish to do it. Options that were previously commented out are now enabled, since the user can simply blacklist them instead. --- README.md | 9 +++++ bonemarketsolver/__main__.py | 10 +++++ bonemarketsolver/data/skulls.py | 44 ++++++++++----------- bonemarketsolver/data/torsos.py | 18 ++++----- bonemarketsolver/objects/blacklistaction.py | 39 ++++++++++++++++++ 5 files changed, 89 insertions(+), 31 deletions(-) create mode 100644 bonemarketsolver/objects/blacklistaction.py diff --git a/README.md b/README.md index fca9820..11adac5 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,15 @@ For best results, it is recommended to specify every world quality you can. Depe You do *not* need to specify buyers to use this solver. The solver will try to maximize profit margin for all available buyers if you do not specify any, which may be very useful if you have no particular objective in mind. Specifying multiple buyers will cause the solver to choose from among them. +### Blacklisting + +If you wish to prohibit certain options, you may do so by passing their exact identifiers (e.g. "Skull.VICTIM\_SKULL") to the blacklist. + +You may wish to blacklist options that: +* require a Profession, Ambition, or story you do not have +* add Exhaustion +* you simply don't have a good source for + ## Caveats The solver aims to maximize profit margin, rather than sheer profit. This is because it may be more profitable to make many smaller skeletons than one massive skeleton. diff --git a/bonemarketsolver/__main__.py b/bonemarketsolver/__main__.py index 8b0d085..d00f5d8 100644 --- a/bonemarketsolver/__main__.py +++ b/bonemarketsolver/__main__.py @@ -1,6 +1,7 @@ import argparse import curses +from .objects.blacklistaction import BlacklistAction from .objects.enumaction import EnumAction from .solve import * @@ -85,6 +86,15 @@ skeleton_parameters.add_argument( dest='maximum_exhaustion' ) +skeleton_parameters.add_argument( + "--blacklist", + action=BlacklistAction, + nargs='+', + help="components, options, or buyers that should not be used by the solver", + metavar="Enum.MEMBER", + dest='blacklist' + ) + solver_options = parser.add_argument_group( "solver options", diff --git a/bonemarketsolver/data/skulls.py b/bonemarketsolver/data/skulls.py index f17e35f..d01a763 100644 --- a/bonemarketsolver/data/skulls.py +++ b/bonemarketsolver/data/skulls.py @@ -47,14 +47,14 @@ class Skull(Enum): ) # Adds Exhaustion - # ENGRAVED_SKULL = Action( - # "Affix a Custom-Engraved Skull to your (Skeleton Type)", - # cost = Cost.ACTION.value + Cost.ENGRAVED_SKULL.value, - # value = 10000, - # skulls_needed = -1, - # skulls = 1, - # exhaustion = 2 - # ) + ENGRAVED_SKULL = Action( + "Affix a Custom-Engraved Skull to your (Skeleton Type)", + cost = Cost.ACTION.value + Cost.ENGRAVED_SKULL.value, + value = 10000, + skulls_needed = -1, + skulls = 1, + exhaustion = 2 + ) EYELESS_SKULL = Action( "Affix an Eyeless Skull to your (Skeleton Type)", @@ -76,13 +76,13 @@ class Skull(Enum): ) # Seeking the Name of Mr. Eaten - # OWN_SKULL = Action( - # "Duplicate your own skull and affix it here", - # cost = Cost.ACTION.value + 1000*Cost.BONE_FRAGMENT.value, - # value = -250, - # skulls_needed = -1, - # skulls = 1 - # ) + OWN_SKULL = Action( + "Duplicate your own skull and affix it here", + cost = Cost.ACTION.value + 1000*Cost.BONE_FRAGMENT.value, + value = -250, + skulls_needed = -1, + skulls = 1 + ) PENTAGRAMMIC_SKULL = Action( "Affix a Pentagrammic Skull to your (Skeleton Type)", @@ -139,13 +139,13 @@ class Skull(Enum): ) # Licentiate - # VICTIM_SKULL = Action( - # "Cap this with a victim’s skull", - # cost = Cost.ACTION.value, - # value = 250, - # skulls_needed = -1, - # skulls = 1 - # ) + VICTIM_SKULL = Action( + "Cap this with a victim’s skull", + cost = Cost.ACTION.value, + value = 250, + skulls_needed = -1, + skulls = 1 + ) def __str__(self): return str(self.value) diff --git a/bonemarketsolver/data/torsos.py b/bonemarketsolver/data/torsos.py index c63bb14..29fccb9 100644 --- a/bonemarketsolver/data/torsos.py +++ b/bonemarketsolver/data/torsos.py @@ -20,15 +20,15 @@ class Torso(Enum): ) # Licentiate - # VICTIM_SKELETON = Action( - # "Supply a skeleton of your own", - # cost = Cost.ACTION.value, - # torso_style = 10, - # value = 250, - # skulls_needed = 1, - # arms = 2, - # legs = 2 - # ) + VICTIM_SKELETON = Action( + "Supply a skeleton of your own", + cost = Cost.ACTION.value, + torso_style = 10, + value = 250, + skulls_needed = 1, + arms = 2, + legs = 2 + ) HUMAN_RIBCAGE = Action( "Build on the Human Ribcage", diff --git a/bonemarketsolver/objects/blacklistaction.py b/bonemarketsolver/objects/blacklistaction.py new file mode 100644 index 0000000..1a714b2 --- /dev/null +++ b/bonemarketsolver/objects/blacklistaction.py @@ -0,0 +1,39 @@ +__all__ = ['BlacklistAction'] +__author__ = "Jeremy Saklad" + +from argparse import Action + +from ..data.adjustments import Adjustment +from ..data.appendages import Appendage +from ..data.buyers import Buyer +from ..data.declarations import Declaration +from ..data.embellishments import Embellishment +from ..data.skulls import Skull +from ..data.torsos import Torso + +def convert_to_enum(value): + split = value.split(".", 1) + + enum = globals()[split[0]] + return enum[split[1]] + +class BlacklistAction(Action): + def __init__(self, **kwargs): + nargs = kwargs.get('nargs') + + super(BlacklistAction, self).__init__(**kwargs) + + self._nargs = nargs + + def __call__(self, parser, namespace, values, option_string=None): + if self._nargs is None or self._nargs == '?': + # Convert value back into an Enum + enum = convert_to_enum(value) + + setattr(namespace, self.dest, enum) + else: + # Convert values back into Enums + enums = [convert_to_enum(value) for value in values] + + items = getattr(namespace, self.dest, list()) + enums + setattr(namespace, self.dest, items)