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 < 1 or i > 99:
print("Arguments not between 1 and 99 inclusive are illegal.")
else:
if i < 40:
print("Character level lower than Zalera's level, 40.")
self.isDivisibleByN(i, 5)
self.isDivisibleByN(i, 4)
self.isDivisibleByN(i, 3)
self.isDivisibleByN(i, 2)
self.isPrime(i)
def main():
parser = ArgumentParser(description='FFXII Zalera calculator')
parser.add_argument(
'vars',
type=int,
help="One or more character levels, excluding guest characters.",
nargs='*'
)
args = parser.parse_args()
if len(args.vars) > 6:
print(
"Too many arguments were provided. Guest characters are irrelevant.")
elif not args.vars:
print("No arguments were provided.")
else:
calc = ZaleraCalculator(args.vars)
calc.generateReport()
if __name__ == '__main__':
main()
Example usage:
python ffxii.py 38 45 47 41 49 60
Example output:
Character with Lvl: 38
Character level lower than Zalera's level, 40.
Divisible by 2, vulnerable to Sleep.
Character with Lvl: 45
Divisible by 5, vulnerable to Reverse.
Divisible by 3, vulnerable to Disable.
Character with Lvl: 47
Prime, vulnerable to Death.
Character with Lvl: 41
Prime, vulnerable to Death.
Character with Lvl: 49
Character with Lvl: 60
Divisible by 5, vulnerable to Reverse.
Divisible by 4, vulnerable to Break.
Divisible by 3, vulnerable to Disable.
Divisible by 2, vulnerable to Sleep.
Line 51 violates PEP8, but all alternatives I could think of wouldn't be any better. There's a couple of magic numbers in the code, notably 6
, 40
and 99
, but since those are never going to change I'm not inclined to put them into pseudo-constants.
The program is 'dumb', it doesn't remember much. So regardless of whether a number is divisible by 4, it will also check whether it's divisible by 2. It's written in a straight-forward way and can probably be written in a more generic manner. However, I couldn't figure out how without overly complicating the program. A smarter approach with a Report
class containing all results would probably be better. Prime not needing it's own output method would probably be better too, but I couldn't figure out how to merge it into the dictionary I use for the other vulnerabilities.
Since the player can't reach a level higher than 99, values above are invalid. Because 99 is a relatively small number, a cheap prime test has been used instead of a Sieve.
Any and all feedback is appreciated.