patternpythonMinor
Zalera calculator
Viewed 0 times
calculatorzalerastackoverflow
Problem
I recently started replaying Final Fantasy XII. One of its optional bosses, Zalera, has, among others, the following mechanics:
His attack sequence is Lv. 2 Sleep, Lv. 3 Disable, Lv. 4 Break and Lv. 5 Reverse, affecting characters whose levels are divisible by 2, 3, 4 or 5, respectively, and then Prime Lv. Death, which affects characters whose levels are a prime number.
Character levels not affected by any of these spells are levels 49, 77, and 91. Levels (under 100) susceptible to Prime Lv. Death are: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, and 97.
To calculate which levels were affected by what attack, I wrote a calculator. It takes 1 to 6 numbers between 1 and 99 inclusive and reports what will affect the player characters with those numbers. Since there's at maximum 6 player characters under the player's control, I limited the amount to a full party size of 6.
Code:
```
from argparse import ArgumentParser
class ZaleraCalculator:
def __init__(self, vars):
self.vars = vars
self.vulnerable = {
2: "Sleep",
3: "Disable",
4: "Break",
5: "Reverse"
}
def isDivisibleByN(self, x, n):
if x % n == 0:
print("Divisible by {}, vulnerable to {}."
.format(n, self.vulnerable[n]))
def isPrime(self, x):
# Primality test, works good enough for values this low
if 2 in [x, pow(2, x, x)]:
print("Prime, vulnerable to Death.")
def generateReport(self):
for i in self.vars:
print("\nCharacter with Lvl: {}".format(i))
if i 99:
print("Arguments not between 1 and 99 inclusive are illegal.")
else:
if i 6:
print(
"Too many arguments were provided. Guest characters are irrelevant.")
elif not args.vars:
print("No arguments were provided.")
else:
calc = ZaleraCalculator(a
His attack sequence is Lv. 2 Sleep, Lv. 3 Disable, Lv. 4 Break and Lv. 5 Reverse, affecting characters whose levels are divisible by 2, 3, 4 or 5, respectively, and then Prime Lv. Death, which affects characters whose levels are a prime number.
Character levels not affected by any of these spells are levels 49, 77, and 91. Levels (under 100) susceptible to Prime Lv. Death are: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, and 97.
To calculate which levels were affected by what attack, I wrote a calculator. It takes 1 to 6 numbers between 1 and 99 inclusive and reports what will affect the player characters with those numbers. Since there's at maximum 6 player characters under the player's control, I limited the amount to a full party size of 6.
Code:
```
from argparse import ArgumentParser
class ZaleraCalculator:
def __init__(self, vars):
self.vars = vars
self.vulnerable = {
2: "Sleep",
3: "Disable",
4: "Break",
5: "Reverse"
}
def isDivisibleByN(self, x, n):
if x % n == 0:
print("Divisible by {}, vulnerable to {}."
.format(n, self.vulnerable[n]))
def isPrime(self, x):
# Primality test, works good enough for values this low
if 2 in [x, pow(2, x, x)]:
print("Prime, vulnerable to Death.")
def generateReport(self):
for i in self.vars:
print("\nCharacter with Lvl: {}".format(i))
if i 99:
print("Arguments not between 1 and 99 inclusive are illegal.")
else:
if i 6:
print(
"Too many arguments were provided. Guest characters are irrelevant.")
elif not args.vars:
print("No arguments were provided.")
else:
calc = ZaleraCalculator(a
Solution
I think that using a class here is overkill. You don't need to maintain any state, you just need to analyze the values provided by the user. Moreover, primality or divisability testing have almost nothing to do with this class, they are just tools it uses and should not be methods.
So I would write these functions, one of them using a constant:
I changed a couple of things:
Now let's look at how to build a better command-line parser.
First off,
Lastly, you may want to make use of the
The
So I would write these functions, one of them using a constant:
SPELLS = {
2: "Sleep",
3: "Disable",
4: "Break",
5: "Reverse",
}
def usable_spells(level):
for spell_level, effect in SPELLS.iteritems():
if level % spell_level == 0:
yield spell_level, effect
def is_prime(x):
# Primality test, works good enough for values this low
return 2 in [x, pow(2, x, x)]
def generate_report(character_levels, zalera_level=40):
for level in character_levels:
print "\nCharacter with Lvl:", level
if level < zalera_level:
print "Character level lower than Zalera's level,", zalera_level
for spell in usable_spells(level):
print "Divisible by {}, vulnerable to {}.".format(*spell)
if is_prime(level):
print "Prime, vulnerable to Death."I changed a couple of things:
- used Python's naming conventions (snake_case);
- removed some calls to
formatbecause you can get the same result using plain oldprint;
- iterated through the spells using a loop rather than manually enumerating all the values (you can force the top-down ordering using
sorted(SPELLS.iteritems(), reverse=True);
- added Zalera's level as a parameter with a default value, to name it;
- removed the check for level correctness as we can ask
argparseto do it for us.
Now let's look at how to build a better command-line parser.
First off,
nargs can take the value '+' to indicate that at least one value is required. Second, instead of printing the error yourself (to stdout!), you can tell argparse that you encountered an error analyzing its results: just call error() on your parser object.Lastly, you may want to make use of the
choices parameter on add_argument as it allow you to pass an iterable of values that will be considered acceptable by argparse; the rest being invalid. In the end, I would write:def parse_command_line():
parser = ArgumentParser(description='FFXII Zalera calculator')
parser.add_argument(
"levels",
type=int,
help="One or more character levels, excluding guest characters.",
nargs="+",
choices=xrange(1, 100),
metavar="CHARACTER_LEVEL")
args = parser.parse_args()
if len(args.levels) > 6:
parser.error(
"Too many arguments were provided. "
"Guest characters are irrelevant.")
return args.levels
if __name__ == "__main__":
generate_report(parse_command_line())The
metavar parameter is here to avoid that argparse prints the whole set of choices on the help message. Also note the way to break too long strings literal: as long as there is a line continuation (implicit such as inside parenthesis or explicit using \ at the end of a line) consecutive string literals will be considered the same string.Code Snippets
SPELLS = {
2: "Sleep",
3: "Disable",
4: "Break",
5: "Reverse",
}
def usable_spells(level):
for spell_level, effect in SPELLS.iteritems():
if level % spell_level == 0:
yield spell_level, effect
def is_prime(x):
# Primality test, works good enough for values this low
return 2 in [x, pow(2, x, x)]
def generate_report(character_levels, zalera_level=40):
for level in character_levels:
print "\nCharacter with Lvl:", level
if level < zalera_level:
print "Character level lower than Zalera's level,", zalera_level
for spell in usable_spells(level):
print "Divisible by {}, vulnerable to {}.".format(*spell)
if is_prime(level):
print "Prime, vulnerable to Death."def parse_command_line():
parser = ArgumentParser(description='FFXII Zalera calculator')
parser.add_argument(
"levels",
type=int,
help="One or more character levels, excluding guest characters.",
nargs="+",
choices=xrange(1, 100),
metavar="CHARACTER_LEVEL")
args = parser.parse_args()
if len(args.levels) > 6:
parser.error(
"Too many arguments were provided. "
"Guest characters are irrelevant.")
return args.levels
if __name__ == "__main__":
generate_report(parse_command_line())Context
StackExchange Code Review Q#146417, answer score: 7
Revisions (0)
No revisions yet.