gecoder 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +16 -10
- data/THANKS +10 -9
- data/example/minesweeper.rb +136 -0
- data/example/nonogram.rb +155 -0
- data/example/survo.rb +131 -0
- data/ext/gecoder.cpp +10 -14
- data/ext/gecoder.h +3 -6
- data/lib/gecoder/bindings/bindings.rb +11 -6
- data/lib/gecoder/interface/constraints.rb +10 -2
- data/lib/gecoder/interface/constraints/extensional_regexp.rb +8 -4
- data/lib/gecoder/interface/constraints/set/connection.rb +3 -2
- data/lib/gecoder/interface/constraints/set_enum/channel.rb +1 -1
- data/lib/gecoder/interface/search.rb +38 -7
- data/lib/gecoder/version.rb +1 -1
- data/specs/search.rb +43 -0
- data/tasks/distribution.rake +25 -20
- data/tasks/rcov.rake +2 -0
- data/tasks/specs.rake +2 -0
- data/tasks/website.rake +3 -3
- data/vendor/rust/include/rust_conversions.hh +22 -3
- data/vendor/rust/rust/class.rb +48 -24
- data/vendor/rust/rust/function.rb +1 -1
- data/vendor/rust/rust/templates/CxxClassDefinitions.rusttpl +2 -2
- metadata +249 -248
data/CHANGES
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
== Version 1.1.0
|
2
|
+
This release fixes a compilation error when installing the gecoder-with-gecode gem using GCC 4.4+ .
|
3
|
+
|
4
|
+
* Added the :time_limit to Gecode#solve! which can be used to limit the amount of time that the solver is allowed to search for a solution.
|
5
|
+
* [#29630] Fixed compilation errors when installing gecoder-with-gecode using GCC 4.4+ .
|
6
|
+
|
1
7
|
== Version 1.0.0
|
2
8
|
This release represents a commitment to the current syntax.
|
3
9
|
|
@@ -10,7 +16,7 @@ This release changes the preferred way of defining a model to including Gecode::
|
|
10
16
|
* Updated the backend to Gecode 2.2.0. See http://www.gecode.org/gecode-doc-latest/PageChanges_2_2_0.html for a list of changes.
|
11
17
|
|
12
18
|
== Version 0.9.0
|
13
|
-
This release adds a generous amount of sugar designed to cut down on often
|
19
|
+
This release adds a generous amount of sugar designed to cut down on often
|
14
20
|
repeated code. It also adds the ability to freely combine constraints
|
15
21
|
without needing temporary variables.
|
16
22
|
|
@@ -24,11 +30,11 @@ without needing temporary variables.
|
|
24
30
|
* [#21580] Fixed a bug that caused ranges that use three dots to not be correctly interpreted by the negated integer domain constraint.
|
25
31
|
* [#21581] Fixed a bug that caused the abs constraint to prune away valid solutions in some cases.
|
26
32
|
|
27
|
-
== Version 0.8.3
|
33
|
+
== Version 0.8.3
|
28
34
|
This release adds regular expression constraints, the last channel
|
29
35
|
constraint and various minor convenience improvements. It also fixes a
|
30
36
|
rather serious bug that occurs when using the latest patchlevels of Ruby
|
31
|
-
1.8.6.
|
37
|
+
1.8.6.
|
32
38
|
|
33
39
|
* [#20888] Fixed a GC bug causing problems with Ruby 1.8.6 at patchlevels around 230.
|
34
40
|
* Boolean constraints can no longer be specified using the form "bool.must == true". Only the "bool.must_be.true" form can now be used.
|
@@ -39,7 +45,7 @@ rather serious bug that occurs when using the latest patchlevels of Ruby
|
|
39
45
|
* Added integer and boolean regular expression constraints. Thanks goes to Eivind Eklund for providing the idea for the syntax used to specify the regular expressions.
|
40
46
|
|
41
47
|
== Version 0.8.2
|
42
|
-
This release adds search statistics along with some new arithmetic constraints
|
48
|
+
This release adds search statistics along with some new arithmetic constraints
|
43
49
|
and channel constraints between boolean and integer variables.
|
44
50
|
|
45
51
|
* Wrapping an enumerable that is already wrapped is no longer allowed.
|
@@ -49,17 +55,17 @@ and channel constraints between boolean and integer variables.
|
|
49
55
|
* Added channel constraints between enumerations of boolean variables and single integer variables.
|
50
56
|
|
51
57
|
== Version 0.8.1
|
52
|
-
This release adds tuple constraints along with a couple of minor features. It
|
58
|
+
This release adds tuple constraints along with a couple of minor features. It
|
53
59
|
also fixes a bug introduced in the previous version.
|
54
60
|
|
55
61
|
* [#19435] Fixed a bug causing inconsistencies during BAB-search. The bug stopped the send+more=money example from working correctly.
|
56
62
|
* Fixed the "raw_bindings" and "sudoku-set" examples, which were broken by the 0.8.0 release.
|
57
|
-
* Integers can now be used to specify singleton lower and upper bounds when creating set variables.
|
63
|
+
* Integers can now be used to specify singleton lower and upper bounds when creating set variables.
|
58
64
|
* Added convenience methods Model#maximize! and Model#minimize! for optimizing single variables.
|
59
65
|
* Added tuple constraints for enumerations of integer and boolean variables.
|
60
66
|
|
61
67
|
== Version 0.8.0
|
62
|
-
This release makes the jump from using Gecode 1.3.1 to using Gecode 2.1.1 .
|
68
|
+
This release makes the jump from using Gecode 1.3.1 to using Gecode 2.1.1 .
|
63
69
|
The following changes have been made to the interface as a result of the jump.
|
64
70
|
|
65
71
|
* Removed the distinct constraint for sets.
|
@@ -87,7 +93,7 @@ int variable domains are specified (breaking backward-compatibility).
|
|
87
93
|
* Variables can now be created inside the optimization block.
|
88
94
|
|
89
95
|
== Version 0.6.0
|
90
|
-
This release adds most of the remaining set constraints. It also makes
|
96
|
+
This release adds most of the remaining set constraints. It also makes
|
91
97
|
backward-compatibility breaking changes to the way that properties of variables
|
92
98
|
are accessed.
|
93
99
|
|
@@ -101,7 +107,7 @@ are accessed.
|
|
101
107
|
* Enumerations containing variables now provide a convenience method #values which returns an array of the enum's values.
|
102
108
|
|
103
109
|
== Version 0.5.0
|
104
|
-
This release adds set variables and some of their constraints, along with the
|
110
|
+
This release adds set variables and some of their constraints, along with the
|
105
111
|
last of the boolean constraints.
|
106
112
|
|
107
113
|
* Added exclusive or and implication.
|
@@ -125,7 +131,7 @@ This release adds most of the integer variable constraints supported by Gecode.
|
|
125
131
|
* Added arithmetic constraints (min, max, abs and variable multiplication).
|
126
132
|
|
127
133
|
== Version 0.3.0
|
128
|
-
This release fleshes out the existing constraints with things such as
|
134
|
+
This release fleshes out the existing constraints with things such as
|
129
135
|
reification and adds boolean variables and their basic constraints.
|
130
136
|
|
131
137
|
* The constructor of Gecode::Model no longer has to be called by classes inheriting from it.
|
data/THANKS
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
This is a list of people who have contributed to making Gecode/R what it
|
2
2
|
is today. It is sorted in alphabetical order by last name.
|
3
3
|
|
4
|
-
* David Cuadrado -
|
5
|
-
* Eivind Eklund -
|
4
|
+
* David Cuadrado - Created the initial raw bindings from Gecode to Ruby.
|
5
|
+
* Eivind Eklund - Provided the idea that led to the syntax used to specify
|
6
6
|
the regular expressions used in regexp constraints.
|
7
|
-
* Google and Ruby Central -
|
8
|
-
|
9
|
-
* James Edward Gray II -
|
7
|
+
* Google and Ruby Central - Got the project started with funding through
|
8
|
+
Google Summer of Code 2007.
|
9
|
+
* James Edward Gray II - Provided superb mentoring during Google Summer
|
10
10
|
of Code 2007, influencing many decisions made.
|
11
|
-
*
|
12
|
-
*
|
11
|
+
* Hakan Kjellerstrand - Created many new example models.
|
12
|
+
* Mikael Lagerkvist - Provided support from the Gecode side.
|
13
|
+
* Andreas Launila - Created the interface on top of the raw bindings and
|
13
14
|
the website.
|
14
|
-
* Adam Rose -
|
15
|
-
|
15
|
+
* Adam Rose - Suggested that Gecode/R should use a mixin rather than
|
16
|
+
inheritance.
|
16
17
|
|
17
18
|
If you think that you should be on this list then please contact the
|
18
19
|
gecoder-devel mailing list.
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Copyright 2009, Hakan Kjellerstrand <hakank@bonetmail.com>
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with this program; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require File.dirname(__FILE__) + '/example_helper'
|
18
|
+
require 'enumerator'
|
19
|
+
|
20
|
+
#
|
21
|
+
# Minesweeper in Gecode/R
|
22
|
+
#
|
23
|
+
# From gecode/examples/minesweeper.cc:
|
24
|
+
# """
|
25
|
+
# A specification is a square matrix of characters. Alphanumeric
|
26
|
+
# characters represent the number of mines adjacent to that field.
|
27
|
+
# Dots represent fields with an unknown number of mines adjacent to
|
28
|
+
# it (or an actual mine).
|
29
|
+
# """
|
30
|
+
#
|
31
|
+
# E.g.
|
32
|
+
# "..2.3."
|
33
|
+
# "2....."
|
34
|
+
# "..24.3"
|
35
|
+
# "1.34.."
|
36
|
+
# ".....3"
|
37
|
+
# ".3.3.."
|
38
|
+
# """
|
39
|
+
#
|
40
|
+
# Also see
|
41
|
+
#
|
42
|
+
# http://www.janko.at/Raetsel/Minesweeper/index.htm
|
43
|
+
#
|
44
|
+
# http://en.wikipedia.org/wiki/Minesweeper_(computer_game)
|
45
|
+
#
|
46
|
+
# Ian Stewart on Minesweeper: http://www.claymath.org/Popular_Lectures/Minesweeper/
|
47
|
+
#
|
48
|
+
# Richard Kaye's Minesweeper Pages
|
49
|
+
# http://web.mat.bham.ac.uk/R.W.Kaye/minesw/minesw.htm
|
50
|
+
# Some Minesweeper Configurations
|
51
|
+
# http://web.mat.bham.ac.uk/R.W.Kaye/minesw/minesw.pdf
|
52
|
+
#
|
53
|
+
# Compare with my other Minesweeper models:
|
54
|
+
#
|
55
|
+
# - MiniZinc: http://www.hakank.org/minizinc/minesweeper.mzn
|
56
|
+
#
|
57
|
+
# - Choco: http://www.hakank.org/choco/MineSweeper.java
|
58
|
+
#
|
59
|
+
# - JaCoP: http://www.hakank.org/JaCoP/MineSweeper.java
|
60
|
+
#
|
61
|
+
#
|
62
|
+
# Model created by Hakan Kjellerstrand, hakank@bonetmail.com
|
63
|
+
# See also my Gecode/R page: http://www.hakank.org/gecode_r
|
64
|
+
#
|
65
|
+
# Slight modifications made by Andreas Launila to keep the example in
|
66
|
+
# line with the other example models.
|
67
|
+
class Minesweeper
|
68
|
+
include Gecode::Mixin
|
69
|
+
|
70
|
+
# The provided +game+ should be a matrix encoding the problem in the
|
71
|
+
# following way:
|
72
|
+
# -1 for the unknowns,
|
73
|
+
# >= 0 for number of mines in the neighbourhood
|
74
|
+
def initialize(game)
|
75
|
+
# Boolean variables representing whether each square has a mine or
|
76
|
+
# not.
|
77
|
+
mines_is_an bool_var_matrix(game.row_size, game.column_size)
|
78
|
+
|
79
|
+
# Place the constraints.
|
80
|
+
game.row_size.times do |i|
|
81
|
+
game.column_size.times do |j|
|
82
|
+
# The sum of all the number of mines in the neighbourhood of this cell
|
83
|
+
# must agree with the problem specification.
|
84
|
+
if game[i,j] >= 0 then
|
85
|
+
neighbourhood = mines.minor([i-1,0].max..(i+1), [j-1,0].max..(j+1))
|
86
|
+
neighbourhood.to_a.flatten.sum.must == game[i,j]
|
87
|
+
end
|
88
|
+
|
89
|
+
# A square can not contain a mine if the number of neighbouring
|
90
|
+
# mines is known.
|
91
|
+
if game[i,j] >= 0 then
|
92
|
+
mines[i,j].must_be.false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
branch_on mines, :variable => :largest_degree, :value => :max
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Array
|
102
|
+
# A helper for summing the contents of an array.
|
103
|
+
def sum
|
104
|
+
inject{ |sum, x| sum + x }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Problem from Gecode/examples/minesweeper.cc problem 2
|
109
|
+
X = -1 # unknowns
|
110
|
+
game = Matrix[
|
111
|
+
[1,X,X,2,X,2,X,2,X,X],
|
112
|
+
[X,3,2,X,X,X,4,X,X,1],
|
113
|
+
[X,X,X,1,3,X,X,X,4,X],
|
114
|
+
[3,X,1,X,X,X,3,X,X,X],
|
115
|
+
[X,2,1,X,1,X,X,3,X,2],
|
116
|
+
[X,3,X,2,X,X,2,X,1,X],
|
117
|
+
[2,X,X,3,2,X,X,2,X,X],
|
118
|
+
[X,3,X,X,X,3,2,X,X,3],
|
119
|
+
[X,X,3,X,3,3,X,X,X,X],
|
120
|
+
[X,2,X,2,X,X,X,2,2,X]
|
121
|
+
]
|
122
|
+
|
123
|
+
minesweeper = Minesweeper.new(game)
|
124
|
+
# Find all of the solutions.
|
125
|
+
num_solutions = 0
|
126
|
+
minesweeper.each_solution do |s|
|
127
|
+
num_solutions += 1
|
128
|
+
puts "\nSolution ##{num_solutions}\n";
|
129
|
+
# Print the solution.
|
130
|
+
s.mines.values.enum_slice(s.mines.column_size).each do |row|
|
131
|
+
puts row.map{ |filled| filled ? "X " : ". " }.join
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
puts "\nNumber of solutions: #{num_solutions}"
|
136
|
+
|
data/example/nonogram.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# Copyright 2009, Hakan Kjellerstrand <hakank@bonetmail.com>
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with this program; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require File.dirname(__FILE__) + '/example_helper'
|
18
|
+
require 'enumerator'
|
19
|
+
|
20
|
+
#
|
21
|
+
# Nonogram (a.k.a. Painting by Numbers) in Gecode/R
|
22
|
+
#
|
23
|
+
# http://en.wikipedia.org/wiki/Nonogram
|
24
|
+
# """
|
25
|
+
# Nonograms or Paint by Numbers are picture logic puzzles in which cells
|
26
|
+
# in a grid have to be colored or left blank according to numbers given
|
27
|
+
# at the side of the grid to reveal a hidden picture. In this puzzle
|
28
|
+
# type, the numbers measure how many unbroken lines of filled-in squares
|
29
|
+
# there are in any given row or column. For example, a clue of "4 8 3"
|
30
|
+
# would mean there are sets of four, eight, and three filled squares, in
|
31
|
+
# that order, with at least one blank square between successive groups.
|
32
|
+
# """
|
33
|
+
#
|
34
|
+
# Also see
|
35
|
+
# * Brunetti, Sara & Daurat, Alain (2003)
|
36
|
+
# "An algorithm reconstructing convex lattice sets"
|
37
|
+
# http://geodisi.u-strasbg.fr/~daurat/papiers/tomoqconv.pdf
|
38
|
+
#
|
39
|
+
# * CSPLib problem 12 at http://www.csplib.org/
|
40
|
+
#
|
41
|
+
# * http://www.puzzlemuseum.com/nonogram.htm
|
42
|
+
#
|
43
|
+
# * Haskell solution:
|
44
|
+
# http://twan.home.fmf.nl/blog/haskell/Nonograms.details
|
45
|
+
#
|
46
|
+
# * My MiniZinc model http://www.hakank.org/minizinc/nonogram.mzn
|
47
|
+
#
|
48
|
+
#
|
49
|
+
# Model created by Hakan Kjellerstrand, hakank@bonetmail.com
|
50
|
+
# See also my Gecode/R page: http://www.hakank.org/gecode_r
|
51
|
+
#
|
52
|
+
# Slight modifications made by Andreas Launila to keep the example in
|
53
|
+
# line with the other example models.
|
54
|
+
class Nonogram
|
55
|
+
include Gecode::Mixin
|
56
|
+
|
57
|
+
def initialize(row_rules, col_rules)
|
58
|
+
# A matrix of variables where each variable represents whether the
|
59
|
+
# square has been filled in or not.
|
60
|
+
filled_is_an bool_var_matrix(row_rules.size, col_rules.size)
|
61
|
+
|
62
|
+
# Place the constraints on the rows.
|
63
|
+
row_rules.each_with_index do |row_rule, i|
|
64
|
+
filled.row(i).must.match parse_regex(row_rule)
|
65
|
+
end
|
66
|
+
# Place the constraints on the columns.
|
67
|
+
col_rules.each_with_index do |col_rule, i|
|
68
|
+
filled.column(i).must.match parse_regex(col_rule)
|
69
|
+
end
|
70
|
+
|
71
|
+
branch_on filled, :variable => :none, :value => :max
|
72
|
+
end
|
73
|
+
|
74
|
+
# Parses a nonogram segment and converts it to a "regexp"
|
75
|
+
# e.g. [3,2] -> [repeat(false), repeat(true,3,3), at_least_once(false),
|
76
|
+
# repeat(true,2,2),repeat(false)]
|
77
|
+
def parse_regex(a)
|
78
|
+
r = [repeat(false)]
|
79
|
+
a.each_with_index do |e,i|
|
80
|
+
r << repeat(true,e,e)
|
81
|
+
if i < a.length-1 then
|
82
|
+
r << at_least_once(false)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
r << repeat(false)
|
86
|
+
return r
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# A nonogram problem taken from Wikipedia
|
91
|
+
# http://en.wikipedia.org/wiki/Nonogram
|
92
|
+
# Animation:
|
93
|
+
# http://en.wikipedia.org/wiki/File:Paint_by_numbers_Animation.gif
|
94
|
+
#
|
95
|
+
row_rules =
|
96
|
+
[
|
97
|
+
[3],
|
98
|
+
[5],
|
99
|
+
[3,1],
|
100
|
+
[2,1],
|
101
|
+
[3,3,4],
|
102
|
+
[2,2,7],
|
103
|
+
[6,1,1],
|
104
|
+
[4,2,2],
|
105
|
+
[1,1],
|
106
|
+
[3,1],
|
107
|
+
[6],
|
108
|
+
[2,7],
|
109
|
+
[6,3,1],
|
110
|
+
[1,2,2,1,1],
|
111
|
+
[4,1,1,3],
|
112
|
+
[4,2,2],
|
113
|
+
[3,3,1],
|
114
|
+
[3,3],
|
115
|
+
[3],
|
116
|
+
[2,1]
|
117
|
+
]
|
118
|
+
|
119
|
+
col_rules =
|
120
|
+
[
|
121
|
+
[2],
|
122
|
+
[1,2],
|
123
|
+
[2,3],
|
124
|
+
[2,3],
|
125
|
+
[3,1,1],
|
126
|
+
[2,1,1],
|
127
|
+
[1,1,1,2,2],
|
128
|
+
[1,1,3,1,3],
|
129
|
+
[2,6,4],
|
130
|
+
[3,3,9,1],
|
131
|
+
[5,3,2],
|
132
|
+
[3,1,2,2],
|
133
|
+
[2,1,7],
|
134
|
+
[3,3,2],
|
135
|
+
[2,4],
|
136
|
+
[2,1,2],
|
137
|
+
[2,2,1],
|
138
|
+
[2,2],
|
139
|
+
[1],
|
140
|
+
[1]
|
141
|
+
]
|
142
|
+
|
143
|
+
nonogram = Nonogram.new(row_rules, col_rules)
|
144
|
+
# Find all of the solutions.
|
145
|
+
num_solutions = 0
|
146
|
+
nonogram.each_solution do |s|
|
147
|
+
num_solutions += 1
|
148
|
+
puts "\nSolution ##{num_solutions}\n"
|
149
|
+
# Output the solution.
|
150
|
+
s.filled.values.enum_slice(s.filled.column_size).each do |row|
|
151
|
+
puts row.map{ |filled| filled ? "#" : " " }.join
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
puts "\nNumber of solutions: #{num_solutions}"
|
data/example/survo.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# Copyright 2009, Hakan Kjellerstrand <hakank@bonetmail.com>
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
5
|
+
# the Free Software Foundation; either version 2 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public License
|
14
|
+
# along with this program; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require File.dirname(__FILE__) + '/example_helper'
|
18
|
+
require 'enumerator'
|
19
|
+
|
20
|
+
# Survo Puzzle in Gecode/R
|
21
|
+
#
|
22
|
+
# http://en.wikipedia.org/wiki/Survo_Puzzle
|
23
|
+
# """
|
24
|
+
# Survo puzzle is a kind of logic puzzle presented (in April 2006) and studied
|
25
|
+
# by Seppo Mustonen. The name of the puzzle is associated to Mustonen's
|
26
|
+
# Survo system which is a general environment for statistical computing and
|
27
|
+
# related areas.
|
28
|
+
#
|
29
|
+
# In a Survo puzzle the task is to fill an m * n table by integers 1,2,...,m*n so
|
30
|
+
# that each of these numbers appears only once and their row and column sums are
|
31
|
+
# equal to integers given on the bottom and the right side of the table.
|
32
|
+
# Often some of the integers are given readily in the table in order to
|
33
|
+
# guarantee uniqueness of the solution and/or for making the task easier.
|
34
|
+
# """
|
35
|
+
#
|
36
|
+
# See also
|
37
|
+
# http://www.survo.fi/english/index.html
|
38
|
+
# http://www.survo.fi/puzzles/index.html
|
39
|
+
#
|
40
|
+
# References:
|
41
|
+
# Mustonen, S. (2006b). "On certain cross sum puzzles", http://www.survo.fi/papers/puzzles.pdf
|
42
|
+
# Mustonen, S. (2007b). "Enumeration of uniquely solvable open Survo puzzles.", http://www.survo.fi/papers/enum_survo_puzzles.pdf
|
43
|
+
# Kimmo Vehkalahti: "Some comments on magic squares and Survo puzzles", http://www.helsinki.fi/~kvehkala/Kimmo_Vehkalahti_Windsor.pdf
|
44
|
+
# R code: http://koti.mbnet.fi/tuimala/tiedostot/survo.R
|
45
|
+
#
|
46
|
+
# Compare with my other Survo Puzzle models
|
47
|
+
#
|
48
|
+
# - MiniZinc: http://www.hakank.org/minizinc/survo_puzzle.mzn
|
49
|
+
# - JaCoP: http://www.hakank.org/JaCoP/SurvoPuzzle.java
|
50
|
+
# - Choco: http://www.hakank.org/choco/SurvoPuzzle.java
|
51
|
+
#
|
52
|
+
# Model created by Hakan Kjellerstrand, hakank@bonetmail.com
|
53
|
+
# See also my Gecode/R page: http://www.hakank.org/gecode_r
|
54
|
+
#
|
55
|
+
# Slight modifications made by Andreas Launila to keep the example in
|
56
|
+
# line with the other example models.
|
57
|
+
class SurvoPuzzle
|
58
|
+
include Gecode::Mixin
|
59
|
+
|
60
|
+
# The +clues+ are given as an m*n matrix where 0 represents that the
|
61
|
+
# cell has no clue. The row sums and column sums are specified by the
|
62
|
+
# +rowsums+ array of length m, and the +colsums+ array of length n
|
63
|
+
# respectively.
|
64
|
+
def initialize(clues, rowsums, colsums)
|
65
|
+
r = rowsums.length # Number of rows
|
66
|
+
c = colsums.length # Number of columns
|
67
|
+
|
68
|
+
# Integer variables representing each cell in the m*n table.
|
69
|
+
cells_is_an int_var_matrix(r, c, 1..r*c)
|
70
|
+
|
71
|
+
# Add the constraints.
|
72
|
+
# Each number must appear only once.
|
73
|
+
cells.must_be.distinct
|
74
|
+
|
75
|
+
# The row sums must be satisfied.
|
76
|
+
cells.row_vectors.each_with_index do |row, i|
|
77
|
+
row.sum.must == rowsums[i]
|
78
|
+
end
|
79
|
+
|
80
|
+
# The column sums must be satisfied.
|
81
|
+
cells.column_vectors.each_with_index do |column, i|
|
82
|
+
column.sum.must == colsums[i]
|
83
|
+
end
|
84
|
+
|
85
|
+
# The clues must be satisfied.
|
86
|
+
cells.row_size.times do |i|
|
87
|
+
cells.column_size.times do |j|
|
88
|
+
cells[i,j].must == clues[i,j] if clues[i,j] > 0
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
branch_on cells, :variable => :smallest_size, :value => :min
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class Vector
|
97
|
+
# A helper for summing the contents of a vector.
|
98
|
+
def sum
|
99
|
+
inject{ |sum, element| sum + element }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Default problem:
|
104
|
+
# Survo puzzle 126/2008 (25) #363-33148
|
105
|
+
# From http://www.survo.fi/puzzles/280708.txt
|
106
|
+
rowsums = [32,79,60]
|
107
|
+
colsums = [24,22,43,35,39,8]
|
108
|
+
clues = Matrix[
|
109
|
+
[ 0, 4, 0, 0, 0, 0,32],
|
110
|
+
[12, 0, 0,16,17, 0,79],
|
111
|
+
[ 0, 0,15, 0, 0, 2,60],
|
112
|
+
]
|
113
|
+
|
114
|
+
survo_puzzle = SurvoPuzzle.new(clues, rowsums, colsums)
|
115
|
+
num_solutions = 0
|
116
|
+
survo_puzzle.each_solution do |s|
|
117
|
+
num_solutions += 1
|
118
|
+
puts "\nSolution ##{num_solutions}";
|
119
|
+
# Print the solution.
|
120
|
+
s.cells.values.enum_slice(s.cells.column_size).each_with_index do |row,i|
|
121
|
+
row.each{ |element| printf('%3d ', element) }
|
122
|
+
printf ' = %3d ', rowsums[i]
|
123
|
+
puts
|
124
|
+
end
|
125
|
+
colsums.size.times{ printf('%2s= ', '') }
|
126
|
+
puts
|
127
|
+
colsums.each{ |element| printf('%3d ', element) }
|
128
|
+
end
|
129
|
+
|
130
|
+
puts "\nNumber of solutions: #{num_solutions}"
|
131
|
+
|