Exemplo n.º 1
0
def generate(num, prompt_default=True):
    """Generates Python file for a problem."""
    p = Problem(num)

    problem_text = p.text

    msg = "Generate file for problem %i?" % num
    click.confirm(msg, default=prompt_default, abort=True)

    # Allow skipped problem files to be recreated
    if p.glob:
        filename = str(p.file)
        msg = '"{}" already exists. Overwrite?'.format(filename)
        click.confirm(click.style(msg, fg='red'), abort=True)
    else:
        # Try to keep prefix consistent with existing files
        previous_file = Problem(num - 1).file
        prefix = previous_file.prefix if previous_file else ''
        filename = p.filename(prefix=prefix)

    header = 'Project Euler Problem %i' % num
    divider = '=' * len(header)
    text = '\n'.join([header, divider, '', problem_text])
    content = '\n'.join(['"""', text, '"""'])

    with open(filename, 'w') as f:
        f.write(content + '\n\n\n')

    click.secho('Successfully created "{}".'.format(filename), fg='green')

    # Copy over problem resources if required
    if p.resources:
        p.copy_resources()
Exemplo n.º 2
0
def verify(num, filename=None, exit=True):
    """Verifies the solution to a problem."""
    p = Problem(num)

    filename = filename or p.filename()

    if not os.path.isfile(filename):
        # Attempt to verify the first problem file matched by glob
        if p.glob:
            filename = str(p.file)
        else:
            click.secho('No file found for problem %i.' % p.num, fg='red')
            sys.exit(1)

    solution = p.solution
    click.echo('Checking "{}" against solution: '.format(filename), nl=False)

    cmd = (sys.executable or 'python', filename)
    start = clock()
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    stdout = proc.communicate()[0]
    end = clock()
    time_info = format_time(start, end)

    # Return value of anything other than 0 indicates an error
    if proc.poll() != 0:
        click.secho('Error calling "{}".'.format(filename), fg='red')
        click.secho(time_info, fg='cyan')

        # Return None if option is not --verify-all, otherwise exit
        return sys.exit(1) if exit else None

    # Decode output if returned as bytes (Python 3)
    if isinstance(stdout, bytes):
        output = stdout.decode('ascii')

    # Split output lines into array; make empty output more readable
    output_lines = output.splitlines() if output else ['[no output]']

    # If output is multi-lined, print the first line of the output on a
    # separate line from the "checking against solution" message, and
    # skip the solution check (multi-line solution won't be correct)
    if len(output_lines) > 1:
        is_correct = False
        click.echo()  # force output to start on next line
        click.secho('\n'.join(output_lines), bold=True, fg='red')
    else:
        is_correct = output_lines[0] == solution
        fg_colour = 'green' if is_correct else 'red'
        click.secho(output_lines[0], bold=True, fg=fg_colour)

    click.secho(time_info, fg='cyan')

    # Remove any suffix from the filename if its solution is correct
    if is_correct:
        p.file.change_suffix('')

    # Exit here if answer was incorrect, otherwise return is_correct value
    return sys.exit(1) if exit and not is_correct else is_correct
Exemplo n.º 3
0
def verify(num, filename=None, exit=True):
    """Verifies the solution to a problem."""
    p = Problem(num)

    filename = filename or p.filename()

    if not os.path.isfile(filename):
        # Attempt to verify the first problem file matched by glob
        if p.glob:
            filename = str(p.file)
        else:
            click.secho('No file found for problem %i.' % p.num, fg='red')
            sys.exit(1)

    solution = p.solution
    click.echo('Checking "{}" against solution: '.format(filename), nl=False)

    cmd = (sys.executable or 'python', filename)
    start = clock()
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    stdout = proc.communicate()[0]
    end = clock()
    time_info = format_time(start, end)

    # Return value of anything other than 0 indicates an error
    if proc.poll() != 0:
        click.secho('Error calling "{}".'.format(filename), fg='red')
        click.secho(time_info, fg='cyan')

        # Return None if option is not --verify-all, otherwise exit
        return sys.exit(1) if exit else None

    # Decode output if returned as bytes (Python 3)
    if isinstance(stdout, bytes):
        output = stdout.decode('ascii')

    # Split output lines into array; make empty output more readable
    output_lines = output.splitlines() if output else ['[no output]']

    # If output is multi-lined, print the first line of the output on a
    # separate line from the "checking against solution" message, and
    # skip the solution check (multi-line solution won't be correct)
    if len(output_lines) > 1:
        is_correct = False
        click.echo()  # force output to start on next line
        click.secho('\n'.join(output_lines), bold=True, fg='red')
    else:
        is_correct = output_lines[0] == solution
        fg_colour = 'green' if is_correct else 'red'
        click.secho(output_lines[0], bold=True, fg=fg_colour)

    click.secho(time_info, fg='cyan')

    # Remove any suffix from the filename if its solution is correct
    if is_correct:
        p.file.change_suffix('')

    # Exit here if answer was incorrect, otherwise return is_correct value
    return sys.exit(1) if exit and not is_correct else is_correct
Exemplo n.º 4
0
def generateFile(problem, filename=None, content=None, correct=False):
    """
    Uses Problem.solution to generate a problem file. The correct
    argument controls whether the generated file is correct or not.
    """
    p = Problem(problem)
    filename = filename or p.filename()

    with open(filename, 'w') as f:
        if correct:
            f.write('print({})'.format(p.solution))
        elif content:
            f.write(content)
Exemplo n.º 5
0
    def test_expected_problem(self):
        """Check that problem #1 returns the correct problem text"""
        problem_one = textwrap.dedent("""
            If we list all the natural numbers below 10 that are multiples of 3 or 5,
            we get 3, 5, 6 and 9. The sum of these multiples is 23.

            Find the sum of all the multiples of 3 or 5 below 1000.
            """)

        self.assertEqual(problem_one.strip(), Problem(1).text)
Exemplo n.º 6
0
def generateFile(problem, filename=None, correct=False):
    """
    Uses Problem().solution to generate a problem file. The correct
    argument controls whether the generated file is correct or not.
    """
    p = Problem(problem)
    filename = filename or p.filename

    with open(filename, 'a') as file:
        if correct:
            file.write('print({0})\n'.format(p.solution))
Exemplo n.º 7
0
def generate(num, prompt_default=True):
    """Generates Python file for a problem."""
    p = Problem(num)

    problem_text = p.text

    msg = "Generate file for problem %i?" % num
    click.confirm(msg, default=prompt_default, abort=True)

    # Allow skipped problem files to be recreated
    if p.glob:
        filename = str(p.file)
        msg = '"{}" already exists. Overwrite?'.format(filename)
        click.confirm(click.style(msg, fg='red'), abort=True)
    else:
        # Try to keep prefix consistent with existing files
        previous_file = Problem(num - 1).file
        prefix = previous_file.prefix if previous_file else ''
        filename = p.filename(prefix=prefix)

    header = 'Project Euler Problem %i' % num
    divider = '=' * len(header)
    text = '\n'.join([header, divider, '', problem_text])
    content = '\n'.join(['"""', text, '"""'])

    with open(filename, 'w') as f:
        f.write(content + '\n\n\n')

    click.secho('Successfully created "{}".'.format(filename), fg='green')

    # Copy over problem resources if required
    if p.resources:
        p.copy_resources()
Exemplo n.º 8
0
def main(option, problem):
    """Python-based Project Euler command line tool."""
    # No problem given (or given option ignores the problem argument)
    if problem == 0 or option in (skip, verify_all):
        # Determine the highest problem number in the current directory
        files = problem_glob()
        problem = max(int(file[:3]) for file in files) if files else 0

        # No Project Euler files in current directory (no glob results)
        if problem == 0:
            # Generate the first problem file if option is appropriate
            if option not in (cheat, preview, verify_all):
                msg = "No Project Euler files found in the current directory."
                click.echo(msg)
                option = generate

            # Set problem number to 1
            problem = 1

        # --preview and no problem; preview the next problem
        elif option is preview:
            problem += 1

        # No option and no problem; generate next file if answer is
        # correct (verify() will exit if the solution is incorrect)
        if option is None:
            verify(Problem(problem))
            problem += 1
            option = generate

    # Problem given but no option; decide between generate and verify
    elif option is None:
        option = verify if any(Problem(problem).iglob) else generate

    # Execute function based on option (pass Problem object as argument)
    option(Problem(problem))
    sys.exit(0)
Exemplo n.º 9
0
    def test_problem_format(self):
        """
        Ensure each parsed problem only contains one problem (that one problem
        does not "bleed" into the next one due to an issue with line breaks)
        """

        # Determine largest problem in problems.txt
        problems_file = os.path.join(EULER_DATA, 'problems.txt')
        with open(problems_file) as f:
            for line in f:
                if line.startswith('Problem '):
                    largest_problem = line.split(' ')[1]

        for problem in range(1, int(largest_problem) + 1):
            problemText = Problem(problem).text

            msg = "Error encountered when parsing problem {}.".format(problem)

            self.assertFalse('========='in problemText, msg=msg)
            self.assertFalse('\n\n\n' in problemText, msg=msg)
Exemplo n.º 10
0
    def test_problem_format(self):
        """
        Ensure each parsed problem only contains one problem (that one problem
        does not "bleed" into the next one due to an issue with line breaks)
        """

        # Determine largest problem in problems.txt
        problemsFile = os.path.join(os.path.dirname(__file__), 'data',
                                    'problems.txt')
        with open(problemsFile) as file:
            largest = ''
            for line in file:
                if line.startswith('Problem'):
                    largest = line.strip()

            largestProblem = int(largest.split(' ')[1])

        for problem in range(1, largestProblem + 1):
            problemText = Problem(problem).text

            msg = "Error encountered when parsing problem {0}.".format(problem)

            self.assertFalse('=========' in problemText, msg=msg)
            self.assertFalse('\n\n\n' in problemText, msg=msg)
Exemplo n.º 11
0
 def test_filename_format(self):
     """Check that filenames are being formatted correctly"""
     self.assertEqual(Problem(1).filename(), "001.py")
     self.assertEqual(Problem(10).filename(), "010.py")
     self.assertEqual(Problem(100).filename(), "100.py")
Exemplo n.º 12
0
def verify_all(current_p):
    """
    Verifies all problem files in the current directory and
    prints an overview of the status of each problem.
    """

    # Define various problem statuses
    statuses = (
        ('correct', 'C', 'green'),
        ('incorrect', 'I', 'red'),
        ('error', 'E', 'yellow'),
        ('skipped', 'S', 'cyan'),
        ('missing', '.', 'white'),
    )

    status = OrderedDict(
        (key, click.style(symbol, fg=colour, bold=True))
        for key, symbol, colour in statuses
    )

    overview = {}

    # Search through problem files using glob module
    for filename in glob.glob('[0-9][0-9][0-9]*.py'):
        p = Problem(int(filename[:3]))

        # Catch KeyboardInterrupt during verification to allow the user
        # to skip the verification of a problem if it takes too long
        try:
            is_correct = verify(p, filename=filename, exit=False)
        except KeyboardInterrupt:
            overview[p.num] = status['skipped']
        else:
            if is_correct is None: # error was returned by problem file
                overview[p.num] = status['error']
            elif is_correct:
                overview[p.num] = status['correct']
            elif not is_correct:
                overview[p.num] = status['incorrect']

                # Attempt to add "skipped" suffix to the filename if the
                # problem file is not the current problem. This is useful
                # when the --verify-all is used in a directory containing
                # files generated pre-v1.1 (before files with suffixes)
                if p.num != current_p.num:
                    rename_file(filename, p.suf_name('skipped'))

        # Separate each verification with a newline
        click.echo()

    # No Project Euler files in the current directory
    if not overview:
        click.echo("No Project Euler files found in the current directory.")
        sys.exit(1)

    # Print overview of the status of each problem
    legend = ', '.join('{0} = {1}'.format(v, k) for k, v in status.items())

    click.echo('-' * 63)
    click.echo(legend + '\n')

    # Rows needed for overview is based on the current problem number
    num_of_rows = (current_p.num + 19) // 20

    for row in range(1, num_of_rows + 1):
        low, high = (row * 20) - 19, (row * 20)
        click.echo("Problems {0:03d}-{1:03d}: ".format(low, high), nl=False)

        for problem in range(low, high + 1):
            # Add missing status to problems with no corresponding file
            status = overview[problem] if problem in overview else '.'

            # Separate problem indicators into groups of 5
            spacer = '   ' if (problem % 5 == 0) else ' '

            # Start a new line at the end of each row
            click.secho(status + spacer, nl=(problem % 20 == 0))

    click.echo()
Exemplo n.º 13
0
def verify_all(current_p):
    """
    Verifies all problem files in the current directory and
    prints an overview of the status of each problem.
    """

    overview = {}

    # Search through problem files using glob module
    for filename in glob.glob("[0-9][0-9][0-9]*.py"):
        p = Problem(int(filename[:3]))

        # Catch KeyboardInterrupt during verification to allow the user
        # to skip the verification of a problem if it takes too long
        try:
            is_correct = verify(p, filename=filename, exit=False)
        except KeyboardInterrupt:
            overview[p.num] = click.style("S", fg="cyan")
        else:
            if is_correct is None:  # error was returned by problem file
                overview[p.num] = click.style("E", fg="yellow")
            elif is_correct:
                overview[p.num] = click.style("C", fg="green")
            elif not is_correct:
                overview[p.num] = click.style("I", fg="red")

                # Attempt to add "skipped" suffix to the filename if the
                # problem file is not the current problem. This is useful
                # when the --verify-all is used in a directory containing
                # files generated pre-v1.1 (before files with suffixes)
                if p.num != current_p.num:
                    skipped_name = p.suf_name("skipped")
                    rename_file(filename, skipped_name)

        # Separate each verification with a newline
        click.echo()

    # No Project Euler files in the current directory
    if not overview:
        click.echo("No Project Euler files found in the current directory.")
        sys.exit(1)

    # Print overview of the status of each problem
    click.echo("-" * 63)

    legend = ", ".join(
        "{0} = {1}".format(click.style(symbol, bold=True, fg=colour), name)
        for symbol, name, colour in (
            ("C", "correct", "green"),
            ("I", "incorrect", "red"),
            ("E", "error", "yellow"),
            ("S", "skipped", "cyan"),
            (".", "missing", "white"),
        )
    )

    click.echo(legend + "\n")

    # Rows needed for overview is based on the current problem number
    num_of_rows = (current_p.num + 19) // 20

    for row in range(1, num_of_rows + 1):
        low, high = (row * 20) - 19, (row * 20)
        click.echo("Problems {0:03d}-{1:03d}: ".format(low, high), nl=False)

        for problem in range(low, high + 1):
            # Add missing status to problems with no problem file
            status = overview[problem] if problem in overview else "."
            click.secho(status, bold=True, nl=False)

            # Separate problem indicators into groups of 5
            click.echo("   " if problem % 5 == 0 else " ", nl=False)

        # Start a new line at the end of each row
        click.echo()

    click.echo()
Exemplo n.º 14
0
def generate(num, prompt_default=True):
    """Generates Python file for a problem."""

    def wrap(text, wrap_len):
        """Wraps text at given character width.
        Args:
            text (str): text to wrap
            wrap_len (int): character width at which to wrap
        Returns:
            String with newlines inserted as appropriate
        """
        if len(text) > wrap_len:
            words = iter(text.split())
            wrapped_lines = []
            wrapped_line = next(words) # assume 1st word is shorter than wrap_len
            for word in words:
                if len(wrapped_line) + len(word) < wrap_len: # < rather than <= to account for 1 char space
                    wrapped_line = ' '.join([wrapped_line, word])
                else:
                    wrapped_lines.append(wrapped_line)
                    wrapped_line = word
            wrapped_lines.append(wrapped_line)
            return '\n'.join(wrapped_lines)
        else:
            return text

    p = Problem(num)

    problem_text = p.text

    msg = "Generate file for problem %i?" % num
    click.confirm(msg, default=prompt_default, abort=True)

    # Allow skipped problem files to be recreated
    if p.glob:
        filename = str(p.file)
        msg = '"{}" already exists. Overwrite?'.format(filename)
        click.confirm(click.style(msg, fg='red'), abort=True)
    else:
        # Try to keep prefix consistent with existing files
        previous_file = Problem(num - 1).file
        prefix = previous_file.prefix if previous_file else ''
        filename = p.filename(prefix=prefix)

    header = 'Project Euler Problem {}\n\n{}'.format(num, wrap(p.title, 76))
    divider = '=' * len(header.split('\n')[-1])
    text = '\n'.join([header, divider, '', problem_text])
    content = '\n'.join([
        '#!/usr/bin/env python',
        '# -*- coding: utf-8 -*-',
        '',
        '"""',
        text,
        '"""',
        '',
        'def problem_{}():'.format(num),
        '    response = 0',
        '    print(response)',
        '',
        "if __name__ == '__main__':",
        '    problem_{}()'.format(num)
    ])

    with open(filename, 'w') as f:
        f.write(content)

    click.secho('Successfully created "{}".'.format(filename), fg='green')

    # Copy over problem resources if required
    if p.resources:
        p.copy_resources()
Exemplo n.º 15
0
def skip(num):
    """Generates Python file for the next problem."""
    click.echo("Current problem is problem %i." % num)
    generate(num + 1, prompt_default=False)
    Problem(num).file.change_suffix('-skipped')
Exemplo n.º 16
0
def preview(num):
    """Prints the text of a problem."""
    # Define problem_text before echoing in case problem does not exist
    problem_text = Problem(num).text
    click.secho("Project Euler Problem %i" % num, bold=True)
    click.echo(problem_text)
Exemplo n.º 17
0
def cheat(num):
    """View the answer to a problem."""
    # Define solution before echoing in case solution does not exist
    solution = click.style(Problem(num).solution, bold=True)
    click.confirm("View answer to problem %i?" % num, abort=True)
    click.echo("The answer to problem {} is {}.".format(num, solution))
Exemplo n.º 18
0
def verify_all(current_p):
    """
    Verifies all problem files in the current directory and
    prints an overview of the status of each problem.
    """

    # Define various problem statuses
    keys = ('correct', 'incorrect', 'error', 'skipped', 'missing')
    symbols = ('C', 'I', 'E', 'S', '.')
    colours = ('green', 'red', 'yellow', 'cyan', 'white')

    status = OrderedDict(
        (key, click.style(symbol, fg=colour, bold=True))
        for key, symbol, colour in zip(keys, symbols, colours))

    overview = {}

    # Search through problem files using glob module
    files = problem_glob()

    # No Project Euler files in the current directory
    if not files:
        click.echo("No Project Euler files found in the current directory.")
        sys.exit(1)

    for file in files:
        p = Problem(int(file[:3]))

        # Catch KeyboardInterrupt during verification to allow the user
        # to skip the verification of a problem if it takes too long
        try:
            is_correct = verify(p, filename=file, exit=False)
        except KeyboardInterrupt:
            overview[p.num] = status['skipped']
        else:
            if is_correct is None:  # error was returned by problem file
                overview[p.num] = status['error']
            elif is_correct:
                overview[p.num] = status['correct']
            elif not is_correct:
                overview[p.num] = status['incorrect']

                # Attempt to add "skipped" suffix to the filename if the
                # problem file is not the current problem. This is useful
                # when the --verify-all is used in a directory containing
                # files generated pre-v1.1 (before files with suffixes)
                if p.num != current_p.num:
                    rename_file(file, p.suf_name('skipped'))

        # Separate each verification with a newline
        click.echo()

    # Print overview of the status of each problem
    legend = ', '.join('{0} = {1}'.format(v, k) for k, v in status.items())

    click.echo('-' * 63)
    click.echo(legend + '\n')

    # Rows needed for overview is based on the current problem number
    num_of_rows = (current_p.num + 19) // 20

    for row in range(1, num_of_rows + 1):
        low, high = (row * 20) - 19, (row * 20)
        click.echo("Problems {0:03d}-{1:03d}: ".format(low, high), nl=False)

        for problem in range(low, high + 1):
            # Add missing status to problems with no corresponding file
            status = overview[problem] if problem in overview else '.'

            # Separate problem indicators into groups of 5
            spacer = '   ' if (problem % 5 == 0) else ' '

            # Start a new line at the end of each row
            click.secho(status + spacer, nl=(problem % 20 == 0))

    click.echo()
Exemplo n.º 19
0
def skip(p):
    """Generates Python file for the next problem."""
    click.echo("Current problem is problem %i." % p.num)
    next_p = Problem(p.num + 1)
    generate(next_p, prompt_default=False)
    rename_file(p.filename, p.suf_name('skipped'))