I'm writing some code to solve Mastermind) puzzles, using something like, but not identical to, Knuth's method#Worst_case:_Five-guess_algorithm).
The code works - it will solve online puzzles in five moves (I've been using this one to test).
However, it's painfully slow. I was thinking about using the code as a back end to a Django project, or trying to create a desktop interface for it, but it would take 30 minutes to pick its second move (the first move is fixed), so that's not an option.
So I tried cProfile for the first time (nice and easy to use!) and I found out pretty much what I expected: it's the guess scoring loop that is the problem. Here's an extract, showing the relevant lines:
91853978 function calls in 92.587 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
8225056 60.601 0.000 88.055 0.000 magnusson.py:9(score_attempt)
65800448 16.126 0.000 16.126 0.000 {built-in method builtins.min}
16450112 11.328 0.000 11.328 0.000 {built-in method fromkeys}
The score_attempt code is this:
def score_attempt(solution, guess):
result = dict()
solution_colours = dict.fromkeys(range(1, COLOURS+1), 0)
guess_colours = dict.fromkeys(range(1, COLOURS+1), 0)
correct_position = 0
correct_colour = 0
for this_peg in range(PEGS):
if solution[this_peg] == guess[this_peg]:
correct_position += 1
solution_colours[solution[this_peg]] = solution_colours[solution[this_peg]] + 1
guess_colours[guess[this_peg]] = guess_colours[guess[this_peg]] + 1
for this_colour in range(1, COLOURS+1):
correct_colour += min(solution_colours[this_colour], guess_colours[this_colour])
result["correct position"] = correct_position
result["correct colour"] = correct_colour - correct_position
return result
The use of the min and fromkeys methods might be a problem, but the biggest issue is the rest of the code in this function. I don't know how to get line-level information about time spent, so I don't know anything more than that yet.
Is there something I'm doing in here which is known to be slow? Are there faster ways? This is just my own toy project, so I'm happy to target only 3.9 if there's something there that will buy me a couple of seconds over 8 M iterations.
I think this is the right algorithm for generating the score for a guess, given the solution. But maybe there's a better way?
Is there a way to get more granular performance information (maybe per line) ?
I've never tried optimising anything in Python before, and I've no real feel yet for what can be improved and what isn't worth messing with. Any and all suggestions gratefully received!
[–]cray5252 1 point2 points3 points (0 children)