winston 0.0.2 → 0.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.
- checksums.yaml +5 -5
- data/.gitignore +0 -1
- data/.tool-versions +1 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +36 -0
- data/README.md +188 -14
- data/Rakefile +5 -0
- data/bench/run.rb +310 -0
- data/lib/winston/csp.rb +30 -2
- data/lib/winston/dsl.rb +69 -0
- data/lib/winston/heuristics.rb +44 -0
- data/lib/winston/solvers/backtrack.rb +91 -0
- data/lib/winston/solvers/mac.rb +529 -0
- data/lib/winston/solvers/min_conflicts.rb +125 -0
- data/lib/winston.rb +8 -5
- data/spec/examples/map_coloring_spec.rb +48 -0
- data/spec/winston/backtrack_spec.rb +34 -1
- data/spec/winston/csp_spec.rb +25 -1
- data/spec/winston/dsl_spec.rb +71 -0
- data/spec/winston/heuristics_spec.rb +47 -0
- data/spec/winston/mac_spec.rb +44 -0
- data/spec/winston/min_conflicts_spec.rb +44 -0
- data/winston.gemspec +6 -5
- metadata +44 -21
- data/lib/winston/backtrack.rb +0 -40
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
require "winston"
|
|
2
2
|
|
|
3
|
-
describe Winston::Backtrack do
|
|
3
|
+
describe Winston::Solvers::Backtrack do
|
|
4
4
|
let(:csp) { Winston::CSP.new }
|
|
5
5
|
subject { described_class.new(csp) }
|
|
6
6
|
|
|
@@ -44,5 +44,38 @@ describe Winston::Backtrack do
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
|
+
|
|
48
|
+
context "with heuristics" do
|
|
49
|
+
it "uses MRV to pick the smallest remaining domain first" do
|
|
50
|
+
csp.add_variable :a, domain: [1, 2, 3]
|
|
51
|
+
csp.add_variable :b, domain: [1]
|
|
52
|
+
csp.add_variable :c, domain: [1, 2]
|
|
53
|
+
|
|
54
|
+
solver = described_class.new(csp, variable_strategy: :mrv)
|
|
55
|
+
result = solver.search
|
|
56
|
+
|
|
57
|
+
expect(result.keys).to eq([:b, :c, :a])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "uses LCV to prefer values that leave more options" do
|
|
61
|
+
csp.add_variable :a, domain: [2, 1]
|
|
62
|
+
csp.add_variable :b, domain: [2, 3]
|
|
63
|
+
csp.add_constraint(:a, :b) { |a, b| b > a }
|
|
64
|
+
|
|
65
|
+
solver = described_class.new(csp, value_strategy: :lcv)
|
|
66
|
+
result = solver.search
|
|
67
|
+
|
|
68
|
+
expect(result[:a]).to eq(1)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "supports forward checking" do
|
|
72
|
+
csp.add_variable :a, domain: [1]
|
|
73
|
+
csp.add_variable :b, domain: [1]
|
|
74
|
+
csp.add_constraint(:a, :b) { |a, b| a != b }
|
|
75
|
+
|
|
76
|
+
solver = described_class.new(csp, forward_checking: true)
|
|
77
|
+
expect(solver.search).to be(false)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
47
80
|
end
|
|
48
81
|
end
|
data/spec/winston/csp_spec.rb
CHANGED
|
@@ -87,8 +87,32 @@ describe Winston::CSP do
|
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
it "should pass a collection of preset variables to the solver" do
|
|
90
|
-
expect(solver).to receive(:search).with(a: 1)
|
|
90
|
+
expect(solver).to receive(:search).with({ a: 1 })
|
|
91
91
|
subject.solve(solver)
|
|
92
92
|
end
|
|
93
|
+
|
|
94
|
+
it "builds a solver by name with options" do
|
|
95
|
+
subject.add_constraint(:a, :b) { |a, b| a < b }
|
|
96
|
+
result = subject.solve(:backtrack, variable_strategy: :mrv)
|
|
97
|
+
|
|
98
|
+
expect(result).to eq({ a: 1, b: 2 })
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it "raises for an unknown solver name" do
|
|
102
|
+
expect { subject.solve(:unknown) }.to raise_error(ArgumentError, /Unknown solver/)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "returns false when preset assignments violate a constraint" do
|
|
106
|
+
subject.add_constraint(:a) { |a| a > 2 }
|
|
107
|
+
expect(solver).to_not receive(:search)
|
|
108
|
+
expect(subject.solve(solver)).to be(false)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "returns false when a constraint among preset variables is violated" do
|
|
112
|
+
subject.add_variable :c, value: 2
|
|
113
|
+
subject.add_constraint(:a, :c) { |a, c| a == c }
|
|
114
|
+
expect(solver).to_not receive(:search)
|
|
115
|
+
expect(subject.solve(solver)).to be(false)
|
|
116
|
+
end
|
|
93
117
|
end
|
|
94
118
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require "winston"
|
|
2
|
+
|
|
3
|
+
describe Winston::DSL do
|
|
4
|
+
describe ".define" do
|
|
5
|
+
it "builds a CSP with variables and constraints" do
|
|
6
|
+
csp = Winston.define do
|
|
7
|
+
var :a, domain: [1, 2, 3]
|
|
8
|
+
var :b, domain: [1, 2, 3]
|
|
9
|
+
constraint(:a, :b) { |a, b| a > b }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
expect(csp.solve).to eq({ a: 2, b: 1 })
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "supports named domains" do
|
|
16
|
+
csp = Winston.define do
|
|
17
|
+
domain :digits, [1, 2, 3]
|
|
18
|
+
var :a, domain: :digits
|
|
19
|
+
var :b, domain: :digits
|
|
20
|
+
constraint(:a, :b) { |a, b| a != b }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
result = csp.solve
|
|
24
|
+
expect([1, 2, 3]).to include(result[:a])
|
|
25
|
+
expect([1, 2, 3]).to include(result[:b])
|
|
26
|
+
expect(result[:a]).to_not eq(result[:b])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "uses named constraints via use_constraint" do
|
|
30
|
+
csp = Winston.define do
|
|
31
|
+
var :a, domain: [1, 2]
|
|
32
|
+
var :b, domain: [1, 2]
|
|
33
|
+
use_constraint :all_different, :a, :b
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
expect(csp.solve).to eq({ a: 1, b: 2 })
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "passes options to named constraints" do
|
|
40
|
+
csp = Winston.define do
|
|
41
|
+
var :a, domain: [1, 2, 3]
|
|
42
|
+
use_constraint :not_in_list, :a, list: [1, 2]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
expect(csp.solve).to eq({ a: 3 })
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe ".register_constraint" do
|
|
50
|
+
it "registers and uses a custom constraint by name" do
|
|
51
|
+
custom = Class.new(Winston::Constraint) do
|
|
52
|
+
def validate(assignments)
|
|
53
|
+
values = values_at(assignments)
|
|
54
|
+
values.all? { |v| v == 2 }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
Winston.register_constraint(:all_twos) do |variables, allow_nil, **_options|
|
|
59
|
+
custom.new(variables: variables, allow_nil: allow_nil)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
csp = Winston.define do
|
|
63
|
+
var :a, domain: [1, 2]
|
|
64
|
+
var :b, domain: [1, 2]
|
|
65
|
+
use_constraint :all_twos, :a, :b
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
expect(csp.solve).to eq({ a: 2, b: 2 })
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require "winston"
|
|
2
|
+
|
|
3
|
+
describe Winston::Heuristics do
|
|
4
|
+
let(:csp) { Winston::CSP.new }
|
|
5
|
+
|
|
6
|
+
describe ".mrv" do
|
|
7
|
+
it "picks the variable with the fewest remaining values" do
|
|
8
|
+
csp.add_variable :a, domain: [1, 2, 3]
|
|
9
|
+
csp.add_variable :b, domain: [1, 2]
|
|
10
|
+
csp.add_variable :c, domain: [1, 2, 3]
|
|
11
|
+
csp.add_constraint(:a) { |a| a == 1 }
|
|
12
|
+
|
|
13
|
+
vars = csp.variables.each_value.to_a
|
|
14
|
+
chosen = described_class.mrv.call(vars, {}, csp)
|
|
15
|
+
|
|
16
|
+
expect(chosen.name).to eq(:a)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe ".lcv" do
|
|
21
|
+
it "orders values to leave more options for other variables" do
|
|
22
|
+
csp.add_variable :a, domain: [2, 1]
|
|
23
|
+
csp.add_variable :b, domain: [2, 3]
|
|
24
|
+
csp.add_constraint(:a, :b) { |a, b| b > a }
|
|
25
|
+
|
|
26
|
+
values = csp.domain_for(:a)
|
|
27
|
+
ordered = described_class.lcv.call(values, csp.variables[:a], {}, csp)
|
|
28
|
+
|
|
29
|
+
expect(ordered).to eq([1, 2])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe ".in_order" do
|
|
34
|
+
it "returns values as-is" do
|
|
35
|
+
values = [3, 1, 2]
|
|
36
|
+
ordered = described_class.in_order.call(values, nil, {}, csp)
|
|
37
|
+
|
|
38
|
+
expect(ordered).to eq(values)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe ".forward_checking" do
|
|
43
|
+
it "returns true to enable forward checking" do
|
|
44
|
+
expect(described_class.forward_checking).to be(true)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "winston"
|
|
2
|
+
|
|
3
|
+
describe Winston::Solvers::MAC do
|
|
4
|
+
let(:csp) { Winston::CSP.new }
|
|
5
|
+
|
|
6
|
+
def valid_solution?(csp, assignments)
|
|
7
|
+
csp.constraints.all? { |constraint| constraint.validate(assignments) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "solves a simple constraint problem" do
|
|
11
|
+
csp.add_variable :a, domain: [1, 2, 3]
|
|
12
|
+
csp.add_variable :b, domain: [1, 2, 3]
|
|
13
|
+
csp.add_variable :c, domain: [1, 2, 3]
|
|
14
|
+
csp.add_constraint(:a, :b) { |a, b| a > b }
|
|
15
|
+
csp.add_constraint(:b, :c) { |b, c| b > c }
|
|
16
|
+
|
|
17
|
+
solver = described_class.new(csp)
|
|
18
|
+
result = solver.search
|
|
19
|
+
|
|
20
|
+
expect(result).to be_a(Hash)
|
|
21
|
+
expect(valid_solution?(csp, result)).to be(true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "respects preset assignments" do
|
|
25
|
+
csp.add_variable :a, value: 2
|
|
26
|
+
csp.add_variable :b, domain: [1, 2, 3]
|
|
27
|
+
csp.add_constraint(:a, :b) { |a, b| a > b }
|
|
28
|
+
|
|
29
|
+
solver = described_class.new(csp)
|
|
30
|
+
result = solver.search
|
|
31
|
+
|
|
32
|
+
expect(result[:a]).to eq(2)
|
|
33
|
+
expect(valid_solution?(csp, result)).to be(true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "returns false when constraints are impossible" do
|
|
37
|
+
csp.add_variable :a, domain: [1]
|
|
38
|
+
csp.add_variable :b, domain: [1]
|
|
39
|
+
csp.add_constraint(:a, :b) { |a, b| a != b }
|
|
40
|
+
|
|
41
|
+
solver = described_class.new(csp)
|
|
42
|
+
expect(solver.search).to be(false)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "winston"
|
|
2
|
+
|
|
3
|
+
describe Winston::Solvers::MinConflicts do
|
|
4
|
+
let(:csp) { Winston::CSP.new }
|
|
5
|
+
|
|
6
|
+
def valid_solution?(csp, assignments)
|
|
7
|
+
csp.constraints.all? { |constraint| constraint.validate(assignments) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "finds a valid solution for a simple problem" do
|
|
11
|
+
csp.add_variable :a, domain: [1, 2, 3]
|
|
12
|
+
csp.add_variable :b, domain: [1, 2, 3]
|
|
13
|
+
csp.add_variable :c, domain: [1, 2, 3]
|
|
14
|
+
csp.add_constraint(:a, :b) { |a, b| a > b }
|
|
15
|
+
csp.add_constraint(:b, :c) { |b, c| b > c }
|
|
16
|
+
|
|
17
|
+
solver = described_class.new(csp, max_steps: 1_000, random: Random.new(1))
|
|
18
|
+
result = solver.search
|
|
19
|
+
|
|
20
|
+
expect(result).to be_a(Hash)
|
|
21
|
+
expect(valid_solution?(csp, result)).to be(true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "respects preset assignments" do
|
|
25
|
+
csp.add_variable :a, value: 2
|
|
26
|
+
csp.add_variable :b, domain: [1, 2, 3]
|
|
27
|
+
csp.add_constraint(:a, :b) { |a, b| a > b }
|
|
28
|
+
|
|
29
|
+
solver = described_class.new(csp, max_steps: 1_000, random: Random.new(1))
|
|
30
|
+
result = solver.search
|
|
31
|
+
|
|
32
|
+
expect(result[:a]).to eq(2)
|
|
33
|
+
expect(valid_solution?(csp, result)).to be(true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "returns false when no solution is found within max_steps" do
|
|
37
|
+
csp.add_variable :a, domain: [1]
|
|
38
|
+
csp.add_variable :b, domain: [1]
|
|
39
|
+
csp.add_constraint(:a, :b) { |a, b| a != b }
|
|
40
|
+
|
|
41
|
+
solver = described_class.new(csp, max_steps: 10, random: Random.new(1))
|
|
42
|
+
expect(solver.search).to be(false)
|
|
43
|
+
end
|
|
44
|
+
end
|
data/winston.gemspec
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
Gem::Specification.new do |gem|
|
|
2
2
|
gem.name = 'winston'
|
|
3
|
-
gem.version = '0.0
|
|
3
|
+
gem.version = '0.1.0'
|
|
4
4
|
gem.authors = ['David Michael Nelson']
|
|
5
5
|
gem.homepage = 'http://github.com/dmnelson/winston'
|
|
6
6
|
|
|
7
7
|
gem.summary = 'Constraint Satisfaction Problem (CSP) implementation for Ruby'
|
|
8
|
-
gem.description =
|
|
8
|
+
gem.description = 'A small, practical CSP library for Ruby with multiple solvers, heuristics, and a DSL.'
|
|
9
9
|
gem.license = 'MIT'
|
|
10
|
+
gem.required_ruby_version = ">= 3.0"
|
|
10
11
|
|
|
11
12
|
gem.files = `git ls-files`.split($/)
|
|
12
13
|
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
13
14
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
14
15
|
gem.require_paths = ["lib"]
|
|
15
16
|
|
|
16
|
-
gem.add_development_dependency "bundler", "~>
|
|
17
|
-
gem.add_development_dependency "rake"
|
|
18
|
-
gem.add_development_dependency
|
|
17
|
+
gem.add_development_dependency "bundler", "~> 2.4", ">= 2.4.22"
|
|
18
|
+
gem.add_development_dependency "rake", "~> 13.1"
|
|
19
|
+
gem.add_development_dependency "rspec", "~> 3.13"
|
|
19
20
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: winston
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Michael Nelson
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-02-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -16,63 +16,78 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '2.4'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 2.4.22
|
|
20
23
|
type: :development
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
27
|
- - "~>"
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
29
|
+
version: '2.4'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 2.4.22
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: rake
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
30
36
|
requirements:
|
|
31
|
-
- - "
|
|
37
|
+
- - "~>"
|
|
32
38
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
39
|
+
version: '13.1'
|
|
34
40
|
type: :development
|
|
35
41
|
prerelease: false
|
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
43
|
requirements:
|
|
38
|
-
- - "
|
|
44
|
+
- - "~>"
|
|
39
45
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
46
|
+
version: '13.1'
|
|
41
47
|
- !ruby/object:Gem::Dependency
|
|
42
48
|
name: rspec
|
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
|
44
50
|
requirements:
|
|
45
|
-
- - "
|
|
51
|
+
- - "~>"
|
|
46
52
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
53
|
+
version: '3.13'
|
|
48
54
|
type: :development
|
|
49
55
|
prerelease: false
|
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
57
|
requirements:
|
|
52
|
-
- - "
|
|
58
|
+
- - "~>"
|
|
53
59
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
55
|
-
description:
|
|
56
|
-
|
|
60
|
+
version: '3.13'
|
|
61
|
+
description: A small, practical CSP library for Ruby with multiple solvers, heuristics,
|
|
62
|
+
and a DSL.
|
|
63
|
+
email:
|
|
57
64
|
executables: []
|
|
58
65
|
extensions: []
|
|
59
66
|
extra_rdoc_files: []
|
|
60
67
|
files:
|
|
61
68
|
- ".gitignore"
|
|
69
|
+
- ".tool-versions"
|
|
62
70
|
- ".travis.yml"
|
|
63
71
|
- CHANGELOG.md
|
|
64
72
|
- Gemfile
|
|
73
|
+
- Gemfile.lock
|
|
65
74
|
- LICENSE
|
|
66
75
|
- README.md
|
|
67
76
|
- Rakefile
|
|
77
|
+
- bench/run.rb
|
|
68
78
|
- lib/winston.rb
|
|
69
|
-
- lib/winston/backtrack.rb
|
|
70
79
|
- lib/winston/constraint.rb
|
|
71
80
|
- lib/winston/constraints/all_different.rb
|
|
72
81
|
- lib/winston/constraints/not_in_list.rb
|
|
73
82
|
- lib/winston/csp.rb
|
|
74
83
|
- lib/winston/domain.rb
|
|
84
|
+
- lib/winston/dsl.rb
|
|
85
|
+
- lib/winston/heuristics.rb
|
|
86
|
+
- lib/winston/solvers/backtrack.rb
|
|
87
|
+
- lib/winston/solvers/mac.rb
|
|
88
|
+
- lib/winston/solvers/min_conflicts.rb
|
|
75
89
|
- lib/winston/variable.rb
|
|
90
|
+
- spec/examples/map_coloring_spec.rb
|
|
76
91
|
- spec/examples/numbers_spec.rb
|
|
77
92
|
- spec/examples/sudoku_spec.rb
|
|
78
93
|
- spec/winston/backtrack_spec.rb
|
|
@@ -81,13 +96,17 @@ files:
|
|
|
81
96
|
- spec/winston/constraints/not_in_list_spec.rb
|
|
82
97
|
- spec/winston/csp_spec.rb
|
|
83
98
|
- spec/winston/domain_spec.rb
|
|
99
|
+
- spec/winston/dsl_spec.rb
|
|
100
|
+
- spec/winston/heuristics_spec.rb
|
|
101
|
+
- spec/winston/mac_spec.rb
|
|
102
|
+
- spec/winston/min_conflicts_spec.rb
|
|
84
103
|
- spec/winston/variable_spec.rb
|
|
85
104
|
- winston.gemspec
|
|
86
105
|
homepage: http://github.com/dmnelson/winston
|
|
87
106
|
licenses:
|
|
88
107
|
- MIT
|
|
89
108
|
metadata: {}
|
|
90
|
-
post_install_message:
|
|
109
|
+
post_install_message:
|
|
91
110
|
rdoc_options: []
|
|
92
111
|
require_paths:
|
|
93
112
|
- lib
|
|
@@ -95,19 +114,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
95
114
|
requirements:
|
|
96
115
|
- - ">="
|
|
97
116
|
- !ruby/object:Gem::Version
|
|
98
|
-
version: '0'
|
|
117
|
+
version: '3.0'
|
|
99
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
119
|
requirements:
|
|
101
120
|
- - ">="
|
|
102
121
|
- !ruby/object:Gem::Version
|
|
103
122
|
version: '0'
|
|
104
123
|
requirements: []
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
signing_key:
|
|
124
|
+
rubygems_version: 3.5.22
|
|
125
|
+
signing_key:
|
|
108
126
|
specification_version: 4
|
|
109
127
|
summary: Constraint Satisfaction Problem (CSP) implementation for Ruby
|
|
110
128
|
test_files:
|
|
129
|
+
- spec/examples/map_coloring_spec.rb
|
|
111
130
|
- spec/examples/numbers_spec.rb
|
|
112
131
|
- spec/examples/sudoku_spec.rb
|
|
113
132
|
- spec/winston/backtrack_spec.rb
|
|
@@ -116,4 +135,8 @@ test_files:
|
|
|
116
135
|
- spec/winston/constraints/not_in_list_spec.rb
|
|
117
136
|
- spec/winston/csp_spec.rb
|
|
118
137
|
- spec/winston/domain_spec.rb
|
|
138
|
+
- spec/winston/dsl_spec.rb
|
|
139
|
+
- spec/winston/heuristics_spec.rb
|
|
140
|
+
- spec/winston/mac_spec.rb
|
|
141
|
+
- spec/winston/min_conflicts_spec.rb
|
|
119
142
|
- spec/winston/variable_spec.rb
|
data/lib/winston/backtrack.rb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
module Winston
|
|
2
|
-
class Backtrack
|
|
3
|
-
def initialize(csp)
|
|
4
|
-
@csp = csp
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def search(assignments = {})
|
|
8
|
-
return assignments if complete?(assignments)
|
|
9
|
-
var = select_unassigned_variable(assignments)
|
|
10
|
-
domain_values(var).each do |value|
|
|
11
|
-
assigned = assignments.merge(var.name => value)
|
|
12
|
-
if valid?(var.name, assigned)
|
|
13
|
-
result = search(assigned)
|
|
14
|
-
return result if result
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
false
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
attr_reader :csp
|
|
23
|
-
|
|
24
|
-
def complete?(assignments)
|
|
25
|
-
assignments.size == csp.variables.size
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def valid?(changed, assignments)
|
|
29
|
-
csp.validate(changed, assignments)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def select_unassigned_variable(assignments)
|
|
33
|
-
csp.variables.reject { |k,v| assignments.include?(k) }.each_value.first
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def domain_values(var)
|
|
37
|
-
csp.domain_for(var.name)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|