File Coverage

blib/lib/App/ForKids/LogicalPuzzleGenerator.pm
Criterion Covered Total %
statement 41 338 12.1
branch 0 54 0.0
condition 0 21 0.0
subroutine 14 40 35.0
pod 24 24 100.0
total 79 477 16.5


line stmt bran cond sub pod time code
1             package App::ForKids::LogicalPuzzleGenerator;
2              
3 1     1   51627 use 5.006;
  1         3  
4 1     1   5 use strict;
  1         1  
  1         28  
5 1     1   5 use warnings FATAL => 'all';
  1         2  
  1         26  
6 1     1   4 use Carp;
  1         2  
  1         51  
7 1     1   375 use App::ForKids::LogicalPuzzleGenerator::Variable::Name;
  1         3  
  1         21  
8 1     1   331 use App::ForKids::LogicalPuzzleGenerator::Variable::Color;
  1         2  
  1         21  
9 1     1   342 use App::ForKids::LogicalPuzzleGenerator::Variable::Animal;
  1         2  
  1         21  
10 1     1   329 use App::ForKids::LogicalPuzzleGenerator::Variable::Fruit;
  1         2  
  1         21  
11 1     1   337 use App::ForKids::LogicalPuzzleGenerator::Variable::Race;
  1         4  
  1         21  
12 1     1   385 use App::ForKids::LogicalPuzzleGenerator::Variable::Profession;
  1         2  
  1         20  
13 1     1   335 use App::ForKids::LogicalPuzzleGenerator::Fact::True;
  1         3  
  1         19  
14 1     1   334 use App::ForKids::LogicalPuzzleGenerator::Fact::NotTrue;
  1         2  
  1         21  
15 1     1   404 use Capture::Tiny ':all';
  1         21920  
  1         108  
16 1     1   389 use AI::Prolog;
  1         219116  
  1         6  
17              
18             =head1 NAME
19              
20             App::ForKids::LogicalPuzzleGenerator - The great new App::ForKids::LogicalPuzzleGenerator!
21              
22             =head1 VERSION
23              
24             Version 0.02
25              
26             =cut
27              
28             our $VERSION = '0.02';
29              
30              
31             =head1 SYNOPSIS
32              
33             The module generates a logical puzzle. The field "story" contains a text describing the puzzle (multiple sessions)
34             while the field "solution" contains the data describing the solution.
35              
36             use App::ForKids::LogicalPuzzleGenerator;
37              
38             my $x = App::ForKids::LogicalPuzzleGenerator->new(range=>3, amount_of_facts_per_session => 4);
39            
40             print $$x{intro_story};
41            
42             print $$x{story};
43            
44             print $$x{solution_story};
45              
46             =head1 SUBROUTINES/METHODS
47              
48             =head2 new
49              
50             It requires "range" (a value 2..4). Optionally one can pass amount_of_facts_per_session (the default equals 3)
51             and debug.
52              
53             =cut
54              
55             sub new {
56 0     0 1   my $class = shift;
57 0           my $this = { @_ };
58 0           bless $this, $class;
59            
60 0 0         croak "missing range" unless defined $$this{range};
61 0 0 0       croak "invalid range" if $$this{range}<2 or $$this{range}>4;
62            
63 0           $$this{amount_of_variables} = $$this{range};
64 0           $$this{amount_of_values} = $$this{range};
65 0 0         $$this{amount_of_facts_per_session} = 3 unless defined $$this{amount_of_facts_per_session};
66            
67 0 0         croak "invalid amount_of_facts_per_session" if $$this{amount_of_facts_per_session} < 1;
68              
69 0           $this->generate_a_solution();
70 0           $this->create_variables();
71 0           $this->generate_facts();
72 0           $this->generate_story();
73 0           $this->generate_solution_story();
74 0           $this->generate_intro_story();
75            
76 0           return $this;
77             }
78              
79             =head2 generate_facts
80             =cut
81              
82             sub generate_facts
83             {
84 0     0 1   my $this = shift;
85 0           for my $i (0..$$this{amount_of_variables}-1)
86             {
87 0           for my $j ($i+1..$$this{amount_of_variables}-1)
88             {
89 0           for my $r (@{$$this{solution}})
  0            
90             {
91 0           push @{$$this{facts}},
92             App::ForKids::LogicalPuzzleGenerator::Fact::True->new(
93             first => $i,
94             second => $j,
95             a => $$r{$i},
96 0           b => $$r{$j},
97             known => 0
98             );
99              
100 0           for my $na (0..$$this{amount_of_values}-1)
101             {
102 0           for my $nb (0..$$this{amount_of_values}-1)
103             {
104 0 0 0       if (($$r{$i} == $na && $$r{$j} != $nb) || ($$r{$i} != $na && $$r{$j} == $nb))
      0        
      0        
105             {
106 0           push @{$$this{facts}},
  0            
107             App::ForKids::LogicalPuzzleGenerator::Fact::NotTrue->new(
108             first => $i,
109             second => $j,
110             a => $na,
111             b => $nb,
112             known => 0
113             );
114             }
115             }
116             }
117             }
118             }
119             }
120             }
121              
122             =head2 generate_prolog_for_categories
123             =cut
124              
125             sub generate_prolog_for_categories
126             {
127 0     0 1   my $this = shift;
128 0           my $program .= "% categories:\n";
129 0           for my $i (0..$#{$$this{variable}})
  0            
130             {
131 0           for my $value (@{$$this{variable}[$i]{selected_values}})
  0            
132             {
133 0           my $z = ref($$this{variable}[$i]);
134 0           $z =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
135 0           $program .= "is_value_${z}('$value').\n";
136             }
137             }
138 0           return $program;
139             }
140              
141             =head2 generate_prolog_for_facts
142             =cut
143              
144             sub generate_prolog_for_facts
145             {
146 0     0 1   my $this = shift;
147 0           my $known_facts_ref = shift;
148 0           my $program = "";
149              
150 0           for my $i (0..$$this{amount_of_variables}-2)
151             {
152 0           my $z1 = ref($$this{variable}[$i]);
153 0           $z1 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
154 0           for my $j ($i+1..$$this{amount_of_variables}-1)
155             {
156 0           my $z2 = ref($$this{variable}[$j]);
157 0           $z2 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
158 0           $program .= "forbidden_";
159 0           $program .= "${z1}_";
160 0           $program .= "${z2}(fake, fake).\n";
161             }
162             }
163              
164              
165 0           for my $f (grep { $$_{value} == 1 } @$known_facts_ref)
  0            
166             {
167 0           $program .= "forbidden_";
168 0           my $z1 = ref($$this{variable}[$$f{first}]);
169 0           $z1 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
170 0           $program .= "${z1}_";
171 0           my $z2 = ref($$this{variable}[$$f{second}]);
172 0           $z2 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
173 0           $program .= "${z2}('$$this{variable}[$$f{first}]{selected_values}[$$f{a}]', '$$this{variable}[$$f{second}]{selected_values}[$$f{b}]'):-!,fail.\n";
174             }
175              
176 0           for my $f (grep { $$_{value} == 0 } @$known_facts_ref)
  0            
177             {
178 0           $program .= "forbidden_";
179 0           my $z1 = ref($$this{variable}[$$f{first}]);
180 0           $z1 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
181 0           $program .= "${z1}_";
182 0           my $z2 = ref($$this{variable}[$$f{second}]);
183 0           $z2 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
184 0           $program .= "${z2}('$$this{variable}[$$f{first}]{selected_values}[$$f{a}]', '$$this{variable}[$$f{second}]{selected_values}[$$f{b}]').\n";
185             }
186 0           return join("", map { "$_\n" } reverse sort
187             {
188 0           $a =~ /^(\w+)/; my $t1 = $1;
  0            
  0            
189 0           $b =~ /^(\w+)/; my $t2 = $1;
  0            
190              
191 0 0         if ($t1 ne $t2)
192             {
193 0           return $t1 cmp $t2;
194             }
195 0           return $a =~ /fail/ <=> $b =~ /fail/;
196             } split/\n/, $program);
197             }
198              
199             =head2 generate_prolog_for_possible_solutions
200             =cut
201              
202              
203             sub generate_prolog_for_possible_solutions
204             {
205 0     0 1   my $this = shift;
206 0           my $program .= "possible_solution(".join(",", map { "Z$_" } 0..$#{$$this{variable}})."):-";
  0            
  0            
207             $program .= join(",", map {
208 0           my $z = ref($$this{variable}[$_]);
209 0           $z =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
210 0           "is_value_${z}(Z${_})"
211 0           } 0..$#{$$this{variable}});
  0            
212              
213 0           for my $i (0..$$this{amount_of_variables}-2)
214             {
215 0           my $z1 = ref($$this{variable}[$i]);
216 0           $z1 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
217 0           for my $j ($i+1..$$this{amount_of_variables}-1)
218             {
219 0           my $z2 = ref($$this{variable}[$j]);
220 0           $z2 =~ s/App::ForKids::LogicalPuzzleGenerator::Variable:://;
221 0           $program .= ", ";
222 0           $program .= "not(forbidden_${z1}_${z2}(Z${i}, Z${j}))";
223             }
224             }
225 0           $program .= ".\n\n";
226 0           return $program;
227             }
228              
229             =head2 generate_prolog_for_printing_possible_solutions
230              
231             =cut
232              
233              
234             sub generate_prolog_for_printing_possible_solutions
235             {
236 0     0 1   my $this = shift;
237            
238 0           my $program .= "print_all_possible_solutions :-\n";
239 0           $program .= " possible_solution(".join(",", map { "Z_0_$_" } 0..$#{$$this{variable}})."),\n";
  0            
  0            
240 0           $program .= " eq(Z_0_0, '$$this{variable}[0]{selected_values}[0]'),\n";
241              
242 0           for my $i (1..$$this{amount_of_variables}-1)
243             {
244 0           $program .= " possible_solution(".join(",", map { "Z_${i}_${_}" } 0..$#{$$this{variable}})."),\n";
  0            
  0            
245              
246 0           $program .= " eq(Z_${i}_0,'$$this{variable}[0]{selected_values}[$i]'),\n";
247              
248 0           for my $j (0..$i-1)
249             {
250 0           $program .= " ".join(",", map { "not(eq(Z_${j}_${_},Z_${i}_${_}))" } 0..$#{$$this{variable}}).",\n";
  0            
  0            
251             }
252             }
253              
254 0           for my $i (0..$$this{amount_of_variables}-1)
255             {
256 0           $program .= " write([ ".join(",", map { "Z_${i}_${_}" } 0..$#{$$this{variable}}) ."]),\n";
  0            
  0            
257             }
258 0           $program .= " nl, fail.\n";
259 0           $program .= "print_all_possible_solutions.\n";
260              
261 0           return $program;
262             }
263              
264              
265             =head2 generate_program
266              
267             =cut
268              
269             sub generate_program
270             {
271 0     0 1   my $this = shift;
272 0           my $program = "";
273 0           $program .= $this->generate_prolog_for_categories();
274 0           my @known_facts = grep { $$_{known} } @{$$this{facts}};
  0            
  0            
275 0           $program .= $this->generate_prolog_for_facts(\@known_facts);
276 0           $program .= $this->generate_prolog_for_possible_solutions();
277 0           $program .= $this->generate_prolog_for_printing_possible_solutions();
278              
279 0           for my $r (@{$$this{solution}})
  0            
280             {
281 0           $program .= "% ".join(",", map { $$this{variable}[$_]{selected_values}[$$r{$_}] } sort keys %$r)."\n";
  0            
282             }
283            
284 0           return $program;
285             }
286              
287              
288             =head2 get_result
289              
290             =cut
291              
292             sub get_result
293             {
294 0     0 1   my $this = shift;
295 0           my $line = shift;
296 0           my $code = $this->generate_program();
297            
298 0           my $p = AI::Prolog->new($code);
299            
300 0           my $c;
301            
302 0 0         if ($$this{debug})
303             {
304             $c = tee_stdout {
305 0     0     $p->query("print_all_possible_solutions");
306 0           $p->results();
307 0           };
308             }
309             else
310             {
311             $c = capture_stdout {
312 0     0     $p->query("print_all_possible_solutions");
313 0           $p->results();
314 0           };
315             }
316              
317 0           my %h = ();
318 0           for my $k (split(/\n/,$c))
319             {
320 0           $h{$k} = 1;
321             }
322            
323 0 0         print scalar(keys %h), "\n" if $$this{debug};
324 0           return sort keys %h;
325             }
326              
327             =head2 add_positive_fact_with_name_to_the_story
328              
329             =cut
330              
331              
332             sub add_positive_fact_with_name_to_the_story
333             {
334 0     0 1   my ($this, $current_wizard, $f) = @_;
335              
336 0           my $story = "";
337 0 0         if ($$f{a} == $current_wizard)
338             {
339 0           $story .= $$this{variable}[$$f{second}]->get_description_I($$this{variable}[$$f{second}]{selected_values}[$$f{b}])." ";
340             }
341             else
342             {
343             $story .= $$this{variable}[$$f{second}]->get_description_X(
344             $$this{variable}[$$f{first}]{selected_values}[$$f{a}],
345 0           $$this{variable}[$$f{second}]{selected_values}[$$f{b}]
346             )." ";
347             }
348 0           return $story;
349             }
350              
351             =head2 add_positive_fact_without_name_to_the_story
352              
353             =cut
354              
355             sub add_positive_fact_without_name_to_the_story
356             {
357 0     0 1   my ($this, $current_wizard, $f) = @_;
358 0           my $story = "";
359            
360             $story .= $$this{variable}[$$f{first}]->get_description_the_one_who($$this{variable}[$$f{first}]{selected_values}[$$f{a}])." ".
361 0           $$this{variable}[$$f{second}]->get_description_he_likes($$this{variable}[$$f{second}]{selected_values}[$$f{b}])." ";
362              
363 0           return $story;
364             }
365              
366              
367             =head2 add_negative_fact_with_name_to_the_story
368              
369             =cut
370              
371             sub add_negative_fact_with_name_to_the_story
372             {
373 0     0 1   my ($this, $current_wizard, $f) = @_;
374              
375 0           my $story = "";
376              
377 0 0         if ($$f{a} == $current_wizard)
378             {
379 0           $story .= $$this{variable}[$$f{second}]->get_description_I_dont($$this{variable}[$$f{second}]{selected_values}[$$f{b}])." ";
380             }
381             else
382             {
383             $story .= $$this{variable}[$$f{second}]->get_description_X_does_not(
384             $$this{variable}[$$f{first}]{selected_values}[$$f{a}],
385 0           $$this{variable}[$$f{second}]{selected_values}[$$f{b}]
386             )." ";
387             }
388              
389 0           return $story;
390             }
391              
392             =head2 add_negative_fact_without_name_to_the_story
393             =cut
394              
395             sub add_negative_fact_without_name_to_the_story
396             {
397 0     0 1   my ($this, $current_wizard, $f) = @_;
398 0           my $story = "";
399            
400             $story .= $$this{variable}[$$f{first}]->get_description_the_one_who($$this{variable}[$$f{first}]{selected_values}[$$f{a}])." ".
401 0           $$this{variable}[$$f{second}]->get_description_he_does_not($$this{variable}[$$f{second}]{selected_values}[$$f{b}])." ";
402            
403 0           return $story;
404             }
405              
406              
407              
408             =head2 add_negative_fact_to_the_story
409             =cut
410              
411              
412             sub add_negative_fact_to_the_story
413             {
414 0     0 1   my ($this, $current_wizard, $f) = @_;
415              
416 0           my $story = "";
417              
418 0 0         if ($$f{first} == 0)
419             {
420 0           $story .= $this->add_negative_fact_with_name_to_the_story($current_wizard, $f);
421             }
422             else
423             {
424 0           $story .= $this->add_negative_fact_without_name_to_the_story($current_wizard, $f);
425             }
426              
427 0           return $story;
428             }
429              
430              
431             =head2 add_fact_to_the_story
432             =cut
433              
434              
435             sub add_fact_to_the_story
436             {
437 0     0 1   my ($this, $current_wizard, $f) = @_;
438              
439 0           my $story = "";
440 0 0         if ($$f{value})
441             {
442 0           $story .= $this->add_positive_fact_to_the_story($current_wizard, $f);
443             }
444             else
445             {
446 0           $story .= $this->add_negative_fact_to_the_story($current_wizard, $f);
447             }
448 0           return $story;
449             }
450              
451             =head2 get_subsequent_fact
452              
453             =cut
454              
455             sub get_subsequent_fact
456             {
457 0     0 1   my ($this, $amount_of_results_ref, $amount_of_results_with_this_fact_ref) = @_;
458              
459 0           my $f;
460 0           my @unknown_facts = grep { !$$_{known} } @{$$this{facts}};
  0            
  0            
461 0 0         if (@unknown_facts)
462             {
463 0           while (1)
464             {
465 0           my @result_without_this_fact = $this->get_result(__LINE__);
466 0           $f = $unknown_facts[int(rand()*@unknown_facts)];
467 0           $$f{known} = 1;
468 0           my @result_with_this_fact = $this->get_result(__LINE__);
469              
470 0 0         if (@result_without_this_fact > @result_with_this_fact)
471             {
472 0           $$amount_of_results_with_this_fact_ref = scalar(@result_with_this_fact);
473 0           $$amount_of_results_ref = scalar(@result_with_this_fact);
474 0           $$f{number} = $$this{counter}++;
475 0           last;
476             }
477             else
478             {
479 0           $$f{known} = 0;
480 0           next;
481             }
482             }
483             }
484              
485 0           return $f;
486             }
487              
488              
489              
490             =head2 get_session_with
491              
492             =cut
493              
494             sub get_session_with
495             {
496 0     0 1   my $this = shift;
497 0           my $current_wizard = shift;
498 0           my $amount_of_results_ref = shift;
499 0           my $story = "";
500 0           my $introduction;
501              
502 0           for my $i (0..$$this{amount_of_facts_per_session}-1)
503             {
504 0           my $amount_of_results_with_this_fact = undef;
505 0           my $f = $this->get_subsequent_fact($amount_of_results_ref, \$amount_of_results_with_this_fact);
506              
507 0           push @{$$this{sessions}[-1]{facts}}, $f;
  0            
508              
509 0 0         if (!$f)
510             {
511 0           $$amount_of_results_ref = 1;
512 0           last;
513             }
514              
515 0 0         if (!$introduction)
516             {
517             $story .= "- ".sprintf("My name is %s.",
518 0           $$this{variable}[0]{selected_values}[$current_wizard])." ";
519 0           $introduction = 1;
520             }
521              
522 0           $story .= $this->add_fact_to_the_story($current_wizard, $f);
523              
524 0           $$f{known} = 1;
525              
526 0 0 0       if (defined($amount_of_results_with_this_fact) && $amount_of_results_with_this_fact == 1)
527             {
528 0           return $story;
529             }
530             }
531              
532 0           return $story;
533             }
534              
535              
536             =head2 get_all_sessions
537              
538             =cut
539              
540              
541             sub get_all_sessions
542             {
543 0     0 1   my $this = shift;
544 0           my $recent_wizard = undef;
545 0           my $story;
546 0           my $amount_of_results = undef;
547              
548 0           while (1)
549             {
550 0           my $current_wizard = undef;
551              
552 0           while (1)
553             {
554 0           $current_wizard = int(rand()*($$this{amount_of_variables}));
555 0 0 0       last if !defined($recent_wizard) || $current_wizard != $recent_wizard;
556             }
557              
558 0           push @{$$this{sessions}},
  0            
559             { wizard => $current_wizard, amount_of_results => $amount_of_results, facts => [] };
560              
561 0           $story .= $this->get_session_with($current_wizard, \$amount_of_results)."\n";
562              
563 0           $story .= "\n";
564 0           $recent_wizard = $current_wizard;
565              
566 0 0 0       if (defined($amount_of_results) && $amount_of_results == 1)
567             {
568 0           last;
569             }
570             }
571 0           return $story;
572             }
573              
574             =head2 generate_story
575             =cut
576              
577              
578             sub generate_story
579             {
580 0     0 1   my $this = shift;
581 0           my $amount = $$this{amount_of_variables};
582              
583 0           my $story = "";
584              
585 0           $$this{sessions} = [];
586              
587 0           $story .= $this->get_all_sessions();
588              
589 0           $$this{story} = $story;
590             }
591              
592              
593             =head2 generate_solution_story
594             =cut
595              
596             sub generate_solution_story
597             {
598 0     0 1   my $this = shift;
599              
600 0           my $solution_story = "";
601              
602 0           for my $r (@{$$this{solution}})
  0            
603             {
604             $solution_story .= join(" ",
605             map { $$this{variable}[$_]->get_description_X($$this{variable}[0]{selected_values}[$$r{0}],
606 0           $$this{variable}[$_]{selected_values}[$$r{$_}]) } grep { $_ > 0 } sort keys %$r)."\n";
  0            
  0            
607             }
608              
609 0           $$this{solution_story} = $solution_story;
610             }
611              
612             =head2 generate_intro_story
613              
614             =cut
615             sub generate_intro_story
616             {
617 0     0 1   my $this = shift;
618 0           my $amount = $$this{amount_of_variables};
619            
620 0           my $intro_story = "";
621            
622 0           my @names = @{$$this{variable}[0]{selected_values}};
  0            
623            
624 0           $intro_story .= join(",", @names[0..$#names-1])." and ".$names[-1]." live here.\n";
625            
626 0           for my $v (1..$amount-1)
627             {
628 0           $intro_story .= $$this{variable}[$v]->get_description()." (".join(",",@{$$this{variable}[$v]{selected_values}}).").\n";
  0            
629             }
630            
631 0           $$this{intro_story} = $intro_story;
632             }
633              
634              
635             =head2 create_variables
636             =cut
637              
638             sub create_variables
639             {
640 0     0 1   my $this = shift;
641 0           $$this{variable} = [ App::ForKids::LogicalPuzzleGenerator::Variable::Name->new(amount_of_values => $$this{amount_of_values}) ];
642              
643 0           for my $i (1..$$this{amount_of_variables}-1)
644             {
645 0           while (1)
646             {
647 0           my $z = $this->get_new_variable();
648              
649 0 0         if (!grep { ref($_) eq ref($z) } @{$$this{variable}})
  0            
  0            
650             {
651 0           push @{$$this{variable}}, $z;
  0            
652 0           last;
653             }
654             }
655             }
656             }
657              
658              
659             =head2 get_new_variable
660              
661             =cut
662              
663              
664             sub get_new_variable
665             {
666 0     0 1   my $this = shift;
667 0           my $r = int(rand()*5);
668              
669 0 0         if ($r == 0)
    0          
    0          
    0          
    0          
670             {
671 0           return App::ForKids::LogicalPuzzleGenerator::Variable::Animal->new(amount_of_values => $$this{amount_of_values});
672             }
673             elsif ($r == 1)
674             {
675 0           return App::ForKids::LogicalPuzzleGenerator::Variable::Fruit->new(amount_of_values => $$this{amount_of_values});
676             }
677             elsif ($r == 2)
678             {
679 0           return App::ForKids::LogicalPuzzleGenerator::Variable::Color->new(amount_of_values => $$this{amount_of_values});
680             }
681             elsif ($r == 3)
682             {
683 0           return App::ForKids::LogicalPuzzleGenerator::Variable::Race->new(amount_of_values => $$this{amount_of_values});
684             }
685             elsif ($r == 4)
686             {
687 0           return App::ForKids::LogicalPuzzleGenerator::Variable::Profession->new(amount_of_values => $$this{amount_of_values});
688             }
689             }
690              
691             =head2 get_variable_number_i_has_appropriate_value
692              
693             =cut
694              
695              
696             sub get_variable_number_i_has_appropriate_value
697             {
698 0     0 1   my ($this, $i, $record) = @_;
699              
700 0           for my $r (@{$$this{solution}})
  0            
701             {
702 0 0         if ($$r{$i} == $$record{$i})
703             {
704 0           return 0;
705             }
706             }
707 0           return 1;
708             }
709              
710             =head2 generate_a_solution
711              
712             =cut
713              
714             sub generate_a_solution
715             {
716 0     0 1   my $this = shift;
717 0           for my $i (0..$$this{amount_of_variables}-1)
718             {
719 0           my $record = { 0 => $i };
720 0           for my $j (1..$$this{amount_of_variables}-1)
721             {
722 0           while (1)
723             {
724 0           my $p = int(rand()*$$this{amount_of_values});
725 0           $$record{$j} = $p;
726 0 0         last if $this->get_variable_number_i_has_appropriate_value($j, $record);
727             }
728             }
729 0           push @{$$this{solution}}, $record;
  0            
730             }
731             }
732              
733              
734             =head1 AUTHOR
735              
736             Pawel Biernacki, C<< >>
737              
738             =head1 BUGS
739              
740             Please report any bugs or feature requests to C, or through
741             the web interface at L. I will be notified, and then you'll
742             automatically be notified of progress on your bug as I make changes.
743              
744              
745              
746              
747             =head1 SUPPORT
748              
749             You can find documentation for this module with the perldoc command.
750              
751             perldoc App::ForKids::LogicalPuzzleGenerator
752              
753              
754             You can also look for information at:
755              
756             =over 4
757              
758             =item * RT: CPAN's request tracker (report bugs here)
759              
760             L
761              
762             =item * AnnoCPAN: Annotated CPAN documentation
763              
764             L
765              
766             =item * CPAN Ratings
767              
768             L
769              
770             =item * Search CPAN
771              
772             L
773              
774             =back
775              
776              
777             =head1 ACKNOWLEDGEMENTS
778              
779              
780             =head1 LICENSE AND COPYRIGHT
781              
782             Copyright 2016 Pawel Biernacki.
783              
784             This program is free software; you can redistribute it and/or modify it
785             under the terms of the the Artistic License (2.0). You may obtain a
786             copy of the full license at:
787              
788             L
789              
790             Any use, modification, and distribution of the Standard or Modified
791             Versions is governed by this Artistic License. By using, modifying or
792             distributing the Package, you accept this license. Do not use, modify,
793             or distribute the Package, if you do not accept this license.
794              
795             If your Modified Version has been derived from a Modified Version made
796             by someone other than you, you are nevertheless required to ensure that
797             your Modified Version complies with the requirements of this license.
798              
799             This license does not grant you the right to use any trademark, service
800             mark, tradename, or logo of the Copyright Holder.
801              
802             This license includes the non-exclusive, worldwide, free-of-charge
803             patent license to make, have made, use, offer to sell, sell, import and
804             otherwise transfer the Package with respect to any patent claims
805             licensable by the Copyright Holder that are necessarily infringed by the
806             Package. If you institute patent litigation (including a cross-claim or
807             counterclaim) against any party alleging that the Package constitutes
808             direct or contributory patent infringement, then this Artistic License
809             to you shall terminate on the date that such litigation is filed.
810              
811             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
812             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
813             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
814             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
815             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
816             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
817             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
818             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
819              
820              
821             =cut
822              
823             1; # End of App::ForKids::LogicalPuzzleGenerator