def test_read_puz_locked_puzzle(self): puz_object = puz.read('fixtures/puz/nyt_locked.puz') puzzle = crossword.from_puz(puz_object) for x, y in puzzle.cells: self.assertEqual(puzzle[x, y].cell, None) self.assertEqual(puzzle[0, 0].solution, 'B') self.assertEqual(puzzle[14, 14].solution, 'N')
def test_to_ipuz_only_include_ipuz_specific_data(self): puz_object = puz.read('fixtures/puz/chronicle_20140815.puz') puzzle = crossword.from_puz(puz_object) ipuz_dict = crossword.to_ipuz(puzzle) self.assertNotIn('puzzletype', ipuz_dict) self.assertNotIn('fileversion', ipuz_dict) self.assertNotIn('extensions', ipuz_dict)
def test_read_and_write_round_trip(self): puz_object = puz.read('fixtures/puz/chronicle_20140815.puz') puzzle = crossword.from_puz(puz_object) new_puz_object = crossword.to_puz(puzzle) for attr in dir(puz_object): if not callable(getattr(puz_object, attr)): self.assertEqual(getattr(puz_object, attr), getattr(new_puz_object, attr))
def test_read_puz_to_crossword(self): puz_object = puz.read('fixtures/puz/chronicle_20140815.puz') puzzle = crossword.from_puz(puz_object) self.assertEqual(puzzle.meta.title, 'Que Pasa? - August 15, 2014') self.assertEqual(puzzle.meta.creator, 'by Ian Livengood / Edited by Brad Wilber ') self.assertEqual(puzzle.block, '.') self.assertEqual(puzzle.clues.across[67], 'Jupiter, but not Zeus') self.assertEqual(puzzle.clues.down[60], 'Cadenza automaker')
def test_all_fixtures(self): for f in glob.glob('../puzfiles/*.puz'): puz_obj = puz.read(f) loaded_obj = crossword.to_puz(crossword.from_puz(puz_obj)) for attr in dir(puz_obj): if not callable(getattr(puz_obj, attr)): eq = getattr(puz_obj, attr) == getattr(loaded_obj, attr) if not eq: print(attr, eq)
def test_read_and_write_round_trip(self): puz_object = puz.read('fixtures/puz/chronicle_20140815.puz') puzzle = crossword.from_puz(puz_object) new_puz_object = crossword.to_puz(puzzle) for attr in dir(puz_object): if not callable(getattr(puz_object, attr)): self.assertEqual( getattr(puz_object, attr), getattr(new_puz_object, attr) )
def test_read_puz_to_crossword(self): puz_object = puz.read('fixtures/puz/chronicle_20140815.puz') puzzle = crossword.from_puz(puz_object) self.assertEqual( puzzle.meta.title, 'Que Pasa? - August 15, 2014' ) self.assertEqual( puzzle.meta.creator, 'by Ian Livengood / Edited by Brad Wilber ' ) self.assertEqual(puzzle.block, '.') self.assertEqual(puzzle.clues.across[67], 'Jupiter, but not Zeus') self.assertEqual(puzzle.clues.down[60], 'Cadenza automaker')
def parse_puz(contents, filename): rebus_shorthands = list("⚷⚳♇♆⛢♄♃♂♁♀☿♹♸♷♶♵♴♳⅘⅗⅖⅕♚♛♜♝♞♟⚅⚄⚃⚂⚁⚀♣♦♥♠+&%$@?*zyxwvutsrqponmlkjihgfedcba0987654321") try: puzobj = puz.load(contents) puzzle = crossword.from_puz(puzobj) except puz.PuzzleFormatError as e: emsg = e.message if "<html>" in contents.decode('utf-8').lower(): emsg += " (looks like html)" raise xdfile.PuzzleParseError(emsg) grid_dict = dict(list(zip(string.ascii_uppercase, string.ascii_uppercase))) xd = xdfile.xdfile('', filename) xd.set_header("Author", puzobj.author) xd.set_header("Copyright", puzobj.copyright) xd.set_header("Notes", puzobj.notes) xd.set_header("Postscript", "".join(x for x in puzobj.postscript if ord(x) >= ord(' '))) xd.set_header("Preamble", puzobj.preamble) xd.set_header("Title", puzobj.title) used_rebuses = {} # [puz_rebus_gridvalue_as_string] -> our_rebus_gridvalue rebus = {} # [our_rebus_gridvalue] -> full_cell r = puzobj.rebus() if r.has_rebus(): grbs = puzobj.extensions[b"GRBS"] if sum(x for x in grbs if x != 0) > 0: # check for an actual rebus for pair in puzobj.extensions[b"RTBL"].decode("cp1252").split(";"): pair = pair.strip() if not pair: continue key, value = pair.split(":") rebuskey = rebus_shorthands.pop() used_rebuses[key] = rebuskey rebus[rebuskey] = decode(value) rebustr = xdfile.REBUS_SEP.join([("%s=%s" % (k, v)) for k, v in sorted(rebus.items())]) xd.set_header("Rebus", rebustr) for r, row in enumerate(puzzle): rowstr = "" for c, cell in enumerate(row): if puzzle.block is None and cell.solution == '.': rowstr += xdfile.BLOCK_CHAR elif cell.solution == puzzle.block: rowstr += xdfile.BLOCK_CHAR elif cell.solution == ':': rowstr += xdfile.OPEN_CHAR elif cell == puzzle.empty: rowstr += xdfile.UNKNOWN_CHAR else: n = r * puzobj.width + c reb = puzobj.rebus() if reb.has_rebus() and n in reb.get_rebus_squares(): ch = str(reb.table[n] - 1) rowstr += used_rebuses[ch] cell.solution = rebus[used_rebuses[ch]] else: ch = cell.solution if ch not in grid_dict: if ch in rebus_shorthands: cellch = ch rebus_shorthands.remove(ch) warn("%s: unknown grid character '%s', assuming rebus of itself" % (filename, ch)) else: cellch = rebus_shorthands.pop() warn("%s: unknown grid character '%s', assuming rebus (as '%s')" % (filename, ch, cellch)) xd.set_header("Rebus", xd.get_header("Rebus") + " %s=%s" % (cellch, ch)) grid_dict[ch] = cellch rowstr += grid_dict[ch] xd.grid.append(rowstr) assert xd.size() == (puzzle.width, puzzle.height), "non-matching grid sizes" # clues answers = {} for posdir, posnum, answer in xd.iteranswers(): answers[posdir[0] + str(posnum)] = answer try: for number, clue in puzzle.clues.across(): cluenum = "A" + str(number) if cluenum not in answers: raise xdfile.IncompletePuzzleParse(xd, "Clue number doesn't match grid: " + cluenum) xd.clues.append((("A", number), decode(clue), answers.get(cluenum, ""))) # xd.append_clue_break() for number, clue in puzzle.clues.down(): cluenum = "D" + str(number) if cluenum not in answers: raise xdfile.IncompletePuzzleParse(xd, "Clue doesn't match grid: " + cluenum) xd.clues.append((("D", number), decode(clue), answers.get(cluenum, ""))) except KeyError as e: raise xdfile.IncompletePuzzleParse(xd, "Clue doesn't match grid: " + str(e)) return xd
def parse_puz(contents, filename): rebus_shorthands = list( "⚷⚳♇♆⛢♄♃♂♁♀☿♹♸♷♶♵♴♳⅘⅗⅖⅕♚♛♜♝♞♟⚅⚄⚃⚂⚁⚀♣♦♥♠+&%$@?*zyxwvutsrqponmlkjihgfedcba0987654321" ) try: puzobj = puz.load(contents) puzzle = crossword.from_puz(puzobj) except puz.PuzzleFormatError as e: emsg = e.message if "<html>" in contents.decode('utf-8').lower(): emsg += " (looks like html)" raise xdfile.PuzzleParseError(emsg) grid_dict = dict(list(zip(string.ascii_uppercase, string.ascii_uppercase))) xd = xdfile.xdfile('', filename) xd.set_header("Author", puzobj.author) xd.set_header("Copyright", puzobj.copyright) xd.set_header("Notes", puzobj.notes) xd.set_header("Postscript", "".join(x for x in puzobj.postscript if ord(x) >= ord(' '))) xd.set_header("Preamble", puzobj.preamble) xd.set_header("Title", puzobj.title) used_rebuses = {} # [puz_rebus_gridvalue_as_string] -> our_rebus_gridvalue rebus = {} # [our_rebus_gridvalue] -> full_cell r = puzobj.rebus() if r.has_rebus(): grbs = puzobj.extensions[b"GRBS"] if sum(x for x in grbs if x != 0) > 0: # check for an actual rebus for pair in puzobj.extensions[b"RTBL"].decode("cp1252").split(";"): pair = pair.strip() if not pair: continue key, value = pair.split(":") rebuskey = rebus_shorthands.pop() used_rebuses[key] = rebuskey rebus[rebuskey] = decode(value) rebustr = xdfile.REBUS_SEP.join([ ("%s=%s" % (k, v)) for k, v in sorted(rebus.items()) ]) xd.set_header("Rebus", rebustr) for r, row in enumerate(puzzle): rowstr = "" for c, cell in enumerate(row): if puzzle.block is None and cell.solution == '.': rowstr += xdfile.BLOCK_CHAR elif cell.solution == puzzle.block: rowstr += xdfile.BLOCK_CHAR elif cell.solution == ':': rowstr += xdfile.OPEN_CHAR elif cell == puzzle.empty: rowstr += xdfile.UNKNOWN_CHAR else: n = r * puzobj.width + c reb = puzobj.rebus() if reb.has_rebus() and n in reb.get_rebus_squares(): ch = str(reb.table[n] - 1) rowstr += used_rebuses[ch] cell.solution = rebus[used_rebuses[ch]] else: ch = cell.solution if ch not in grid_dict: if ch in rebus_shorthands: cellch = ch rebus_shorthands.remove(ch) warn( "%s: unknown grid character '%s', assuming rebus of itself" % (filename, ch)) else: cellch = rebus_shorthands.pop() warn( "%s: unknown grid character '%s', assuming rebus (as '%s')" % (filename, ch, cellch)) xd.set_header( "Rebus", xd.get_header("Rebus") + " %s=%s" % (cellch, ch)) grid_dict[ch] = cellch rowstr += grid_dict[ch] xd.grid.append(rowstr) assert xd.size() == (puzzle.width, puzzle.height), "non-matching grid sizes" # clues answers = {} for posdir, posnum, answer in xd.iteranswers(): answers[posdir[0] + str(posnum)] = answer try: for number, clue in puzzle.clues.across(): cluenum = "A" + str(number) if cluenum not in answers: raise xdfile.IncompletePuzzleParse( xd, "Clue number doesn't match grid: " + cluenum) xd.clues.append( (("A", number), decode(clue), answers.get(cluenum, ""))) # xd.append_clue_break() for number, clue in puzzle.clues.down(): cluenum = "D" + str(number) if cluenum not in answers: raise xdfile.IncompletePuzzleParse( xd, "Clue doesn't match grid: " + cluenum) xd.clues.append( (("D", number), decode(clue), answers.get(cluenum, ""))) except KeyError as e: raise xdfile.IncompletePuzzleParse( xd, "Clue doesn't match grid: " + str(e)) return xd
def read_puzzle(self): puz_object = puz.read( '/Users/rachelprisock/Documents/Crosswords/NY_Time_Sat_Sample.puz') global puzzle puzzle = crossword.from_puz(puz_object)
def parse_puz(contents): puz_object = puz.load(contents) puzzle = crossword.from_puz(puz_object) grid_dict = dict(zip(string.uppercase, string.uppercase)) xd = xdfile.xdfile() md = dict([ (hdr_renames.get(k.lower(), k), v) for k, v in puzzle.meta() if v ]) if " / " in md.get("author", ""): author, editor = md.get("author").split(" / ") editor = editor.strip() author = author.strip() author = author.lstrip("By ") editor = editor.lstrip("Edited by ") md["author"] = author md["editor"] = editor if "Washington Post" in md.get("copyright", ""): a = md["author"] if " - " in a: datestr, rest = a.split(" - ") md["date"] = reparse_date(datestr) if "By " in rest: md["title"], rest = rest.split(" By ") else: md["title"], rest = rest.split(" by ", 1) if "Edited by " in rest: md["author"], md["editor"] = rest.split(", Edited by ") elif "edited by " in rest: md["author"], md["editor"] = rest.split(", edited by ") else: md["author"] = rest md["copyright"] = md["copyright"].lstrip("Copyright") for k, v in sorted(md.items(), key=lambda x: hdr_order.index(x[0])): if v: k = k[0].upper() + k[1:].lower() v = decode(v.strip()) v = v.replace(u"© ", "") xd.headers.append((k, v)) answers = { } clue_num = 1 for r, row in enumerate(puzzle): rowstr = "" for c, cell in enumerate(row): if puzzle.block is None and cell.solution == '.': rowstr += xdfile.BLOCK_CHAR elif puzzle.block == cell.solution: rowstr += xdfile.BLOCK_CHAR elif cell == puzzle.empty: rowstr += "." else: if cell.solution not in grid_dict: grid_dict[cell.solution] = rebus_shorthands.pop() rowstr += grid_dict[cell.solution] # compute number shown in box new_clue = False if is_block(puzzle, c-1, r): # across clue start j = 0 answer = "" while not is_block(puzzle, c+j, r): answer += puzzle[c+j, r].solution j += 1 if len(answer) > 1: new_clue = True answers["A"+str(clue_num)] = answer if is_block(puzzle, c, r-1): # down clue start j = 0 answer = "" while not is_block(puzzle, c, r+j): answer += puzzle[c, r+j].solution j += 1 if len(answer) > 1: new_clue = True answers["D"+str(clue_num)] = answer if new_clue: clue_num += 1 xd.grid.append(rowstr) for number, clue in puzzle.clues.across(): xd.clues.append((("A", number), decode(clue), answers["A"+str(number)])) for number, clue in puzzle.clues.down(): xd.clues.append((("D", number), decode(clue), answers["D"+str(number)])) return xd
def parse_puz(contents, filename): rebus_shorthands = list(u"♚♛♜♝♞♟⚅⚄⚃⚂⚁⚀♣♦♥♠Фθиλπφя+&%$@?*zyxwvutsrqponmlkjihgfedcba0987654321") if not filename.lower().endswith('.puz'): return puz_object = puz.load(contents) puzzle = crossword.from_puz(puz_object) grid_dict = dict(zip(string.uppercase, string.uppercase)) xd = xdfile.xdfile() md = dict([ (k.lower(), v) for k, v in puzzle.meta() if v ]) author = md.get("creator", "") if " / " in author: author, editor = author.split(" / ") else: editor = "" author = author.strip() editor = editor.strip() for editsep in [ "edited by ", "ed. " ]: try: i = author.lower().index(editsep) if i == 0: editor = author[len(editsep):] author = editor.split(",")[1] elif i > 0: assert not editor editor = author[i+len(editsep):] author = author[:i] except: pass author = author.strip() editor = editor.strip() while author.lower().startswith("by "): author = author[3:] if author and author[-1] in ",.": author = author[:-1] md["creator"] = author md["editor"] = editor for k, v in sorted(md.items(), key=lambda x: hdr_order.index(x[0])): if v: k = k[0].upper() + k[1:].lower() v = decode(v.strip()) v = v.replace(u"©", "(c)") xd.headers.append((k, v)) answers = { } clue_num = 1 for r, row in enumerate(puzzle): rowstr = "" for c, cell in enumerate(row): if puzzle.block is None and cell.solution == '.': rowstr += xdfile.BLOCK_CHAR elif puzzle.block == cell.solution: rowstr += xdfile.BLOCK_CHAR elif cell == puzzle.empty: rowstr += "." else: if cell.solution not in grid_dict: grid_dict[cell.solution] = rebus_shorthands.pop() rowstr += grid_dict[cell.solution] # compute number shown in box new_clue = False if is_block(puzzle, c-1, r): # across clue start j = 0 answer = "" while not is_block(puzzle, c+j, r): answer += puzzle[c+j, r].solution j += 1 if len(answer) > 1: new_clue = True answers["A"+str(clue_num)] = answer if is_block(puzzle, c, r-1): # down clue start j = 0 answer = "" while not is_block(puzzle, c, r+j): answer += puzzle[c, r+j].solution j += 1 if len(answer) > 1: new_clue = True answers["D"+str(clue_num)] = answer if new_clue: clue_num += 1 xd.grid.append(rowstr) for number, clue in puzzle.clues.across(): xd.clues.append((("A", number), decode(clue), answers["A"+str(number)])) for number, clue in puzzle.clues.down(): xd.clues.append((("D", number), decode(clue), answers["D"+str(number)])) return xd