winston 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b136df3119babb39b3950a0cc96b393d3344453e
4
- data.tar.gz: c10c1db2ceda4d4a553b209e7fceb433375eeabd
3
+ metadata.gz: 86cee2ee3ce5ed70188c907b9889a640f7ea6458
4
+ data.tar.gz: 6b9601b07bdf710141e1b9894c6b8e7b8389b694
5
5
  SHA512:
6
- metadata.gz: d47e3a5f91c608f5c5ad49a649bc9aabf9fd29a243ce0efffa89610a2ba1ad37c68ea026cefbf0b42a1fd0574887af20a50820bd357a0c85f5bd8fe5670ea0a9
7
- data.tar.gz: f4a9e5f2c31ef671a50618eb0236d15f59861cf39c2659c980dc34e2e7123f4a84eff5c7c4602220ef0da16ca82eb4541050ffbdeadde0b623601e147b848469
6
+ metadata.gz: 24362a9745841f0f85b255309412ee32a815ff8da790f5028c74285381908203ed11fc1231b4f1eca5185fce2f3d3d8d7aeeffbe428c0f927aabc5b68a661cf8
7
+ data.tar.gz: 90f64cc3d08170211a819d379162f029874010cf7a660d21b21f8fb6113be8dbccadb6b77f9231704328c5c149b24aa115b4f9bf6000731e3a88903b0506b158
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
@@ -0,0 +1,13 @@
1
+ Winston
2
+ ===============
3
+
4
+ http://github.com/dmnelson/winston
5
+
6
+ CHANGELOG
7
+ ---------
8
+
9
+ ### 0.0.2
10
+ * Added NotInList constraint.
11
+ * Added Sudoku example spec.
12
+ * Added 'allow_nil' option for constraints, so not all variables are necessarily required.
13
+ * Changed AllDifferent constraint to be restricted to given variables when not global
data/README.md CHANGED
@@ -5,6 +5,8 @@ It provides a useful way to solve problems like resource allocation or planning
5
5
 
6
6
  The most common example of usage for CSPs is probably the game [Sudoku](http://en.wikipedia.org/wiki/Sudoku).
7
7
 
8
+ [![Gem Version](https://badge.fury.io/rb/winston.svg)](http://badge.fury.io/rb/winston) [![Build Status](https://travis-ci.org/dmnelson/winston.svg)](https://travis-ci.org/dmnelson/winston) [![Code Climate](https://codeclimate.com/github/dmnelson/winston/badges/gpa.svg)](https://codeclimate.com/github/dmnelson/winston)
9
+
8
10
  ## Installation
9
11
 
10
12
  Add this line to your application's Gemfile:
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ task :default do
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ Rake::Task["spec"].execute
7
+ end
@@ -5,3 +5,4 @@ require 'winston/backtrack'
5
5
  require 'winston/csp'
6
6
 
7
7
  require 'winston/constraints/all_different'
8
+ require 'winston/constraints/not_in_list'
@@ -34,7 +34,7 @@ module Winston
34
34
  end
35
35
 
36
36
  def domain_values(var)
37
- csp.variables[var.name].domain.values
37
+ csp.domain_for(var.name)
38
38
  end
39
39
  end
40
40
  end
@@ -1,19 +1,32 @@
1
1
  module Winston
2
2
  class Constraint
3
3
 
4
- def initialize(variables = nil, predicate = nil)
5
- @variables = variables || []
4
+ attr_reader :variables, :predicate, :allow_nil, :global
5
+
6
+ def initialize(variables: nil, predicate: nil, allow_nil: false)
7
+ @variables = [variables].flatten.compact
6
8
  @predicate = predicate
9
+ @allow_nil = allow_nil
7
10
  @global = @variables.empty?
8
11
  end
9
12
 
10
13
  def elegible_for?(changed_var, assignments)
11
- @global || (@variables.include?(changed_var) && @variables.all? { |v| assignments.key? v })
14
+ global || (variables.include?(changed_var) && has_required_values?(assignments))
12
15
  end
13
16
 
14
17
  def validate(assignments)
15
- return false unless @predicate
16
- @predicate.call(*assignments.values_at(*@variables), assignments)
18
+ return false unless predicate
19
+ predicate.call(*values_at(assignments), assignments)
20
+ end
21
+
22
+ protected
23
+
24
+ def values_at(assignments)
25
+ assignments.values_at(*variables)
26
+ end
27
+
28
+ def has_required_values?(assignments)
29
+ allow_nil || variables.all? { |v| assignments.key?(v) }
17
30
  end
18
31
  end
19
32
  end
@@ -2,7 +2,8 @@ module Winston
2
2
  module Constraints
3
3
  class AllDifferent < Winston::Constraint
4
4
  def validate(assignments)
5
- assignments.values.uniq.size == assignments.size
5
+ values = global ? assignments.values : values_at(assignments).compact
6
+ values.uniq.size == values.size
6
7
  end
7
8
  end
8
9
  end
@@ -0,0 +1,18 @@
1
+ module Winston
2
+ module Constraints
3
+ class NotInList < Winston::Constraint
4
+
5
+ attr_reader :list
6
+
7
+ def initialize(variables: nil, allow_nil: false, list: [])
8
+ super(variables: variables, allow_nil: allow_nil)
9
+ @list = list
10
+ end
11
+
12
+ def validate(assignments)
13
+ values = global ? assignments.values : values_at(assignments)
14
+ !values.any? { |v| list.include?(v) }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -17,8 +17,8 @@ module Winston
17
17
  variables[name] = Variable.new(name, value: value, domain: domain)
18
18
  end
19
19
 
20
- def add_constraint(*variables, constraint: nil, &block)
21
- constraint ||= Constraint.new(variables, block)
20
+ def add_constraint(*variables, constraint: nil, allow_nil: false, &block)
21
+ constraint ||= Constraint.new(variables: variables, allow_nil: allow_nil, predicate: block)
22
22
  constraints << constraint
23
23
  end
24
24
 
@@ -29,6 +29,13 @@ module Winston
29
29
  true
30
30
  end
31
31
 
32
+ def domain_for(variable_name)
33
+ variable = variables[variable_name]
34
+ return [] if variable.nil? || variable.domain.nil?
35
+
36
+ variable.domain.values
37
+ end
38
+
32
39
  private
33
40
 
34
41
  def var_assignments
@@ -0,0 +1,120 @@
1
+ require "winston"
2
+
3
+ describe "Sudoku Example" do
4
+ let(:csp) { Winston::CSP.new }
5
+
6
+ describe "Unique values for every row, column and quadrant" do
7
+ before do
8
+ domain = [1, 2, 3, 4, 5, 6, 7, 8, 9]
9
+
10
+ # Adding variables
11
+ 1.upto(9).each do |x|
12
+ 1.upto(9).each do |j|
13
+ csp.add_variable({ x: x, y: j }, domain: domain)
14
+ end
15
+ end
16
+
17
+ variables = csp.variables.keys
18
+
19
+ # add columns and rows constraints
20
+ 1.upto(9).each do |i|
21
+ column = variables.select { |name| name[:x] == i }
22
+ row = variables.select { |name| name[:y] == i }
23
+
24
+ csp.add_constraint constraint: Winston::Constraints::AllDifferent.new(variables: row, allow_nil: true)
25
+ csp.add_constraint constraint: Winston::Constraints::AllDifferent.new(variables: column, allow_nil: true)
26
+ end
27
+
28
+ # blocks
29
+ variables.each_slice(3).group_by { |i| (i[0][:y] -1) % 9 }.values.flatten.each_slice(9).each do |vars|
30
+ csp.add_constraint constraint: Winston::Constraints::AllDifferent.new(variables: vars, allow_nil: true)
31
+ end
32
+ end
33
+
34
+ it "should return a valid solution" do
35
+ expect(csp.solve).to eq({
36
+ {:x=>1, :y=>1}=>1,
37
+ {:x=>1, :y=>2}=>2,
38
+ {:x=>1, :y=>3}=>3,
39
+ {:x=>1, :y=>4}=>4,
40
+ {:x=>1, :y=>5}=>5,
41
+ {:x=>1, :y=>6}=>6,
42
+ {:x=>1, :y=>7}=>7,
43
+ {:x=>1, :y=>8}=>8,
44
+ {:x=>1, :y=>9}=>9,
45
+ {:x=>2, :y=>1}=>4,
46
+ {:x=>2, :y=>2}=>5,
47
+ {:x=>2, :y=>3}=>6,
48
+ {:x=>2, :y=>4}=>7,
49
+ {:x=>2, :y=>5}=>8,
50
+ {:x=>2, :y=>6}=>9,
51
+ {:x=>2, :y=>7}=>1,
52
+ {:x=>2, :y=>8}=>2,
53
+ {:x=>2, :y=>9}=>3,
54
+ {:x=>3, :y=>1}=>7,
55
+ {:x=>3, :y=>2}=>8,
56
+ {:x=>3, :y=>3}=>9,
57
+ {:x=>3, :y=>4}=>1,
58
+ {:x=>3, :y=>5}=>2,
59
+ {:x=>3, :y=>6}=>3,
60
+ {:x=>3, :y=>7}=>4,
61
+ {:x=>3, :y=>8}=>5,
62
+ {:x=>3, :y=>9}=>6,
63
+ {:x=>4, :y=>1}=>2,
64
+ {:x=>4, :y=>2}=>1,
65
+ {:x=>4, :y=>3}=>4,
66
+ {:x=>4, :y=>4}=>3,
67
+ {:x=>4, :y=>5}=>6,
68
+ {:x=>4, :y=>6}=>5,
69
+ {:x=>4, :y=>7}=>8,
70
+ {:x=>4, :y=>8}=>9,
71
+ {:x=>4, :y=>9}=>7,
72
+ {:x=>5, :y=>1}=>3,
73
+ {:x=>5, :y=>2}=>6,
74
+ {:x=>5, :y=>3}=>5,
75
+ {:x=>5, :y=>4}=>8,
76
+ {:x=>5, :y=>5}=>9,
77
+ {:x=>5, :y=>6}=>7,
78
+ {:x=>5, :y=>7}=>2,
79
+ {:x=>5, :y=>8}=>1,
80
+ {:x=>5, :y=>9}=>4,
81
+ {:x=>6, :y=>1}=>8,
82
+ {:x=>6, :y=>2}=>9,
83
+ {:x=>6, :y=>3}=>7,
84
+ {:x=>6, :y=>4}=>2,
85
+ {:x=>6, :y=>5}=>1,
86
+ {:x=>6, :y=>6}=>4,
87
+ {:x=>6, :y=>7}=>3,
88
+ {:x=>6, :y=>8}=>6,
89
+ {:x=>6, :y=>9}=>5,
90
+ {:x=>7, :y=>1}=>5,
91
+ {:x=>7, :y=>2}=>3,
92
+ {:x=>7, :y=>3}=>1,
93
+ {:x=>7, :y=>4}=>6,
94
+ {:x=>7, :y=>5}=>4,
95
+ {:x=>7, :y=>6}=>2,
96
+ {:x=>7, :y=>7}=>9,
97
+ {:x=>7, :y=>8}=>7,
98
+ {:x=>7, :y=>9}=>8,
99
+ {:x=>8, :y=>1}=>6,
100
+ {:x=>8, :y=>2}=>4,
101
+ {:x=>8, :y=>3}=>2,
102
+ {:x=>8, :y=>4}=>9,
103
+ {:x=>8, :y=>5}=>7,
104
+ {:x=>8, :y=>6}=>8,
105
+ {:x=>8, :y=>7}=>5,
106
+ {:x=>8, :y=>8}=>3,
107
+ {:x=>8, :y=>9}=>1,
108
+ {:x=>9, :y=>1}=>9,
109
+ {:x=>9, :y=>2}=>7,
110
+ {:x=>9, :y=>3}=>8,
111
+ {:x=>9, :y=>4}=>5,
112
+ {:x=>9, :y=>5}=>3,
113
+ {:x=>9, :y=>6}=>1,
114
+ {:x=>9, :y=>7}=>6,
115
+ {:x=>9, :y=>8}=>4,
116
+ {:x=>9, :y=>9}=>2
117
+ })
118
+ end
119
+ end
120
+ end
@@ -4,7 +4,8 @@ describe Winston::Constraint do
4
4
 
5
5
  let(:variables) { nil }
6
6
  let(:predicate) { nil }
7
- subject { described_class.new(variables, predicate) }
7
+ let(:allow_nil) { false }
8
+ subject { described_class.new(variables: variables, predicate: predicate, allow_nil: allow_nil) }
8
9
 
9
10
  describe "#elegible_for?" do
10
11
  context "global" do
@@ -49,6 +50,50 @@ describe Winston::Constraint do
49
50
  expect(subject.elegible_for?(:a, { b: 2 })).to be(false)
50
51
  end
51
52
  end
53
+
54
+ context "allowing nil" do
55
+ let(:allow_nil) { true }
56
+
57
+ context "for specific variable" do
58
+ let(:variables) { [:a] }
59
+
60
+ it "should return true when that variable is changed" do
61
+ expect(subject.elegible_for?(:a, { a: nil })).to be(true)
62
+ end
63
+
64
+ it "should return false when that variable isn't the one changed" do
65
+ expect(subject.elegible_for?(:b, { a: 1, b: 2 })).to be(false)
66
+ end
67
+
68
+ it "should return true when that variable is changed but doesn't have a value" do
69
+ expect(subject.elegible_for?(:a, { b: 2 })).to be(true)
70
+ end
71
+ end
72
+
73
+ context "for multiple variables" do
74
+ let(:variables) { [:a, :b] }
75
+
76
+ it "should return true when one of those variables is changed" do
77
+ expect(subject.elegible_for?(:a, { a: 1, b: nil })).to be(true)
78
+ end
79
+
80
+ it "should return false when one of those variables isn't the one changed" do
81
+ expect(subject.elegible_for?(:c, { a: 1, b: 2 })).to be(false)
82
+ end
83
+
84
+ it "should return true when one of those variables is changed but doesn't have a value for every one of them" do
85
+ expect(subject.elegible_for?(:b, { b: 2 })).to be(true)
86
+ end
87
+
88
+ it "should return true when one of those variables is changed but doesn't have a value for every one of them" do
89
+ expect(subject.elegible_for?(:a, { b: 2 })).to be(true)
90
+ end
91
+
92
+ it "should return true when one of those variables is changed but doesn't have a value for any of them" do
93
+ expect(subject.elegible_for?(:a, {})).to be(true)
94
+ end
95
+ end
96
+ end
52
97
  end
53
98
 
54
99
  describe "#validate" do
@@ -3,13 +3,6 @@ require "winston"
3
3
  describe Winston::Constraints::AllDifferent do
4
4
  subject { described_class.new }
5
5
 
6
- describe "#elegible_for?" do
7
- it "should be elegible for everything" do
8
- expect(subject.elegible_for?(:a, {})).to be(true)
9
- expect(subject.elegible_for?(:b, { a: 1 })).to be(true)
10
- end
11
- end
12
-
13
6
  describe "#validate" do
14
7
  it "should return 'true' when all values are unique" do
15
8
  expect(subject.validate(a: 1, b: 2, c: 3, d: 4)).to be(true)
@@ -18,5 +11,17 @@ describe Winston::Constraints::AllDifferent do
18
11
  it "should return 'false' when not all values are unique" do
19
12
  expect(subject.validate(a: 1, b: 2, c: 2, d: 4)).to be(false)
20
13
  end
14
+
15
+ context "for specific variables" do
16
+ subject { described_class.new(variables: [:a, :b, :c]) }
17
+
18
+ it "should return 'true' when all values are unique" do
19
+ expect(subject.validate(a: 1, b: 2, c: 3, d: 3)).to be(true)
20
+ end
21
+
22
+ it "should return 'false' when not all values are unique" do
23
+ expect(subject.validate(a: 1, b: 2, c: 2, d: 4)).to be(false)
24
+ end
25
+ end
21
26
  end
22
27
  end
@@ -0,0 +1,35 @@
1
+ require "winston"
2
+
3
+ describe Winston::Constraints::NotInList do
4
+ subject { described_class.new(list: [ 4, 5, 6 ]) }
5
+
6
+ describe "#validate" do
7
+ it "should return 'true' none of the values are on the list" do
8
+ expect(subject.validate(a: 1, b: 2, c: 3, d: 3)).to be(true)
9
+ end
10
+
11
+ it "should return 'false' when any of the values are on the list" do
12
+ expect(subject.validate(a: 1, b: 2, c: 2, d: 4)).to be(false)
13
+ end
14
+
15
+ it "should return 'false' when all the values are on the list" do
16
+ expect(subject.validate(a: 4, b: 6, c: 5, d: 4)).to be(false)
17
+ end
18
+
19
+ context "for specific variables" do
20
+ subject { described_class.new(variables: [:a, :b, :c], list: [ 4, 5, 6 ]) }
21
+
22
+ it "should return 'true' when none of the values are on the list" do
23
+ expect(subject.validate(a: 1, b: 2, c: 3, d: 6)).to be(true)
24
+ end
25
+
26
+ it "should return 'false' when any of the values are on the list" do
27
+ expect(subject.validate(a: 1, b: 2, c: 5, d: 3)).to be(false)
28
+ end
29
+
30
+ it "should return 'false' when all the values are on the list" do
31
+ expect(subject.validate(a: 4, b: 6, c: 5, d: 3)).to be(false)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -28,7 +28,7 @@ describe Winston::CSP do
28
28
  describe "#add_constraint" do
29
29
  let(:constraint) { double("Constraint") }
30
30
  it "should build a constraint for the given block" do
31
- expect(Winston::Constraint).to receive(:new).with([:a, :b], an_instance_of(Proc)).once.and_return(constraint)
31
+ expect(Winston::Constraint).to receive(:new).with(variables: [:a, :b], allow_nil: false, predicate: an_instance_of(Proc)).once.and_return(constraint)
32
32
  subject.add_constraint(:a, :b) { true }
33
33
  expect(subject.constraints).to include(constraint)
34
34
  end
@@ -60,6 +60,25 @@ describe Winston::CSP do
60
60
  end
61
61
  end
62
62
 
63
+ describe "#domain_for" do
64
+ before do
65
+ subject.add_variable :a, value: 1
66
+ subject.add_variable :b, domain: [1, 2]
67
+ end
68
+
69
+ it "returns domain values for a given variable" do
70
+ expect(subject.domain_for(:b)).to eq([1, 2])
71
+ end
72
+
73
+ it "returns an empty list when a variable doesn't exist in the problem" do
74
+ expect(subject.domain_for(:c)).to be_empty
75
+ end
76
+
77
+ it "return an empty list when a variable doesn't have a domain" do
78
+ expect(subject.domain_for(:a)).to be_empty
79
+ end
80
+ end
81
+
63
82
  describe "#solve" do
64
83
  let(:solver) { double("Solver") }
65
84
  before do
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |gem|
2
2
  gem.name = 'winston'
3
- gem.version = '0.0.1'
3
+ gem.version = '0.0.2'
4
4
  gem.authors = ['David Michael Nelson']
5
5
  gem.homepage = 'http://github.com/dmnelson/winston'
6
6
 
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.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Michael Nelson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-02 00:00:00.000000000 Z
11
+ date: 2015-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -59,6 +59,8 @@ extensions: []
59
59
  extra_rdoc_files: []
60
60
  files:
61
61
  - ".gitignore"
62
+ - ".travis.yml"
63
+ - CHANGELOG.md
62
64
  - Gemfile
63
65
  - LICENSE
64
66
  - README.md
@@ -67,13 +69,16 @@ files:
67
69
  - lib/winston/backtrack.rb
68
70
  - lib/winston/constraint.rb
69
71
  - lib/winston/constraints/all_different.rb
72
+ - lib/winston/constraints/not_in_list.rb
70
73
  - lib/winston/csp.rb
71
74
  - lib/winston/domain.rb
72
75
  - lib/winston/variable.rb
73
76
  - spec/examples/numbers_spec.rb
77
+ - spec/examples/sudoku_spec.rb
74
78
  - spec/winston/backtrack_spec.rb
75
79
  - spec/winston/constraint_spec.rb
76
80
  - spec/winston/constraints/all_different_spec.rb
81
+ - spec/winston/constraints/not_in_list_spec.rb
77
82
  - spec/winston/csp_spec.rb
78
83
  - spec/winston/domain_spec.rb
79
84
  - spec/winston/variable_spec.rb
@@ -104,9 +109,11 @@ specification_version: 4
104
109
  summary: Constraint Satisfaction Problem (CSP) implementation for Ruby
105
110
  test_files:
106
111
  - spec/examples/numbers_spec.rb
112
+ - spec/examples/sudoku_spec.rb
107
113
  - spec/winston/backtrack_spec.rb
108
114
  - spec/winston/constraint_spec.rb
109
115
  - spec/winston/constraints/all_different_spec.rb
116
+ - spec/winston/constraints/not_in_list_spec.rb
110
117
  - spec/winston/csp_spec.rb
111
118
  - spec/winston/domain_spec.rb
112
119
  - spec/winston/variable_spec.rb