porolog 0.0.8 → 1.0.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 +4 -4
- data/README.md +11 -1
- data/Rakefile +2 -2
- data/bin/porolog +34 -13
- data/coverage/badge.svg +1 -1
- data/coverage/index.html +43822 -25140
- data/doc/Array.html +158 -51
- data/doc/Object.html +2 -2
- data/doc/Porolog.html +1584 -1113
- data/doc/Symbol.html +2 -2
- data/doc/_index.html +40 -56
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +8 -4
- data/doc/index.html +8 -4
- data/doc/method_list.html +424 -248
- data/doc/top-level-namespace.html +1 -1
- data/lib/porolog.rb +184 -61
- data/lib/porolog/arguments.rb +12 -11
- data/lib/porolog/core_ext.rb +27 -9
- data/lib/porolog/error.rb +3 -0
- data/lib/porolog/goal.rb +57 -15
- data/lib/porolog/instantiation.rb +55 -9
- data/lib/porolog/predicate.rb +52 -26
- data/lib/porolog/predicate/builtin.rb +825 -0
- data/lib/porolog/rule.rb +8 -24
- data/lib/porolog/scope.rb +1 -1
- data/lib/porolog/tail.rb +5 -0
- data/lib/porolog/value.rb +3 -3
- data/lib/porolog/variable.rb +29 -11
- data/test/porolog/arguments_test.rb +45 -66
- data/test/porolog/core_ext_test.rb +25 -0
- data/test/porolog/goal_test.rb +86 -9
- data/test/porolog/instantiation_test.rb +36 -0
- data/test/porolog/porolog_test.rb +285 -30
- data/test/porolog/predicate/builtin_test.rb +1340 -0
- data/test/porolog/predicate_test.rb +78 -16
- data/test/porolog/rule_test.rb +19 -0
- data/test/porolog/variable_test.rb +44 -55
- data/test/samples_test.rb +277 -0
- data/test/test_helper.rb +9 -0
- metadata +9 -5
@@ -152,22 +152,6 @@ describe 'Porolog' do
|
|
152
152
|
|
153
153
|
end
|
154
154
|
|
155
|
-
describe '.builtin' do
|
156
|
-
|
157
|
-
it 'should incremently return Predicates' do
|
158
|
-
iota1 = Predicate.builtin :iota
|
159
|
-
zeta1 = Predicate.builtin :zeta
|
160
|
-
iota2 = Predicate.builtin :iota
|
161
|
-
zeta2 = Predicate.builtin :zeta
|
162
|
-
|
163
|
-
assert_Predicate iota1, :_iota_1, []
|
164
|
-
assert_Predicate zeta1, :_zeta_1, []
|
165
|
-
assert_Predicate iota2, :_iota_2, []
|
166
|
-
assert_Predicate zeta2, :_zeta_2, []
|
167
|
-
end
|
168
|
-
|
169
|
-
end
|
170
|
-
|
171
155
|
describe '#initialize' do
|
172
156
|
|
173
157
|
it 'can create predicates in different scopes' do
|
@@ -340,6 +324,84 @@ describe 'Porolog' do
|
|
340
324
|
|
341
325
|
end
|
342
326
|
|
327
|
+
describe '#builtin?' do
|
328
|
+
|
329
|
+
it 'should return false for normal predicates' do
|
330
|
+
p = predicate :normal
|
331
|
+
|
332
|
+
assert_equal false, p.builtin?
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'should return true for builtin predicates' do
|
336
|
+
p = builtin :append
|
337
|
+
|
338
|
+
assert_equal true, p.builtin?
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
describe '#satisfy_builtin' do
|
344
|
+
|
345
|
+
module Porolog
|
346
|
+
class Predicate
|
347
|
+
module Builtin
|
348
|
+
def user_defined(goal, block, *args, &arg_block)
|
349
|
+
puts 'Called user_defined'
|
350
|
+
block.call(goal, *args) || false
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
let(:predicate1) { Predicate.new :user_defined, builtin: true }
|
357
|
+
let(:arguments1) { predicate1.arguments(:m,:n) }
|
358
|
+
let(:goal) { arguments1.goal }
|
359
|
+
|
360
|
+
it 'should satisfy builtin predicate' do
|
361
|
+
called = false
|
362
|
+
|
363
|
+
assert_output "Called user_defined\n" do
|
364
|
+
predicate1.satisfy_builtin(goal) {|*args|
|
365
|
+
called = args
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
assert_equal [goal, goal[:m], goal[:n]], called
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
describe '.call_builtin' do
|
375
|
+
|
376
|
+
it 'should raise an exception when no such builtin predicate exists' do
|
377
|
+
assert_raises NameError do
|
378
|
+
Predicate.call_builtin(:non_existent)
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should call a builtin predicate' do
|
384
|
+
module Porolog
|
385
|
+
class Predicate
|
386
|
+
module Builtin
|
387
|
+
def custom(goal, block, *args, &arg_block)
|
388
|
+
block.call(goal, *args) || false
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
called = false
|
394
|
+
|
395
|
+
goal = new_goal :a, :b, :c
|
396
|
+
block = ->(goal, *args){
|
397
|
+
called = [goal] + args
|
398
|
+
}
|
399
|
+
Porolog::Predicate.call_builtin(:custom, goal, block, goal[:X], [1,2,3]) { 1 }
|
400
|
+
assert_equal [goal, goal[:X], [1,2,3]], called
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
|
343
405
|
end
|
344
406
|
|
345
407
|
end
|
data/test/porolog/rule_test.rb
CHANGED
@@ -304,6 +304,25 @@ describe 'Porolog' do
|
|
304
304
|
assert_equal [goal, subgoal, [false]], rule_spy.calls[0].args
|
305
305
|
end
|
306
306
|
|
307
|
+
it 'should handle nil expression' do
|
308
|
+
rule = Rule.new args1, []
|
309
|
+
|
310
|
+
goal.expects(:terminate!).with().times(0)
|
311
|
+
rule_spy = Spy.on(rule, :satisfy_conjunction).and_call_through
|
312
|
+
called = false
|
313
|
+
|
314
|
+
result = rule.satisfy_conjunction(goal, subgoal, rule.definition) do |solution_goal|
|
315
|
+
#:nocov:
|
316
|
+
called = true
|
317
|
+
#:nocov:
|
318
|
+
end
|
319
|
+
|
320
|
+
assert result, 'satisfy_conjunction should not succeed'
|
321
|
+
assert called, 'the satisfy block should not be called'
|
322
|
+
assert_equal 1, rule_spy.calls.size
|
323
|
+
assert_equal [goal, subgoal, []], rule_spy.calls[0].args
|
324
|
+
end
|
325
|
+
|
307
326
|
it 'should evaluate conjunctions until a fail' do
|
308
327
|
conjunction = [true, true, true, false, true, :CUT, true]
|
309
328
|
rule = Rule.new args1, conjunction
|
@@ -122,6 +122,7 @@ describe 'Porolog' do
|
|
122
122
|
|
123
123
|
it 'should return variable from instantiated variables' do
|
124
124
|
variable = Variable.new 'v', goal1
|
125
|
+
variable.instantiate 'string value'
|
125
126
|
|
126
127
|
assert_equal :variable, variable.type
|
127
128
|
end
|
@@ -310,7 +311,13 @@ describe 'Porolog' do
|
|
310
311
|
assert_equal headtail, goal1.value_of(:x)
|
311
312
|
assert_equal headtail, goal1.value_of(:y)
|
312
313
|
assert_equal headtail, goal1.value_of(:z)
|
313
|
-
assert_Goal_variables goal1, {
|
314
|
+
assert_Goal_variables goal1, {
|
315
|
+
m: nil,
|
316
|
+
n: nil,
|
317
|
+
x: [nil, UNKNOWN_TAIL],
|
318
|
+
y: [nil, UNKNOWN_TAIL],
|
319
|
+
z: [nil, UNKNOWN_TAIL]
|
320
|
+
}, [
|
314
321
|
'Goal1.:m',
|
315
322
|
' Goal1.:y[:head]',
|
316
323
|
' Goal1.:x',
|
@@ -450,59 +457,6 @@ describe 'Porolog' do
|
|
450
457
|
].join("\n")
|
451
458
|
end
|
452
459
|
|
453
|
-
it 'should raise an exception when multiple values are detected' do
|
454
|
-
skip 'because multiple unequal values are avoided'
|
455
|
-
|
456
|
-
# :nocov:
|
457
|
-
variable1 = Variable.new :x, goal1
|
458
|
-
variable2 = Variable.new :y, goal2
|
459
|
-
variable3 = Variable.new :z, goal3
|
460
|
-
|
461
|
-
value1 = Value.new [1,2,3], goal2
|
462
|
-
value2 = Value.new 'word', goal3
|
463
|
-
|
464
|
-
i1 = variable1.instantiate variable2
|
465
|
-
i2 = variable1.instantiate variable3
|
466
|
-
i3 = variable2.instantiate value1
|
467
|
-
i4 = variable3.instantiate value2
|
468
|
-
|
469
|
-
assert_Instantiation i1, variable1, variable2, nil, nil
|
470
|
-
assert_Instantiation i2, variable1, variable3, nil, nil
|
471
|
-
assert_Instantiation i3, variable2, value1, nil, nil
|
472
|
-
assert_Instantiation i4, variable3, value2, nil, nil
|
473
|
-
|
474
|
-
assert_raises Variable::MultipleValuesError do
|
475
|
-
assert_equal value1.value, variable1.value
|
476
|
-
assert_equal value2.value, variable1.value
|
477
|
-
end
|
478
|
-
|
479
|
-
assert_Goal_variables goal1, { m: nil, n: nil, x: [1,2,3] }, [
|
480
|
-
'Goal1.:m',
|
481
|
-
'Goal1.:n',
|
482
|
-
'Goal1.:x',
|
483
|
-
' Goal2.:y',
|
484
|
-
' Goal2.[1, 2, 3]',
|
485
|
-
' Goal3.:z',
|
486
|
-
].join("\n")
|
487
|
-
assert_Goal_variables goal2, { m: nil, n: nil, y: [1,2,3] }, [
|
488
|
-
'Goal2.:m',
|
489
|
-
'Goal2.:n',
|
490
|
-
'Goal2.:y',
|
491
|
-
' Goal1.:x',
|
492
|
-
' Goal3.:z',
|
493
|
-
' Goal2.[1, 2, 3]',
|
494
|
-
].join("\n")
|
495
|
-
assert_Goal_variables goal3, { m: nil, n: nil, z: [1,2,3] }, [
|
496
|
-
'Goal3.:m',
|
497
|
-
'Goal3.:n',
|
498
|
-
'Goal3.:z',
|
499
|
-
' Goal1.:x',
|
500
|
-
' Goal2.:y',
|
501
|
-
' Goal2.[1, 2, 3]',
|
502
|
-
].join("\n")
|
503
|
-
# :nocov:
|
504
|
-
end
|
505
|
-
|
506
460
|
it 'should not raise an exception when multiple values are equal' do
|
507
461
|
variable1 = Variable.new :x, goal1
|
508
462
|
variable2 = Variable.new :y, goal2
|
@@ -571,6 +525,42 @@ describe 'Porolog' do
|
|
571
525
|
assert_equal :variable1, variable1.value
|
572
526
|
end
|
573
527
|
|
528
|
+
it 'should unify a matching flathead and flattail pair' do
|
529
|
+
variable1 = Variable.new :x, goal1
|
530
|
+
|
531
|
+
variable1.values << [1,2,UNKNOWN_TAIL]
|
532
|
+
variable1.values << [UNKNOWN_TAIL,3,4]
|
533
|
+
|
534
|
+
assert_equal [1,2,3,4], variable1.value
|
535
|
+
end
|
536
|
+
|
537
|
+
it 'should unify a matching flattail and flathead pair' do
|
538
|
+
variable1 = Variable.new :x, goal1
|
539
|
+
|
540
|
+
variable1.values << [UNKNOWN_TAIL,3,4]
|
541
|
+
variable1.values << [1,2,UNKNOWN_TAIL]
|
542
|
+
|
543
|
+
assert_equal [1,2,3,4], variable1.value
|
544
|
+
end
|
545
|
+
|
546
|
+
it 'should return nil when the values are incompatible' do
|
547
|
+
variable1 = Variable.new :x, goal1
|
548
|
+
|
549
|
+
variable1.values << [1,3,4]
|
550
|
+
variable1.values << [1,2,UNKNOWN_TAIL]
|
551
|
+
|
552
|
+
assert_nil variable1.value
|
553
|
+
end
|
554
|
+
|
555
|
+
it 'should unify a special case' do
|
556
|
+
variable1 = Variable.new :x, goal1
|
557
|
+
|
558
|
+
variable1.values << [1,[3,4]]
|
559
|
+
variable1.values << goal1[:h]/goal1[:t]
|
560
|
+
|
561
|
+
assert_equal [1,[3,4]], variable1.value
|
562
|
+
end
|
563
|
+
|
574
564
|
end
|
575
565
|
|
576
566
|
describe '#instantiate' do
|
@@ -671,7 +661,6 @@ describe 'Porolog' do
|
|
671
661
|
# -- Assert Values --
|
672
662
|
assert_equal 13, variable1.value
|
673
663
|
assert_equal [7,11,13,23,29], variable2.value
|
674
|
-
#assert_nil variable3.value
|
675
664
|
assert_equal variable3, variable3.value
|
676
665
|
|
677
666
|
# -- Assert instantiations --
|
@@ -0,0 +1,277 @@
|
|
1
|
+
#
|
2
|
+
# test/samples_test.rb - Test Suite for Porolog::Predicate
|
3
|
+
#
|
4
|
+
# Luis Esteban 13 July 2020
|
5
|
+
# created
|
6
|
+
#
|
7
|
+
|
8
|
+
require_relative 'test_helper'
|
9
|
+
|
10
|
+
describe 'Porolog' do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
reset
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'implements delete first predicate' do
|
17
|
+
predicate :delete
|
18
|
+
|
19
|
+
delete(:X, [:X]/:T, :T).fact!
|
20
|
+
|
21
|
+
assert_solutions delete(9, [1,2,3,4], [2,3,4]), []
|
22
|
+
assert_solutions delete(1, [1,2,3,4], [2,3,4]), [{}]
|
23
|
+
|
24
|
+
assert_solutions delete(:Removed, [1,2,3,4], [2,3,4]), [{ Removed: 1 }]
|
25
|
+
assert_solutions delete(1, :Original, [2,3,4]), [{ Original: [1,2,3,4] }]
|
26
|
+
assert_solutions delete(1, [1,2,3,4], :Result), [{ Result: [2,3,4] }]
|
27
|
+
assert_solutions delete(1, [:First,2,3,4], [2,3,4]), [{ First: 1 }]
|
28
|
+
assert_solutions delete(1, [1,:Second,3,4], [2,3,4]), [{ Second: 2 }]
|
29
|
+
assert_solutions delete(1, [1,2,:Third,4], [2,3,4]), [{ Third: 3 }]
|
30
|
+
assert_solutions delete(1, [1,2,3,4], [:Second,3,4]), [{ Second: 2 }]
|
31
|
+
assert_solutions delete(1, [1,2,3,4], [:A, :B, :C]), [{ A: 2, B: 3, C: 4 }]
|
32
|
+
assert_solutions delete(1, [:A,2,3,:D], [:B,3,4]), [{ A: 1, B: 2, D: 4 }]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'implements the delete predicate' do
|
36
|
+
builtin :write
|
37
|
+
predicate :delete
|
38
|
+
|
39
|
+
delete(:X, [:X]/:T, :T).fact!
|
40
|
+
delete(:X, [:H]/:T, [:H]/:NT) << [
|
41
|
+
delete(:X, :T, :NT),
|
42
|
+
]
|
43
|
+
|
44
|
+
assert_solutions delete(1, [1,2,3,4], [1,2,4]), [], goals: 9
|
45
|
+
assert_solutions delete(3, [1,2,3,4], [1,2,4]), [{}]
|
46
|
+
assert_solutions delete(4, [1,2,3,4], [1,2,3]), [{}]
|
47
|
+
assert_solutions delete(4, [1,2,3,4], [1,2,:X]), [{ X: 3 }]
|
48
|
+
|
49
|
+
assert_solutions delete(:Removed, [1,2,3,4], [1,2,4]), [
|
50
|
+
{ Removed: 3 }
|
51
|
+
]
|
52
|
+
|
53
|
+
assert_solutions delete(:Removed, [1,2,3,4], [1,2,3]), [
|
54
|
+
{ Removed: 4 }
|
55
|
+
]
|
56
|
+
|
57
|
+
assert_solutions delete(3, :Original, [1,2,4]), [
|
58
|
+
{ Original: [3, 1, 2, 4] },
|
59
|
+
{ Original: [1, 3, 2, 4] },
|
60
|
+
{ Original: [1, 2, 3, 4] },
|
61
|
+
{ Original: [1, 2, 4, 3] },
|
62
|
+
]
|
63
|
+
|
64
|
+
assert_solutions delete(:X, [1,2,3,4], :L), [
|
65
|
+
{ X: 1, L: [2, 3, 4] },
|
66
|
+
{ X: 2, L: [1, 3, 4] },
|
67
|
+
{ X: 3, L: [1, 2, 4] },
|
68
|
+
{ X: 4, L: [1, 2, 3] },
|
69
|
+
]
|
70
|
+
|
71
|
+
assert_solutions delete(:X, [1,2,3,4], [:A,:B,:C]), [
|
72
|
+
{ X: 1, A: 2, B: 3, C: 4 },
|
73
|
+
{ X: 2, A: 1, B: 3, C: 4 },
|
74
|
+
{ X: 3, A: 1, B: 2, C: 4 },
|
75
|
+
{ X: 4, A: 1, B: 2, C: 3 },
|
76
|
+
]
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'implements the permutation predicate' do
|
80
|
+
warn name
|
81
|
+
predicate :delete, :permutation
|
82
|
+
|
83
|
+
permutation([],[]).fact!
|
84
|
+
permutation(:List, [:H]/:Permutation) << [
|
85
|
+
delete(:H, :List, :Rest),
|
86
|
+
permutation(:Rest, :Permutation)
|
87
|
+
]
|
88
|
+
|
89
|
+
delete(:X, [:X]/:T, :T).fact!
|
90
|
+
delete(:X, [:H]/:T, [:H]/:NT) << [
|
91
|
+
delete(:X, :T, :NT),
|
92
|
+
]
|
93
|
+
|
94
|
+
assert_solutions permutation([1,2,3,4], [1,2,3,4]), [{}]
|
95
|
+
assert_solutions permutation([3,2,1,4], [1,2,3,4]), [{}]
|
96
|
+
assert_solutions permutation([3,2,:A,4], [1,2,3,4]), [
|
97
|
+
{ A: 1 }
|
98
|
+
]
|
99
|
+
assert_solutions permutation([3,2,:A,:B], [1,2,3,4]), [
|
100
|
+
{ A: 1, B: 4 },
|
101
|
+
{ A: 4, B: 1 },
|
102
|
+
]
|
103
|
+
assert_solutions permutation([3,2,:A,4], [1,2,:C,4]), [
|
104
|
+
{ A: 1, C: 3 }
|
105
|
+
]
|
106
|
+
assert_solutions permutation([2,3,1], :L), [
|
107
|
+
{ L: [2, 3, 1] },
|
108
|
+
{ L: [2, 1, 3] },
|
109
|
+
{ L: [3, 2, 1] },
|
110
|
+
{ L: [3, 1, 2] },
|
111
|
+
{ L: [1, 2, 3] },
|
112
|
+
{ L: [1, 3, 2] }
|
113
|
+
]
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'solves the Einstein / Zebra riddle' do
|
117
|
+
warn name
|
118
|
+
builtin :is, :member, :is_noteq
|
119
|
+
predicate :same, :not_same, :left, :beside, :houses
|
120
|
+
|
121
|
+
same(:X,:X).fact!
|
122
|
+
|
123
|
+
not_same(:X,:X).cut_falicy!
|
124
|
+
not_same(:X,:Y).fact!
|
125
|
+
|
126
|
+
(1..5).to_a.each_cons(2) do |left, right|
|
127
|
+
left(left,right).fact!
|
128
|
+
beside(left,right).fact!
|
129
|
+
beside(right,left).fact!
|
130
|
+
end
|
131
|
+
|
132
|
+
HOUSES = [1, 2, 3, 4, 5 ]
|
133
|
+
|
134
|
+
PEOPLE = [:Brit, :Dane, :Swede, :German, :Norwegian]
|
135
|
+
SMOKES = [:Blend, :Pallmall, :Dunhill, :Winfield, :Rothmans ]
|
136
|
+
PETS = [:Dog, :Cats, :Fish, :Birds, :Horses ]
|
137
|
+
COLOURS = [:Red, :Blue, :Green, :White, :Yellow ]
|
138
|
+
DRINKS = [:Tea, :Beer, :Milk, :Water, :Coffee ]
|
139
|
+
|
140
|
+
FISH_INDEX = PETS.index(:Fish)
|
141
|
+
|
142
|
+
houses(:People, :Smokes, :Pets, :Colours, :Drinks) << [
|
143
|
+
same(:Milk, 3), # 8. The man living in the house right in the center drinks milk.
|
144
|
+
same(:Norwegian, 1), # 9. The Norwegian lives in the first house.
|
145
|
+
left(:Green, :White), # 4. The green house is on the left of the white house.
|
146
|
+
beside(:Norwegian, :Blue), # 14. The Norwegian lives next to the blue house.
|
147
|
+
not_same(:Blue, :White),
|
148
|
+
beside(:Blend, :Water), # 15. The man who smokes Blend has a neighbour who drinks water.
|
149
|
+
not_same(:Milk, :Water),
|
150
|
+
beside(:Horses, :Dunhill), # 11. The man who keeps horses lives next to the man who smokes Dunhill.
|
151
|
+
same(:Yellow, :Dunhill), # 7. The owner of the yellow house smokes Dunhill.
|
152
|
+
same(:Green, :Coffee), # 5. The green house owner drinks coffee.
|
153
|
+
not_same(:Milk, :Coffee),
|
154
|
+
not_same(:Water, :Coffee),
|
155
|
+
is_noteq(:Red, HOUSES, :Green, :White, :Blue, :Yellow),
|
156
|
+
same(:Brit, :Red), # 1. The Brit lives in a red house.
|
157
|
+
not_same(:Brit, :Norwegian),
|
158
|
+
beside(:Blend, :Cats), # 10. The man who smokes Blend lives next to the one who keeps cats.
|
159
|
+
not_same(:Horses, :Cats),
|
160
|
+
is_noteq(:Tea, HOUSES, :Coffee, :Milk, :Water),
|
161
|
+
same(:Dane, :Tea), # 3. The Dane drinks tea.
|
162
|
+
is_noteq(:Beer, HOUSES, :Tea, :Coffee, :Milk, :Water),
|
163
|
+
same(:Winfield, :Beer), # 12. The owner who smokes Winfield drinks beer.
|
164
|
+
is_noteq(:German, HOUSES, :Norwegian, :Dane, :Brit),
|
165
|
+
is_noteq(:Swede, HOUSES, :German, :Norwegian, :Dane, :Brit),
|
166
|
+
same(:Swede, :Dog), # 2. The Swede keeps a dog.
|
167
|
+
is_noteq(:Pallmall, HOUSES, :Dunhill, :Blend, :Winfield),
|
168
|
+
is_noteq(:Rothmans, HOUSES, :Pallmall, :Dunhill, :Blend, :Winfield),
|
169
|
+
same(:Pallmall, :Birds), # 6. The person who smokes Pall Mall keeps birds.
|
170
|
+
same(:German, :Rothmans), # 13. The German smokes Rothmans.
|
171
|
+
is_noteq(:Fish, HOUSES, :Dog, :Birds, :Cats, :Horses),
|
172
|
+
|
173
|
+
same(:People, PEOPLE),
|
174
|
+
same(:Smokes, SMOKES),
|
175
|
+
same(:Pets, PETS),
|
176
|
+
same(:Colours, COLOURS),
|
177
|
+
same(:Drinks, DRINKS),
|
178
|
+
]
|
179
|
+
|
180
|
+
solutions = assert_solutions houses(:People, :Smokes, :Pets, :Colours, :Drinks), [
|
181
|
+
{
|
182
|
+
People: [3, 2, 5, 4, 1],
|
183
|
+
Smokes: [2, 3, 1, 5, 4],
|
184
|
+
Pets: [5, 1, 4, 3, 2],
|
185
|
+
Colours: [3, 2, 4, 5, 1],
|
186
|
+
Drinks: [2, 5, 3, 1, 4]
|
187
|
+
}
|
188
|
+
], goals: 1873
|
189
|
+
|
190
|
+
solution = solutions.first
|
191
|
+
|
192
|
+
assert_equal :German, PEOPLE[solution[:People].index(solution[:Pets][FISH_INDEX])]
|
193
|
+
|
194
|
+
def house_details(solution, h)
|
195
|
+
[
|
196
|
+
PEOPLE[solution[:People].index(h)],
|
197
|
+
SMOKES[solution[:Smokes].index(h)],
|
198
|
+
PETS[solution[:Pets].index(h)],
|
199
|
+
COLOURS[solution[:Colours].index(h)],
|
200
|
+
DRINKS[solution[:Drinks].index(h)]
|
201
|
+
]
|
202
|
+
end
|
203
|
+
|
204
|
+
assert_equal [:Norwegian, :Dunhill, :Cats, :Yellow, :Water ], house_details(solution, 1)
|
205
|
+
assert_equal [:Dane, :Blend, :Horses, :Blue, :Tea ], house_details(solution, 2)
|
206
|
+
assert_equal [:Brit, :Pallmall, :Birds, :Red, :Milk ], house_details(solution, 3)
|
207
|
+
assert_equal [:German, :Rothmans, :Fish, :Green, :Coffee], house_details(solution, 4)
|
208
|
+
assert_equal [:Swede, :Winfield, :Dog, :White, :Beer ], house_details(solution, 5)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'instantiates lists using member and length builtin predicates' do
|
212
|
+
builtin :member, :length
|
213
|
+
predicate :lists
|
214
|
+
|
215
|
+
lists(:L) << [
|
216
|
+
member(:N,[1,2,3,4,5]),
|
217
|
+
length(:L, :N),
|
218
|
+
member(:N, :L)
|
219
|
+
]
|
220
|
+
|
221
|
+
assert_solutions lists(:L), [
|
222
|
+
{ L: [1] },
|
223
|
+
{ L: [2, nil] },
|
224
|
+
{ L: [nil, 2] },
|
225
|
+
{ L: [3, nil, nil] },
|
226
|
+
{ L: [nil, 3, nil] },
|
227
|
+
{ L: [nil, nil, 3] },
|
228
|
+
{ L: [4, nil, nil, nil] },
|
229
|
+
{ L: [nil, 4, nil, nil] },
|
230
|
+
{ L: [nil, nil, 4, nil] },
|
231
|
+
{ L: [nil, nil, nil, 4] },
|
232
|
+
{ L: [5, nil, nil, nil, nil] },
|
233
|
+
{ L: [nil, 5, nil, nil, nil] },
|
234
|
+
{ L: [nil, nil, 5, nil, nil] },
|
235
|
+
{ L: [nil, nil, nil, 5, nil] },
|
236
|
+
{ L: [nil, nil, nil, nil, 5] },
|
237
|
+
], goals: 13
|
238
|
+
end
|
239
|
+
|
240
|
+
it 'implements a prime number search' do
|
241
|
+
builtin :gtr, :is, :noteq, :between
|
242
|
+
predicate :prime, :search_prime
|
243
|
+
|
244
|
+
prime(2).fact!
|
245
|
+
prime(3).fact!
|
246
|
+
prime(:X) << [
|
247
|
+
between(:X, 4, 20000),
|
248
|
+
is(:X_mod_2, :X){|x| x % 2 },
|
249
|
+
noteq(:X_mod_2, 0),
|
250
|
+
search_prime(:X, 3),
|
251
|
+
]
|
252
|
+
|
253
|
+
search_prime(:X, :N) << [
|
254
|
+
is(:N_squared, :N){|n| n ** 2 },
|
255
|
+
gtr(:N_squared, :X),
|
256
|
+
:CUT
|
257
|
+
]
|
258
|
+
|
259
|
+
search_prime(:X, :N) << [
|
260
|
+
is(:X_mod_N, :X, :N){|x,n| x % n },
|
261
|
+
noteq(:X_mod_N, 0),
|
262
|
+
is(:M, :N){|n| n + 2 },
|
263
|
+
:CUT,
|
264
|
+
search_prime(:X, :M),
|
265
|
+
]
|
266
|
+
|
267
|
+
known_primes = [
|
268
|
+
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
|
269
|
+
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
|
270
|
+
211, 223, 227, 229
|
271
|
+
]
|
272
|
+
|
273
|
+
assert_equal known_primes, prime(:number).solve_for(:number, max_solutions: 50)
|
274
|
+
assert_equal 3016, Goal.goal_count
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|