winston 0.0.1 → 0.0.2

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 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