def fill( puzzle_name ): """ ********************************************************************************************************************** """ # puzzle_structure.read_raw_puzzle( puzzle_name ) puzzle = puzzle_structure.create_puzzle( puzzle_name, 'skeleton' ) # clue_scraper.lookup_all_clues( puzzle_name ) puzzle_structure.sort_answers( puzzle_name ) f = open( 'puzzles/' + puzzle_name + '/' + puzzle_name + '-answers.txt', 'r' ) answers = f.read().splitlines()[3:] for i in range( len( answers ) ): answers[i] = answers[i].strip().split( '\t' ) answers[i] = [ int( answers[i][0] ), int( answers[i][1] ), answers[i][2], ast.literal_eval( answers[i][3] ), ast.literal_eval( answers[i][4] ) ] recursive_solver( puzzle, answers, 0 ) puzzle_structure.print_puzzle( puzzle )
def recursive_solver( puzzle, answers, answer_index ): """ ********************************************************************************************************************** """ time.sleep( .25 ) puzzle_structure.print_puzzle( puzzle ) ## exit condition ## if ( answer_index == len( answers ) ): return 1 cur_info = answers[ answer_index ] candidates = cur_info[4] print( answer_index, candidates ) for candidate in candidates: if ( is_valid( candidate, puzzle, cur_info[0], cur_info[1], cur_info[2] ) ): puzzle = fill_answer( candidate, puzzle, cur_info[0], cur_info[1], cur_info[2] ) if recursive_solver( puzzle, answers, answer_index + 1 ): return 1 puzzle = fill_answer( ' ' * len( candidate ), puzzle, cur_info[0], cur_info[1], cur_info[2] )
def fill_all_singletons(answers, puzzle, ignore_longs): """ ********************************************************************************************************************** Fills the grid with all singleton answers, i.e. answers that are the only candidate. Remove filled answers once filled @param: {string[][]} answers The list of information about each clue @param: {string[][]} puzzle The current grid representation @param: {boolean} ignore_longs When true, ignore long answers, as they are more likely to have generated a false positive @return: {string[][], string[][]} The updated answers and puzzle state """ to_remove = [] for info in answers: ## clear console using escape sequence ## print(chr(27) + '[2J') ## print the puzzle to stdout ## puzzle_structure.print_puzzle(puzzle) time.sleep(TIME_DELAY) candidates = info[4] if len(candidates) != 1: break answer = candidates[0] ## ignore long answers ## if len(answer) > 6 and ignore_longs: continue row = info[0] col = info[1] direction = info[2] ## fill the answer ## fill_answer(answer, puzzle, row, col, direction) to_remove.append(info) ## remove all filled answers ## for info in to_remove: answers.remove(info) return answers, puzzle
def fill_all_singletons( answers, puzzle, ignore_longs ): """ ********************************************************************************************************************** Fills the grid with all singleton answers, i.e. answers that are the only candidate. Remove filled answers once filled @param: {string[][]} answers The list of information about each clue @param: {string[][]} puzzle The current grid representation @param: {boolean} ignore_longs When true, ignore long answers, as they are more likely to have generated a false positive @return: {string[][], string[][]} The updated answers and puzzle state """ to_remove = [] for info in answers: ## clear console using escape sequence ## print( chr( 27 ) + '[2J' ) ## print the puzzle to stdout ## puzzle_structure.print_puzzle( puzzle ) time.sleep( TIME_DELAY ) candidates = info[4] if len( candidates ) != 1: break answer = candidates[0] ## ignore long answers ## if len( answer ) > 6 and ignore_longs: continue row = info[0] col = info[1] direction = info[2] ## fill the answer ## fill_answer( answer, puzzle, row, col, direction ) to_remove.append( info ) ## remove all filled answers ## for info in to_remove: answers.remove( info ) return answers, puzzle
def fill_empty_squares(puzzle): """ ********************************************************************************************************************** Fill any remaining squares with 'E' since it's the most common letter @param: {string[][]} puzzle The current puzzle @param: {string[][]} The updated puzzle """ height = len(puzzle) width = len(puzzle[0]) ## find empty squares ## for i in range(height): for j in range(width): if puzzle[i][j] == ' ': puzzle[i][j] = 'E' ## clear console using escape sequence ## print(chr(27) + '[2J') ## print the puzzle to stdout ## puzzle_structure.print_puzzle(puzzle) time.sleep(TIME_DELAY) return puzzle
def fill_empty_squares( puzzle ): """ ********************************************************************************************************************** Fill any remaining squares with 'E' since it's the most common letter @param: {string[][]} puzzle The current puzzle @param: {string[][]} The updated puzzle """ height = len( puzzle ) width = len( puzzle[0] ) ## find empty squares ## for i in range ( height ): for j in range ( width ): if puzzle[i][j] == ' ': puzzle[i][j] = 'E' ## clear console using escape sequence ## print( chr( 27 ) + '[2J' ) ## print the puzzle to stdout ## puzzle_structure.print_puzzle( puzzle ) time.sleep( TIME_DELAY ) return puzzle
def score_puzzle(diff_arr): """ Evaluate the puzzle based on the differences between our solution and the correct solution. One point for each correct letter, plus 10 points for each fully correct word. @param: {string[][]} diff_arr The puzzle with differences highlighted @return: {float, float} Percent of squares correct, percent of words correct """ ## count the wrong letters ## height = len(diff_arr) width = len(diff_arr[0]) total_letters = 0 wrong_letters = 0 total_answers = 0 correct_answers = 0 for i in range(height): for j in range(width): if (diff_arr[i][j]) != '0': total_letters += 1 ## see if word is correct ## k, l = i, j if (k == 0 and diff_arr[k][l] != '0') or ( diff_arr[k - 1][l] == '0' and diff_arr[k][l] != '0'): # start of a 'down' clue total_answers += 1 while (True): if diff_arr[k][l].islower(): break if (k + 1) == height or diff_arr[k][ l] == '0': # whole answer is correct correct_answers += 1 break k += 1 k, l = i, j if (l == 0 and diff_arr[k][l] != '0') or ( diff_arr[k][l - 1] == '0' and diff_arr[k][l] != '0'): # start of an 'across' clue total_answers += 1 while (True): if diff_arr[k][l].islower(): break if (l + 1) == width or diff_arr[k][ l] == '0': # whole answer is correct correct_answers += 1 break l += 1 ## see if letter is correct ## if diff_arr[i][j].islower(): wrong_letters += 1 ## print puzzle with wrong letters highlighted ## print(chr(27)) puzzle_structure.print_puzzle(diff_arr) ## print some statistics ## print( str(total_letters - wrong_letters) + ' squares correct out of ' + str(total_letters)) print( str(correct_answers) + ' answers correct out of ' + str(total_answers)) print('score: ' + str((total_letters - wrong_letters) + 10 * total_answers)) letters_correct = 100 * (float(total_letters - wrong_letters) / total_letters) words_correct = 100 * (float(correct_answers) / total_answers) return letters_correct, words_correct
def score_puzzle( diff_arr ): """ ********************************************************************************************************************** Evaluate the puzzle based on the differences between our solution and the correct solution. One point for each correct letter, plus 10 points for each fully correct word. @param: {string[][]} diff_arr The puzzle with differences highlighted @return: {float, float} Percent of squares correct, percent of words correct """ ## count the wrong letters ## height = len( diff_arr ) width = len( diff_arr[0] ) total_letters = 0 wrong_letters = 0 total_answers = 0 correct_answers = 0 for i in range( height ): for j in range( width ): if ( diff_arr[i][j] ) != '0': total_letters += 1 ## see if word is correct ## k, l = i, j if ( k == 0 and diff_arr[k][l] != '0' ) or ( diff_arr[k-1][l] == '0' and diff_arr[k][l] != '0' ): # start of a 'down' clue total_answers += 1 while ( True ): if diff_arr[k][l].islower(): break if ( k + 1 ) == height or diff_arr[k][l] == '0': # whole answer is correct correct_answers += 1 break k += 1 k, l = i, j if ( l == 0 and diff_arr[k][l] != '0' ) or ( diff_arr[k][l-1] == '0' and diff_arr[k][l] != '0' ): # start of an 'across' clue total_answers += 1 while ( True ): if diff_arr[k][l].islower(): break if ( l + 1 ) == width or diff_arr[k][l] == '0': # whole answer is correct correct_answers += 1 break l += 1 ## see if letter is correct ## if diff_arr[i][j].islower(): wrong_letters += 1 ## print puzzle with wrong letters highlighted ## print( chr(27) + "[2J" ) puzzle_structure.print_puzzle( diff_arr ) ## print some statistics ## print( str( total_letters - wrong_letters ) + ' squares correct out of ' + str( total_letters ) ) print( str( correct_answers ) + ' answers correct out of ' + str( total_answers ) ) print( 'score: ' + str( ( total_letters - wrong_letters ) + 10 * total_answers ) ) letters_correct = 100 * ( float ( total_letters - wrong_letters ) / total_letters ) words_correct = 100 * ( float( correct_answers ) / total_answers ) return letters_correct, words_correct
def fill(puzzle_name): """ ********************************************************************************************************************** This does all the work solving the puzzle, starting with reading the raw input, then generating all candidate answers and filling the grid @param: {string} puzzle_name """ ## read the raw puzzle input ## puzzle_structure.read_raw_puzzle(puzzle_name) ## create a blank skeleton ## puzzle = puzzle_structure.create_puzzle(puzzle_name, 'skeleton') ## scrape all the clues ## clue_scraper.lookup_all_clues(puzzle_name) ## ask for input to keep solving ## # print( 'Answers computed. Proceed with solving? ' ) # cont = sys.stdin.readline() ## keep track of the puzzle at the previous iteration to control ## ## how long to solve at each step ## previous_puzzle_state = [] puzzle_structure.sort_answers(puzzle_name) f = open('puzzles/' + puzzle_name + '/' + puzzle_name + '-answers.txt', 'r') answers = f.read().splitlines()[3:] ## extract answer data into list ## for i in range(len(answers)): answers[i] = answers[i].strip().split('\t') answers[i][0] = int(answers[i][0]) answers[i][1] = int(answers[i][1]) answers[i][3] = ast.literal_eval(answers[i][3]) answers[i][4] = ast.literal_eval(answers[i][4]) ## fill singleton answers and update the candidates until this no longer ## ## yields any change in the puzzle ## while puzzle != previous_puzzle_state: ## update previous state ## previous_puzzle_state = [row[:] for row in puzzle] ## fill all singletons, update possibilites, and resort ## answers, puzzle = fill_all_singletons(answers, puzzle, True) answers = update_candidates(answers, puzzle) answers = sorted(answers, cmp=puzzle_structure.compare_answers) answers = refactor_answers(answers) previous_puzzle_state = [] ## update answers with single word and wikipedia title searches ## ## then fill singletons until this no longer yields any change ## while puzzle != previous_puzzle_state: ## update previous state ## previous_puzzle_state = [row[:] for row in puzzle] answers = search_dictionaries(answers) answers = sorted(answers, cmp=puzzle_structure.compare_answers) answers = refactor_answers(answers) ## fill the singletons ## answers, puzzle = fill_all_singletons(answers, puzzle, False) answers = update_candidates(answers, puzzle) ## fill based on the first candidate. this is pretty arbitrary ## for answer in answers: if len(answer[4]) > 0: puzzle = fill_answer(answer[4][0], puzzle, answer[0], answer[1], answer[2]) ## clear console using escape sequence ## print(chr(27) + '[2J') ## print the puzzle to stdout ## puzzle_structure.print_puzzle(puzzle) time.sleep(TIME_DELAY) ## fill the rest of the squares with 'E'. this is extremely arbitrary ## puzzle = fill_empty_squares(puzzle) ## write the final output for evaluation ## puzzle_structure.write_puzzle(puzzle_name, puzzle)
def fill( puzzle_name ): """ ********************************************************************************************************************** This does all the work solving the puzzle, starting with reading the raw input, then generating all candidate answers and filling the grid @param: {string} puzzle_name """ ## read the raw puzzle input ## puzzle_structure.read_raw_puzzle( puzzle_name ) ## create a blank skeleton ## puzzle = puzzle_structure.create_puzzle( puzzle_name, 'skeleton' ) ## scrape all the clues ## clue_scraper.lookup_all_clues( puzzle_name ) ## ask for input to keep solving ## # print( 'Answers computed. Proceed with solving? ' ) # cont = sys.stdin.readline() ## keep track of the puzzle at the previous iteration to control ## ## how long to solve at each step ## previous_puzzle_state = [] puzzle_structure.sort_answers( puzzle_name ) f = open( 'puzzles/' + puzzle_name + '/' + puzzle_name + '-answers.txt', 'r' ) answers = f.read().splitlines()[3:] ## extract answer data into list ## for i in range( len( answers ) ): answers[i] = answers[i].strip().split( '\t' ) answers[i][0] = int( answers[i][0] ) answers[i][1] = int( answers[i][1] ) answers[i][3] = ast.literal_eval( answers[i][3] ) answers[i][4] = ast.literal_eval( answers[i][4] ) ## fill singleton answers and update the candidates until this no longer ## ## yields any change in the puzzle ## while puzzle != previous_puzzle_state: ## update previous state ## previous_puzzle_state = [ row[:] for row in puzzle ] ## fill all singletons, update possibilites, and resort ## answers, puzzle = fill_all_singletons( answers, puzzle, True ) answers = update_candidates( answers, puzzle ) answers = sorted( answers, cmp=puzzle_structure.compare_answers ) answers = refactor_answers( answers ) previous_puzzle_state = [] ## update answers with single word and wikipedia title searches ## ## then fill singletons until this no longer yields any change ## while puzzle != previous_puzzle_state: ## update previous state ## previous_puzzle_state = [ row[:] for row in puzzle ] answers = search_dictionaries( answers ) answers = sorted( answers, cmp=puzzle_structure.compare_answers ) answers = refactor_answers( answers ) ## fill the singletons ## answers, puzzle = fill_all_singletons( answers, puzzle, False ) answers = update_candidates( answers, puzzle ) ## fill based on the first candidate. this is pretty arbitrary ## for answer in answers: if len( answer[4] ) > 0: puzzle = fill_answer( answer[4][0], puzzle, answer[0], answer[1], answer[2] ) ## clear console using escape sequence ## print( chr( 27 ) + '[2J' ) ## print the puzzle to stdout ## puzzle_structure.print_puzzle( puzzle ) time.sleep( TIME_DELAY ) ## fill the rest of the squares with 'E'. this is extremely arbitrary ## puzzle = fill_empty_squares( puzzle ) ## write the final output for evaluation ## puzzle_structure.write_puzzle( puzzle_name, puzzle )