/
ProblemSolver.pm6
118 lines (106 loc) · 3.06 KB
/
ProblemSolver.pm6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use ProblemSolver::State;
unit class ProblemSolver;
has Bool $.stop-on-first-solution = False;
has Bool $!found-solution = False;
has Array of Callable %!constraints{Signature};
has $!variables handles <add-variable> = ProblemSolver::State.new;
#has ProblemSolver::State $!variables handles <add-variable> .= new;
has &.print-found is rw;
has Array of Callable %!heuristics;
method add-constraint(&const) {
%!constraints{&const.signature}.push: &const
}
method add-heuristic($var, &heu) {
%!heuristics{$var}.push: &heu
}
method solve {
for $!variables.found-vars -> $key {
self!remove-values($!variables, :variable($key), :value($!variables.get($key))) if %!heuristics{$key}:exists;
}
self!solve-all($!variables)
}
method !solve-all($todo) {
do if $todo.found-everything {
my %tmp = $todo.found-hash;
do if self!run-constraints(%tmp, :debug) {
$!found-solution = True;
%tmp
}
} else {
my @resp;
my $key = $todo.next-var;
for $todo.iterate-over($key) -> $new {
next unless self!run-constraints($new.found-hash) or $new.has-empty-vars;
self!remove-values($new, :variable($key), :value($new.get($key))) if %!heuristics{$key}:exists;
&!print-found($new.found-hash) if &!print-found;
@resp.push: self!solve-all($new);
last if $!stop-on-first-solution and $!found-solution
}
|@resp
}
}
method !remove-values($todo, Str :$variable, :$value) {
if %!heuristics{$variable}:exists {
for @( %!heuristics{$variable} ) -> &func {
func($todo, $value)
}
}
}
method !run-constraints(%values, :$debug) {
my @cons = self!get-constraints-for-vars(%values);
for @cons -> &func {
return False if not func(|%values)
}
True
}
method !get-constraints-for-vars(%vars) {
my @keys = %!constraints.keys.grep: -> \sig { %vars ~~ sig }
|%!constraints{@keys}.map: |*
}
method constraint-vars(&red, @vars) {
my $pars = &red.signature.params.elems;
my @comb = @vars.combinations($pars);
for @comb -> @pars {
my $sig = @pars.map({":\${$_}!"}).join(", ");
my $cal = @pars.map({"\${$_}"}).join(", ");
use MONKEY-SEE-NO-EVAL;
my &func = EVAL "-> $sig, | \{ red($cal)\}";
no MONKEY-SEE-NO-EVAL;
$.add-constraint(&func)
}
for @vars -> $var {
my @v = @vars.grep(* !eq $var);
$.add-heuristic($var, -> $todo, $value {
for @v (&) $todo.not-found-vars -> $var {
$todo.find-and-remove-from: $var.key, -> $v { not red($v, $value) }
}
})
}
}
method unique-vars(@vars) {
my @comb = @vars.combinations(2);
for @comb -> @pars {
my $sig = @pars.map({":\${$_}!"}).join(", ");
my $cal = @pars.map({"\${$_}"}).join(", ");
use MONKEY-SEE-NO-EVAL;
my &func = EVAL "-> $sig, | \{ [!~~] $cal \}";
no MONKEY-SEE-NO-EVAL;
$.add-constraint(&func)
}
for @vars -> $var {
my @v = @vars.grep(* !eq $var);
$.add-heuristic($var, -> $todo, $value {
for @v (&) $todo.not-found-vars -> $var {
$todo.remove-from: $var.key, $value
}
})
}
}
method no-order-vars(+@vars) {
for @vars -> $var {
my @v = @vars.grep(* !eq $var);
$.add-heuristic($var, -> $todo, $value {
$todo.recursive-remove-from-vars: @v, $value
})
}
}