# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com
#
# 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.
"""
Ormat game in Google CP Solver.
From bit-player "The Ormat Game"
http://bit-player.org/2010/the-ormat-game
'''
I'm going to give you a square grid, with some of the cells colored
and others possibly left blank. We'll call this a template. Perhaps
the grid will be one of these 3x3 templates:
[see pictures at the web page]
You have a supply of transparent plastic overlays that match the
grid in size and shape and that also bear patterns of black dots:
[ibid.]
Your task is to assemble a subset of the overlays and lay them on
the template in such a way that dots cover all the colored squares
but none of the blank squares. You are welcome to superimpose multiple
dots on any colored square, but overall you want to use as few overlays
as possible. To make things interesting, I'll suggest a wager. I'll pay
you $3 for a correct covering of a 3x3 template, but you have to pay me
$1 for each overlay you use. Is this a good bet?
'''
This is a prototype which the following limitations:
- the overlays is not generated dynamically for each n
- it just shows which overlays that is used. It would be nice
with a much more graphical output.
- the questions asked by bit-player is not answered (it requires
more analysis)
That said, here is solutions of the three problems stated at the web page
with minimum number of overlays.
x is an indicator matrix which overlay to use
overlay used is a set representation of the overlay used
num_overlays is the number of overlays used
Compare with the following model:
* MiniZinc: http://hakank.org/minizinc/ormat_game.mzn
This model was created by Hakan Kjellerstrand (hakank@bonetmail.com)
Also see my other Google CP Solver models: http://www.hakank.org/google_or_tools/
"""
import string, sys
from constraint_solver import pywrapcp
#
# Generate all the overlays for a specific size (n)
#
def get_overlays(n=3, debug=0):
#
# decision variables
#
x = []
for i in range(n):
t = []
for j in range(n):
t.append(solver.IntVar(0,1, 'x[%i,%i]'%(i,j)))
x.append(t)
x_flat = [x[i][j] for i in range(n) for j in range(n)]
#
# constraints
#
model = Model (
[ Sum(row) == 1 for row in x.row],
[ Sum(col) == 1 for col in x.col]
)
overlays = []
for library in libs:
solver = model.load(library) # Load up model into solver
if solver.solve():
num_solutions = 1
if debug:
print 'x (', num_solutions-1, '):\n',x
overlays.append([[x[i,j].get_value() for j in range(n)] for i in range(n) ])
while solver.getNextSolution() == SAT:
num_solutions += 1
if debug:
print 'x (', num_solutions-1, '):\n',x
overlays.append([[x[i,j].get_value() for i in range(n)] for j in range(n) ])
#print 'Number of solutions: ', num_solutions
#print 'Nodes:', solver.getNodes(), ' Time:', solver.getTime()
#print 'getPropags:', solver.getPropags()
#print 'getBacktracks:', solver.getBacktracks()
#print 'getFailures:', solver.getFailures()
else:
print 'No solution for getting the overlay'
#print '\n\n'
return overlays
#
# Generate all the problems of size n
#
# def all_problems(libs, n=3, debug=0):
# x = Matrix(n, n, 0, 1,'x')
# model = Model (
# # Sum(x.flat) >= n,
# [ Sum(row) >= 1 for row in x.row],
# [ Sum(col) >= 1 for col in x.col],
# )
# problems = []
# for library in libs:
# solver = model.load(library)
# if solver.solve():
# num_solutions = 1
# if debug:
# print 'x1:\n',x
# problems.append([[x[i,j].get_value() for j in range(n)] for i in range(n) ])
# while solver.getNextSolution() == SAT:
# num_solutions += 1
# if debug:
# print "x2:",x
# problems.append([[x[i,j].get_value() for i in range(n)] for j in range(n) ])
# else:
# print 'No solution'
# print '\n\n'
# return problems
#
# print a solution
#
def print_solution(x, overlays):
f = len(x)
n = len(overlays[0])
print "f:",f, " n: ", n
for o in range(f):
if x[o].Value() == 1:
print "overlay", o
for i in range(n):
for j in range(n):
print overlays[o][i][j], " ",
print ''
print ''
#
# print a problem
#
def print_problem(problem, n):
print "Problem:"
for i in range(n):
for j in range(n):
print problem[i][j], " ",
print ''
print ''
def main(problem, overlays, n, debug=0):
# Create the solver.
solver = pywrapcp.Solver('Pandigital numbers')
#
# data
#
f = len(overlays)
#
# declare variables
#
x = [solver.IntVar(0, 1, 'x[i]'%i] for i in range(f)]
num_overlays = solver.IntVar(0, f, 'num_overlays')
#
# constraints
#
#
# solution and search
#
solution = solver.Assignment()
solution.Add(x)
solution.Add(num1)
solution.Add(num2)
solution.Add(res)
# db: DecisionBuilder
db = solver.Phase(x,
solver.INT_VAR_SIMPLE,
solver.INT_VALUE_DEFAULT)
solver.NewSearch(db)
num_solutions = 0
solutions = []
while solver.NextSolution():
# solutions.append([x[i].Value() for i in range(x_len)])
print_solution([x[i].Value() for i in range(x_len)], len1, len2, x_len)
num_solutions += 1
solver.EndSearch()
print
print "num_solutions:", num_solutions
print "failures:", solver.failures()
print "branches:", solver.branches()
print "wall_time:", solver.wall_time()
print
# problem 1
# n = 3
# problem = [
# [1,0,0],
# [0,1,1],
# [0,1,1]
# ]
# # Problem grid 2
# n = 3
# problem = [
# [1,1,1],
# [1,1,1],
# [1,1,1]
# ]
# Note: Before solve y matrix has y2.2 in {} which is very bad, and
# is the reason (or a symptom) that this problem shows no solution.
#
# x before solve: [x0 in {0,1}, x1 in {1}, x2 in {0,1}, x3 in {1}, x4 in {1}, x5 in {0,1}]
# y before solve:
# [[y0.0 in {0..2}, y0.1 in {0,1}, y0.2 in {0..3}],
# [y1.0 in {0..3}, y1.1 in {0..2}, y1.2 in {0,1}],
# [y2.0 in {0,1}, y2.1 in {0..3}, y2.2 in {}]]
#
# Strange: another run has not this empty domain for y2.2...
#
# # Problem grid 3
# n = 3
# problem = [
# [1,1,1],
# [1,1,1],
# [1,1,0]
# ]
# This rotation of the above works
n = 3
problem = [
[1,1,1],
[1,1,1],
[0,1,1]
]
# This is a _bad_ problem since all rows
# and colutions must have at least one cell=1
# n = 3
# problem = [
# [0,0,0],
# [0,1,1],
# [0,1,1]
# ]
# # Problem grid 4 (n = 4)
# n = 4
# problem = [
# [1,1,1,1],
# [1,1,1,1],
# [1,1,1,1],
# [1,1,0,0]
# ]
# variant
# n = 4
# problem = [
# [1,1,1,1],
# [1,1,1,1],
# [1,1,1,1],
# [1,1,1,0]
# ]
# variant
# n = 4
# problem = [
# [1,1,1,1],
# [1,1,1,1],
# [1,1,1,1],
# [1,1,1,1]
# ]
# # Problem grid 5 (n = 5)
# # This is under the section "Out of bounds"
# n = 5
# problem = [
# [1,1,1,1,1],
# [1,1,1,1,1],
# [1,1,1,1,1],
# [1,1,1,1,1],
# [1,1,0,0,0]
# ]
# # Problem grid 6 (n = 6)
# n = 6
# # This is under the section "Out of bounds"%
# problem = [
# [1,1,1,1,1,1],
# [1,1,1,1,1,1],
# [1,1,1,1,1,1],
# [1,1,1,1,1,1],
# [1,1,1,1,1,1],
# [1,1,0,0,0,0]
# ]
## Test all problems of a certain size
# n=3
# debug = 0
# num_problems = 0
# num_solved = 0
# overlays = get_overlays(['Mistral'], n, debug)
# problems = all_problems(['Mistral'], n, debug)
# print "num_problems:", len(problems)
# for p in problems:
# print_problem(p,n)
# num_problems += 1
# num_sol = ormat_game(['Mistral'], p, overlays, n, debug)
# num_solved += num_sol
# # print "num_sol:",num_sol
# print "num_problems: ", num_problems
# print "num_solved:", num_solved
if __name__ == '__main__':
debug = 1
print_problem(problem, n)
overlays = get_overlays(['Mistral'], n, debug)
# print "overlays:", overlays
main(problem, overlays, n, debug)