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.
This commit is contained in:
Jeremy Saklad 2021-08-13 09:45:27 -05:00
parent dabf0ff4d7
commit be44c9c489
Signed by: Jeremy Saklad
GPG Key ID: 9CA2149583EDBF84
5 changed files with 89 additions and 31 deletions

View File

@ -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. 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 ## 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. 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.

View File

@ -1,6 +1,7 @@
import argparse import argparse
import curses import curses
from .objects.blacklistaction import BlacklistAction
from .objects.enumaction import EnumAction from .objects.enumaction import EnumAction
from .solve import * from .solve import *
@ -85,6 +86,15 @@ skeleton_parameters.add_argument(
dest='maximum_exhaustion' 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 = parser.add_argument_group(
"solver options", "solver options",

View File

@ -47,14 +47,14 @@ class Skull(Enum):
) )
# Adds Exhaustion # Adds Exhaustion
# ENGRAVED_SKULL = Action( ENGRAVED_SKULL = Action(
# "Affix a Custom-Engraved Skull to your (Skeleton Type)", "Affix a Custom-Engraved Skull to your (Skeleton Type)",
# cost = Cost.ACTION.value + Cost.ENGRAVED_SKULL.value, cost = Cost.ACTION.value + Cost.ENGRAVED_SKULL.value,
# value = 10000, value = 10000,
# skulls_needed = -1, skulls_needed = -1,
# skulls = 1, skulls = 1,
# exhaustion = 2 exhaustion = 2
# ) )
EYELESS_SKULL = Action( EYELESS_SKULL = Action(
"Affix an Eyeless Skull to your (Skeleton Type)", "Affix an Eyeless Skull to your (Skeleton Type)",
@ -76,13 +76,13 @@ class Skull(Enum):
) )
# Seeking the Name of Mr. Eaten # Seeking the Name of Mr. Eaten
# OWN_SKULL = Action( OWN_SKULL = Action(
# "Duplicate your own skull and affix it here", "Duplicate your own skull and affix it here",
# cost = Cost.ACTION.value + 1000*Cost.BONE_FRAGMENT.value, cost = Cost.ACTION.value + 1000*Cost.BONE_FRAGMENT.value,
# value = -250, value = -250,
# skulls_needed = -1, skulls_needed = -1,
# skulls = 1 skulls = 1
# ) )
PENTAGRAMMIC_SKULL = Action( PENTAGRAMMIC_SKULL = Action(
"Affix a Pentagrammic Skull to your (Skeleton Type)", "Affix a Pentagrammic Skull to your (Skeleton Type)",
@ -139,13 +139,13 @@ class Skull(Enum):
) )
# Licentiate # Licentiate
# VICTIM_SKULL = Action( VICTIM_SKULL = Action(
# "Cap this with a victims skull", "Cap this with a victims skull",
# cost = Cost.ACTION.value, cost = Cost.ACTION.value,
# value = 250, value = 250,
# skulls_needed = -1, skulls_needed = -1,
# skulls = 1 skulls = 1
# ) )
def __str__(self): def __str__(self):
return str(self.value) return str(self.value)

View File

@ -20,15 +20,15 @@ class Torso(Enum):
) )
# Licentiate # Licentiate
# VICTIM_SKELETON = Action( VICTIM_SKELETON = Action(
# "Supply a skeleton of your own", "Supply a skeleton of your own",
# cost = Cost.ACTION.value, cost = Cost.ACTION.value,
# torso_style = 10, torso_style = 10,
# value = 250, value = 250,
# skulls_needed = 1, skulls_needed = 1,
# arms = 2, arms = 2,
# legs = 2 legs = 2
# ) )
HUMAN_RIBCAGE = Action( HUMAN_RIBCAGE = Action(
"Build on the Human Ribcage", "Build on the Human Ribcage",

View File

@ -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)