diff --git a/python/data/nonogram_regular/nonogram_bear.py b/python/data/nonogram_regular/nonogram_bear.py new file mode 100644 index 0000000000..ccf5f67616 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_bear.py @@ -0,0 +1,47 @@ +# 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. +# +# Nonogram problem from Gecode: Bear +# http:#www.gecode.org/gecode-doc-latest/classNonogram.html +# +rows = 8 +row_rule_len = 2 +row_rules = [ + [0,1], + [0,2], + [4,4], + [0,12], + [0,8], + [0,9], + [3,4], + [2,2] + ] + +cols = 13 +col_rule_len = 2 +col_rules = [ + [0,2], + [2,1], + [3,2], + [0,6], + [1,4], + [0,3], + [0,4], + [0,4], + [0,4], + [0,5], + [0,4], + [1,3], + [0,2] + ] diff --git a/python/data/nonogram_regular/nonogram_car.py b/python/data/nonogram_regular/nonogram_car.py new file mode 100644 index 0000000000..6c3188ae65 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_car.py @@ -0,0 +1,53 @@ +# 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. +# +# Problem from ECLiPSe +# http:#eclipse.crosscoreop.com/eclipse/examples/nono.ecl.txt +# Problem n3 ( http:#www.pro.or.jp/~fuji/java/puzzle/nonogram/index-eng.html ) +# 'Car' +# +rows = 10; +row_rule_len = 4; +row_rules = [ + [0,0,0,4], + [0,1,1,6], + [0,1,1,6], + [0,1,1,6], + [0,0,4,9], + [0,0,1,1], + [0,0,1,1], + [0,2,7,2], + [1,1,1,1], + [0,0,2,2] + ] + +cols = 15; +col_rule_len = 2; +col_rules = [ + [0,4], + [1,2], + [1,1], + [5,1], + [1,2], + [1,1], + [5,1], + [1,1], + [4,1], + [4,1], + [4,2], + [4,1], + [4,1], + [4,2], + [0,4] + ] diff --git a/python/data/nonogram_regular/nonogram_castle.py b/python/data/nonogram_regular/nonogram_castle.py new file mode 100644 index 0000000000..97352ae89d --- /dev/null +++ b/python/data/nonogram_regular/nonogram_castle.py @@ -0,0 +1,121 @@ +# 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. +# +# Nonogram problem from Gecode: Castle +# From http:#www.cs.kuleuven.be/~bmd/nonogram.pl +# +rows = 35 +row_rule_len = 19 +row_rules = [ + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,1,1,1], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,6,3,1,3], + [0,0,0,0,0,0,0,0,0,0,0,0,0,5,8,4,3,1,5], + [0,0,0,0,0,0,0,0,0,0,0,7,3,4,1,3,5,1,7], + [0,0,0,0,0,0,2,2,4,9,1,5,1,1,1,1,1,1,1], + [0,0,0,0,0,0,0,0,0,0,0,4,5,10,2,1,8,7,1], + [0,0,0,0,0,0,0,0,0,0,0,0,5,1,3,3,16,1,2], + [0,0,0,0,0,0,0,0,0,0,0,8,5,1,2,4,9,1,3], + [0,0,0,0,0,0,0,4,5,3,14,1,1,1,1,4,1,1,3], + [3,3,2,2,2,4,1,1,1,1,1,1,1,1,3,1,1,3,2], + [0,0,0,0,0,0,0,0,8,2,7,2,1,1,2,1,1,3,3], + [0,0,0,0,0,0,1,5,9,12,2,1,1,3,1,1,2,2,1], + [0,0,3,2,2,1,1,1,1,4,1,1,1,3,3,1,1,2,2], + [0,0,0,0,0,0,0,5,2,2,2,2,1,5,2,1,1,2,5], + [0,0,0,0,0,0,0,3,5,9,2,1,1,6,3,1,3,2,3], + [0,0,0,0,0,0,0,1,4,1,1,1,4,1,5,5,3,3,3], + [0,0,0,0,0,0,0,0,0,4,1,1,1,1,3,4,6,6,3], + [0,0,0,0,0,0,0,3,1,3,1,1,3,3,1,1,4,6,1], + [0,0,0,0,0,0,0,0,3,1,5,1,1,3,1,1,9,4,1], + [0,0,0,0,0,2,1,1,7,1,4,1,1,1,1,1,1,3,5], + [0,0,0,0,0,0,0,0,9,2,1,3,1,1,1,1,4,2,1], + [0,0,0,0,0,0,0,0,0,1,14,1,1,2,2,2,10,1,2], + [0,0,0,0,0,0,0,0,0,1,9,2,1,2,6,1,5,3,2], + [0,0,0,0,0,0,0,1,9,9,1,2,2,3,1,1,4,3,1], + [0,0,0,0,0,0,0,0,0,10,1,3,4,1,3,2,1,2,8], + [0,0,0,0,0,0,0,0,0,0,9,1,3,5,1,1,1,2,7], + [0,0,0,0,0,0,0,4,5,1,2,5,1,3,1,1,2,1,3], + [0,0,0,0,0,1,1,1,1,2,6,2,3,2,1,1,2,3,1], + [0,0,0,0,0,0,0,0,1,6,1,5,7,1,3,3,2,4,3], + [0,0,0,0,0,0,0,0,0,1,2,1,2,9,1,5,2,6,2], + [0,0,0,0,0,0,0,0,0,0,0,10,2,2,13,1,3,3,1], + [0,0,0,0,0,0,0,0,2,2,1,6,2,3,3,2,2,2,1], + [0,0,0,0,0,0,0,2,2,1,1,12,2,2,9,2,2,2,2], + [0,0,0,0,0,0,0,0,0,0,5,1,2,4,1,5,11,2,2], + [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,6,18] + ] + +cols = 60 +col_rule_len = 10 +col_rules = [ + [0,0,0,2,3,1,5,1,7,1], + [0,0,0,2,4,2,3,2,3,5], + [0,0,2,6,3,1,1,5,1,5], + [2,4,2,1,1,1,4,1,1,2], + [0,0,0,2,8,2,1,5,2,5], + [0,0,0,3,1,6,2,5,1,5], + [0,3,3,3,1,1,6,1,1,1], + [0,3,2,2,2,2,8,1,1,3], + [0,0,0,1,4,4,3,7,1,1], + [0,0,0,1,2,2,2,3,7,9], + [0,0,1,2,3,1,1,5,2,2], + [0,0,0,2,2,3,1,1,6,1], + [0,0,0,0,1,3,1,5,4,1], + [0,0,1,3,1,1,6,1,3,1], + [0,0,3,3,4,5,1,4,2,1], + [0,0,0,0,2,3,3,9,7,1], + [0,0,2,3,2,2,1,1,3,5], + [0,0,4,2,1,1,1,1,2,3], + [0,0,0,4,2,2,1,4,3,2], + [0,0,0,0,0,0,4,3,16,2], + [0,0,0,0,0,1,2,5,7,1], + [0,0,0,0,4,3,2,2,7,1], + [0,0,0,0,0,2,3,1,10,1], + [0,0,0,0,2,4,2,1,4,1], + [0,0,0,0,0,1,6,7,3,1], + [0,0,0,0,0,0,3,11,3,1], + [0,0,0,0,0,7,1,11,2,1], + [0,0,0,2,2,2,2,2,2,2], + [0,0,0,3,1,1,1,1,2,1], + [0,0,0,2,2,2,2,1,1,1], + [0,0,0,1,1,1,1,2,1,2], + [0,0,2,2,2,2,1,1,1,1], + [0,0,0,0,0,4,1,1,2,2], + [0,0,0,0,0,5,2,17,2,1], + [0,0,0,0,9,2,3,1,4,2], + [0,0,0,0,9,4,2,1,1,1], + [0,0,0,0,0,5,4,2,1,4], + [0,0,0,11,1,2,1,4,1,2], + [0,0,0,0,0,3,4,2,4,4], + [0,0,2,1,4,1,2,1,5,2], + [0,0,0,0,0,8,4,1,1,2], + [0,0,0,0,0,1,1,3,2,3], + [0,0,0,0,1,3,1,8,1,6], + [0,0,0,0,0,0,2,1,7,14], + [0,0,0,1,2,4,4,1,2,3], + [1,1,4,2,1,1,1,1,1,4], + [0,0,0,0,3,5,3,1,1,4], + [0,0,0,0,2,4,2,2,1,2], + [0,0,0,0,0,4,2,3,8,4], + [0,0,0,0,0,4,15,2,2,4], + [0,0,0,0,4,1,10,2,1,2], + [0,0,0,0,2,12,6,1,2,4], + [0,0,0,3,1,3,1,3,3,4], + [0,0,0,0,3,1,2,3,4,1], + [0,0,0,5,2,2,2,3,3,3], + [0,1,2,2,2,2,4,1,1,3], + [0,0,0,2,1,4,2,7,1,1], + [0,0,0,0,5,2,2,3,6,3], + [0,0,0,3,3,2,2,3,2,3], + [0,0,0,4,1,2,1,1,2,1] + ] diff --git a/python/data/nonogram_regular/nonogram_crocodile.py b/python/data/nonogram_regular/nonogram_crocodile.py new file mode 100644 index 0000000000..4762413ca5 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_crocodile.py @@ -0,0 +1,50 @@ +# 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. +# +# Nonogram problem from Gecode: "Unknown" +# http://www.gecode.org/gecode-doc-latest/classNonogram.html +# +rows = 9 +row_rule_len = 5 +row_rules = [ + [0, 0, 0, 0, 3], + [0, 0, 2, 3, 2], + [0, 0, 0, 10, 3], + [0, 0, 0, 0, 15], + [1, 1, 1, 1, 6], + [0, 0, 0, 1, 7], + [0, 0, 0, 1, 4], + [0, 0, 0, 1, 4], + [0, 0, 0, 0, 4] + ] + +cols = 15 +col_rule_len = 2 +col_rules = [ + [0, 3], + [0, 4], + [2, 2], + [3, 1], + [2, 3], + [3, 2], + [2, 3], + [4, 2], + [3, 2], + [0, 6], + [1, 3], + [1, 3], + [1, 4], + [0, 5], + [0, 5] + ] diff --git a/python/data/nonogram_regular/nonogram_difficult.py b/python/data/nonogram_regular/nonogram_difficult.py new file mode 100644 index 0000000000..5c45f265d6 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_difficult.py @@ -0,0 +1,57 @@ +# 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. +# +# Nonogram "difficult" +# From Gecode +# +rows = 15 +row_rule_len = 2 +row_rules = [ + [0, 3], + [1, 1], + [1, 1], + [1, 1], + [1, 2], + [0, 5], + [0, 1], + [0, 2], + [0, 1], + [0, 1], + [1, 2], + [1, 2], + [2, 1], + [2, 2], + [0, 3] + ] + +cols = 15 +col_rule_len = 2 +col_rules = [ + [0, 3], + [0, 2], + [0, 2], + [0, 1], + [0, 2], + [0, 3], + [0, 2], + [0, 4], + [0, 3], + [0, 4], + [2, 1], + [1, 1], + [1, 1], + [1, 1], + [0, 3] + ] + diff --git a/python/data/nonogram_regular/nonogram_dragonfly.py b/python/data/nonogram_regular/nonogram_dragonfly.py new file mode 100644 index 0000000000..ffd187682a --- /dev/null +++ b/python/data/nonogram_regular/nonogram_dragonfly.py @@ -0,0 +1,66 @@ +# 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. +# +# Nonogram problem from Gecode: Dragonfly +# http://www.gecode.org/gecode-doc-latest/classNonogram.html +# +rows = 20 +row_rule_len = 5 +row_rules = [ + [0,0,0,7,1], + [0,0,1,1,2], + [0,0,2,1,2], + [0,0,1,2,2], + [0,0,4,2,3], + [0,0,3,1,4], + [0,0,3,1,3], + [0,0,2,1,4], + [0,0,0,2,9], + [0,0,2,1,5], + [0,0,0,2,7], + [0,0,0,0,14], + [0,0,0,8,2], + [0,0,6,2,2], + [0,2,8,1,3], + [0,1,5,5,2], + [1,3,2,4,1], + [3,1,2,4,1], + [1,1,3,1,3], + [0,2,1,1,2] + ] + +cols = 20 +col_rule_len = 5 +col_rules = [ + [0,1,1,1,2], + [3,1,2,1,1], + [1,4,2,1,1], + [0,1,3,2,4], + [0,1,4,6,1], + [0,0,1,11,1], + [0,5,1,6,2], + [0,0,0,0,14], + [0,0,0,7,2], + [0,0,0,7,2], + [0,0,6,1,1], + [0,0,0,9,2], + [0,3,1,1,1], + [0,0,3,1,3], + [0,0,2,1,3], + [0,0,2,1,5], + [0,0,3,2,2], + [0,0,3,3,2], + [0,0,2,3,2], + [0,0,0,2,6] + ] diff --git a/python/data/nonogram_regular/nonogram_gondola.py b/python/data/nonogram_regular/nonogram_gondola.py new file mode 100644 index 0000000000..5e181d4f1a --- /dev/null +++ b/python/data/nonogram_regular/nonogram_gondola.py @@ -0,0 +1,97 @@ +# 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. +# +# Gondola +# From http://www.conceptispuzzles.com +# +rows = 30 +row_rule_len = 8 +row_rules = [ + [0,0,0,0,0,0,5,6], + [0,0,0,0,6,1,1,1], + [0,0,0,0,0,3,11,3], + [0,0,6,1,1,1,1,1], + [0,7,1,1,1,2,1,3], + + [0,0,4,1,1,2,1,4], + [0,7,1,1,1,2,3,1], + [0,0,7,1,1,3,1,1], + [0,0,4,1,1,1,1,9], + [0,0,0,0,4,8,1,1], + + [0,0,0,4,1,4,1,3], + [0,0,0,4,1,7,1,5], + [4,1,1,2,1,4,1,1], + [0,0,0,4,9,2,1,2], + [0,0,4,1,3,1,2,1], + + [0,0,4,1,6,1,1,1], + [0,0,0,0,4,8,3,1], + [0,0,0,0,10,3,5,3], + [0,0,4,1,2,3,5,2], + [0,0,0,0,3,5,2,8], + + [0,0,0,2,6,3,1,1], + [0,0,0,0,0,1,12,1], + [0,0,0,0,0,20,1,1], + [0,0,0,0,0,0,2,25], + [0,0,0,0,0,2,3,20], + + [2,5,3,2,2,2,2,1], + [0,0,0,0,0,1,2,22], + [0,0,0,0,0,0,0,20], + [0,0,0,0,0,0,3,18], + [0,0,0,0,0,0,1,2] + ] + +cols = 30 +col_rule_len = 8 +col_rules = [ + [0,0,2,2,2,1,2,1], + [0,0,0,2,2,2,1,2], + [0,0,0,2,2,2,3,1], + [0,0,0,0,0,18,2,1], + [0,0,0,0,0,23,1,1], + + [0,0,0,0,0,20,2,1], + [0,0,0,0,0,0,16,4], + [0,0,0,0,0,0,2,6], + [0,0,0,0,0,1,7,8], + [0,0,3,1,1,8,2,1], + + [0,0,0,1,1,7,9,1], + [0,0,0,0,7,1,1,15], + [0,0,1,1,3,1,12,3], + [0,1,1,1,1,3,2,8], + [0,1,1,1,2,3,4,8], + + [0,1,1,1,1,3,1,14], + [0,0,0,0,7,6,8,3], + [0,0,0,0,0,1,4,9], + [0,0,0,1,2,1,1,7], + [0,0,0,0,5,1,3,3], + + [0,0,0,0,0,2,1,6], + [0,0,0,0,0,5,2,6], + [0,0,0,0,1,4,2,3], + [0,0,0,0,0,1,7,8], + [0,0,0,0,7,4,5,6], + + [2,1,1,1,2,3,3,3], + [0,0,0,7,2,1,1,6], + [0,1,1,2,1,1,1,6], + [0,2,1,1,1,3,2,3], + [0,0,0,0,1,1,9,6] + ] + diff --git a/python/data/nonogram_regular/nonogram_hard.py b/python/data/nonogram_regular/nonogram_hard.py new file mode 100644 index 0000000000..23b2dc2a12 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_hard.py @@ -0,0 +1,48 @@ +# 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. +# +# Nonogram "hard" +# Note: I don't remember where I found this. +# It isn't very hard, though. +# +rows = 10 +row_rule_len = 4 +row_rules = [ + [0,0,0,1], + [0,0,0,3], + [0,0,1,3], + [0,0,2,4], + [0,0,1,2], + [0,2,1,1], + [1,1,1,1], + [0,2,1,1], + [0,0,2,2], + [0,0,0,5] + ] + +cols = 10 +col_rule_len = 4 +col_rules = [ + [0,0,0,4], + [0,0,1,3], + [0,0,2,3], + [0,0,1,2], + [0,0,2,2], + [0,1,1,1], + [1,1,1,1], + [0,1,1,1], + [0,0,1,2], + [0,0,0,5] + ] + diff --git a/python/data/nonogram_regular/nonogram_hen.py b/python/data/nonogram_regular/nonogram_hen.py new file mode 100644 index 0000000000..a503aaf76e --- /dev/null +++ b/python/data/nonogram_regular/nonogram_hen.py @@ -0,0 +1,44 @@ +# 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. +# +# https://prof.ti.bfh.ch/hew1/informatik3/prolog/p-99/p98.pl +# 'Hen' +# +rows = 9 +row_rule_len = 2 +row_rules = [ + [0,3], + [2,1], + [3,2], + [2,2], + [0,6], + [1,5], + [0,6], + [0,1], + [0,2] + ] + +cols = 8 +col_rule_len = 2 +col_rules = [ + [1,2], + [3,1], + [1,5], + [7,1], + [0,5], + [0,3], + [0,4], + [0,3] + ] + diff --git a/python/data/nonogram_regular/nonogram_lambda.py b/python/data/nonogram_regular/nonogram_lambda.py new file mode 100644 index 0000000000..6cb1459512 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_lambda.py @@ -0,0 +1,48 @@ +# 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. +# +# From http://twan.home.fmf.nl/blog/haskell/Nonograms.details +# The lambda picture +# +rows = 12 +row_rule_len = 3 +row_rules = [ + [0,0,2], + [0,1,2], + [0,1,1], + [0,0,2], + [0,0,1], + [0,0,3], + [0,0,3], + [0,2,2], + [0,2,1], + [2,2,1], + [0,2,3], + [0,2,2] + ] + +cols = 10 +col_rule_len = 2 +col_rules = [ + [2,1], + [1,3], + [2,4], + [3,4], + [0,4], + [0,3], + [0,3], + [0,3], + [0,2], + [0,2] + ] diff --git a/python/data/nonogram_regular/nonogram_n4.py b/python/data/nonogram_regular/nonogram_n4.py new file mode 100644 index 0000000000..15935d7b4e --- /dev/null +++ b/python/data/nonogram_regular/nonogram_n4.py @@ -0,0 +1,39 @@ +# 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. +# +# http://eclipse.crosscoreop.com/eclipse/examples/nono.ecl.txt +# Problem n4 +# +rows = 6 +row_rule_len = 2 +row_rules = [ + [2,1], + [0,1], + [0,2], + [0,2], + [0,1], + [1,2] + ] + +cols = 6 +col_rule_len = 2 +col_rules = [ + [1,2], + [0,1], + [0,2], + [0,2], + [0,1], + [2,1] + ] + diff --git a/python/data/nonogram_regular/nonogram_n6.py b/python/data/nonogram_regular/nonogram_n6.py new file mode 100644 index 0000000000..65fd2ca15c --- /dev/null +++ b/python/data/nonogram_regular/nonogram_n6.py @@ -0,0 +1,57 @@ +# 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. +# +# Nonogram problem n6 +# http://eclipse.crosscoreop.com/eclipse/examples/nono.ecl.txt +# +rows = 15 +row_rule_len = 4 +row_rules = [ + [0,0,0,5], + [0,0,2,2], + [0,0,1,1], + [0,0,1,1], + [0,0,4,4], + [2,2,1,2], + [0,1,3,1], + [1,1,1,1], + [0,2,7,2], + [0,4,1,5], + [0,2,1,1], + [0,1,1,2], + [0,1,1,1], + [0,2,5,2], + [0,0,3,4] + ] + +cols = 15 +col_rule_len = 4 +col_rules = [ + [0,0,0,4], + [0,0,2,2], + [0,0,1,5], + [0,1,2,2], + [0,5,2,1], + [2,1,1,2], + [0,1,3,1], + [0,1,1,6], + [0,1,3,1], + [2,1,2,2], + [0,4,2,1], + [0,1,1,1], + [0,1,3,2], + [0,2,2,3], + [0,0,0,4] + ] + diff --git a/python/data/nonogram_regular/nonogram_nonunique.py b/python/data/nonogram_regular/nonogram_nonunique.py new file mode 100644 index 0000000000..d6b41dde6b --- /dev/null +++ b/python/data/nonogram_regular/nonogram_nonunique.py @@ -0,0 +1,53 @@ +# 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. +# +# Nonogram problem from Gecode: Nonunique +# There are 43 solutions to this nonogram. +# http://www.gecode.org/gecode-doc-latest/classNonogram.html +# +rows = 15 +row_rule_len = 4 +row_rules = [ + [0,0,2,2], + [0,0,2,2], + [0,0,0,4], + [0,0,1,1], + [0,0,1,1], + [1,1,1,1], + [0,0,1,1], + [0,0,1,4], + [0,1,1,1], + [0,1,1,4], + [0,0,1,3], + [0,0,1,2], + [0,0,0,5], + [0,0,2,2], + [0,0,3,3] + ] + +cols = 11 +col_rule_len = 5 +col_rules = [ + [0,0,0,0,5], + [0,0,1,2,4], + [0,0,2,1,3], + [0,2,2,1,1], + [0,1,1,1,1], + [0,0,0,1,5], + [2,1,1,3,2], + [2,1,1,1,1], + [0,0,1,4,1], + [0,0,0,1,1], + [0,0,0,0,1] + ] diff --git a/python/data/nonogram_regular/nonogram_p199.py b/python/data/nonogram_regular/nonogram_p199.py new file mode 100644 index 0000000000..e93e2f0d19 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_p199.py @@ -0,0 +1,66 @@ +# 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. +# +# Nonogram problem: P199, difficulty 8 +# From http://87.230.22.228/examples/nono_regular.ecl.txt +# +rows = 20 +row_rule_len = 6 +row_rules = [ + [0,0,0,1,1,4], + [0,0,0,0,1,6], + [1,1,1,1,2,3], + [0,0,1,1,2,3], + [0,0,3,1,2,3], + [0,0,4,5,2,2], + [0,0,0,7,3,2], + [0,0,3,5,1,2], + [0,0,2,2,4,1], + [0,0,2,2,3,4], + [0,0,0,2,5,2], + [0,0,2,1,5,1], + [0,0,2,2,3,1], + [0,0,0,6,2,2], + [0,0,0,0,1,7], + [0,0,0,2,2,2], + [0,0,0,0,1,4], + [0,0,0,3,1,1], + [0,0,0,0,1,1], + [0,0,0,0,1,1] + ] + +cols = 20 +col_rule_len = 5 +col_rules = [ + [0,0,0,6,1], + [0,0,0,8,3], + [0,0,3,2,1], + [1,1,2,2,1], + [1,2,2,1,1], + [0,1,1,1,1], + [0,0,0,2,3], + [0,4,1,2,2], + [0,0,5,2,1], + [0,0,8,1,1], + [0,0,0,7,2], + [0,0,3,5,2], + [0,0,0,2,5], + [0,0,2,1,4], + [0,2,2,2,2], + [2,2,1,1,1], + [3,1,1,1,1], + [0,5,4,2,1], + [0,7,4,1,1], + [0,0,0,0,4] + ] diff --git a/python/data/nonogram_regular/nonogram_p200.py b/python/data/nonogram_regular/nonogram_p200.py new file mode 100644 index 0000000000..03399e7e2d --- /dev/null +++ b/python/data/nonogram_regular/nonogram_p200.py @@ -0,0 +1,77 @@ +# 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. +# +# Nonogram problem from Gecode: P200 +# http://www.gecode.org/gecode-doc-latest/classNonogram.html +# +rows = 25 +row_rule_len = 7 +row_rules = [ + [0,0,0,0,2,2,3], + [0,0,4,1,1,1,4], + [0,0,4,1,2,1,1], + [4,1,1,1,1,1,1], + [0,2,1,1,2,3,5], + [0,1,1,1,1,2,1], + [0,0,3,1,5,1,2], + [0,3,2,2,1,2,2], + [2,1,4,1,1,1,1], + [0,2,2,1,2,1,2], + [0,1,1,1,3,2,3], + [0,0,1,1,2,7,3], + [0,0,1,2,2,1,5], + [0,0,3,2,2,1,2], + [0,0,0,3,2,1,2], + [0,0,0,0,5,1,2], + [0,0,0,2,2,1,2], + [0,0,0,4,2,1,2], + [0,0,0,6,2,3,2], + [0,0,0,7,4,3,2], + [0,0,0,0,7,4,4], + [0,0,0,0,7,1,4], + [0,0,0,0,6,1,4], + [0,0,0,0,4,2,2], + [0,0,0,0,0,2,1] + ] + +cols = 25 +col_rule_len = 6 +col_rules = [ + [0,0,1,1,2,2], + [0,0,0,5,5,7], + [0,0,5,2,2,9], + [0,0,3,2,3,9], + [0,1,1,3,2,7], + [0,0,0,3,1,5], + [0,7,1,1,1,3], + [1,2,1,1,2,1], + [0,0,0,4,2,4], + [0,0,1,2,2,2], + [0,0,0,4,6,2], + [0,0,1,2,2,1], + [0,0,3,3,2,1], + [0,0,0,4,1,15], + [1,1,1,3,1,1], + [2,1,1,2,2,3], + [0,0,1,4,4,1], + [0,0,1,4,3,2], + [0,0,1,1,2,2], + [0,7,2,3,1,1], + [0,2,1,1,1,5], + [0,0,0,1,2,5], + [0,0,1,1,1,3], + [0,0,0,4,2,1], + [0,0,0,0,0,3] + ] + diff --git a/python/data/nonogram_regular/nonogram_ps.py b/python/data/nonogram_regular/nonogram_ps.py new file mode 100644 index 0000000000..74448237f7 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_ps.py @@ -0,0 +1,44 @@ +# 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. +# +# From +# http://www-lp.doc.ic.ac.uk/UserPages/staff/ft/alp/humour/visual/nono.html +# Via ECLiPSe http://87.230.22.228/examples/nono_regular.ecl.txt +# +rows = 9 +row_rule_len = 2 +row_rules = [ + [0,3], + [2,1], + [3,2], + [2,2], + [0,6], + [1,5], + [0,6], + [0,1], + [0,2] + ] + +cols = 8 +col_rule_len = 2 +col_rules = [ + [1,2], + [3,1], + [1,5], + [7,1], + [0,5], + [0,3], + [0,4], + [0,3] + ] diff --git a/python/data/nonogram_regular/nonogram_soccer_player.py b/python/data/nonogram_regular/nonogram_soccer_player.py new file mode 100644 index 0000000000..715c412cd0 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_soccer_player.py @@ -0,0 +1,67 @@ +# 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. +# +# Nonogram problem from Wikipedia, soccer player +# http://en.wikipedia.org/wiki/Nonogram +# Also see http://en.wikipedia.org/wiki/Image:Paint_by_numbers_Animation.gif +# +rows = 20 +row_rule_len = 5 +row_rules = [ + [0,0,0,0,3], + [0,0,0,0,5], + [0,0,0,3,1], + [0,0,0,2,1], + [0,0,3,3,4], + [0,0,2,2,7], + [0,0,6,1,1], + [0,0,4,2,2], + [0,0,0,1,1], + [0,0,0,3,1], + [0,0,0,0,6], + [0,0,0,2,7], + [0,0,6,3,1], + [1,2,2,1,1], + [0,4,1,1,3], + [0,0,4,2,2], + [0,0,3,3,1], + [0,0,0,3,3], + [0,0,0,0,3], + [0,0,0,2,1] + ] + +cols = 20 +col_rule_len = 5 +col_rules = [ + [0,0,0,0,2], + [0,0,0,1,2], + [0,0,0,2,3], + [0,0,0,2,3], + [0,0,3,1,1], + [0,0,2,1,1], + [1,1,1,2,2], + [1,1,3,1,3], + [0,0,2,6,4], + [0,3,3,9,1], + [0,0,5,3,2], + [0,3,1,2,2], + [0,0,2,1,7], + [0,0,3,3,2], + [0,0,0,2,4], + [0,0,2,1,2], + [0,0,2,2,1], + [0,0,0,2,2], + [0,0,0,0,1], + [0,0,0,0,1] + ] diff --git a/python/data/nonogram_regular/nonogram_t2.py b/python/data/nonogram_regular/nonogram_t2.py new file mode 100644 index 0000000000..f12911ca74 --- /dev/null +++ b/python/data/nonogram_regular/nonogram_t2.py @@ -0,0 +1,44 @@ +# 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. +# +# http:#www.cs.mu.oz.au/433/tenpenki.html +# Note: This problem has 2 solutions. +# +rows = 6 +row_rule_len = 6 +row_rules = [ + [0, 0, 0, 2, 2, 3], + [1, 1, 1, 1, 1, 1], + [0, 0, 1, 1, 1, 1], + [0, 0, 0, 1, 1, 3], + [0, 1, 1, 1, 1, 1], + [0, 0, 0, 2, 2, 1]] + +cols = 14 +col_rule_len = 3 +col_rules = [ + [0, 0, 4], + [0, 1, 1], + [0, 1, 1], + [0, 1, 1], + [0, 0, 0], + [0, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 1, 1], + [0, 0, 0], + [0, 0, 6], + [0, 1, 1], + [0, 1, 1], + [0, 0, 2]] diff --git a/python/nonogram_regular.py b/python/nonogram_regular.py new file mode 100644 index 0000000000..261b739813 --- /dev/null +++ b/python/nonogram_regular.py @@ -0,0 +1,353 @@ +# 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. + +""" + + Nonogram (Painting by numbers) in Google CP Solver. + + http://en.wikipedia.org/wiki/Nonogram + ''' + Nonograms or Paint by Numbers are picture logic puzzles in which cells in a + grid have to be colored or left blank according to numbers given at the + side of the grid to reveal a hidden picture. In this puzzle type, the + numbers measure how many unbroken lines of filled-in squares there are + in any given row or column. For example, a clue of '4 8 3' would mean + there are sets of four, eight, and three filled squares, in that order, + with at least one blank square between successive groups. + + ''' + + See problem 12 at http://www.csplib.org/. + + http://www.puzzlemuseum.com/nonogram.htm + + Haskell solution: + http://twan.home.fmf.nl/blog/haskell/Nonograms.details + + Brunetti, Sara & Daurat, Alain (2003) + 'An algorithm reconstructing convex lattice sets' + http://geodisi.u-strasbg.fr/~daurat/papiers/tomoqconv.pdf + + + The Comet model (http://www.hakank.org/comet/nonogram_regular.co) + was a major influence when writing this Google CP solver model. + + I have also blogged about the development of a Nonogram solver in Comet + using the regular constraint. + * 'Comet: Nonogram improved: solving problem P200 from 1:30 minutes + to about 1 second' + http://www.hakank.org/constraint_programming_blog/2009/03/comet_nonogram_improved_solvin_1.html + + * 'Comet: regular constraint, a much faster Nonogram with the regular constraint, + some OPL models, and more' + http://www.hakank.org/constraint_programming_blog/2009/02/comet_regular_constraint_a_muc_1.html + + Compare with the other models: + * Gecode/R: http://www.hakank.org/gecode_r/nonogram.rb (using 'regexps') + * MiniZinc: http://www.hakank.org/minizinc/nonogram_regular.mzn + * MiniZinc: http://www.hakank.org/minizinc/nonogram_create_automaton.mzn + * MiniZinc: http://www.hakank.org/minizinc/nonogram_create_automaton2.mzn + Note: nonogram_create_automaton2.mzn is the preferred model + + 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 sys + +from constraint_solver import pywrapcp + + +# +# Global constraint regular +# +# This is a translation of MiniZinc's regular constraint (defined in +# lib/zinc/globals.mzn), via the Comet code refered above. +# All comments are from the MiniZinc code. +# ''' +# The sequence of values in array 'x' (which must all be in the range 1..S) +# is accepted by the DFA of 'Q' states with input 1..S and transition +# function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' +# (which must be in 1..Q) and accepting states 'F' (which all must be in +# 1..Q). We reserve state 0 to be an always failing state. +# ''' +# +# x : IntVar array +# Q : number of states +# S : input_max +# d : transition matrix +# q0: initial state +# F : accepting states +def regular(x, Q, S, d, q0, F): + + solver = x[0].solver() + + assert Q > 0, 'regular: "Q" must be greater than zero' + assert S > 0, 'regular: "S" must be greater than zero' + + # d2 is the same as d, except we add one extra transition for + # each possible input; each extra transition is from state zero + # to state zero. This allows us to continue even if we hit a + # non-accepted input. + + # int d2[0..Q, 1..S] + d2 = [] + for i in range(Q+1): + row = [] + for j in range(S): + if i == 0: + row.append(0) + else: + row.append(d[i-1][j]) + d2.append(row) + + d2_flatten = [d2[i][j] for i in range(Q+1) for j in range(S)] + + # If x has index set m..n, then a[m-1] holds the initial state + # (q0), and a[i+1] holds the state we're in after processing + # x[i]. If a[n] is in F, then we succeed (ie. accept the + # string). + x_range = range(0,len(x)) + m = 0 + n = len(x) + + a = [solver.IntVar(0, Q+1, 'a[%i]' % i) for i in range(m, n+1)] + + # Check that the final state is in F + solver.Add(solver.MemberCt(a[-1], F)) + # First state is q0 + solver.Add(a[m] == q0) + for i in x_range: + solver.Add(x[i] >= 1) + solver.Add(x[i] <= S) + # Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == solver.Element(d2_flatten, ((a[i])*S)+(x[i]-1))) + + +# +# Make a transition (automaton) matrix from a +# single pattern, e.g. [3,2,1] +# +def make_transition_matrix(pattern): + + p_len = len(pattern) + num_states = p_len + sum(pattern) + + # this is for handling 0-clues. It generates + # just the state 1,2 + if num_states == 0: + num_states = 1 + + t_matrix = [] + for i in range(num_states): + row = [] + for j in range(2): + row.append(0) + t_matrix.append(row) + + # convert pattern to a 0/1 pattern for easy handling of + # the states + tmp = [0 for i in range(num_states)] + c = 0 + tmp[c] = 0 + for i in range(p_len): + for j in range(pattern[i]): + c += 1 + tmp[c] = 1 + if c < num_states-1: + c += 1 + tmp[c] = 0 + + t_matrix[num_states-1][0] = num_states + t_matrix[num_states-1][1] = 0 + + for i in range(num_states): + if tmp[i] == 0: + t_matrix[i][0] = i+1 + t_matrix[i][1] = i+2 + else: + if i < num_states-1: + if tmp[i+1] == 1: + t_matrix[i][0] = 0 + t_matrix[i][1] = i+2 + else: + t_matrix[i][0] = i+2 + t_matrix[i][1] = 0 + + + # print 'The states:' + # for i in range(num_states): + # for j in range(2): + # print t_matrix[i][j], + # print + # print + + return t_matrix + +# +# check each rule by creating an automaton +# and regular +# +def check_rule(rules, y): + solver = y[0].solver() + + r_len = sum([1 for i in range(len(rules)) if rules[i] > 0]) + rules_tmp = [] + for i in range(len(rules)): + if rules[i] > 0: + rules_tmp.append(rules[i]) + + transition_fn = make_transition_matrix(rules_tmp) + n_states = len(transition_fn) + input_max = 2 + + # Note: we cannot use 0 since it's the failing state + initial_state = 1 + accepting_states = [n_states] # This is the last state + + regular(y, n_states, input_max, transition_fn, + initial_state, accepting_states) + + + +def main(rows, row_rule_len, row_rules, + cols, col_rule_len, col_rules): + + # Create the solver. + solver = pywrapcp.Solver('Regular test') + + # + # data + # + + # + # variables + # + board = {} + for i in range(rows): + for j in range(cols): + board[i,j] = solver.IntVar(1,2,'board[%i,%i]' % (i,j)) + board_flat = [board[i,j] for i in range(rows) for j in range(cols)] + + # Flattened board for labeling. + # This labeling was inspired by a suggestion from + # Pascal Van Hentenryck about my Comet nonogram model. + board_label = [] + if rows * row_rule_len < cols * col_rule_len: + for i in range(rows): + for j in range(cols): + board_label.append(board[i,j]) + else: + for j in range(cols): + for i in range(rows): + board_label.append(board[i,j]) + + + # + # constraints + # + for i in range(rows): + check_rule([row_rules[i][j] for j in range(row_rule_len)], + [board[i,j] for j in range(cols)]) + + for j in range(cols): + check_rule([col_rules[j][k] for k in range(col_rule_len)], + [board[i,j] for i in range(rows)]) + + + # + # solution and search + # + db = solver.Phase(board_label, + solver.CHOOSE_FIRST_UNBOUND, + solver.ASSIGN_MIN_VALUE) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + print + num_solutions += 1 + for i in range(rows): + row = [board[i,j].Value()-1 for j in range(cols)] + row_pres = [] + for j in row: + if j == 1: + row_pres.append('#') + else: + row_pres.append(' ') + print ' ', ''.join(row_pres) + + print + print ' ', '-' * cols + + if num_solutions >= 2: + print '2 solutions is enough...' + break + + solver.EndSearch() + print + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + + +# +# Default problem +# +# From http://twan.home.fmf.nl/blog/haskell/Nonograms.details +# The lambda picture +# +rows = 12 +row_rule_len = 3 +row_rules = [ + [0,0,2], + [0,1,2], + [0,1,1], + [0,0,2], + [0,0,1], + [0,0,3], + [0,0,3], + [0,2,2], + [0,2,1], + [2,2,1], + [0,2,3], + [0,2,2] + ] + +cols = 10 +col_rule_len = 2 +col_rules = [ + [2,1], + [1,3], + [2,4], + [3,4], + [0,4], + [0,3], + [0,3], + [0,3], + [0,2], + [0,2] + ] + + +if __name__ == '__main__': + if len(sys.argv) > 1: + file = sys.argv[1] + execfile(file) + main(rows, row_rule_len, row_rules, + cols, col_rule_len, col_rules) diff --git a/python/nurse_rostering.py b/python/nurse_rostering.py new file mode 100644 index 0000000000..16cd45fef5 --- /dev/null +++ b/python/nurse_rostering.py @@ -0,0 +1,268 @@ +# 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. + +""" + + Nurse rostering in Google CP Solver. + + This is a simple nurse rostering model using a DFA and + my decomposition of regular constraint. + + The DFA is from MiniZinc Tutorial, Nurse Rostering example: + - one day off every 4 days + - no 3 nights in a row. + + + 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/ + +""" + +from constraint_solver import pywrapcp +from collections import defaultdict + +# +# Global constraint regular +# +# This is a translation of MiniZinc's regular constraint (defined in +# lib/zinc/globals.mzn), via the Comet code refered above. +# All comments are from the MiniZinc code. +# ''' +# The sequence of values in array 'x' (which must all be in the range 1..S) +# is accepted by the DFA of 'Q' states with input 1..S and transition +# function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' +# (which must be in 1..Q) and accepting states 'F' (which all must be in +# 1..Q). We reserve state 0 to be an always failing state. +# ''' +# +# x : IntVar array +# Q : number of states +# S : input_max +# d : transition matrix +# q0: initial state +# F : accepting states +def regular(x, Q, S, d, q0, F): + + solver = x[0].solver() + + assert Q > 0, 'regular: "Q" must be greater than zero' + assert S > 0, 'regular: "S" must be greater than zero' + + # d2 is the same as d, except we add one extra transition for + # each possible input; each extra transition is from state zero + # to state zero. This allows us to continue even if we hit a + # non-accepted input. + + # Comet: int d2[0..Q, 1..S] + d2 = [] + for i in range(Q+1): + row = [] + for j in range(S): + if i == 0: + row.append(0) + else: + row.append(d[i-1][j]) + d2.append(row) + + d2_flatten = [d2[i][j] for i in range(Q+1) for j in range(S)] + + # If x has index set m..n, then a[m-1] holds the initial state + # (q0), and a[i+1] holds the state we're in after processing + # x[i]. If a[n] is in F, then we succeed (ie. accept the + # string). + x_range = range(0,len(x)) + m = 0 + n = len(x) + + a = [solver.IntVar(0, Q+1, 'a[%i]' % i) for i in range(m, n+1)] + + # Check that the final state is in F + solver.Add(solver.MemberCt(a[-1], F)) + # First state is q0 + solver.Add(a[m] == q0) + for i in x_range: + solver.Add(x[i] >= 1) + solver.Add(x[i] <= S) + + # Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == solver.Element(d2_flatten, ((a[i])*S)+(x[i]-1))) + + + + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Nurse rostering using regular') + + # + # data + # + + # Note: If you change num_nurses or num_days, + # please also change the constraints + # on nurse_stat and/or day_stat. + num_nurses = 7 + num_days = 14 + + day_shift = 1 + night_shift = 2 + off_shift = 3 + shifts = [day_shift, night_shift, off_shift] + + # the DFA (for regular) + n_states = 6 + input_max = 3 + initial_state = 1 # 0 is for the failing state + accepting_states = [1,2,3,4,5,6] + + transition_fn = [ + # d,n,o + [2,3,1], # state 1 + [4,4,1], # state 2 + [4,5,1], # state 3 + [6,6,1], # state 4 + [6,0,1], # state 5 + [0,0,1] # state 6 + ] + + days = ['d','n','o'] # for presentation + + # + # declare variables + # + x = {} + for i in range(num_nurses): + for j in range(num_days): + x[i,j] = solver.IntVar(shifts, 'x[%i,%i]'% (i,j)) + + x_flat = [x[i,j] for i in range(num_nurses) for j in range(num_days)] + + # summary of the nurses + nurse_stat = [solver.IntVar(0, num_days, 'nurse_stat[%i]'%i) + for i in range(num_nurses)] + + # summary of the shifts per day + day_stat = {} + for i in range(num_days): + for j in shifts: + day_stat[i,j] = solver.IntVar(0, num_nurses, 'day_stat[%i,%i]'% (i,j)) + + day_stat_flat = [day_stat[i,j] for i in range(num_days) for j in shifts] + + + # + # constraints + # + for i in range(num_nurses): + reg_input = [x[i,j] for j in range(num_days)] + regular(reg_input, n_states, input_max, transition_fn, + initial_state, accepting_states) + + # + # Statistics and constraints for each nurse + # + for i in range(num_nurses): + # number of worked days (day or night shift) + b = [solver.IsEqualCstVar(x[i,j], day_shift) + + solver.IsEqualCstVar(x[i,j], night_shift) + for j in range(num_days)] + solver.Add(nurse_stat[i] == solver.Sum(b)) + + # Each nurse must work between 7 and 10 + # days during this period + solver.Add(nurse_stat[i] >= 7) + solver.Add(nurse_stat[i] <= 10) + + + # + # Statistics and constraints for each day + # + for j in range(num_days): + for t in shifts: + b = [solver.IsEqualCstVar(x[i,j], t) + for i in range(num_nurses)] + solver.Add(day_stat[j,t] == solver.Sum(b)) + + # + # Some constraints for this day: + # + # Note: We have a strict requirements of + # the number of shifts. + # Using atleast constraints is much harder + # in this model. + # + if j % 7 == 5 or j % 7 == 6: + # special constraints for the weekends + solver.Add(day_stat[j,day_shift] == 2) + solver.Add(day_stat[j,night_shift] == 1) + solver.Add(day_stat[j,off_shift] == 4 ) + else: + # workdays: + + # - exactly 3 on day shift + solver.Add(day_stat[j,day_shift] == 3) + # - exactly 2 on night + solver.Add(day_stat[j,night_shift] == 2) + # - exactly 1 off duty + solver.Add(day_stat[j,off_shift] == 2 ) + + + # + # solution and search + # + db = solver.Phase(day_stat_flat + x_flat + nurse_stat, + solver.CHOOSE_FIRST_UNBOUND, + solver.ASSIGN_MIN_VALUE) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + num_solutions += 1 + + for i in range(num_nurses): + print 'Nurse%i: ' % i, + this_day_stat = defaultdict(int) + for j in range(num_days): + d = days[x[i,j].Value()-1] + this_day_stat[d] += 1 + print d, + print ' day_stat:', [(d, this_day_stat[d]) for d in this_day_stat], + print 'total:', nurse_stat[i].Value(), 'workdays' + print + + print 'Statistics per day:' + for j in range(num_days): + print 'Day%2i: ' % j, + for t in shifts: + print day_stat[j,t].Value(), + print + print + + # We just show 2 solutions + if num_solutions >= 2: + break + + + solver.EndSearch() + print + print 'num_solutions:', num_solutions + print 'failures:', solver.failures() + print 'branches:', solver.branches() + print 'wall_time:', solver.wall_time(), 'ms' + + +if __name__ == '__main__': + main() diff --git a/python/regular.py b/python/regular.py new file mode 100644 index 0000000000..6a5e7dcbf0 --- /dev/null +++ b/python/regular.py @@ -0,0 +1,225 @@ +# 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. + +""" + + Global constraint regular in Google CP Solver. + + This is a translation of MiniZinc's regular constraint (defined in + lib/zinc/globals.mzn). All comments are from the MiniZinc code. + ''' + The sequence of values in array 'x' (which must all be in the range 1..S) + is accepted by the DFA of 'Q' states with input 1..S and transition + function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' + (which must be in 1..Q) and accepting states 'F' (which all must be in + 1..Q). We reserve state 0 to be an always failing state. + ''' + + It is, however, translated from the Comet model: + * Comet: http://www.hakank.org/comet/regular.co + + Here we test with the following regular expression: + 0*1{3}0+1{2}0+1{1}0* + using an array of size 10. + + 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/ + +""" + +from constraint_solver import pywrapcp + + +# +# Global constraint regular +# +# This is a translation of MiniZinc's regular constraint (defined in +# lib/zinc/globals.mzn), via the Comet code refered above. +# All comments are from the MiniZinc code. +# ''' +# The sequence of values in array 'x' (which must all be in the range 1..S) +# is accepted by the DFA of 'Q' states with input 1..S and transition +# function 'd' (which maps (1..Q, 1..S) -> 0..Q)) and initial state 'q0' +# (which must be in 1..Q) and accepting states 'F' (which all must be in +# 1..Q). We reserve state 0 to be an always failing state. +# ''' +# +# x : IntVar array +# Q : number of states +# S : input_max +# d : transition matrix +# q0: initial state +# F : accepting states +def regular(x, Q, S, d, q0, F): + + solver = x[0].solver() + + assert Q > 0, 'regular: "Q" must be greater than zero' + assert S > 0, 'regular: "S" must be greater than zero' + + # d2 is the same as d, except we add one extra transition for + # each possible input; each extra transition is from state zero + # to state zero. This allows us to continue even if we hit a + # non-accepted input. + + # int d2[0..Q, 1..S]; + d2 = [] + for i in range(Q+1): + row = [] + for j in range(S): + if i == 0: + row.append(0) + else: + row.append(d[i-1][j]) + d2.append(row) + + d2_flatten = [d2[i][j] for i in range(Q+1) for j in range(S)] + + # If x has index set m..n, then a[m-1] holds the initial state + # (q0), and a[i+1] holds the state we're in after processing + # x[i]. If a[n] is in F, then we succeed (ie. accept the + # string). + x_range = range(0,len(x)) + m = 0 + n = len(x) + + a = [solver.IntVar(0, Q+1, 'a[%i]' % i) for i in range(m, n+1)] + + # Check that the final state is in F + solver.Add(solver.MemberCt(a[-1], F)) + # First state is q0 + solver.Add(a[m] == q0) + for i in x_range: + solver.Add(x[i] >= 1) + solver.Add(x[i] <= S) + + # Determine a[i+1]: a[i+1] == d2[a[i], x[i]] + solver.Add(a[i+1] == solver.Element(d2_flatten, ((a[i])*S)+(x[i]-1))) + + + +# +# Make a transition (automaton) matrix from a +# single pattern, e.g. [3,2,1] +# +def make_transition_matrix(pattern): + + p_len = len(pattern) + print 'p_len:', p_len + num_states = p_len + sum(pattern) + print 'num_states:', num_states + t_matrix = [] + for i in range(num_states): + row = [] + for j in range(2): + row.append(0) + t_matrix.append(row) + + # convert pattern to a 0/1 pattern for easy handling of + # the states + tmp = [0 for i in range(num_states)] + c = 0 + tmp[c] = 0 + for i in range(p_len): + for j in range(pattern[i]): + c += 1 + tmp[c] = 1 + if c < num_states-1: + c += 1 + tmp[c] = 0 + print 'tmp:', tmp + + + t_matrix[num_states-1][0] = num_states + t_matrix[num_states-1][1] = 0 + + for i in range(num_states): + if tmp[i] == 0: + t_matrix[i][0] = i+1 + t_matrix[i][1] = i+2 + else: + if i < num_states-1: + if tmp[i+1] == 1: + t_matrix[i][0] = 0 + t_matrix[i][1] = i+2 + else: + t_matrix[i][0] = i+2 + t_matrix[i][1] = 0 + + print 'The states:' + for i in range(num_states): + for j in range(2): + print t_matrix[i][j], + print + print + + return t_matrix + +def main(): + + # Create the solver. + solver = pywrapcp.Solver('Regular test') + + # + # data + # + + this_len = 10 + pp = [3,2,1] + + transition_fn = make_transition_matrix(pp) + n_states = len(transition_fn) + input_max = 2 + + # Note: we use '1' and '2' (rather than 0 and 1) + # since 0 represents the failing state. + initial_state = 1 + + accepting_states = [n_states] + + # declare variables + reg_input = [solver.IntVar(1, input_max, 'reg_input[%i]' % i) + for i in range(this_len)] + + # + # constraints + # + regular(reg_input, n_states, input_max, transition_fn, + initial_state, accepting_states) + + + # + # solution and search + # + db = solver.Phase(reg_input, + solver.CHOOSE_MIN_SIZE_HIGHEST_MAX, + solver.ASSIGN_MIN_VALUE) + + solver.NewSearch(db) + + num_solutions = 0 + while solver.NextSolution(): + print 'reg_input:', [reg_input[i].Value()-1 for i in range(this_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(), 'ms' + + +if __name__ == '__main__': + main()