Skip to main content
deleted 131 characters in body
Source Link
import sys
from collections import namedtuple


Statistics = namedtuple('Statistics', 'strength, perception, endurance, charisma, intelligence, agility, luck')


def ask_for_value_in_list(prompt_message, *authorized_values):
    while True:
        value = input(prompt_message)
        word = ''.join(filter(str.isalpha, value)).lower()
        if word in authorized_values:
            return word
        if word == 'exit':
            raise ValueError('asked to exit')
        print('Only one of the following values is allowed:', authorized_values, file=sys.stderr)


def ask_for_integer(prompt_message):
    while True:
        value = input(prompt_message)
        try:
            return int(value)
        except ValueError:
            if value.lower() == 'stop':
                raise
            print('An integer is required!', file=sys.stderr)


def ask_for_stats(stat_name, max_points):
    message = '[{} points left] {} >>> '.format(max_points, stat_name)
    while True:
        value = ask_for_integer(message)
        if 1 <= value <= min(10, max_points):
            return value
        print('The provided value is out of bounds, try again.')


def build_statistics(max_points=26):
    statistics = dict.fromkeys(Statistics._fields)
    for statistic in statistics:
        if not max_points:
            raise ValueError('character exhausted statistics points')
        value = ask_for_stats(statistic, max_points)
        max_points -= value
        statistics[statistic] = value
    return Statistics(**statistics)


def main():
    try:
        stats = build_statistics()
    except ValueError:
        print('Could not build statistics, aborting.', file=sys.stderr)
    else:
        print('Statistics collected:', stats)


if __name__ == '__main__':
    main()

I didn't put back the check for the strength being at least 10 points, but it should be easy enough to add it back if you really need it.

import sys
from collections import namedtuple


Statistics = namedtuple('Statistics', 'strength, perception, endurance, charisma, intelligence, agility, luck')


def ask_for_value_in_list(prompt_message, *authorized_values):
    while True:
        value = input(prompt_message)
        word = ''.join(filter(str.isalpha, value)).lower()
        if word in authorized_values:
            return word
        if word == 'exit':
            raise ValueError('asked to exit')
        print('Only one of the following values is allowed:', authorized_values, file=sys.stderr)


def ask_for_integer(prompt_message):
    while True:
        value = input(prompt_message)
        try:
            return int(value)
        except ValueError:
            if value.lower() == 'stop':
                raise
            print('An integer is required!', file=sys.stderr)


def ask_for_stats(stat_name, max_points):
    message = '[{} points left] {} >>> '.format(max_points, stat_name)
    while True:
        value = ask_for_integer(message)
        if 1 <= value <= max_points:
            return value
        print('The provided value is out of bounds, try again.')


def build_statistics(max_points=26):
    statistics = dict.fromkeys(Statistics._fields)
    for statistic in statistics:
        if not max_points:
            raise ValueError('character exhausted statistics points')
        value = ask_for_stats(statistic, max_points)
        max_points -= value
        statistics[statistic] = value
    return Statistics(**statistics)


def main():
    try:
        stats = build_statistics()
    except ValueError:
        print('Could not build statistics, aborting.', file=sys.stderr)
    else:
        print('Statistics collected:', stats)


if __name__ == '__main__':
    main()

I didn't put back the check for the strength being at least 10 points, but it should be easy enough to add it back if you really need it.

import sys
from collections import namedtuple


Statistics = namedtuple('Statistics', 'strength, perception, endurance, charisma, intelligence, agility, luck')


def ask_for_value_in_list(prompt_message, *authorized_values):
    while True:
        value = input(prompt_message)
        word = ''.join(filter(str.isalpha, value)).lower()
        if word in authorized_values:
            return word
        if word == 'exit':
            raise ValueError('asked to exit')
        print('Only one of the following values is allowed:', authorized_values, file=sys.stderr)


def ask_for_integer(prompt_message):
    while True:
        value = input(prompt_message)
        try:
            return int(value)
        except ValueError:
            if value.lower() == 'stop':
                raise
            print('An integer is required!', file=sys.stderr)


def ask_for_stats(stat_name, max_points):
    message = '[{} points left] {} >>> '.format(max_points, stat_name)
    while True:
        value = ask_for_integer(message)
        if 1 <= value <= min(10, max_points):
            return value
        print('The provided value is out of bounds, try again.')


def build_statistics(max_points=26):
    statistics = dict.fromkeys(Statistics._fields)
    for statistic in statistics:
        if not max_points:
            raise ValueError('character exhausted statistics points')
        value = ask_for_stats(statistic, max_points)
        max_points -= value
        statistics[statistic] = value
    return Statistics(**statistics)


def main():
    try:
        stats = build_statistics()
    except ValueError:
        print('Could not build statistics, aborting.', file=sys.stderr)
    else:
        print('Statistics collected:', stats)


if __name__ == '__main__':
    main()
Source Link

First things first: your code reads nowhere near Python code. You should read into PEP8 to properly format it.

Second, you should strive to avoid global as hard as you can as it easily make the code error-prone, hard to reason about, and hard to test. In your case, of the two global variables you use, stopMessage is read-only so it is much like a constant where the use of global is unnecessary. The second one, statsMax is trickier. For your simple use-case, it seems to work, but if you were to create a second player, statsMax would be 0 and it would be impossible. Instead, you should make it a parameter of some sort: the __init__ method of the stats class seems a good starting point:

class stats(statsStore):
    def __init__(self, max_points=26):
        ...
        self.max_points = max_points

There is also the fragmentation of the source code into two files. Even though it is good to consider at some point to be able to group related functionnalities under a namespace, you have too few functionnalities for it to be usefull. It even lead to the duplication of the stopMessage. Keep it simple for now with a single file.

The design of the stats class is also very fragile: the statsFunc method is not meant to be called outside of statsList and statsList itself have to be called for the stats object to be considered initialized. This is the role of a simple function, maybe a classmethod on the statsStore class itself. Moreover, statsStore is only there to be a collection of values: better use a namedtuple here.

One more thing that intrigues me is how you use some parameters. For instance stat in statsFunc or Check in checkIn_List. It is like you want to declare a variable before use, but you never do anything with the value passed as parameter. You don't need that. Just assign a value to a new name whenever you need a new variable, and don't pass unnecessary parameters around.

Lastly, instead of putting tests at the end of the file, you should wrap them under an if __name__ == '__main__' test so they don't get executed when you import your file (in a Python shell to test it, or in an other module).


Proposed improvements:

import sys
from collections import namedtuple


Statistics = namedtuple('Statistics', 'strength, perception, endurance, charisma, intelligence, agility, luck')


def ask_for_value_in_list(prompt_message, *authorized_values):
    while True:
        value = input(prompt_message)
        word = ''.join(filter(str.isalpha, value)).lower()
        if word in authorized_values:
            return word
        if word == 'exit':
            raise ValueError('asked to exit')
        print('Only one of the following values is allowed:', authorized_values, file=sys.stderr)


def ask_for_integer(prompt_message):
    while True:
        value = input(prompt_message)
        try:
            return int(value)
        except ValueError:
            if value.lower() == 'stop':
                raise
            print('An integer is required!', file=sys.stderr)


def ask_for_stats(stat_name, max_points):
    message = '[{} points left] {} >>> '.format(max_points, stat_name)
    while True:
        value = ask_for_integer(message)
        if 1 <= value <= max_points:
            return value
        print('The provided value is out of bounds, try again.')


def build_statistics(max_points=26):
    statistics = dict.fromkeys(Statistics._fields)
    for statistic in statistics:
        if not max_points:
            raise ValueError('character exhausted statistics points')
        value = ask_for_stats(statistic, max_points)
        max_points -= value
        statistics[statistic] = value
    return Statistics(**statistics)


def main():
    try:
        stats = build_statistics()
    except ValueError:
        print('Could not build statistics, aborting.', file=sys.stderr)
    else:
        print('Statistics collected:', stats)


if __name__ == '__main__':
    main()

I didn't put back the check for the strength being at least 10 points, but it should be easy enough to add it back if you really need it.