pairs 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/pairs.rb +35 -6
- data/pairs.gemspec +2 -2
- data/spec/lib/pairs_spec.rb +95 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c45b4690d8e587e6f573da694da1e9c2de38765
|
4
|
+
data.tar.gz: 8fb165db6f151c56d7f46cb02db8f7cfef578a74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe982314f97243308c4c5f5084e709c7a840ba66742a2a0450a3eca596e5599522f6954171edd4c591fdac8136c6a17a9292bee13203c489264ef276d4856659
|
7
|
+
data.tar.gz: e9b9181531de3bc6b8960b9f962a97eb093f224621fc9394c2e37de679f5fb7e366fbd583fe5f7d7a7b54e9b1803542c385cb84aceade2ad00551ecd9330e112
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.2.0
|
4
|
+
|
5
|
+
* Add helper constraints `together`, `separate`, `alone`, and `accompanied`
|
6
|
+
* Change max_attempts to 10,000
|
7
|
+
* Raise an error if unsolvable
|
8
|
+
* Create predicate methods for use in constraints
|
9
|
+
|
3
10
|
## 0.1.0
|
4
11
|
|
5
12
|
* Initial release, basic constraints
|
data/lib/pairs.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
class Pairs
|
2
|
+
class NoSolutionError < StandardError; end
|
3
|
+
|
2
4
|
attr_reader :max_attempts, :block
|
3
5
|
|
4
|
-
|
6
|
+
MAX_ATTEMPTS = 10_000
|
7
|
+
|
8
|
+
def initialize(max_attempts: MAX_ATTEMPTS, &block)
|
5
9
|
@max_attempts = max_attempts
|
6
10
|
@block = block
|
7
11
|
end
|
@@ -16,10 +20,6 @@ class Pairs
|
|
16
20
|
until all.empty?
|
17
21
|
these = all.sample(2)
|
18
22
|
|
19
|
-
next unless clean_room.__constraints__.all? { |constraint|
|
20
|
-
constraint.call(these)
|
21
|
-
}
|
22
|
-
|
23
23
|
result << these
|
24
24
|
|
25
25
|
these.each do |item|
|
@@ -27,8 +27,14 @@ class Pairs
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
return result
|
30
|
+
return result if clean_room.__constraints__.all? { |constraint|
|
31
|
+
result.all? { |pair|
|
32
|
+
constraint.call(pair)
|
33
|
+
}
|
34
|
+
}
|
31
35
|
end
|
36
|
+
|
37
|
+
raise NoSolutionError
|
32
38
|
end
|
33
39
|
|
34
40
|
def solution
|
@@ -54,9 +60,32 @@ class Pairs
|
|
54
60
|
__constraints__ << block
|
55
61
|
end
|
56
62
|
|
63
|
+
def together(a, b)
|
64
|
+
constraint { |both| both.include?(a) ? both.include?(b) : true }
|
65
|
+
end
|
66
|
+
|
67
|
+
def separate(a, b)
|
68
|
+
constraint { |both| both.include?(a) ? !both.include?(b) : true }
|
69
|
+
end
|
70
|
+
|
71
|
+
def alone(a)
|
72
|
+
constraint { |both| both.include?(a) ? both == [a] : true }
|
73
|
+
end
|
74
|
+
|
75
|
+
def accompanied(a)
|
76
|
+
constraint { |both| both.include?(a) ? both.count == 2 : true }
|
77
|
+
end
|
78
|
+
|
57
79
|
def method_missing(method_name, *args, &block)
|
58
80
|
return super unless args.count == 1
|
59
81
|
|
82
|
+
predicate_method_name = "#{method_name}?".intern
|
83
|
+
unless methods.include?(predicate_method_name)
|
84
|
+
define_singleton_method(predicate_method_name) do |item|
|
85
|
+
__items__[method_name].include?(item)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
60
89
|
__items__[method_name] ||= []
|
61
90
|
__items__[method_name] << args.first
|
62
91
|
end
|
data/pairs.gemspec
CHANGED
@@ -4,12 +4,12 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "pairs"
|
7
|
-
spec.version = "0.
|
7
|
+
spec.version = "0.2.0"
|
8
8
|
spec.authors = ["Justin Campbell"]
|
9
9
|
spec.email = ["justin@justincampbell.me"]
|
10
10
|
spec.summary = "Constraint solver for pairs"
|
11
11
|
spec.description = "Constraint solver for pairs"
|
12
|
-
spec.homepage = ""
|
12
|
+
spec.homepage = "https://github.com/justincampbell/pairs"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
data/spec/lib/pairs_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Pairs do
|
4
|
-
let(:pairs) { Pairs.new(&block) }
|
4
|
+
let(:pairs) { Pairs.new(max_attempts: max_attempts, &block) }
|
5
|
+
let(:max_attempts) { Pairs::MAX_ATTEMPTS }
|
5
6
|
let(:block) { -> { } }
|
6
7
|
let(:solution) { pairs.solution }
|
7
8
|
|
@@ -60,4 +61,97 @@ describe Pairs do
|
|
60
61
|
end
|
61
62
|
end
|
62
63
|
end
|
64
|
+
|
65
|
+
context "with an unsolvable constraint" do
|
66
|
+
let(:block) {
|
67
|
+
-> {
|
68
|
+
n 1; n 2; n 3
|
69
|
+
constraint { |both| both.reduce(:+) == 100 }
|
70
|
+
}
|
71
|
+
}
|
72
|
+
let(:max_attempts) { 1 }
|
73
|
+
|
74
|
+
generative do
|
75
|
+
it "raises a NoSolutionError" do
|
76
|
+
expect { solution }.to raise_error(Pairs::NoSolutionError)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "with predicate constraints" do
|
82
|
+
let(:block) {
|
83
|
+
-> {
|
84
|
+
good "a"; good "b"; bad "c"; bad "d"
|
85
|
+
constraint { |a, b| !(bad?(a) && bad?(b)) }
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
generative do
|
90
|
+
it "returns a solution" do
|
91
|
+
expect(solution.size).to eq(2)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#together" do
|
97
|
+
let(:block) {
|
98
|
+
-> {
|
99
|
+
item "a"; item "b"; item "c"; item "d"
|
100
|
+
together "a", "b"
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
generative do
|
105
|
+
it "keeps those 2 items together" do
|
106
|
+
expect(solution.any? { |both| both.sort == %w[a b] }).to eq(true)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#separate" do
|
112
|
+
let(:block) {
|
113
|
+
-> {
|
114
|
+
item "a"; item "b"; item "c"; item "d"
|
115
|
+
separate "a", "b"
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
generative do
|
120
|
+
it "keeps those 2 items separate" do
|
121
|
+
expect(solution.any? { |both| both.sort == %w[a b] }).to_not eq(true)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#alone" do
|
127
|
+
context "with an odd number" do
|
128
|
+
let(:block) {
|
129
|
+
-> {
|
130
|
+
item "a"; item "b"; item "c"
|
131
|
+
alone "c"
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
generative do
|
136
|
+
it "keeps that item alone" do
|
137
|
+
expect(solution.last).to eq(["c"])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "#accompanied" do
|
144
|
+
let(:block) {
|
145
|
+
-> {
|
146
|
+
item "a"; item "b"; item "c"
|
147
|
+
accompanied "c"
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
generative do
|
152
|
+
it "ensures that item is accompanied" do
|
153
|
+
expect(solution.first).to include("c")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
63
157
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pairs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Campbell
|
@@ -84,7 +84,7 @@ files:
|
|
84
84
|
- pairs.gemspec
|
85
85
|
- spec/lib/pairs_spec.rb
|
86
86
|
- spec/spec_helper.rb
|
87
|
-
homepage:
|
87
|
+
homepage: https://github.com/justincampbell/pairs
|
88
88
|
licenses:
|
89
89
|
- MIT
|
90
90
|
metadata: {}
|