text:
Check if a number is a product of consecutive integer numbers
Some numbers such as: 6, 12, 20, 30, 42, 56, 60, 90, 120 and so on as can be expressed as a product of consecutive integer numbers as shown below.
6   = 2 * 3
12  = 3 * 4
30  = 5 * 6
60  = 3 * 4 * 5
90  = 9 * 10
120 = 4 * 5 * 6

Write a program or function that outputs a list of consecutive integers which product equals the specified number.
Examples of numbers that are not fit for this logic are:
99  = 9 * 11  (Product of non-consecutive numbers)
121 = 11 * 11 (Same numbers)
2   = 1 * 2   (Product of itself and 1)
13  = 13      (Product of only one number)

Please note that for the case of 2 = 2 * 1, we do not consider it as a valid result, as a integer multiplied by 1 gives the same result. For this question, we would consider only integers >= 2 in the product.
Input
A valid 32-bit positive integer. Can be from standard input, a function argument, etc.
Output
A list of consecutive integer numbers >= 2 (in either ascending or descending order).
If there are several combinations of consecutive integers, just provide one instance will do. If you provide more, its fine.
Restrictions
The code should take a reasonable amount of time (< 5 minutes) to run on a standard computer for all valid inputs (positive 32-bit integers). If there is a consecutive integer product, the code should output one or more within the time limit. Else, the code should terminate with no output within the time limit.
This is code golf, so the shortest code in bytes wins.

This puzzle, as stated, is not a good fit for this site's format. This site is for contests where there's a good way to decide a winner (e.g., shortest code, fastest code, most upvotes, etc.). You haven't provided any such way.

I recommend you make this a code-golf (shortest code.) you need to put some limits on it though. For example numbers 0 to 1000000, max execution time 10 sec, etc.

Tried to edit it to salvage this question. But I haven't made any questions before, so if you see anything, please do edit.

@bitpwner Aside of few typos, seems fine to me. Voted to reopen.

I think you mean 30=5*6.

@KyleKanos Opps, corrected.

What should we do if the input is an invalid number? Specifically, is looping forever OK?

@isaacg According to the spec, the input is always valid.

To clarify: we only need handle inputs that can be expressed in this manner, right? If not, what is the expected output for inputs that can't be?

The special handling of 2 feels like an unnecessary edge case. '[An] integer multiplied by 1 gives the same result' is not a valid justification: no other number multiplied by 1 satisfies the property of the factors being consecutive. Allowing 1 as a factor doesn't trivialise the problem at all.

Java - 124
String f(int t){int s=2,h=3,p=s,i;String o="";for(;p!=t&&s*s<t;p=p<t?p*h++:p/s++);if(p==t)for(i=s;i<h;o+++=i+" ");return o;}

Starting at 2, this loops until the start number is > the square root of the target (or target is reached exactly). If the product is low, it multiplies by the high number and increments it. If high, it divides by the starting number and increments it.
For example, for 30, it would check:
2*3     = 6 (too low, multiply)
2*3*4   = 24 (too low, multiply)
2*3*4*5 = 120 (too high, divide)
3*4*5   = 60 (too high, divide)
4*5     = 20 (too low, multiply)
4*5*6   = 120 (too high, divide)
5*6     = 30 (bingo!)

Outputs a space-separated string of factors in ascending order.
With line breaks:
String p(int t){
    int s=2,h=3,p=s,i;
    String o="";
    for(;p!=t&&s*s<t;p=p<t?p*h++:p/s++);
    if(p==t)
        for(i=s;i<h;o+=i+" ");
    return o;
}

Python - 104 97 95 92 try it
n=input()
s=i=2
c=1
while s<n:
 s*=i+c;c+=1
 if s==n:print range(i,i+c)
 if s/n:i+=1;s,c=i,1

If n is, e.g., set to 120 beforehand, the program outputs the two solutions:
[2, 3, 4, 5]
[4, 5, 6]

Sorry, I forgot to define some input.

replace c=c+1, i=i+1 with c+=1, i+=1

Oh yeah, didn't think about +=. But I miss ++ in Python...

if s>=n and if s/n are equivalent, so you can provide all solutions in the same number of characters.

Cool! I updated the code. :)

You can save three characters by changing s=s*(i+c) to s*=i+c.

Oh wow, of course! Thanks! :)

Clojure - 127 109 bytes
(defn f[x](first(for[r[range]y(r 2 x)v[(take-while #(<=(apply * %(r y %))x)(r y x))]:when(=(apply * v)x)]v)))

Example:
(map f [6 12 30 60 90 120 1404816 99 121 2 13])
=> ((2 3) (3 4) (5 6) (3 4 5) (9 10) (2 3 4 5) (111 112 113) nil nil nil nil)

Explanation:
This is basic, quite unoptimized functional approach. I create a lazy list of all the possibilities using a simple loop over them (it does skip all combinations which would give too big numbers, preventing overflow) and take the first of them. If no possibilities exist, it returns nil.
Easiest to test in http://tryclj.com/ .

I also now noticed that I can return all the possibilities: 120 bytes 102 bytes, but gives results in a nested list.
(defn f[x](for[r[range]y(r 2 x)v[(take-while #(<=(apply * %(r y %))x)(r y x))]:when(=(apply * v)x)]v))

Example:
(map f [6 12 30 60 90 120 1404816 99 121 2 13])
=> (((2 3)) ((3 4)) ((5 6)) ((3 4 5)) ((9 10)) ((2 3 4 5) (4 5 6)) ((111 112 113)) () () () ())

R, 65 63 61 58 bytes
Edit: -2 bytes, and then -2 more bytes, thanks to Giuseppe
Edit: -3 bytes thanks to Robin Ryder
x=scan();`[`=`for`;i[1:x,j[1:x-i,if(prod(i:j)==x)a=i:j]];a

Try it online!
I really tried to find something cleverer and shorter, but couldn't.
Loops (unusually for R) through all combinations of consecutive integers from 1..x 2..x, printing any that whose product equals x.  Breaking out of two loops is very awkward, so its shorter code to just print all the successes. (see explanation of Robin Ryder's improvement below).
Giuseppe's (second) 2-byte saving comes from redefining the array-index operator [] as the for command.  This is a classic R golf - since all operations and commands are functions that can be redefined (including indexing and for) - but has the side-effect of making the code almost unreadable afterwards... (and, if you ever used this in 'real' code, it would make all of R almost unuseable afterwards, since indexing is now no longer possible!).
Robin's 3-byte saving skips my previous careful avoidance of non-valid ranges that don't include 1, and instead sets-up the loops so that the last range found will always be valid.  So, we just need to remember this (as the variable a here) and output it when we're finished.  This has the effect that non-valid inputs leave a undefined, and so 'terminate' with no output by throwing an error.

think you can save 2 chars: tio

Thanks, but it doesn't seem to work well for "2".  I'm kind-of sure there's a shorter way to set-up the loops, though, a bit like you suggest, but I can't seem to find it...

63? this does still work for 2 at least...

Okay, this golf hurts me down to my core, and I may never recover from it: 61 bytes

Aargh! My eyes!

Here is a 59 byte version which builds on @Giuseppe 's brilliant abomination. I don't whether the output is acceptable: after a first correct output, it sometimes also lists additional solutions with a 1 included.

@RobinRyder I'm afraid that including 1 in the range used for the 2 for loops means that it illegally outputs consecutive ranges including 1...  (although after Giuseppe's golfing it's quite difficult to understand what's going on at all...)

@Giuseppe it turns out that the first golf unfortunately fails for 0, but I'll take your [ repurposing instead, thanks very much!

I don't think 0 is positive?

Perfect!  I didn't notice that!  Thanks x2.

@DominicvanEssen Indeed, it outputs some ranges with a 1, but the first solution it gives always corresponds to the rules, so it might be acceptable.

@Robin Thanks!  That was the best approach in the end!  I rearranged a bit so that the last solution is always the valid one, and this even saved another byte (but I consider this 'your' byte anyway!).  It errors now on non-valid input, but without output so I think that's Ok...

CJam, 31 bytes
q~:Qmq,A,m*{2f+~,f+_:*Q={p}*}%;

It's a brute-force approach, but execution time is only a couple of seconds using the official Java interpreter.
If you want to test the code using the online interpreter, you should keep the input reasonably low. Anything less than 226 still works on my machine.
Examples
$ TIME="%e s"
$ time cjam product.cjam <<< 2
0.12 s
$ time cjam product.cjam <<< 6
[2 3]
0.10 s
$ time cjam product.cjam <<< 120
[2 3 4 5]
[4 5 6]
0.12 s
$ time cjam product.cjam <<< 479001600
[2 3 4 5 6 7 8 9 10 11 12]
0.68 s
$ time cjam product.cjam <<< 4294901760
[65535 65536]
1.48 s
$ time cjam product.cjam <<< 4294967295
1.40 s

How it works
q~:Q      " Read from STDIN, interpret the input and save the result in variable “Q”.     ";
mq,       " Push the array [ 0 1 2 … (Q ** 0.5 - 1) ].                                    ";
A,m*      " Push the array [ 0 1 2 … 9 ] and take the Cartesian product.                  ";
{         " For each pair in the Cartesian product:                                       ";
  2f+     " Add 2 to each component.                                                      ";
  ~       " Dump the array's elements on the stack.                                       ";
  ,       " Push the array [ 0 1 2 … n ], where “n” is the topmost integer on the stack.  ";
  f+      " Add “m” to each element, where “m” is the integer below the array.            ";
  _:*     " Duplicate the resulting array and push the product of its elements.           ";
  Q={p}*  " If the product is equal to “Q”, print.                                        ";
}%        " Collect the remaining results into an array.                                  ";
;         " Discard the array from the stack.                                             ";

Java, 162
returns an array of integers, or null if there are no consecutive numbers that exist.
int[] e(int n){for(int i=1;i<n;i++){int h=i+1,c=1,s=i;while(s<n){c++;s*=h++;}if(s==n){int[] o=new int[c];for(int j=0;j<c;j++){o[j]=h-j-1;}return o;}}return null;}

ungolfed:
int[] execute(int input){
    for(int i=1; i<input; i++){
        int highest = i+1, count = 1, sum = i;
        while(sum < input){
            count++;
            sum *= highest++;
        }
        if(sum == input){
            int[] numbers = new int[count];
            for(int j=0; j<count; j++){
                numbers[j] = highest-j-1;
            }
            return numbers;
        }
    }
    return null;
}

C 105 110 try it
n,k,l;main(i){for(scanf("%d",&n);++i<n;)for(k=1,l=i;k<n;)if(k*=l++,k==n)for(l=n;l/=i;)printf("%d ",i++);}

144 with bonus: this one iterates through every number and finds matching products
main(i,j,k,l,m){for(scanf("%d",&m);++i<13;)for(j=0;++j<46341-i;){for(l=k=1;k<=i;)l*=j+k++;if(l==m)for(puts(""),k=0;k<i;)printf("%d ",j+k+++1);}}

Nice, very simple and elegant! Definitely worked for some of the smaller numbers I threw at it. Then I gave it 50815512 (7128 x 7129) and it went into an infinite loop. Is it overflowing when it tries to compute 7128 x 7129 x 7130 = 362314600560?

thanks! apparently the condition k < n goes too high because of k *= l++. i could append unsigned long long to the beginning but... that would ruin lives

PHP 258 chars, 201 not counting factorial function.
The simplest way to mathematically express "consecutive factors that equal a number" is X!/Y! Where X is the highest number and Y is the lowest minus one. Unfortunately I stopped taking calculus before I learned to solve Z = X!/Y!, so I had to bruteforce it a little.
Messy, ungolfed version:
<?php
// PHP does not define a factorial function, so I've kludged one in.
function fact($n) {
    $r = 1;
    for($i=$n; $i>1; $i--) {
        $r *= $i;
    }
    return $r;
}

$input = intval($argv[1]);

if( $input < 2 ) { die('invalid input'); }

printf("input: %s\n", $input);

$max=min(ceil(sqrt($input)),170); // integer breakdown for > 170!
$grid = array();
for( $x=1;$x<$max;$x++ ) {
    for( $y=$max;$y>=1;$y-- ) {
        if( $y >= $x ) { continue; } // Skip results that would be < 1
        $cur = fact($x)/fact($y);
        if( $cur > $input ) { // too large!
            echo "\n"; continue 2;
        }
        if( $cur == $input ) { //just right
            printf("%7d\n\nFound %s == %s\n", $cur, implode(' * ', range($y+1, $x)), $cur);
            break 2;
        }
        printf("%7d ", $cur);
    }
    echo "\n";
}
if($cur!=$input){printf("No consecutive factors produce %d\n", $input);}

Example output:
input: 518918400

  2
  3       6
  4      12      24
  5      20      60     120
  6      30     120     360     720
  7      42     210     840    2520    5040
  8      56     336    1680    6720   20160   40320
  9      72     504    3024   15120   60480  181440  362880
 10      90     720    5040   30240  151200  604800 1814400 3628800
 11     110     990    7920   55440  332640 1663200 6652800 19958400 39916800
 12     132    1320   11880   95040  665280 3991680 19958400 79833600 239500800 479001600
 13     156    1716   17160  154440 1235520 8648640 51891840 259459200
 14     182    2184   24024  240240 2162160 17297280 121080960
 15     210    2730   32760  360360 3603600 32432400 259459200
 16     240    3360   43680  524160 5765760 57657600 518918400

Found 9 * 10 * 11 * 12 * 13 * 14 * 15 * 16 == 518918400

Golfed:
<? function f($n){$r=1;for($i=$n;$i>1;$i--)$r*=$i;return $r;}$i=$argv[1];$m=min(ceil(sqrt($i)),170);for($x=1;$x<$m;$x++){for($y=$m;$y>0;$y--){if($y>=$x)continue;$c=f($x)/f($y);if($c>$i)continue 2;if($c==$i){$y++;echo "$y $x";break 2;}}}if($c!=$i){echo 'No';}

Output:
[sammitch@vm ~/golf] time php consecutive_golf.php 518918400
9 16
real 0m0.019s
user 0m0.011s
sys  0m0.009s
[sammitch@vm ~/golf] time php consecutive_golf.php 518918401
No
real 0m0.027s
user 0m0.017s
sys  0m0.011s

I was not expecting the run time to be quite this quick!

this idea came to my mind too and it looks very efficitent but i doubt it can be shortened enough "to be qualified".

@bebe it's 258 chars, not too bad for PHP. If I weren't so lazy and obstinate I'd do it in a real language. :P

X!/Y! is the product of integers N such that Y<N<=X. Does that help at all?

Pyth, 35
JvwKr2 4W-ZJ~@KgJZ1=YurGHK=Zu*NTY)Y

Note: My code actually finds the shortest representation of the input as a representation of consecutive integers >=2, so on invalid input it will print a 1 element list, possibly after a very long time. Since the problem statement says the input will be valid, I assume this is OK.
Short explanation:
Essentially, the program stores the upper and lower limits of a range, calculates the product of the numbers in the range using a reduce, adjusts the endpoints as necessary, and repeats until the product equals the input.
Long explanation:
For each snippet of code, I will give equivalent python, as well as a more detailed explanation and reasoning.
Jvw => J=eval(input())
Standard way to take input in Pyth.
Kr2 4 => K=range(2,4) => K=[2,3]
Here's the first weird part: Instead of storing the endpoints as separate variables, I'm storing them as elements of a list. The reason will soon be clear. Also, instead of doing a simple assignment, which in Pyth would be K[2 3), I'm using a range to save a character.
W-ZJ => while Z-J => while Z!=J
At this point, you might ask, "What is Z? You haven't defined it." In Pyth, all variables come predefined. Z happens to start as 0. However, Z will be set to the value of the product later, so this check will serve to end the while loop once the list is at the correct value.
~@K>JZ1 => K[J>Z] += 1
Here's why I'm storing the values in a list, not in separate variables: I want to increment one of the two endpoints, depending on whether the product is currently too high or too low. That would be a rather long conditional if the endpoints were separate variables, but with the magic of list indexing, it becomes short. Also, the fact that this check comes before the product, and the fact that Z is initialized to 0, ensure that K will be [2,4] by the time we first take the product, which are the proper endpoints.
=YurGHK => Y=reduce(lambda G,H: range(G,H),K) => Y=range(K[0],K[1])
Now, I need the actual list that the product will be taken over, and that will be printed out if we succeed. Clearly, we will use a range function. The trickiness lies in obtaining the inputs to the range function. The obvious way to do this, by indexing the list, would be =Yr'K@K1. However, by using a reduce function on this two element list, we can shorten that by a character.
=Zu*NTY => Z=reduce(lambda N,T: N*T,Y)
And now, for the whole point of this affair, the reduce operation to find the product of the list.
) => End while
Y => print(Y)
On success, print the list.
Example run:
$ cat seq_prod
JvwKr2 4W-ZJ~@K>JZ1=YurGHK=Zu*NTY)Y

$ cat seq_prod | python3 pyth.py
<debug stuff>
==================================================
[9, 10, 11, 12, 13, 14, 15, 16]

Jelly, 7 bytes
’ḊẆP=¥Ƈ

Try it online!
-1 byte thanks to Shaggy's help!
How it works
’ḊẆP=¥Ƈ - Main link. Takes an integer n on the left
’       - Decrement n
 Ḋ      - Generate the range [2, 3, ..., n-1]
  Ẇ     - Yield all contiguous sublists: [[2], [3], [4], [5], ..., [2, 3, 4, ... n-1]]
     ¥Ƈ - Keep those lists where the following is true:
   P    -   The product of the list...
    =   -   ...is equal to n

Does Jelly not have a built-in for the range [0/1,N) that could save you that removal at the end?

@Shaggy the Ṗ command on an integer N yields [1, N), but that means that the output includes sublists with 1 in them (e.g. 6 -> [1, 2, 3])

@Shaggy However, you did help me find a way to golf a byte off, so thanks!

Looks like Jelly also generates sub-arrays in order of length so, if there's a way to grab all the truthy ones rather than all of them, you might be able to save another byte.

@Shaggy Unfortunately, any way that uses the fact it works on length would add more bytes, I think

Husk, 9 bytes
fo=¹ΠQ↓2ŀ

Try it online!

Vyxal m, 9 7 6 bytes
ḢÞSvΠc

-2 bytes thanks to emanresu A
-1 byte thanks to Steffan
Try it online, or verify more test cases.
Explanation:
Ḣ       # Range from 2 to n-1 (m flag makes it n-1)
 ÞS     # All sublists
   vΠ   # Calculate the product on each
     c  # Contains the input

6 bytes with m flag: ḢÞSvΠc

@Steffan Nice, forgot that was a thing

CJam does not currently work for large numbers due to long computation time
This is my shortest CJam code. Test at http://cjam.aditsu.net/. It works by: defining input as A; creating an array of all numbers from 0 to A-1;  Kicking 0; kicking the smallest numbers until multiplying all numbers in the array is not greater than A; checking if it is greater than A; if not, creating an array from 0 to A-2; and repeating until the answer is found.  If none is found, an exception is thrown. I didn't consider that spaces between numbers were needed so they are inlcuded in the second code which is 32 characters long.
ri:A,{)\;,1{;(;_{*}*_A>}gA<}g

ri:A,{)\;,1{;(;_{*}*_A>}gA<}g" "*

I think your answer is too slow to be valid. Remember, it must complete in no more than 5 minutes on any valid 32 bit integer. How long does it take on 3600060000 == 60000*60001?

fair point, I'll rework it and post if it is short

If you're going to rework it, please delete this answer until then, or else somehow indicate that is not currently valid.

Java - 115
void f(int i){for(int j=2;j<i;j++)for(int k=1,x=j;(x*=j+k)<i;k++);if(x==i)for(i=j;i<j+k;i++)System.out.println(i);}

Slightly less golfed:
void f(int i) {
 for(int j=2; j<i; j++)
  for(int k=1, x=j; (x*=j+k) < i; k++);
   if(x == i)
    for(i=j; i<j+k; i++)
     System.out.println(i);
}

Eh, you created a function and print the return value. Haven't seen that done here before.

I can't get it to print anything...But if it would give me some output, you can golf System.out.println down to System.out.print and the semicolon at the end of for(int k=1,x=j;(x*=j+k)<i;k++) is not only unnecessary but also causes errors.

This doesn't work for me. x, j, k are out of scope in the last if/for blocks because of a ;. If I remove the ;, it doesn't print anything.

@Qwix Changing to print would mean he needs to add a whitespace character to avoid numbers running together.

@Geobits Good point! I probably would have seen that if it had given me some output.

Matlab (88)
Code expects number to be stored in x and output in l.
for n=2:12
r=ceil(x^(1/n))
for s=-3*n:n
l=r-s+(1:n)
if prod(l)==x
return
end;end;l=x;end

Since 13! > 2^32 this code searches only for products of length 2 upto 12. This code has a constant runtime of around 0.001s.

Scala - 86
def p(n:Int)=(2 to n).flatMap(i=>(i to n).map(i to _-1).find(_.product==n)).headOption

This code is very inefficient but optimizing it would only add a few more characters.
It uses a functional approach to check the products of all possible consecutive sequences. (a consecutive sequence of integers is represented as a Range object in Scala)
ungolfed:
def product(n: Int): Option[Range] = {
  def productStartingAt(start: Int): Option[Range] =
    (start to n-1).map(start to _).find(_.product == n)

  (2 to n).flatMap(i => productStartingAt(i)).headOption
}

Dart - 102 chars
This is a slow implementation. It can be made faster but that requires more characters (like doing the loop only until i*i<n)
f(n,[i=2]){
  t(j,p,a)=>p<n?t(++j,p*j,a..add(j)):p>n?f(n,i+1):a;
  for(;i<n;i++)if(n%i<1)return t(i,i,[i]);
}

(The 102 chars is without line breaks and leading spaces).
To use it, do something like:
main() {
  print(f(123456789*123456790));
}

Japt, 9 bytes
o ã æÈ×¶U

Try it
o ã æÈ×¶U     :Implicit input of integer U
o             :Range [0,U)
  ã           :Sub-arrays (in order of length)
    æ         :Get the first that returns true
     È        :When passed through the following function
      ×       :Reduce by multiplication
       ¶U     :Test for equality with U

Uh oh, drawing attention to this old question may lead to me getting outgolfed :/

Zero fear of that here, @cairdcoinheringaahing; this is as short as it gets in Japt.

I'm not familiar enough with Japt to know how this works, but would a translation of my Jelly answer get a similar length?

@cairdcoinheringaahing, will add an explanation shortly but it's nearly the same as yours, except I'm getting the first sub-array instead of all of them, which allows me to avoid the one that includes 1 as Japt generates sub-arrays in order of length.

@cairdcoinheringaahing, explanation added.

Brachylog, 8 bytes
⟦b₂s.×?∧

Try it online!
How it works
 ⟦b₂s.×?∧
 ⟦b₂        in the range from 2 to input
    s       any consecutive sublist
            (with longer list tried out first,
             so [input] will be tried last)
     .      is the output, if …
      ×     the product of the list
       ?    is the input.
        ∧   return the output

"For this question, we would consider only integers >= 2 in the product" and "A list of consecutive integer numbers >= 2" (under Output), so I don't think the output can contain 1

@cairdcoinheringaahing ⟦ (without modifier) is inclusive, so my comment is just about for e.g. the input 12 [3,4] being tested before [12] and thus returned first.

Yes, but for the input of 6 (for example), your program gives an output of [1,2,3]. By what the question says, I don't think you can have 1 in the output, so the correct input is [2,3]

@cairdcoinheringaahing Ah, now I get you! Thanks! And it's fixed. :-)

05AB1E, 7 bytes
L¦¨ŒʒPQ

Outputs a list of all possible results.
Try it online or verify all test cases.
Explanation:
L        # Push a list in the range [1, (implicit) input-integer]
 ¦¨      # Remove the first and last values to make the range [2, input)
   Œ     # Get all sublists of this list
    ʒ    # Filter this list of lists by:
     P   #  Where the product of the sublist
      Q  #  Is equal to the (implicit) input
         # (after which the filtered list of lists is output implicitly as result)

Javascript, 88
Golfed code:
function f(a){for(i=2;i<a;i++){b=[1];for(j=i;j>1;j--)if((b[0]*=b[i-j+1]=j)==a)alert(b)}}

Easier to read (nicely spaced) code:
function f(a){
    for(i=2;i<a;i++){
        b=[1];
        for(j=i;j>1;j--)
            if((b[0]*=b[i-j+1]=j)==a)
                alert(b);
    }
}

For each number from 2 to the input number, it finds the product of consecutive integers from the current number back down to 2. If this product equals the input number, then the series of consecutive numbers, along with the original input number, is output.
It outputs the input number followed by the consecutive integers whose product is the input number.
For example f(120) produces an alert with the text "120,5,4,3,2" and then a second alert with the text "120,6,5,4".

APL (Dyalog Unicode), 18 bytes
{⍵∊∊{⍵×/x}¨x←1↓⍳⍵}

Try it online!
Explanation
{⍵∊∊{⍵×/x}¨x←1↓⍳⍵}
           x←1↓⍳⍵  assign range from 2 to n to x
          ¨        map over range using function
    {⍵×/x}         get products of subsets of size ⍵ in x
                   (this gets all reuquired products of consecutive numbers > 1)
   ∊               enlist/flatten the vector of vectors
 ⍵∊                is n present in the products?

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Fusing Fireworks
Overview
Given a list of fireworks a-z and times 3-78, arrange them with fuses to make them all light up at the correct time.
A line of input is given as space separated letters and numbers:
a 3 b 6 c 6 d 8 e 9 f 9

That example shows that firework a need to light at time 3, b and c both at 6, d at 8, with e and f both at 9. Each line corresponds to one map.
Output is a fuse/firework map for each line, using the symbols |- to show fuses and the letters to show fireworks.
A - fuse connects to fuses and fireworks directly left/right of it, while a | fuse connects with those above/below. For example, the fuses || are not connected, and -| are.
For example, two possible answers to the above are:
---a        ---------f
  |         |||   ||
  |-c       |||   de
--|--d      a||
| b |        |c
f   e        b

All fuse maps should start with a single - in the upper left corner. That is the point where you light the fuse. Each character of fuse takes one second to burn. As you can see, the a is reached in three seconds in both diagrams, b in six, etc.
Now, both of the maps given above are valid for the input given, but one is clearly more efficient. The left one only uses 13 units of fuse, while the right one takes 20.
Fuses do not burn through fireworks! So, for input a 3 b 5, this is not valid:
---a--b

Challenge
Your goal is to minimize the amount of fuse used over all test cases. Scoring is very simple, the total units of fuse used.
If you cannot produce a map for a test case, whether it's an impossible case or not, the score for that case is the sum of all times (41 for the example above).
In the case of a tie, the scoring is modified so that the most compact maps win. The tiebreak score is the area of the bounding box of each map. That is, the length of the longest line times the number of lines. For "impossible" maps this is the square of the largest number (81 for the example above).
In the case that submissions tie both of these scoring methods, the tie goes to the earlier entry/edit.
Your program must be deterministic, for verification purposes.
Test Cases
There are 250 test cases, located here. Each has between 4 and 26 fireworks. The minimum fuse time for a firework is 3. The fireworks in each case are "sorted" by time and letter, meaning b will never light before a.
When posting, please include your full program, your total score, and the resulting map for (at least) the first test case given in the file:
a 6 b 8 c 11 d 11 e 11 f 11 g 12 h 15 i 18 j 18 k 21 l 23 m 26 n 28 o 28 p 30 q 32 r 33 s 33 t 34

Can arbitrarily many fireworks go off at the same time?

Basically, yes. I haven't looked for the largest instance of that in my test cases, but I know it's at least four. The time between two fuses is rand.nextInt(5)%4, so 40% chance of 0, and 20% for each 1,2,3.

Just an suggestion: I'd use a '+' for where the fuses connect or change direction, that would make the output graphics IMHO much more intuitive!

@flawr I'll allow that, provided it's done in a way that doesn't change the score. For instance, -+- in place of --- would not automatically connect fireworks above/below, there still has to be a | above/below to connect it to a firework. -+- in place of -|- is okay as it is.

Are all of the test cases solvable? E.g. if there were five or more fireworks to go off at time 3, I don't think you could fit them all near enough to the start. Likewise, you might be able to fit all of them but they might block the way to the outside for later fireworks.

@Martin In theory, there may be unsolvable cases. In practice, I haven't tested them all.

@Geobits Yes, that's why I think you might want to clarify what happens in such a case. Will you replace the test case?

@Martin No, for impossible test cases everyone gets the "impossible" score, the sum of all times.

C++
Total Length: 9059, Total Area: 27469, Failures: 13.
Note: Score includes failure penalties.

Sample output:
a 6 b 8 c 11 d 11 e 11 f 11 g 12 h 15 i 18 j 18 k 21 l 23 m 26 n 28 o 28 p 30 q 32 r 33 s 33 t 34
------ae
     | |
     |---c
     b||-g
      |d|
      f |
    i---|
  k---| h
   |  j
   |---m
   l  | t
     o-n|
      |s-r
      |-|
      p q
Length: 39, Area: 150.

a 6 b 6 c 6 d 6 e 6 f 6 g 6 h 8 i 9 j 9 k 9 l 12 m 12 n 13 o 14 p 15 q 15 r 15 s 17 t 17 u 17 v 17 w 17 x 20 y 23 z 26
------a  n|--w
|d-||---k|-o|
| g|b  |--m --x
|-|c    ||--r|
||f     l|-q |
||--j u--|--s|-
e|-i    |p|  y|
 h      v t  z-
Length: 56, Area: 120.

Full output: http://pastebin.com/raw.php?i=spBUidBV

Don't you just love brute-force solutions?
This is a little more than a simple backtracking algorithm: our tireless worker moves around the map, placing fuses and fireworks as necessary, while testing all possible moves at any point.
Well, almost---we do restrict the set of moves and abandon nonoptimal states early so that it doesn't take unbearably long (and, in particular, so that it terminates.)
Special care is taken not to create any cycles or unintended paths and not to go back the same way we came, so it's guaranteed that we don't visit the same state twice.
Even so, finding an optimal solution can take a while, so we eventually give up on optimizing a solution if it takes too long.
This algorithm still has some headroom.
For one thing, better solutions can be found by increasing the FRUSTRATION parameters.
There's no competition ATM but these numbers can be cranked up if and when...
Compile with: g++ fireworks.cpp -ofireworks -std=c++11 -pthread -O3.
Run with: ./fireworks.
Reads input from STDIN and writes output to STDOUT (possibly out-of-order.)
/* Magic numbers */
#define THREAD_COUNT 2
/* When FRUSTRATION_MOVES moves have passed since the last solution was found,
 * the last (1-FRUSTRATION_STATES_BACKOFF)*100% of the backtracking states are
 * discarded and FRUSTRATION_MOVES is multiplied by FRUSTRATION_MOVES_BACKOFF.
 * The lower these values are, the faster the algorithm is going to give up on
 * searching for better solutions. */
#define FRUSTRATION_MOVES 1000000
#define FRUSTRATION_MOVES_BACKOFF 0.8
#define FRUSTRATION_STATES_BACKOFF 0.5

#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
#include <thread>
#include <mutex>
#include <string>
#include <sstream>
#include <cassert>

using namespace std;

/* A tile on the board. Either a fuse, a firework, an empty tile or an
 * out-of-boudns tile. */
struct tile {
    /* The tile's value, encoded the "obvious" way (i.e. '-', '|', 'a', etc.)
     * Empty tiles are encoded as '\0' and OOB tiles as '*'. */
    char value;
    /* For fuse tiles, the time at which the fuse is lit. */
    int time;

    operator char&() { return value; }
    operator const char&() const { return value; }

    bool is_fuse() const { return value == '-' || value == '|'; }
    /* A tile is vacant if it's empty or OOB. */
    bool is_vacant() const { return !value || value == '*'; }

    /* Prints the tile. */
    template <typename C, typename T>
    friend basic_ostream<C, T>& operator<<(basic_ostream<C, T>& os,
                                            const tile& t) {
        return os << (t.value ? t.value : ' ');
    }
};
/* Fireworks have the same encoding as tiles. */
typedef tile firework;
typedef vector<firework> fireworks;

/* The fuse map. It has physical dimensions (its bounding-box) but is
 * conceptually infinite (filled with empty tiles.) */
class board {
    /* The tiles, ordered left-to-right top-to-bottom. */
    vector<tile> p_data;
    /* The board dimensions. */
    int p_width, p_height;
    /* The total fuse length. */
    int p_length;

public:
    board(): p_width(0), p_height(0), p_length(0) {}

    /* Physical dimensions. */
    int width() const { return p_width; }
    int height() const { return p_height; }
    int area() const { return width() * height(); }
    /* Total fuse length. */
    int length() const { return p_length; }

    /* Returns the tile at (x, y). If x or y are negative, returns an OOB
     * tile. */
    tile get(int x, int y) const {
        if (x < 0 || y < 0)
            return {'*'};
        else if (x >= width() || y >= height())
            return {'\0'};
        else
            return p_data[y * width() + x];
    }
    /* Sets the tile at (x, y). x and y must be nonnegative and the tile at
     * (x, y) must be empty. */
    board& set(int x, int y, const tile& t) & {
        assert(x >= 0 && y >= 0);
        assert(!get(x, y));
        if (x >= width() || y >= height()) {
            int new_width = x >= width() ? x + 1 : width();
            int new_height = y >= height() ? y + 1 : height();
            vector<tile> temp(new_width * new_height, {'\0'});
            for (int l = 0; l < height(); ++l)
                copy(
                    p_data.begin() + l * width(),
                    p_data.begin() + (l + 1) * width(),
                    temp.begin() + l * new_width
                );
            p_data.swap(temp);
            p_width = new_width;
            p_height = new_height;
        }
        p_data[y * width() + x] = t;
        if (t.is_fuse())
            ++p_length;
        return *this;
    }
    board&& set(int x, int y, const tile& t) && { return move(set(x, y, t)); }

    /* Prints the board. */
    template <typename C, typename T>
    friend basic_ostream<C, T>& operator<<(basic_ostream<C, T>& os,
                                            const board& b) {
        for (int y = 0; y < b.height(); ++y) {
            for (int x = 0; x < b.width(); ++x)
                os << b.get(x, y);
            os << endl;
        }
        return os;
    }
};

/* A state of the tiling algorithm. */
struct state {
    /* The current board. */
    board b;
    /* The next firework to tile. */
    fireworks::const_iterator fw;
    /* The current location. */
    int x, y;
    /* The current movement direction. 'N'orth 'S'outh 'E'ast, 'W'est or
     * 'A'ny. */
    char dir;
};

/* Adds a state to the state-stack if its total fuse length and bounding-box
 * area are not worse than the current best ones. */
void add_state(vector<state>& states, int max_length, int max_area,
                state&& new_s) {
    if (new_s.b.length() < max_length ||
        (new_s.b.length() == max_length && new_s.b.area() <= max_area)
    )
        states.push_back(move(new_s));
}
/* Adds the state after moving in a given direction, if it's a valid move. */
void add_movement(vector<state>& states, int max_length, int max_area,
                    const state& s, char dir) {
    int x = s.x, y = s.y;
    char parallel_fuse;
    switch (dir) {
    case 'E': if (s.dir == 'W') return; ++x; parallel_fuse = '|'; break;
    case 'W': if (s.dir == 'E') return; --x; parallel_fuse = '|'; break;
    case 'S': if (s.dir == 'N') return; ++y; parallel_fuse = '-'; break;
    case 'N': if (s.dir == 'S') return; --y; parallel_fuse = '-'; break;
    }
    const tile t = s.b.get(s.x, s.y), nt = s.b.get(x, y);
    assert(t.is_fuse());
    if (nt.is_fuse() && !(t == parallel_fuse && nt == parallel_fuse))
        add_state(states, max_length, max_area, {s.b, s.fw, x, y, dir});
}
/* Adds the state after moving in a given direction and tiling a fuse, if it's a
 * valid move. */
void add_fuse(vector<state>& states, int max_length, int max_area,
                const state& s, char dir, char fuse) {
    int x = s.x, y = s.y;
    int sgn;
    bool horz;
    switch (dir) {
    case 'E': ++x; sgn = 1; horz = true; break;
    case 'W': --x; sgn = -1; horz = true; break;
    case 'S': ++y; sgn = 1; horz = false; break;
    case 'N': --y; sgn = -1; horz = false; break;
    }
    if (s.b.get(x, y))
        /* Tile is not empty. */
        return;
    /* Make sure we don't create cycles or reconnect a firework. */
    const tile t = s.b.get(s.x, s.y);
    assert(t.is_fuse());
    if (t == '-') {
        if (horz) {
            if (fuse == '-') {
                if (!s.b.get(x + sgn, y).is_vacant() ||
                    s.b.get(x, y - 1) == '|' ||
                    s.b.get(x, y + 1) == '|')
                    return;
            } else {
                if (s.b.get(x + sgn, y) == '-' ||
                    !s.b.get(x, y - 1).is_vacant() ||
                    !s.b.get(x, y + 1).is_vacant())
                    return;
            }
        } else {
            if (!s.b.get(x, y + sgn).is_vacant() ||
                s.b.get(x - 1, y) == '-' ||
                s.b.get(x + 1, y) == '-')
                return;
        }
    } else {
        if (!horz) {
            if (fuse == '|') {
                if (!s.b.get(x, y + sgn).is_vacant() ||
                    s.b.get(x - 1, y) == '-' ||
                    s.b.get(x + 1, y) == '-')
                    return;
            } else {
                if (s.b.get(x, y + sgn) == '|' ||
                    !s.b.get(x - 1, y).is_vacant() ||
                    !s.b.get(x + 1, y).is_vacant())
                    return;
            }
        } else {
            if (!s.b.get(x + sgn, y).is_vacant() ||
                s.b.get(x, y - 1) == '|' ||
                s.b.get(x, y + 1) == '|')
                return;
        }
    }
    /* Ok. */
    add_state(
        states,
        max_length,
        max_area,
        {board(s.b).set(x, y, {fuse, t.time + 1}), s.fw, x, y, dir}
    );
}
/* Adds the state after adding a firework at the given direction, if it's a
 * valid move. */
void add_firework(vector<state>& states, int max_length, int max_area,
                    const state& s, char dir) {
    int x = s.x, y = s.y;
    int sgn;
    bool horz;
    switch (dir) {
    case 'E': ++x; sgn = 1; horz = true; break;
    case 'W': --x; sgn = -1; horz = true; break;
    case 'S': ++y; sgn = 1; horz = false; break;
    case 'N': --y; sgn = -1; horz = false; break;
    }
    if (s.b.get(x, y))
        /* Tile is not empty. */
        return;
    /* Make sure we don't run into an undeliberate fuse. */
    if (horz) {
        if (s.b.get(x + sgn, y) == '-' || s.b.get(x, y - 1) == '|' ||
            s.b.get(x, y + 1) == '|')
            return;
    } else {
        if (s.b.get(x, y + sgn) == '|' || s.b.get(x - 1, y) == '-' ||
            s.b.get(x + 1, y) == '-')
            return;
    }
    /* Ok. */
    add_state(
        states,
        max_length,
        max_area,
        /* After adding a firework, we can move in any direction. */
        {board(s.b).set(x, y, {*s.fw}), s.fw + 1, s.x, s.y, 'A'}
    );
}
void add_possible_moves(vector<state>& states, int max_length, int max_area,
                        const state& s) {
    /* We add the new states in reverse-desirability order. The most
     * (aesthetically) desirable states are added last. */

    const tile t = s.b.get(s.x, s.y);
    assert(t.is_fuse());

    /* Move in all (possible) directions. */
    for (char dir : "WENS")
        if (dir) add_movement(states, max_length, max_area, s, dir);

    /* If the fuse is too short for the next firework, keep adding fuse. */
    if (t.time < s.fw->time) {
        if (t == '-') {
            add_fuse(states, max_length, max_area, s, 'N', '|');
            add_fuse(states, max_length, max_area, s, 'S', '|');
            add_fuse(states, max_length, max_area, s, 'W', '|');
            add_fuse(states, max_length, max_area, s, 'W', '-');
            add_fuse(states, max_length, max_area, s, 'E', '|');
            add_fuse(states, max_length, max_area, s, 'E', '-');
        } else {
            add_fuse(states, max_length, max_area, s, 'W', '-');
            add_fuse(states, max_length, max_area, s, 'E', '-');
            add_fuse(states, max_length, max_area, s, 'N', '-');
            add_fuse(states, max_length, max_area, s, 'N', '|');
            add_fuse(states, max_length, max_area, s, 'S', '-');
            add_fuse(states, max_length, max_area, s, 'S', '|');
        }
    } else if (t.time == s.fw->time) {
        /* If we have enough fuse for the next firework, place the firework (if
         * possible) and don't add more fuse, or else we'll never finish... */
        if (t == '-') {
            add_firework(states, max_length, max_area, s, 'W');
            add_firework(states, max_length, max_area, s, 'E');
        } else {
            add_firework(states, max_length, max_area, s, 'N');
            add_firework(states, max_length, max_area, s, 'S');
        }
    }
}

void thread_proc(mutex& lock, int& total_length, int& total_area,
                    int& failures) {
    fireworks fw;
    vector<state> states;

    while (true) {
        /* Read input. */
        string input;
        {
            lock_guard<mutex> lg(lock);

            while (!cin.eof() && input.empty())
                getline(cin, input);
            if (input.empty())
                break;
        }
        fw.clear();
        int length = 0, area;
        {
            stringstream is;
            is << input;
            while (!is.eof()) {
                char c;
                int t;
                if (is >> c >> t) {
                    /* Fireworks must be sorted by launch time. */
                    assert(fw.empty() || t >= fw.back().time);
                    fw.push_back({c, t});
                    length += t;
                }
            }
            assert(!fw.empty());
            area = fw.back().time * fw.back().time;
        }

        /* Add initial state. */
        states.push_back({board().set(0, 0, {'-', 1}), fw.begin(), 0, 0, 'A'});

        board solution;
        int moves = 0;
        int frustration_moves = FRUSTRATION_MOVES;

        while (!states.empty()) {
            /* Check for solutions (all fireworks consumed.) */
            while (!states.empty() && states.back().fw == fw.end()) {
                state& s = states.back();
                /* Did we find a better solution? */
                if (solution.area() == 0 || s.b.length() < length ||
                    (s.b.length() == length && s.b.area() < area)
                ) {
                    solution = move(s.b);
                    moves = 0;
                    length = solution.length();
                    area = solution.area();
                }
                states.pop_back();
            }

            /* Expand the top state. */
            if (!states.empty()) {
                state s = move(states.back());
                states.pop_back();
                add_possible_moves(states, length, area, s);
            }

            /* Getting frustrated? */
            ++moves;
            if (moves > frustration_moves) {
                /* Get rid of some data. */
                states.erase(
                    states.begin() + states.size() * FRUSTRATION_STATES_BACKOFF,
                    states.end()
                );
                frustration_moves *= FRUSTRATION_MOVES_BACKOFF;
                moves = 0;
            }
        }

        /* Print solution. */
        {
            lock_guard<mutex> lg(lock);

            cout << input << endl;

            if (solution.area())
                cout << solution;
            else {
                cout << "FAILED!" << endl;
                ++failures;
            }

            cout << "Length: " << length <<
                    ", Area: " << area <<
                    "." << endl << endl;
            total_length += length;
            total_area += area;
        }
    }
}

int main(int argc, const char* argv[]) {
    thread threads[THREAD_COUNT];
    mutex lock;
    int total_length = 0, total_area = 0, failures = 0;

    for (int i = 0; i < THREAD_COUNT; ++i)
        threads[i] = thread(thread_proc, ref(lock), ref(total_length),
                            ref(total_area), ref(failures));
    for (int i = 0; i < THREAD_COUNT; ++i)
        threads[i].join();

    cout << "Total Length: " << total_length <<
            ", Total Area: " << total_area <<
            ", Failures: " << failures <<
            "." << endl;
}

Python
Total Length: 17387, Total Area: 62285, Failures: 44.

Sample output:
a 6 b 8 c 11 d 11 e 11 f 11 g 12 h 15 i 18 j 18 k 21 l 23 m 26 n 28 o 28 p 30 q 32 r 33 s 33 t 34
------a
     |----f
     |---c
     b|||---h
      |dg  |
      e    |-j
           |---k
           i  |
              |---m
              l  |-o
                 |--p
                 n |--s
                   |-r
                   q|
                    t
Length: 45, Area: 345.

Full output: http://pastebin.com/raw.php?i=mgiqXCRK

For reference, here's a much simpler approach.
It tries to connect fireworks to a single main fuse line, creating a "staircase" shape.
If a firework can't connect to the main line directly (which happens when two or more fireworks light at the same time) it traces back the main line looking for a point where it can branch perpendicularly down or to the right (and fails if no such point exists.)
Unsurprisingly, it does worse than the brute-force solver, but not by a huge margin.
Honestly, I expected the difference to be somewhat bigger.
Run with: python fireworks.py.
from __future__ import print_function
import sys

total_length = total_area = failures = 0

for line in sys.stdin:
    # Read input.
    line = line.strip()
    if line == "": continue
    fws = line.split(' ')
    # The fireworks are a list of pairs of the form (<letter>, <time>).
    fws = [(fws[i], int(fws[i + 1])) for i in xrange(0, len(fws), 2)]

    # The board is a dictionary of the form <coord>: <tile>.
    # The first tile marks the "starting point" and is out-of-bounds.
    board = {(-1, 0): '*'}
    # The tip of the main "staircase" fuse.
    tip_x, tip_y = -1, 0
    tip_time = 0
    # We didn't fail. Yet...
    failed = False

    for (fw, fw_time) in fws:
        dt = fw_time - tip_time
        # Can we add the firework to the main fuse line?
        if dt > 0:
            # We can. Alternate the direction to create a "staircase" pattern.
            if board[(tip_x, tip_y)] == '-':    dx, dy = 0, 1; fuse = '|'
            else:                               dx, dy = 1, 0; fuse = '-'
            x, y = tip_x, tip_y
            tip_x += dt * dx
            tip_y += dt * dy
            tip_time += dt
        else:
            # We can't. Trace the main fuse back until we find a point where we
            # can thread, or fail if we reach the starting point.
            x, y = tip_x, tip_y
            while board[(x, y)] != '*':
                horz = board[(x, y)] == '-'
                if horz:    dx, dy = 0, 1; fuse = '|'
                else:       dx, dy = 1, 0; fuse = '-'
                if dt > 0 and (x + dx, y + dy) not in board: break
                if horz:    x -= 1
                else:       y -= 1
                dt += 1
            if board[(x, y)] == '*':
                failed = True
                break
        # Add the fuse and firework.
        for i in xrange(dt):
            x += dx; y += dy
            board[(x, y)] = fuse
        board[(x + dx, y + dy)] = fw

    # Print output.
    print(line)
    if not failed:
        max_x, max_y = (max(board, key=lambda p: p[i])[i] + 1 for i in (0, 1))
        for y in xrange(max_y):
            for x in xrange(max_x):
                print(board.get((x, y), ' '), end = "")
            print()
        length = len(board) - len(fws) - 1
        area = max_x * max_y
    else:
        print("FAILED!")
        failures += 1
        length = sum(map(lambda fw: fw[1], fws))
        area = fws[-1][1] ** 2
    print("Length: %d, Area: %d.\n" % (length, area))
    total_length += length; total_area += area

print("Total Length: %d, Total Area: %d, Failures: %d." %
        (total_length, total_area, failures))

Out of curiosity, how long does this take to complete with the current parameters?

@Geobits: It's machine-dependent, obviously, and I didn't watch too closely, but I think about twenty minutes, give or take.

Python3
Total length: 8343; Failures: 4; Total time: ~140.34 seconds
import collections, re, time

def shadow(board):
    return [["#" if b not in ['|', '-', ' '] else b for b in i] for i in board]

def full_length(s):
    return len(re.findall('\-|\|', s))

def adjust_board_size(_x, _y, board, mappings, cells):
    if _y < 0:
        _y = 0
        board = [[' ']+i for i in board]
        mappings = {(x, y+1):b for (x, y), b in mappings.items()}
        cells = [(x, y + 1) for x, y in cells]

    if _x < 0:
        _x = 0
        board = [[' ' for _ in board[0]]] + board
        mappings = {(x + 1, y):b for (x, y), b in mappings.items()}
        cells = [(x + 1, y) for x, y in cells]

    if _x == len(board):
        board = board + [[' ' for _ in board[0]]]

    if _y == len(board[0]):
        board = [i + [' '] for i in board]

    return _x, _y, eval(str(board)), eval(str(mappings)), eval(str(cells))

def draw_path(f, x_y, dist, d, depth, board, mappings, cells) -> iter:
    q = [(*x_y, *i, 0, board, mappings, cells, None) for i in d[:depth]]
    o_dist = mappings[x_y]
    while q:
        x, y, X, Y, prog, board, mappings, cells, smb = q.pop(0)
        if prog == dist:
            _x, _y = x + X, y + Y
            _x, _y, board, mappings, cells = adjust_board_size(_x, _y, board, mappings, cells)
            if (_x, _y) not in cells:
                cells.append((_x, _y))
                board[_x][_y] = f
                yield board, mappings, cells

            continue

        if smb is None:
            q.append((x, y, X, Y, prog, board, mappings, cells, '|' if board[x][y] == '-' else '-'))
            continue

        if (x+X, y+Y) in cells:
            q.extend([(x, y, X, Y, prog, board, mappings, cells, None) for X, Y in [(0, 1), (0, -1), (1, 0), (-1, 0)] if (x + X, y + Y) not in cells and x + X >= 0])
            continue

        _x, _y = x + X, y + Y
        _x, _y, board, mappings, cells = adjust_board_size(_x, _y, board, mappings, cells)
        prog += 1
        board[_x][_y] = smb
        mappings[(_x, _y)] = o_dist + prog
        cells.append((_x, _y))
        q.append((_x, _y, X, Y, prog, board, mappings, cells, smb))

def solver(t, depth = 3, option_depth = 2, board_depth = 3, breadth = -1) -> None:
    t = t.split()
    t = {t[i]:int(t[i+1]) for i in range(0, len(t), 2)}
    u_t = {b:a for a, b in t.items()}
    q = collections.deque([([['-' for _ in range(a)]+[b]], (mp:={(0,i):i+1 for i in range(a)}), [*mp], {*t}-{b})for a, b in [*u_t.items()][:len(u_t) if breadth == -1 else breadth]])
    seen = []
    results = []
    best_score = None
    while q:
        board, mappings, cells, f_works = q.popleft()
        #print('\n'.join(map(''.join, board)))
        if not f_works:
            results.append((board, mappings))
            best_score = len(mappings) if best_score is None else min(len(mappings), best_score)
            #print('got solution!')
            continue

        options = collections.defaultdict(list)
        for f in f_works:
            for (x, y), v in mappings.items():
                if (d:=[(X, Y) for X, Y in [(0, 1), (0, -1), (1, 0), (-1, 0)] if (x+X, y+Y) not in cells and ((board[x][y] == '-' and X) or (board[x][y] == '|' and Y))]):
                    if v + 1 == t[f]:
                        options[f].append(((x, y), 1, d))

                    elif v < t[f]:
                        options[f].append(((x, y), t[f] - v, d))

        #print(sorted([(a, min(b, key=lambda x:x[1])) for a, b in options.items()], key=lambda x:x[1][1]))
        #TODO: group on ties for best performance (include a suboptimal?)
        for f, ((x, y), dist, d), B in sorted([(a, min(b, key=lambda x:x[1]), sum(j[1] == min(b, key=lambda x:x[1]) for j in b)) for a, b in options.items()], key=lambda x:x[1][1])[:option_depth]:
            #print(B)
            if best_score is None or len(mappings) + dist < best_score:
                all_boards = []
                for _board, _mappings, _cells in draw_path(f, (x, y), dist, d, depth, board, mappings, cells):
                    if (S:=shadow(_board)) not in seen:
                        all_boards.append((_board, _mappings, _cells, f_works - {f}, S))

                for _board, _mappings, _cells, _f_works, S in all_boards[:board_depth]:
                    seen.append(S)
                    q.appendleft((_board, _mappings, _cells, _f_works))

    return best_score, sum(t.values())

Usage:
total_length, failed = 0, 0
full_failed = []
t = time.time()
c = 0
for i in filter(None, cases.split('\n')):
    c += 1
    print(c)
    result, f = solver(i.strip(), depth = 1, option_depth = 1, board_depth = 1, breadth = 1)
    if result is None:
        result, f = solver(i.strip(), depth = 1, option_depth = 2, board_depth = 1, breadth = 1)
    if result is None:
        failed += 1
        total_length += f
    else:
        total_length += result

print(total_length, failed, time.time() - t)

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/

sort: votes


text:
Primality Testing in Manufactoria
Background
Manufactoria is a game about programming. The player must use a form of two-dimensional programming language to complete tasks.  If you've never heard of it, the easiest way to learn is to try out the first few levels of the game.
Challenge
Your challenge is to create a program which tests the primality of a number.
The input will be a series of N blue markers in the queue.  If N is prime, then your program should accept it (move the robot to the finish).  If N is composite, then your program should reject it (drop it on the floor somewhere).
Submission Options
Since this is a more complex challenge than the typical Manufactoria challenge, I have decided to allow more ways to submit your answers.
Vanilla
I have created a 13x13 custom level to build and test submissions.  The custom testing level is as follows.

13x13 custom level

The game only allows 8 test cases on a custom level, but your creation should be theoretically able to handle any natural number N, limited only by the available memory.  For informational purposes, the test cases provided in the custom level are as follows:
1 -> reject
2 -> accept
4 -> reject
5 -> accept
7 -> accept
9 -> reject
11-> accept
15-> reject

Expanded Grid
Some users may want more room than a 13x13 grid.  Here is a link to an in-game 15x15 custom level, created by changing a number in the URL:

15x15 custom level

Sadly, larger custom levels don't work, as the additional cells are inaccessible.
The Manufactoria Esolang
There has been an adaption of Manufactoria into an ASCII-based language.  If you want a different way to design/test your creation, or if you are unable to fit your final solution onto the game board, you can use this esolang. You can find information on this esolang here:

Manufactoria esolang

There are a few discrepancies between the esolang and the actual game.  For example, conveyor crossings are handled differently.  Try to avoid taking advantage of these discrepancies.
A Faster Way to Test
The game is very slow when it comes to programs which take several thousands of steps to complete.  My proof-of-concept solution took 28042 steps to reject 15.  Even at the 50x speedup in-game, that simply takes too long.
I found this very helpful website.  Simply copy-paste the link to your answer, and you can test your answer with specific inputs.  The 28042-step process took under a second.
One thing to note is that it will often say something like "incorrectly accepted" even if your machine worked properly.  This is because the webpage only knows the test cases.  For example, it would say that my solution "incorrectly accepted" the number 3, even though my machine was actually correct.
How to Win
The scoring criteria is the number of parts (cells occupied).  This is code golf, so the submission with the fewest parts wins.
For those interested, my benchmark solution has 96 parts and fits on the 13x13 grid.  Finding a better algorithm could lead to colossal improvements, since I know I used a sub-optimal algorithm.

53 parts - 11x11 grid
I just learned to play Manufactoria 2 days ago, so it is probably not very golf-optimized, but at least it solves the problem. It implements trial division of course, via repeated subtraction. All divisors from 2 to N-1 are checked. The time complexity should be O(N^3), I believe.

Solution Link
I was very disappointed to have to use a conveyor belt :)

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/3.0/


text:
Reproduce an image using lines
Write a program that takes in a true-color RGB image I, the maximum number of lines to draw L, and the minimum m and maximum M length of each line. Output an image O that looks as much as possible like I and is drawn using L or fewer straight lines, all of which have Euclidean length between m and M.
Each line must be one solid color, have both endpoints in the bounds of O, and be drawn using Bresenham's line algorithm (which most graphics libraries will already do for you). Individual lines can only be 1-pixel thick.
All lines, even those of length 0, should take up at least one pixel. Lines may be drawn on top of one another.
Before drawing any lines you may initialize the background of O to any solid color (that may depend on I).
Details

O should have the same dimensions as I.
L will always be a nonnegative integer. It may be greater than the area of I.
m and M are nonnegative floating point numbers with M >= m. The distance between two pixels is the Euclidean distance between their centers. If this distance is less than m or greater than M, then a line between those pixels is not allowed.
The lines should not be antialiased.
Opacity and alpha should not be used.
Your program should not take more than an hour to run on a decent modern computer on images with less than a million pixels and L less than 10,000.

Test Images
You should of course show us your most accurate or interesting output images (which I expect will occur when L is between 5% and 25% of the number of pixels in I, and m and M are around one tenth of the diagonal size).
Here are some test images (click for originals). You may also post your own.

Simpler images:

This is a popularity contest. The highest voted submission wins.
Notes

It may be helpful to let L be derived from a percentage of total pixels in I as well as an absolute value. e.g. >>> imageliner I=img.png L=50% m=10 M=20 would be the same thing as >>> imageliner I=img.png L=32 m=10 M=20 if img.png were an 8 by 8 pixel image. Something similar could be done for m and M. This is not required.
Since lines cannot go out of bounds, the longest lines possible will be the diagonal length of I. Having M higher than this should not break anything though.
Naturally, if m is 0 and L is greater than or equal to the number of pixels in I, O could be identical to I by having length 0 "lines" at each pixel location. This behavior is not required.
Arguably, reproducing the shape of I is more important than reproducing the color. You may want to look into edge detection.

To clarify: Are libraries like SimpleCV allowed? And answers may have any choice for I, L, m, and M, including m=0 and L=area?

@epicwisdom Yes, all libraries (except things that already specifically do this task) are allowed. Feel free to use keypoints, edge detection, whatever. Your algorithm should work for any valid choices of I, L, m, M, include m = 0 and L = area. (Though of course, your algorithm may look better for particular tunings of the parameters.)

Then, for instance, this particular library algorithm would be considered an invalid answer?

@epicwisdom Actually I will allow that and other similar things. It looks like it'd still take some clever tweaking to make an image out of the lines it gives you.

Do lines need to have thickness 1?

@aditsu Yes, I should have made that clear, thanks.

Hm, what to do with submissions where the code is too big? It is probably not the most brilliant piece, but the code block feature even inflates the already 26k to over 43k...

@PlasmaHH Post the most pertinent parts and say you didn't have room for all in your answer. Perhaps include the entire code in a pastebin or two.

C++ - somewhat random lines and then some
First some random lines
The first step of the algorithm randomly generates lines, takes for the target image an average of the pixels along this, and then calculates if the summed up square of rgb space distances of all pixels would be lower if we would paint the new line (and only paint it, if it is). The new lines color for this is chosen as the channel wise average of the rgb values, with a -15/+15 random addition.
Things that I noticed and influenced the implementation:

The initial color is the average of the complete image. This is to counter funny effects like when making it white, and the area is black, then already something as a bright green line is seen better, as it is nearer to black than the already white one.
Taking the pure average color for the line is not so good as it turns out to be unable to generate highlights by being overwritten by later lines. Doing a little random deviation helps a bit, but if you look at starry night, it fails if the local contrast is high at many places.

I was experimenting with some numbers, and chose L=0.3*pixel_count(I) and left m=10 and M=50. It will produce nice results starting at around 0.25 to 0.26 for the number of lines, but I chose 0.3 to have more room for accurate details.
For the full sized golden gate image, this resulted in 235929 lines to paint (for which it took a whopping 13 seconds here). Note that all images here are displayed in reduced size and you need to open them in a new tab/download them to view the full resolution.

Erase the unworthy
The next step is rather expensive (for the 235k lines it took about an hour, but that should be well within the "an hour for 10k lines on 1 megapixel" time requirement), but it is also a bit surprising. I go through all the previously painted lines, and remove those that do not make the image better. This leaves me in this run with only 97347 lines that produce the following image:

You probably need to download and compare them in an appropriate image viewer to spot most differences.
and start over again
Now I have a lot of lines that I can paint again to have a total of 235929 again. Not much to say, so here is the image:

short analysis
The whole procedure seems to work like a blurring filter that is sensitive to local contrast and object sizes. But it is also interesting to see where the lines are painted, so the program records these too (For each line, the pixel color will be made one step whiter, at the end the contrast is maximized). Here are the corresponding ones to the three colored above.

animations
And since we all love animations, here are some animated gifs of the whole process for the smaller golden gate image. Note that there is significant dithering due to gif format (and since creators of true color animation file formats and browser manufacturers are in war over their egos, there is no standard format for true color animations, otherwise I could have added an .mng or similar).

Some more
As requested, here are some results of the other images (again you might need to open them in a new tab to not have them downscaled)

Future thoughts
Playing around with the code can give some intresting variations.

Chose the color of the lines by random instead of being based on the average. You might need more than two cycles.
The code in the pastebin also contains some idea of a genetic algorithm, but the image is probably already so good that it would take too many generations, and this code is also too slow to fit into the "one hour" rule.
Do another round of erase/repaint, or even two...
Change the limit of where lines can be erased (e.g. "must make the image at lest N better")

The code
These are just the two main useful functions, the whole code doesn't fit in here and can be found at http://ideone.com/Z2P6Ls
The bmp classes raw and raw_line function do access pixels and lines respectively in an object that can be written to bmp format (It was just some hack lying around and I thought that makes this somewhat independent from any library).
The input file format is PPM
std::pair<bmp,std::vector<line>>  paint_useful( const bmp& orig, bmp& clone, std::vector<line>& retlines, bmp& layer, const std::string& outprefix, size_t x, size_t y )
{
        const size_t pixels = (x*y);
        const size_t lines = 0.3*pixels;
//      const size_t lines = 10000;

//      const size_t start_accurate_color = lines/4;

        std::random_device rnd;

        std::uniform_int_distribution<size_t> distx(0,x-1);
        std::uniform_int_distribution<size_t> disty(0,y-1);
        std::uniform_int_distribution<size_t> col(-15,15);
        std::uniform_int_distribution<size_t> acol(0,255);

        const ssize_t m = 1*1;
        const ssize_t M = 50*50;

        retlines.reserve( lines );

        for (size_t i = retlines.size(); i < lines; ++i)
        {
                size_t x0;
                size_t x1;

                size_t y0;
                size_t y1;

                size_t dist = 0;
                do
                {
                        x0 = distx(rnd);
                        x1 = distx(rnd);

                        y0 = disty(rnd);
                        y1 = disty(rnd);

                        dist = distance(x0,x1,y0,y1);
                }
                while( dist > M || dist < m );

                std::vector<std::pair<int32_t,int32_t>> points = clone.raw_line_pixels(x0,y0,x1,y1);

                ssize_t r = 0;
                ssize_t g = 0;
                ssize_t b = 0;

                for (size_t i = 0; i < points.size(); ++i)
                {
                        r += orig.raw(points[i].first,points[i].second).r;
                        g += orig.raw(points[i].first,points[i].second).g;
                        b += orig.raw(points[i].first,points[i].second).b;
                }

                r += col(rnd);
                g += col(rnd);
                b += col(rnd);

                r /= points.size();
                g /= points.size();
                b /= points.size();

                r %= 255;
                g %= 255;
                b %= 255;

                r = std::max(ssize_t(0),r);
                g = std::max(ssize_t(0),g);
                b = std::max(ssize_t(0),b);

//              r = acol(rnd);
//              g = acol(rnd);
//              b = acol(rnd);

//              if( i > start_accurate_color )
                {
                        ssize_t dp = 0; // accumulated distance of new color to original
                        ssize_t dn = 0; // accumulated distance of current reproduced to original
                        for (size_t i = 0; i < points.size(); ++i)
                        {
                                dp += rgb_distance(
                                                                                orig.raw(points[i].first,points[i].second).r,r,
                                                                                orig.raw(points[i].first,points[i].second).g,g,
                                                                                orig.raw(points[i].first,points[i].second).b,b
                                                                        );

                                dn += rgb_distance(
                                                                                clone.raw(points[i].first,points[i].second).r,orig.raw(points[i].first,points[i].second).r,
                                                                                clone.raw(points[i].first,points[i].second).g,orig.raw(points[i].first,points[i].second).g,
                                                                                clone.raw(points[i].first,points[i].second).b,orig.raw(points[i].first,points[i].second).b
                                                                        );

                        }

                        if( dp > dn ) // the distance to original is bigger, use the new one
                        {
                                --i;
                                continue;
                        }
                        // also abandon if already too bad
//                      if( dp > 100000 )
//                      {
//                              --i;
//                              continue;
//                      }
                }

                layer.raw_line_add(x0,y0,x1,y1,{1u,1u,1u});
                clone.raw_line(x0,y0,x1,y1,{(uint32_t)r,(uint32_t)g,(uint32_t)b});
                retlines.push_back({ (int)x0,(int)y0,(int)x1,(int)y1,(int)r,(int)g,(int)b});

                static time_t last = 0;
                time_t now = time(0);
                if( i % (lines/100) == 0 )
                {
                        std::ostringstream fn;
                        fn << outprefix + "perc_" << std::setw(3) << std::setfill('0') << (i/(lines/100)) << ".bmp";
                        clone.write(fn.str());
                        bmp lc(layer);
                        lc.max_contrast_all();
                        lc.write(outprefix + "layer_" + fn.str());
                }

                if( (now-last) > 10 )
                {
                        last = now;
                        static int st = 0;
                        std::ostringstream fn;
                        fn << outprefix + "inter_" << std::setw(8) << std::setfill('0') << i << ".bmp";
                        clone.write(fn.str());

                        ++st;
                }
        }
        clone.write(outprefix + "clone.bmp");
        return { clone, retlines };
}

void erase_bad( std::vector<line>& lines, const bmp& orig )
{
        ssize_t current_score = evaluate(lines,orig);

        std::vector<line> newlines(lines);

        uint32_t deactivated = 0;
        std::cout << "current_score = " << current_score << "\n";
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                newlines[i].active = false;
                ssize_t score = evaluate(newlines,orig);
                if( score > current_score )
                {
                        newlines[i].active = true;
                }
                else
                {
                        current_score = score;
                        ++deactivated;
                }
                if( i % 1000 == 0 )
                {
                        std::ostringstream fn;
                        fn << "erase_" << std::setw(6) << std::setfill('0') << i << ".bmp";
                        bmp tmp(orig);
                        paint(newlines,tmp);
                        tmp.write(fn.str());
                        paint_layers(newlines,tmp);
                        tmp.max_contrast_all();
                        tmp.write("layers_" + fn.str());
                        std::cout << "\r i = " << i << std::flush;
                }
        }
        std::cout << "\n";
        std::cout << "current_score = " << current_score << "\n";
        std::cout << "deactivated = " << deactivated << "\n";

        bmp tmp(orig);

        paint(newlines,tmp);
        tmp.write("newlines.bmp");
        lines.clear();
        for (size_t i = 0; i < newlines.size(); ++i)
        {
                if( newlines[i].is_active() )
                {
                        lines.push_back(newlines[i]);
                }
        }
}

+1, very nice indeed. Do you have results for the other test images?

@Nathaniel: I have added some. The "simple" images are unintresting because the recreation is almost pixel perfect.

Java - random lines
A very basic solution that draws random lines and compute for them the source picture average color. The background color is set to the source average color.
L = 5000, m = 10, M = 50

L = 10000, m = 10, M = 50

EDIT
I've added a genetic algorithm that handles a population of lines. At each generation, we keep only the 50% best lines, drop the others and generate randomly new ones.
The criteria for keeping the lines are:

their distance to the source picture colors is small
the number of intersections with other lines (the smaller the better)
their length (the longer the better)
their angle with the nearest neighbour (the smaller the better)

To my great disappointment, the algorithm does not really seems to improve the picture quality :-( just the lines are getting more parallel.
First generation (5000 lines)

Tenth generation (5000 lines)

Playing with parameters

package line;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import snake.Image;

public class Lines {

    private final static int NB_LINES = 5000;
    private final static int MIN_LENGTH = 10;
    private final static int MAX_LENGTH = 50;

    public static void main(String[] args) throws IOException {
        BufferedImage src = ImageIO.read(Image.class.getClassLoader().getResourceAsStream("joconde.png"));
        BufferedImage dest = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);

        int [] bgColor = {0, 0, 0};
        int avgRed = 0, avgGreen = 0, avgBlue = 0, count = 0;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                int colsrc = src.getRGB(x, y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
        }

        bgColor[0] = avgBlue/count; bgColor[1] = avgGreen/count; bgColor[2] = avgRed/count;
        for (int y = 0; y < src.getHeight(); y++) {
            for (int x = 0; x < src.getWidth(); x++) {
                dest.getRaster().setPixel(x, y, bgColor);
            }
        }
        List<List<Point>> lines = new ArrayList<List<Point>>();
        Random rand = new Random();
        for (int i = 0; i < NB_LINES; i++) {
            int length = rand.nextInt(MAX_LENGTH - MIN_LENGTH) + MIN_LENGTH;
            double ang = rand.nextDouble() * Math.PI;
            int lx = (int)(Math.cos(ang) * length); // can be negative or positive
            int ly = (int)(Math.sin(ang) * length); // positive only
            int sx = rand.nextInt(dest.getWidth() -1 - Math.abs(lx));
            int sy = rand.nextInt(dest.getHeight() - 1- Math.abs(ly));
            List<Point> line;
            if (lx > 0) {
                line = line(sx, sy, sx+lx, sy+ly);
            } else {
                line = line(sx+Math.abs(lx), sy, sx, sy+ly);
            }
            lines.add(line);
        }

        // render the picture
        int [] color = {0, 0, 0};
        for (List<Point> line : lines) {

            avgRed = 0; avgGreen = 0; avgBlue = 0;
            count = 0;
            for (Point p : line) {
                int colsrc = src.getRGB(p.x, p.y);
                avgRed += colsrc & 255;
                avgGreen += (colsrc >> 8) & 255;
                avgBlue += (colsrc >> 16) & 255;
                count++;
            }
            avgRed /= count; avgGreen /= count; avgBlue /= count;
            color[0] = avgBlue; color[1] = avgGreen; color[2] = avgRed;
            for (Point p : line) {
                dest.getRaster().setPixel(p.x, p.y, color);
            }

        }
        ImageIO.write(dest, "png", new File("a0.png"));

    }

    private static List<Point> line(int x0, int y0, int x1, int y1) {
        List<Point> points = new ArrayList<Point>();
        int deltax = x1 - x0;
        int deltay = y1 - y0;
        int tmp;
        double error = 0;
        double deltaerr = 0;
        if (Math.abs(deltax) >= Math.abs(deltay)) {
            if (x0 > x1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltay) / deltax);
            int y = y0;
            for (int x = x0; x <= x1; x++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (y0 < y1) y++; else y--;
                    error -= 1.0;
                }
            }
        } else {
            if (y0 > y1) { // swap the 2 points
                tmp = x0; x0 = x1; x1 = tmp;
                tmp = y0; y0 = y1; y1 = tmp;
                deltax = - deltax; deltay = -deltay;
            }
            deltaerr = Math.abs (((double)deltax) / deltay);   // Assume deltay != 0 (line is not horizontal),
            int x = x0;
            for (int y = y0; y <= y1; y++) {
                points.add(new Point(x, y));
                error += deltaerr;
                if (error >= 0.5) {
                    if (x0 < x1) x++; else x--;
                    error -= 1.0;
                }
            }
        }
        return points;
    }
}

Finally someone answered :D I'd love to see more examples.

@Calvin Sure. Right now I am working on improving the algorithm by keeping a population of lines, and eliminating e.g. the 20% worse, and re-generating new ones (some kind of genetic algorithm)

I had something like that in mind, but no time to write it. Looking forward to the genetic alg. results :)

Perhaps you want to remove the smaller angle criterion? Why did you put it? The original image looks good although the lines don't have small intersection angle.

@justhalf Done. I've added the angle criterion in an attempt to simulate the painter brush.

The background looks better. Although I think there are too few lines representing Monalisa's skin. haha

The hand is nice

is it really a genetic algorithm if you"randomly generate new ones"? a genetic algorithm generates new ones from the existing population, not just plain random new ones.

@proud Yes, it generates the new ones from the existing by adding a small random variation.

Looks like frosted glass...

C - straight lines
A basic approach in C that operates on ppm files. The algorithm tries to place vertical lines with optimal line length to fill all pixels. The background color and the line colors are calculated as an average value of the original image (the median of each color channel):
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define SIGN(x) ((x > 0) ? 1 : (x < 0) ? -1 : 0)
#define MIN(x, y) ((x > y) ? y : x)
#define MAX(x, y) ((x > y) ? x : y)

typedef struct {
    size_t width;
    size_t height;

    unsigned char *r;
    unsigned char *g;
    unsigned char *b;
} image;

typedef struct {
    unsigned char r;
    unsigned char g;
    unsigned char b;
} color;

void init_image(image *data, size_t width, size_t height) {
    data->width = width;
    data->height = height;
    data->r = malloc(sizeof(data->r) * data->width * data->height);
    data->g = malloc(sizeof(data->g) * data->width * data->height);
    data->b = malloc(sizeof(data->b) * data->width * data->height);
}

#define BUFFER_LEN 1024
int load_image(const char *filename, image* data) {
    FILE *f = fopen(filename, "r");
    char buffer[BUFFER_LEN];          /* read buffer */
    size_t max_value;
    size_t i;
    fgets(buffer, BUFFER_LEN, f);
    if (strncmp(buffer, "P3", 2) != 0) {
        printf("File begins with %s instead of P3\n", buffer);
        return 0;
    }

    fscanf(f, "%u", &data->width);
    fscanf(f, "%u", &data->height);
    fscanf(f, "%u", &max_value);
    assert(max_value==255);

    init_image(data, data->width, data->height);

    for (i = 0; i < data->width * data->height; i++) {
        fscanf(f, "%hhu", &(data->r[i]));
        fscanf(f, "%hhu", &(data->g[i]));
        fscanf(f, "%hhu", &(data->b[i]));
    }
    fclose(f);

    printf("Read %zux%zu pixels from %s.\n", data->width, data->height, filename);
}

int write_image(const char *filename, image *data) {
    FILE *f = fopen(filename, "w");
    size_t i;
    fprintf(f, "P3\n%zu %zu\n255\n", data->width, data->height);
    for (i = 0; i < data->width * data->height; i++) {
        fprintf(f, "%hhu %hhu %hhu ", data->r[i], data->g[i], data->b[i]);
    }
    fclose(f);
}

unsigned char average(unsigned char *data, size_t data_len) {
    size_t i;
    size_t j;
    size_t hist[256];

    for (i = 0; i < 256; i++) hist[i] = 0;
    for (i = 0; i < data_len; i++) hist[data[i]]++;
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist[i];
        if (j >= data_len / 2) return i;
    }
    return 255;
}

void set_pixel(image *data, size_t x, size_t y, unsigned char r, unsigned char g, unsigned char b) {
    data->r[x + data->width * y] = r;
    data->g[x + data->width * y] = g;
    data->b[x + data->width * y] = b;
}

color get_pixel(image *data, size_t x, size_t y) {
    color ret;
    ret.r = data->r[x + data->width * y];
    ret.g = data->g[x + data->width * y];
    ret.b = data->b[x + data->width * y];
    return ret;
}

void fill(image *data, unsigned char r, unsigned char g, unsigned char b) {
    size_t i;
    for (i = 0; i < data->width * data->height; i++) {
        data->r[i] = r;
        data->g[i] = g;
        data->b[i] = b;
    }
}

void line(image *data, size_t x1, size_t y1, size_t x2, size_t y2, unsigned char r, unsigned char g, unsigned char b) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    set_pixel(data, x, y, r, g, b);

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        set_pixel(data, x, y, r, g, b);
    }
}

color average_line(image *data, size_t x1, size_t y1, size_t x2, size_t y2) {
    size_t x, y, t, pdx, pdy, ddx, ddy, es, el;
    int dx, dy, incx, incy, err;
    color ret;
    color px;
    size_t i;
    size_t j;
    size_t hist_r[256];
    size_t hist_g[256];
    size_t hist_b[256];
    size_t data_len = 0;

    for (i = 0; i < 256; i++) {
        hist_r[i] = 0;
        hist_g[i] = 0;
        hist_b[i] = 0;
    }

    dx=x2-x1;
    dy=y2-y1;
    incx=SIGN(dx);
    incy=SIGN(dy);
    if(dx<0) dx=-dx;
    if(dy<0) dy=-dy;
    if (dx>dy) {
        pdx=incx;
        pdy=0;
        ddx=incx;
        ddy=incy;
        es=dy;
        el=dx;
    } else {
        pdx=0;
        pdy=incy;
        ddx=incx;
        ddy=incy;
        es=dx;
        el=dy;
    }
    x=x1;
    y=y1;
    err=el/2;
    px = get_pixel(data, x, y);
    hist_r[px.r]++;
    hist_g[px.g]++;
    hist_b[px.b]++;
    data_len++;

    for(t=0; t<el; t++) {
        err -= es;
        if(err<0) {
            err+=el;
            x+=ddx;
            y+=ddy;
        } else {
            x+=pdx;
            y+=pdy;
        }
        px = get_pixel(data, x, y);
        hist_r[px.r]++;
        hist_g[px.g]++;
        hist_b[px.b]++;
        data_len++;
    }

    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_r[i];
        if (j >= data_len / 2) {
            ret.r = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_g[i];
        if (j >= data_len / 2) {
            ret.g = i;
            break;
        }
    }
    j = 0;
    for (i = 0; i < 256; i++) {
        j += hist_b[i];
        if (j >= data_len / 2) {
            ret.b = i;
            break;
        }
    }
    return ret;
}

void lines(image *source, image *dest, size_t L, float m, float M) {
    size_t i, j;
    float dx;
    float mx, my;
    float mm = MAX(MIN(source->width * source->height / L, M), m);
    unsigned char av_r = average(source->r, source->width * source->height);
    unsigned char av_g = average(source->g, source->width * source->height);
    unsigned char av_b = average(source->b, source->width * source->height);
    fill(dest, av_r, av_g, av_b);
    dx = (float)source->width / L;
    mx = 0;
    my = mm / 2;
    for (i = 0; i < L; i++) {
        color avg;
        mx += dx;
        my += (source->height - mm) / 8;
        if (my + mm / 2 > source->height) {
            my = mm / 2 + ((size_t)(my + mm / 2) % (size_t)(source->height - mm));
        }
        avg = average_line(source, mx, my - mm / 2, mx, my + mm / 2);
        line(dest, mx, my - mm / 2, mx, my + mm / 2, avg.r, avg.g, avg.b);
    }
}

int main(int argc, char *argv[]) {
    image source;
    image dest;
    size_t L;
    float m;
    float M;

    load_image(argv[1], &source);
    L = atol(argv[2]);
    m = atof(argv[3]);
    M = atof(argv[4]);

    init_image(&dest, source.width, source.height);
    lines(&source, &dest, L, m, M);

    write_image(argv[5], &dest);
}

L = 5000, m = 10, M = 50

L = 5000, m = 10, M = 50

L = 100000, m = 10, M = 50

Python 3 -based off of "somewhat random lines and then some", plus sobel edge detection.
the code can theoretically run forever (so I can run it overnight for fun), but it records its progress, so all images are taken from the 1-10 min mark.
It first reads the image, and then uses sobel edge detection to find the angle of all the edges, to make sure that the lines do not trespass on another color.
Once a line of the random length within (lengthmin,lengthmax) is set, it then tests to see if it contributes to the overall image.
While smaller lines are better, I set the line length from 10-50.
from random import randint, uniform
import json
from PIL import Image, ImageDraw, ImageFilter
import math
k=(-1,0,1,-2,0,2,-1,0,1)
k1=(-1,-2,-1,0,0,0,1,2,1)
population=[]
lengthmin=10
lengthmax=50
number_lines=10**8
im=Image.open('0.png')
[x1,y1]=im.size
dx=0
class drawer():
    def __init__(self,genome,score,filename):
        self.genome=genome
        self.score=score
        self.filename=filename
    def initpoint(self,g1):
        g2=self.genome
        im=Image.open('0.png')
        im1=im.filter(ImageFilter.Kernel((3,3),k,1,128))
        im2=im.filter(ImageFilter.Kernel((3,3),k1,1,128))
        im1=im1.filter(ImageFilter.GaussianBlur(radius=4))
        im2=im2.filter(ImageFilter.GaussianBlur(radius=4))
        for x in range(0,number_lines):
            if(x%10**4==0):
                print(x*100/number_lines)
                self.save()
                g1.save('1.png')
            (x,y)=(randint(0,x1-1),randint(0,y1-1))
            w=im1.getpixel((x,y))[0]-128
            z=im2.getpixel((x,y))[0]-128
            w=int(w)
            z=int(z)
            W=(w**2+z**2)**0.5
            if(W!=0):
                w=(w/W)*randint(lengthmin,lengthmax)
                z=(z/W)*randint(lengthmin,lengthmax)
                (w,z)=(z,w)
                (a,b)=(x+w,y+z)
                a=int(a)
                b=int(b)
                x=int(x)
                y=int(y)
                if(a>=x1):
                    a=x1-1
                if(b>=y1):
                    b=y1-1
                if(a<0):
                    a=0
                if(b<0):
                    b=0
                if(x>=x1):
                    x=x1-1
                if(y>=y1):
                    y=y1-1
                if(x<0):
                    x=0
                if(y<0):
                    y=0
                C=[0,0,0]
                D=0
                E=0
                F=0
                G=0
                W=((x-a)**2+(y-b)**2)**0.5
                if(W!=0):
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        C[0]+=im.getpixel((c,d))[0]
                        C[1]+=im.getpixel((c,d))[1]
                        C[2]+=im.getpixel((c,d))[2]
                    C[0]/=W
                    C[1]/=W
                    C[2]/=W
                    C[0]=int(C[0])
                    C[1]=int(C[1])
                    C[2]=int(C[2])
                    for Z in range(0,int(W)):
                        w=(Z/W)
                        (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                        c=int(c)
                        d=int(d)
                        E=0
                        D=0
                        D+=(g1.getpixel((c,d))[0]-im.getpixel((c,d))[0])**2
                        D+=(g1.getpixel((c,d))[1]-im.getpixel((c,d))[1])**2
                        D+=(g1.getpixel((c,d))[2]-im.getpixel((c,d))[2])**2
                        F+=D**0.5
                        E+=(im.getpixel((c,d))[0]-C[0])**2
                        E+=(im.getpixel((c,d))[1]-C[1])**2
                        E+=(im.getpixel((c,d))[2]-C[2])**2
                        G+=E**0.5
                    #print((G/W,F/W))
                    if(G<F):
                        for Z in range(0,int(W)):
                            w=(Z/W)
                            (c,d)=((w*x+(1-w)*a),(w*y+(1-w)*b))
                            c=int(c)
                            d=int(d)
                            g1.putpixel((c,d),(int(C[0]),int(C[1]),int(C[2])))
                        g2.append((x,y,a,b,int(C[0]%256),int(C[1]%256),int(C[2]%256)))
        return(g1)
    def import_file(self):
        with open(self.filename, 'r') as infile:
            self.genome=json.loads(infile.read())
        print(len(self.genome))
    def save(self):
        with open(self.filename, 'w') as outfile:
            data = json.dumps(self.genome)
            outfile.write(data)
population.append(drawer([],0,'0.txt'))
G=0
g1=Image.new('RGB',(x1,y1),'black')
g1=population[0].initpoint(g1)
g1.save('1.png')