# Copyright 2010-2011 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 'WallTime:', 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()

@@ -4830,21 +4830,22 @@
  print '
-WallT
+wall t
 ime:', s

fix display