+
+def puzzle_matches_one(puzzle, pattern):
+ """Returns True if this puzzle matches the given string (regexp)
+
+ A match will be considered on any of puzzle title, round title,
+ puzzle URL, or solution string. The string can include regular
+ expression syntax. Matching is case insensitive.
+ """
+
+ p = re.compile('.*'+pattern+'.*', re.IGNORECASE)
+
+ if p.match(puzzle['name']):
+ return True
+
+ if 'rounds' in puzzle:
+ for round in puzzle['rounds']:
+ if p.match(round):
+ return True
+
+ if p.match(puzzle['url']):
+ return True
+
+ for solution in puzzle['solution']:
+ if p.match(solution):
+ return True
+
+ return False
+
+def puzzle_matches_all(puzzle, patterns):
+ """Returns True if this puzzle matches all of the given list of patterns
+
+ A match will be considered on any of puzzle title, round title,
+ puzzle URL, or solution string. All patterns must match the puzzle
+ somewhere, (that is, there is an implicit logical AND between
+ patterns). Patterns can include regular expression
+ syntax. Matching is case insensitive.
+ """
+
+ for pattern in patterns:
+ if not puzzle_matches_one(puzzle, pattern):
+ return False
+
+ return True