# Copyright 2010-2012 Google # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Hidato puzzle in Google CP Solver. http://www.hidato.com/ ''' Puzzles start semi-filled with numbered tiles. The first and last numbers are circled. Connect the numbers together to win. Consecutive number must touch horizontally, vertically, or diagonally. ''' """ from google.apputils import app from constraint_solver import pywrapcp def BuildPairs(rows, cols): """Build closeness pairs for consecutive numbers. Build set of allowed pairs such that two consecutive numbers touch each other in the grid. Returns: A list of pairs for allowed consecutive position of numbers. Args: rows: the number of rows in the grid cols: the number of columns in the grid """ results = [] for x in range(rows): for y in range(cols): for dx in (-1, 0, 1): for dy in (-1, 0, 1): if (x + dx >= 0 and x + dx < rows and y + dy >= 0 and y + dy < cols and (dx != 0 or dy != 0)): results.append((x * cols + y, (x + dx) * cols + (y + dy))) return results def main(unused_argv): for model in range(1, 7): print print '----- Solving problem %i -----' % model print Solve(model) def Solve(model): """Solve the given model.""" # Create the solver. solver = pywrapcp.Solver('hidato-table') # # models, a 0 indicates an open cell which number is not yet known. # # puzzle = None if model == 1: # Simple problem puzzle = [[6, 0, 9], [0, 2, 8], [1, 0, 0]] elif model == 2: puzzle = [[0, 44, 41, 0, 0, 0, 0], [0, 43, 0, 28, 29, 0, 0], [0, 1, 0, 0, 0, 33, 0], [0, 2, 25, 4, 34, 0, 36], [49, 16, 0, 23, 0, 0, 0], [0, 19, 0, 0, 12, 7, 0], [0, 0, 0, 14, 0, 0, 0]] elif model == 3: # Problems from the book: # Gyora Bededek: "Hidato: 2000 Pure Logic Puzzles" # Problem 1 (Practice) puzzle = [[0, 0, 20, 0, 0], [0, 0, 0, 16, 18], [22, 0, 15, 0, 0], [23, 0, 1, 14, 11], [0, 25, 0, 0, 12]] elif model == 4: # problem 2 (Practice) puzzle = [[0, 0, 0, 0, 14], [0, 18, 12, 0, 0], [0, 0, 17, 4, 5], [0, 0, 7, 0, 0], [9, 8, 25, 1, 0]] elif model == 5: # problem 3 (Beginner) puzzle = [[0, 26, 0, 0, 0, 18], [0, 0, 27, 0, 0, 19], [31, 23, 0, 0, 14, 0], [0, 33, 8, 0, 15, 1], [0, 0, 0, 5, 0, 0], [35, 36, 0, 10, 0, 0]] elif model == 6: # Problem 15 (Intermediate) puzzle = [[64, 0, 0, 0, 0, 0, 0, 0], [1, 63, 0, 59, 15, 57, 53, 0], [0, 4, 0, 14, 0, 0, 0, 0], [3, 0, 11, 0, 20, 19, 0, 50], [0, 0, 0, 0, 22, 0, 48, 40], [9, 0, 0, 32, 23, 0, 0, 41], [27, 0, 0, 0, 36, 0, 46, 0], [28, 30, 0, 35, 0, 0, 0, 0]] r = len(puzzle) c = len(puzzle[0]) print 'Initial game (%i x %i)' % (r, c) PrintMatrix(puzzle) # # declare variables # positions = [solver.IntVar(0, r * c - 1, 'p of %i' % i) for i in range(r * c)] # # constraints # solver.Add(solver.AllDifferent(positions)) # # Fill in the clues # for i in range(r): for j in range(c): if puzzle[i][j] > 0: solver.Add(positions[puzzle[i][j] - 1] == i * c + j) # Consecutive numbers much touch each other in the grid. # We use an allowed assignment constraint to model it. close_tuples = BuildPairs(r, c) for k in range(1, r * c - 1): solver.Add(solver.AllowedAssignments((positions[k], positions[k + 1]), close_tuples)) # # solution and search # # db: DecisionBuilder db = solver.Phase(positions, solver.CHOOSE_MIN_SIZE_LOWEST_MIN, solver.ASSIGN_MIN_VALUE) solver.NewSearch(db) num_solutions = 0 while solver.NextSolution(): num_solutions += 1 PrintOneSolution(positions, r, c, num_solutions) solver.EndSearch() print 'num_solutions:', num_solutions print 'failures:', solver.Failures() print 'branches:', solver.Branches() print 'wall time:', solver.WallTime() def PrintOneSolution(positions, rows, cols, num_solution): """Print a current solution.""" print 'Solution %i:' % num_solution # Create empty board. board = [] for unused_i in range(rows): board.append([0] * cols) # Fill board with solution value. for k in range(rows * cols): position = positions[k].Value() board[position / cols][position % cols] = k + 1 # Print the board. PrintMatrix(board) def PrintMatrix(game): """Pretty print of a matrix.""" rows = len(game) cols = len(game[0]) for i in range(rows): for j in range(cols): if game[i][j] == 0: print ' .', else: print '% 2s' % game[i][j], print print if __name__ == '__main__': app.run()