gecoder 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|