views:

2364

answers:

16

You may remember these drawings from when you were a child, but now it's time to let the computer draw them (in full ascii splendour). Have fun!

Description:

The input are multiple lines (terminated by a newline) which describe a 'field'. There are 'numbers' scattered across this field (seperated by whitespace). All lines can be considered to be the same length (you can pad spaces to the end).

  • the numbers always start at 1
  • they follow the ordering of the natural numbers: every 'next number' is incremented with 1
  • every number is surrounded by (at least) one whitespace on its left and right

Task:

Draw lines between these numbers in their natural order (1 -> 2 -> 3 -> ...N) (assume N <= 99) with the following characteristics:

  1. replace a number with a '+' character
  2. for horizontal lines: use '-'
  3. for vertical lines: use '|'
  4. going left and down or right and up: /
  5. going left and up or right and down: \

Important notes:

  1. When drawing lines of type 4 and 5 you can assume (given the points to connect with coordinates x1, y1 and x2, y2) that distance(x1,x2) == distance(y1,y2). Or in other words (as user jball commented): "consecutive elements that are not horizontally or vertically aligned always align to the slope of the slash or backslash".

  2. It is important to follow the order in which the dots are connected (newer lines can strike out older lines).

-- Sample input 1 --

                                  8 

                                  7  6 
                      10       9       

                                        5            

                                     3  4        
                 11 

                   12                       13    
          1                          2                     

-- Sample output 1 --

                                 +                                
                                /|                                
                               / +--+                             
                     +--------+      \                            
                    /                 \                           
                   /                   +                          
                  /                    |                          
                 /                  +--+                          
                +                   |                             
                 \                  |                             
                  +------------------------+                      
         +--------------------------+        

-- Sample input 2 --

                        64          
                        63              



                    62 61                             
               1  65                                   
                 66    57 58                               
               2      56  59               45                
                   67  55                  46              
             3                               44           
                         54  60            47              
                          53 52   49      48              
             4                51 50       43            

           5                                42              
                                            41               
           6              23                                 
                          22 25  26       40              
                      20 21 24            34                 
              7 13 12                    33                    
                    19              27  32                     
                14                        35               
           8   15                                           
                16                                         
                                   39                        
                17  18         28  31 36                  
               9                     38                       
                10 11          29  30 37                       

-- Sample output 2 -- (unicorn reference)

                       +        
                      /+      
                     //          
                    //        
                   //           
                  /+--+        
              +  +     \         
              | +     +-\+          
              +  \   +   \                +         
             /    +   +   \               +\    
            +          \   \              | +       
            |           +   +             +/           
            |            +--+    +-------+/               
            +                +--+        +              
           /                              \              
          +                                +               
          |                                +                 
          +              +                /             
           \             +\ +---+        +           
            \        +--+  +     \      /+              
             + +--+ /             \    /+|             
            /  |  |+               +  /+ |                 
           /   +  ||              /  //  +            
          +   +   ||             /  //  /                
           \   +  ||            /  //  /              
            \  |  ||           /  +/  /                  
             \ +---+          +   +\ +                  
              +   |           |   | +|                 
               +--+           +---+  +               

Winner:

Shortest solution (by code character count). Input can be read via standard input.

+1  A: 

I cannot do multi-line in a comment, so I will demonstrate here. In the following examples, distance(x1,x2) == distance(y1,y2):

+
|\
+-+

+
|\
| \
+--+

+
|\
| \
|  \
+---+

With the rules as explained, distance(x1,x2) == distance(y1,y2)+2:

+\
| \
+--\+

+\
| \
|  \
+---\+

+\
| \
|  \
|   \
+----\+
Jeff B
Ok, thanks. Give me 15 minutes to update my code, I'll update the question accordingly
ChristopheD
@Jeff B: question updated accordingly. I agree this makes more sense.
ChristopheD
These new rules also make for more compact code possibilities...
ChristopheD
+3  A: 

Here goes!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int sign(int x) {
    if (x < 0)
        return -1;
    if (x > 0)
        return +1;
    return 0;
}

#define MAX_ROWS 100
#define MAX_COLS 100
#define MAX_DIGITS 100

int main(void)
{
    // Read in the digits
    int number[MAX_DIGITS][2];
    int rows = 0;
    int cols = 0;
    char row[MAX_COLS];
    int maxvalue = 0;
    int i, j, value, x;
    for (i = 0; i < MAX_ROWS; i++) {
        if (row != fgets(row, MAX_COLS, stdin))
            break;
        value = 0;
        for (j=0; row[j] != 0; j++) {
            if (row[j] >= '0' && row[j] <= '9') {
                x = j;
                value = 0;
                do {
                    value = 10*value + (row[j]-'0');
                    j++;
                } while (row[j] >= '0' && row[j] <= '9');
                number[value][0] = i;
                number[value][1] = x;
                if (maxvalue < value) maxvalue = value;
                if (rows < i+1) rows = i+1;
                if (cols < x+1) cols = x+1;
            }
        }
    }

    // Create an empty field
    char field[rows][cols];
    memset(field, ' ', rows*cols);

    char lines[] = "\\|/-+-/|\\";
    int dr,dc;
    // Draw the numbers and lines
    field[number[1][0]][number[1][1]] = '+';
    for (i = 2; i <= maxvalue; ++i) {
        int r = number[i-1][0];
        int c = number[i-1][1];
        int rt = number[i][0];
        int ct = number[i][1];
        dr = sign(rt-r);
        dc = sign(ct-c);
        char line = lines[(dr+1)*3+dc+1];
        while (r != rt || c != ct) {
            r += dr;
            c += dc;
            field[r][c] = line;
        }
        field[r][c] = '+';
    }

    for (i = 0; i < rows; ++i) {
        for (j = 0; j < cols; ++j)
            putchar(field[i][j]);
        putchar('\n');
    }

    return 0;
}
morotspaj
I'd turn sign() into a macro.
Javier Badia
int sign(int x){return 1-2*(x<0);}
tstenner
-1: This is supposed to be code golf. I know it's fun to solve these, but the point is to provide a *small* solution.
ryeguy
@tstenner: Too bad that version doesn't handle 0! How about `int sign(int x){return 1-2*(x<0)-(x==0);}`
Wallacoloo
@wallacoloo You're right, but `int sign(int x){return -1+2*(x>=0);}` is correct and short.
tstenner
@tstenner: No, that is not correct. Your sign function does not have identical functionality to the one currently in place. Replace the current sign function with yours and tell me if the new program is capable of drawing horizontal/vertical (non-diagonal) lines. Also, here's a more concise version of what I posted earlier: `int sign(int x){return(x!=0)-2*(x<0);}`
Wallacoloo
+4  A: 

Python - 381

import re
b=list(iter(raw_input,''))
c=sum((zip([i]*999,re.finditer('\\d+',x))for i,x in enumerate(b)),[])
d=sorted((int(m.group()),i,m.start())for i,m in c)
e=[[' ']*max(map(len,b))for x in b]
for(t,u,v),(x,y,z)in zip(d,d[1:]+d[-1:]):
 e[u][v]='+'
 while u!=y or v!=z:i,j=(u<y)-(u>y),(v<z)-(v>z);u+=i;v+=j;e[u][v]=['|','/\\-'[(i==j)+2*(i==0)]][j!=0]
print'\n'.join(map(''.join,e))
Jimmy
Remove the space between `print` and `'\n'` to shave 1 more space off.
KennyTM
There are 5 unneccesary spaces. All the doubly indented lines can be put on the end of the "while" line, tacked together with semicolons. These changes will give 385 chars.
recursive
+5  A: 

F#, 725 chars

open System
let mutable h,s,l=0,Set.empty,Console.ReadLine()
while l<>null do
 l.Split([|' '|],StringSplitOptions.RemoveEmptyEntries)
 |>Seq.iter(fun t->s<-s.Add(int t,h,(" "+l+" ").IndexOf(" "+t+" ")))
 h<-h+1;l<-Console.ReadLine()
let w=Seq.map(fun(k,h,x)->x)s|>Seq.max
let o=Array2D.create h (w+1)' '
Seq.sort s|>Seq.pairwise|>Seq.iter(fun((_,b,a),(_,y,x))->
let a,b,x,y=if b>y then x,y,a,b else a,b,x,y
o.[b,a]<-'+'
o.[y,x]<-'+'
if b=y then for x in(min a x)+1..(max a x)-1 do o.[y,x]<-'-'
elif a=x then for h in b+1..y-1 do o.[h,x]<-'|'
elif a<x then for i in 1..y-b-1 do o.[b+i,a+i]<-'\\'
else for i in 1..y-b-1 do o.[b+i,a-i]<-'/')
for h in 0..h-1 do
 for x in 0..w do printf"%c"o.[h,x]
 printfn""

Legend:

h = height
s = set
l = curLine
w = (one less than) width
o = output array of chars

Lines 1-6: I keep a set of (number, lineNum, xCoord) tuples; as I read in each line of input I find all the numbers and add them to the set.

Line 7-8: Then I create an array of output chars, initialized to all spaces.

Line 9: Sort the set (by 'number'), then take each adjacent pair and ...

Lines 10-16: ... sort so (a,b) is the 'highest' of the two points and (x,y) is the other. Put the '+' signs, and then if horizontal, draw that, else if vertical, draw that, else draw the correct diagonal. If the input is not 'valid', then who knows what happens (this code was littered with 'asserts' before I golf-ized it).

Lines 17-19: Print the result

Brian
Very nice, I don't have F# experience but in general it remains readable (even in this golfing ;-)
ChristopheD
In C# it is legal to replace "StringSplitOptions.RemoveEmptyEntries" with "0". If it works in F#, you may save a bunch of chars :)
Jan
It's actually '1', but yes, awesome! In F#, "enum 1" works, and is much shorter - this will be useful for future golfing :)
Brian
+1  A: 

C++ 637

#include <iostream>
#include <string>
#include <vector>
#define S(x)((x)<0?-1:x>0?1:0)
using namespace std;enum{R=100,C=100,D=100};int main(){string s;
int N[D][2],M=0,q=0,p=0,i,j,V,L,a,b;for(i=0;j=0,(i<R)&&getline(cin,s);i++)
while((j=s.find_first_not_of(" ",j))<=s.size()){L=sscanf(&s[j],"%d",&V);
N[V][0]=i;N[V][1]=j;if(M<V)M=V;if(q<=i)q=i+1;if(p<=j)p=j+1;j+=L+1;}
string F(q*p,' '),l="\\|/-+-/|\\";F[p*N[1][0]+N[1][1]]='+';for(i=2;i<=M;++i){
int r=N[i-1][0],c=N[i-1][1],d=N[i][0],e=N[i][1];for(a=S(d-r),b=S(e-c);r!=d||c!=e;)
r+=a,c+=b,F[p*r+c]=l[(a+1)*3+b+1];F[p*r+c]='+';}for(i=0;i<q;i++)
cout<<string(&F[i*p],p)+"\n";}

Indented, and with a few slightly more meaningful names, that looks like:

#include <iostream>
#include <string>
#include <vector>
#define S(x)((x)<0?-1:x>0?1:0)
using namespace std;
enum{R=100,C=100,D=100};
int main(){
    string s;
    int N[D][2],M=0,rs=0,cs=0,i,j,V,L,dr,dc;
    for(i=0;j=0,(i<R)&&getline(cin,s);i++)
        while((j=s.find_first_not_of(" ",j))<=s.size()){
            L=sscanf(&s[j],"%d",&V);
            N[V][0]=i;
            N[V][1]=j;
            if(M<V)M=V;
            if(rs<=i)rs=i+1;
            if(cs<=j)cs=j+1;
            j+=L+1;
        }
    string F(rs*cs,' '),lines="\\|/-+-/|\\";
    F[cs*N[1][0]+N[1][1]]='+';
    for(i=2;i<=M;++i){
        int r=N[i-1][0],c=N[i-1][1],rt=N[i][0],ct=N[i][1];
        for(dr=S(rt-r),dc=S(ct-c);r!=rt||c!=ct;)
            r+=dr,c+=dc,F[cs*r+c]=lines[(dr+1)*3+dc+1];
        F[cs*r+c]='+';
    }
    for(i=0;i<rs;i++)
        cout<<string(&F[i*cs],cs)+"\n";
}

Despite superficial differences, it's a blatant theft of morotspaj's code.

Jerry Coffin
What's the point of defining C and D when they both equal to 100 (R)?
KennyTM
@KennyTM:there probably isn't any real point in this case. In theory, they could have different values, but in reality it's not like we're trying to write serious, maintainable code here...
Jerry Coffin
+35  A: 

Perl, 222 char (218)

Perl, 384 365 276 273 253 225 222 218 chars (222 when contest ended). Newlines are for "readability" only and are not included in the character count.

Last edit: replaced unweildy $P=($p||=$q)+$q-($Q=$q>$p?$q:$p) with sort.

    @S=map{$n=s/$/$"x97/e;(/./g)[0..95],$/}<>;$"=$\;$_="@S";
    while(/\b$n /){$S[$q=$-[0]]='+';($P,$Q)=sort{$a-$b}$p||$q,$q;
    for(qw'\98 |97 /96 -1'){/\D/;$S[$P]=$&until($Q-$P)%$'||$Q<=($P+=$')}
    $n++;$p=$q}$_="@S";s/\d/ /g;print

We could save one more character changing $_="@S";s/\d/ /g;print to die map/\d/?' ':$_,@S, which would send the output to standard error instead of standard output. I'm not above such shenanigans.

Explanation:

@S=map{$n=s/$/$"x96/e;(/./g)[0..95],$/}<>

This task will be easier if all the lines are the same length (say, 97 characters). This statement takes each line of input, replaces the end-of-line character with 96 spaces, then pushes the first 96 characters plus a newline into the array @S. Note we are also setting $n=1, as 1 is the first number we'll look for in the input.

$"=$\;$_="@S";

Converts the array @S into a single string. It's more convenient to use the variable $_ for pattern matching, and convenient to use the array @S for making updates to the picture.

while(/\b$n /){

Search for the number $n in the variable $_. Evaluating regular expressions in Perl has several side-effects. One is to set the special variable $-[0] with the position of the start of the matched pattern within the matched string. This gives us the position of the number $n in the string $_ and also the array @S.

Of course, the loop will end when $n is high enough that we can't find it in the input.

    $S[$q=$-[0]]='+';

Let $q be the position of the number $n in the string $_ and the array @S, and assign the character '+' at that position.

        $P=($p||=$q)+$q-($Q=$q>$p?$q:$p)
        ($P,$Q)=sort{$a-$b}$p||$q,$q;

The first time through the loop, set $p to $q. After the first time, $p will hold the previous value of $q (which will refer to the position in the input of the previous number). Assign $P and $Q such that $P=min($p,$q), $Q=max($p,$q)

    for(qw'\98 |97 /96 -1'){

By construction, consecutive numbers are either

  • connected by a vertical line. Since the input is constructed to have 97 characters on each line, this case means that $p-$q is divisible by 97.

  • "aligned to the slope of a backslash", which would make $p-$q divisible by 98

  • "aligned to the slope of a forward slash", which would make $p-$q divisible by 96

  • on the same horizontal line

The elements of this list encode the possible number of positions between line segments, and the character to encode that segment.

        /\D/;

Another trivial regex evaluation. As a side-effect, it sets the special variable $& (the MATCH variable) to the line segment character (\ | / or -) and $' (the POSTMATCH variable) to the number (98 97 96 or 1) encoded in the list element.

        $S[$P]=$&until($Q-$P)%$'||$Q<=($P+=$')

This statement draws the line segment between two numbers. If $Q-$P is divisible by $', then keep incrementing $P by $' and assigning the character $& to $S[$P] until $P reaches $Q. More concretely, for example if $Q-$P is divisible by 97, then increment $P by 97 and set $S[$P]='|'. Repeat until $P>=$Q.

    $n++;$p=$q

Prepare for the next iteration of the loop. Increment $n to the next number to search for in the input, and let $p hold the position of the previous number.

$_="@S";s/\d/ /g;print

Convert the array @S into a single string again. Convert any leftover digits (from double digit identifiers in the input where we only overwrote the first digit with a '+') to spaces. Print.

mobrule
+1 because 'for readability only' made me smile
TC
Really stunning!!
Bragboy
@TC Well the first couple of revisions were sort of readable.
mobrule
Crap, I gotta learn Perl. Any language where you can do so much with so little syntax is worth a look.
Sam152
This is just ridiculous. Great work.
ChaosPandion
Good to see you're still golfing strong, mobrule! Three less strokes by using $-[0] instead of length.. -- I wish I could help you more but I'm rather busy these days.
A. Rex
Marking as accepted answer (until proven that this can be beaten); brilliant golfing!
ChristopheD
"$P=($p||=$q)+$q-($Q=$q>$p?$q:$p);" Oh my brain!
M28
This exemplifies the readability of perl.
Stefan Valianu
+2  A: 

C#, 638 chars

using System;
using System.Linq;
using System.Text.RegularExpressions;

class C
{
    static void Main()
    {
        int i=0,j;
        var p = Console.In.ReadToEnd()
            .Split('\n')
            .SelectMany(
                r =>
                {
                    i++; j =0;
                    return Regex.Matches(r, "\\s+(\\d+)").Cast<Match>()
                    .Select(m => { j += m.Length; return new { X = j, Y = i-1, N = int.Parse(m.Groups[1].Value) }; });
                }
        ).OrderBy(a=>a.N).ToList();

        var W = p.Max(a => a.X)+1;
        var k = new char[W*i+W];
        i = 0;
        while (i < p.Count)
        {
            var b = p[i > 0 ? i - 1 : 0]; var a = p[i];
            int h = a.Y - b.Y, w = a.X - b.X;
            var s = "|-/\\"[h == 0 ? 1 : w == 0 ? 0 : h / w > 0 ? 3 : 2];
            while ((h | w) != 0) { k[b.X + w + W * (b.Y + h)] = s; h -= h.CompareTo(0); w -= w.CompareTo(0); }
            k[a.X + a.Y * W] = '+';
            k[W * ++i] = '\n';
        }

        Console.Write(k);
    }
}
Jeff Meatball Yang
Your Regex does not match numbers that start in the first column of a line, does it?
Brian
The spec says that every number is surrounded at least one whitespace character on both left and right.
Jeff Meatball Yang
Ah, oops, touche, I could have shaved 8 more chars off my solution. :)
Brian
How do I use it? I tried pasting thee input and using ^Z to signal EOF, but got an index out-of-range.
Charles
ConsoleApplication1.exe < input.txt
Jeff Meatball Yang
Ah! Cool. I could shave off a few chars if I use your idea.. the only problem is that the way I've done it, the command prompt will mess up the picture when it exits. D'oh.
Charles
+10  A: 
KennyTM
+9  A: 

C, 386

402 386 character in C. Newlines after the first are only for readability.

#include <stdio.h>
int x[101],y[101],c=1,r,w,h,b,i,j,k,m,n;
int main(){
while((b=getchar())-EOF)
b-' '?b-'\n'?ungetc(b,stdin),scanf("%d",&b),x[b]=c++,y[b]=h,c+=b>9:(w=c>w?c:w,++h,c=1):++c;
for(r=0;r<h&&putchar('\n');++r)
for(c=0;c<w;++c){
for(b=' ',i=2,m=x[1]-c,n=y[1]-r;j=m,k=n,m=x[i]-c,n=y[i]-r,x[i++];)
b=j|k&&m|n?j*m>0|k|n?k*n<0?(j-k|m-n?j+k|m+n?j|m?b:'|':'/':'\\'):b:'-':'+';
putchar(b);
}
}
Boojum
+5  A: 

Powershell, 328 304 characters

$i=$l=0;$k=@{}
$s=@($input|%{[regex]::matches($_,"\d+")|%{$k[1*$_.Value]=@{y=$l
x=$_.Index}};$l++;""})
while($a=$k[++$i]){
if($i-eq1){$x=$a.x;$y=$a.y}
do{$d=$a.x.CompareTo($x);$e=$a.y.CompareTo($y)
$s[$y]=$s[($y+=$e)].PadRight($x+1).Remove($x,1).Insert(($x+=$d),
"\-/|+|/-\"[4+$d*3+$e])}while($d-or$e)}$s

and here's a pretty-printed version with comments:

# Usage: gc testfile.txt | dots.ps1

$l=$i=0            # line, dot index (used below)
$k=@{}             # hashtable that maps dot index to coordinates  

# Apply regular expression to each line of the input
$s=@( $input | foreach{     
        [regex]::matches($_,"\d+") | foreach{
            # Store each match in the hashtable
            $k[ 1*$_.Value ] = @{ y = $l; x = $_.Index }
        }
        $l++; # Next line
        ""    # For each line return an empty string.
              # The strings are added to the array $s which
              # is used to produce the final output
    }
)

# Connect the dots!
while( $a = $k[ ++$i ] )
{
    if( $i -eq 1 )  # First dot?
    {
        # Current position is ($x, $y)
        $x = $a.x;
        $y = $a.y
    }

    do
    {
        $d = $a.x.CompareTo( $x )           # sign( $a.x - $x )
        $e = $a.y.CompareTo( $y )           # sign( $a.y - $y )
        $c = '\-/|+|/-\'[ 4 + $d * 3 + $e ] # character            '

        # Move
        $x += $d                            
        $y += $e

        # "Replace" the charcter at the current position
        # PadRight() ensures the string is long enough          
        $s[ $y ]=$s[ $y ].PadRight( $x+1 ).Remove( $x, 1 ).Insert( $x, $c )
    } while( $d -or $e ) # Until the next dot is reached
}

# Print the resulting string array
$s
Danko Durbić
Looks like powershell is pretty good at golfing.
Dykam
It sure is, but I wish I didn't have to start each variable with `$` :) Also, comparison and boolean operators are a bit too long (`-lt -gt -and -or`)
Danko Durbić
Danko: But for some problems the ability of comparison operators to return all *matching* elements of a sequence instead of just doing comparison (kinda an implied foreach) is great for golfing.
Joey
+3  A: 

C#, 422 chars

758 754 641 627 584 546 532 486 457 454 443 440 422 chars (next time maybe I won't submit so soon.)

using A=System.Console;class B{static int C,o,d,e,G,O=1,f,F,u,n;static void Main(
){var s=A.In.ReadToEnd();A.Clear();while(++u<s.Length){f++;if(s[u]<32){u++;F++;f=
0;}if(s[u]>32){if(int.Parse(s[u]+""+s[++u])==O){o=e>f?1:f>e?-1:0;C=d>F?1:F>d?-1:0
;G=e+o;n=d+C;if(O++>1)while(n!=F||G!=f){A.SetCursorPosition(G-=o,n-=C);A.Write(
"+/-|\\"[n==d&&G==e?0:n==F&&G==f?0:C+o==0?1:C==0?2:o==0?3:4]);}e=f;d=F;F=0;f=u=-1
;}f++;}}A.Read();}}

Usage: run, paste (or type) the input, ensure the last line is terminated, press CTRL-Z or F6, press Enter.

Kinda-readable version:

using A = System.Console;
class B
{
    // code golf fun!
    static int C, o, d, e, G, O = 1, f, F, u, n;
    static void Main()
    {
        // read the input into a string char by char until EOF
        var s = A.In.ReadToEnd();

        A.Clear(); // clear console, ready to draw picture

        // O is the "dot" number we're looking for; f is current column; F is current row
        // loop over the field looking for numbers sequentially until no more are found
        while (++u < s.Length)
        {
            f++;
            // any char <32 is expected to be a CR/LF; increment the current row and reset the current column
            if (s[u] < 32)
            {
                u++; // skip the other half of the CR/LF pair
                F++; // next row
                f = 0; // column reset
            }
            // any char >32 is expected to be a number
            if (s[u] > 32)
            {
                // parse the current + next char and see if it's the number we want
                if (int.Parse(s[u] + "" + s[++u]) == O)
                {
                    // set up the coordinates and compare X1 with X2 and Y1 with Y2 to 
                    // figure out the direction of line
                    o = e>f?1:f>e?-1:0; // horizontal direction (same as o=e.CompareTo(f))
                    C = d>F?1:F>d?-1:0; // vertical direction (same as C=d.CompareTo(F))
                    // initial offsets compensate for off-by-one
                    G = e + o;
                    n = d + C;
                    // draw the line (except for the very first dot) 
                    if (O++ > 1)
                        while (n != F || G != f)
                        {
                            // update coordinates and write the desired character 
                            A.SetCursorPosition(G -= o, n -= C);
                            // this lovely line decides which character to print, and prints it
                            A.Write("+/-|\\"[n == d && G == e ? 0 : n == F && G == f ?
                                0 : C + o == 0 ? 1 : C == 0 ? 2 : o == 0 ? 3 : 4]);
                        }
                    // remember the end point of this line, to use as the start point of the next line
                    e = f;
                    d = F;
                    // reset set current row (F), column (f), and field position (u)
                    F = 0;
                    f = u = -1;
                }
                f++; // bump current column because we parse 2 chars when we find a dot
            }
        }
        A.Read(); // just to prevent the command prompt from overwriting the picture
    }
}
Charles
Looks like I exposed a bug in the syntax highlighter.
Charles
Very nice progress (more than 200 characters off since the initial version); good work... It's easy to get entangled in these code-golf questions alright (I've lost my fair share of time on them as well ;-)
ChristopheD
It's so addictive! I'm ashamed of the power it wields over me. (This is my first one. Maybe the addiction will fade...)
Charles
"...paste (or type) the input,..." haha, typing that would not be fun.
Jeff Meatball Yang
I have now reached my goal of getting it down to <= 2* the perl solution (which is currently 222). :)
Charles
+10  A: 

AWK - 296 317 321 324 334 340

Not a prize winner (yet), but I am pleased with the effort (line breaks for display). This new version uses VT-100 escape sequences. The '^[' is just one character, Escape!!! Cut and paste will not work with this version, since the sequence "^[" has to be replaced with the real ESC character. To make it forum friendly, ESC could be specified as "\0x1b", but it takes too much space...

BEGIN{FS="[ ]"}{for(j=i=0;i<NF;j+=length(g)){if(g=$++i){x[g]=k=i+j;y[g]=NR;
m=m>k?m:k}}}END{printf"^[[2J[%d;%dH+",Y=y[i=1],X=x[1];while(a=x[++i])
{a-=X;b=y[i]-Y;t=a?b?a*b>0?92:47:45:124;A=a?a>0?1:-1:0;B=b?b>0?1:-1:0;
for(r=a?a*A:b*B;--r;){printf"^[[%d;%dH%c",Y+=B,X+=A,t}
printf"^[[%d;%dH+",Y+=B,X+=A}}

The older standard version

BEGIN{FS="[ ]"}{for(j=i=0;i<NF;j+=length(g)){if(g=$++i){x[g]=k=i+j;y[g]=NR;
m=m>k?m:k}}}END{q[X=x[1],Y=y[i=1]]=43;while(a=x[++i]){a-=X;b=y[i]-Y;
t=a?b?a*b>0?92:47:45:124;A=a?a>0?1:-1:0;B=b?b>0?1:-1:0;for(r=a?a*A:b*B;--r;
q[X+=A,Y+=B]=t);q[X+=A,Y+=B]=43}for(j=0;++j<NR;){for(i=0;i<m;){t=q[i++,j];
printf"%c",t?t:32}print}}

Now a little explanation

# This will break the input in fields separated by exactly 1 space,
# i.e. the fields will be null or a number.

BEGIN{FS="[ ]"}

# For each line we loop over all fields, if the field is not null 
# it is a number, hence store it.
# Also account for the fact the numbers use space.
# Also, find the maximum width of the line.

{
    for(j=i=0;i<NF;j+=length(g)){
        if(g=$++i){
            k=j+i;x[g]=k;y[g]=NR;m=m>k?m:k
        }
    }
}

# Once we have all the data, let start cooking.

END{
    # First, create a matrix with the drawing.
    # first point is a +

    q[X=x[1],Y=y[i=1]]=43;

    # loop over all points

    while(a=x[++i]){

        # Check next point and select character
        # If a == 0 -> -
        # If b == 0 -> |
        # If a and b have same sign -> \ else /

        a-=X;b=y[i]-Y;t=a?b?a*b>0?92:47:45:124;

        # there is no sgn() function
        A=a?a>0?1:-1:0;B=b?b>0?1:-1:0;

        # Draw the line between the points

        for(k=0;++k<(a?a*A:b*B);){
            q[X+=A,Y+=B]=t
        }

        # store + and move to next point

        q[X+=A,Y+=B]=43
    }
    # Now output all lines. If value in point x,y is 0, emit space
    for(j=0;++j<NR;){
        for(i=0;i<m;){
            t=q[i++,j];printf("%c",t?t:32)
        }
        print
    }
}
Dan Andreatta
Impressive. I played with awk in college but didn't know it was this powerful.
Charles
Shouldn't you count the ^[ as two chars anyway? We're talking about source code, which is plain text, and in plain text it's two chars, right? After all, if we allowed ^[ to count as one, then wouldn't all of these also count as one?: 43, 92, 47, 45, 124
Charles
The source code contain the actual ESC character, it is only printed as two. In fact if you cut and paste the code posted here, it will not work. You will have to replace the sequence ^[ with a real ESC character.
Dan Andreatta
+51  A: 

Commodore 64 BASIC - 313 chars

EDIT: See below for the golfed version

A little trip down the memory lane with PET graphics, POKEs and PEEKs and everything :)

It fits in a single screen:)

The program operates directly in the screen memory, so you just go ahead, clear the screen, place your dots, and type RUN:

Input

You have to wait a minute or so while it finds the dots and then it starts to draw. It isn't fast - you can actually see the lines being drawn, but that's the coolest part :)

Output

Golfed version:

Commodore BASIC seems like a great language for golfing, because it doesn't require whitespace :) You can also shorten most of the commands by entering an unshifted first letter followed by a shifted second letter. For example, POKE can be typed as P[SHIFT+O], which appears as P┌ on the screen:

Golfed version

Danko Durbić
Absolutely great (!!) Would be nice if you could include a character count too...
ChristopheD
Simply amazing !! :)
Olli
Brilliant, absolutely brilliant.
Dan Andreatta
Thanks! I've updated the answer with a golfed version of the program.
Danko Durbić
Too cool. +1 for nostalgia!
Charles
+12  A: 
Grant Peters
+1 for sheer gall. I look forward to the golfed version.
Charles
This isn't MS-DOS batch though, it's Windows batch.
ssg
@ssg, true, though the things done here can be done in dos as well, just that you have to split to multiple files, i'll rename next edit
Grant Peters
+7  A: 

Intel Assembler

Assembled size: 506 bytes

Source: 2252 bytes (hey, it's not a trivial problem this one)

To Assemble: Use A86 To Run: Tested with a WinXP DOS box. Invocation jtd.com < input > output

    mov ax,3
    int 10h
    mov ax,0b800h
    mov es,ax
    mov ah,0bh
    int 21h
    mov bx,255
    cmp al,bl
    mov dh,bh
    mov si,offset a12
    push offset a24
    je a1
    mov si,offset a14
a1: inc bl
a2: mov dl,255
    call si
    cmp al,10
    jb a4
a3: cmp al,10-48
    jne a1
    inc bh
    mov bl,dh
    jmp a2
a4: mov dl,al
    call si
    cmp al,10
    jae a5
    mov ah,dl
    aad
    mov dl,al
a5: mov di,dx
    mov ch,al
    shl di,2
    mov [di+a32],bx
    cmp bl,[offset a30]
    jb a6
    mov [offset a30],bl
a6: cmp bh,[offset a31]
    jb a7
    mov [offset a31],bh
a7: push offset a19
    mov al,80
    mul bh
    add al,bl
    adc ah,0
    add ax,ax
    lea di,[di+2+a32]
    mov [di],ax
    add di,2
    cmp di,[a22-3]
    jbe a8
    mov [a22-3],di
    mov [a25-3],di
a8: mov di,ax
    mov al,dl
    aam
    cmp ah,0
    je a10
a9: add ah,48
    mov es:[di],ah
    add di,2
a10:add al,48
    mov es:[di],al
    mov al,ch
    inc bl
    jmp a3
a11:jmp si
a12:mov ah,0bh
    int 21h
    cmp al,255
    jne a15
    mov ah,8
    int 21h
a13:cmp al,13
    je a11
    sub al,48
    ret
a14:mov ah,1
    int 21h
    cmp al,26
    jne a13
    mov si,offset a15
    ret
a15:cmp dl,255
    je a16
    mov al,32
    ret
a16:mov si,offset a32 + 4
    lodsw
    mov cx,ax
    mov dx,ax
    lodsw
    mov di,ax
    mov b es:[di],1
    mov bp,0f000h
    call a26
    add sp,6
    mov bx,[a22-3]
    mov ax,[offset a31]
    inc ax
a17:mov bp,[offset a30]
a18:mov b[bx],32
    inc bx
    dec bp
    jnz a18
    mov w[bx],0a0dh
    add bx,2
    dec ax
    jnz a17
    mov b[bx],'$'
    add w[a30],2
a19:lodsw
    xchg ax,dx
    cmp ah,dh
    lahf
    mov bl,ah
    cmp al,dl
    lahf
    shr bl,6
    shr ah,4
    and ah,12
    or bl,ah
    mov bh,0
    shl bx,3
a20:mov b es:[di],43
a21:mov al,b[a30]
    mul ch
    add al,cl
    adc ah,0
    mov bp,ax
    mov b[bp+100h],43
a22:add di,[bx + a29]
    add cl,[bx + a29 + 4]
    add ch,[bx + a29 + 6]
    mov b es:[di],1
    mov al,[bx + a29 + 2]
    mov [a21-1],al
    mov [a22-1],al
    mov bp,01000h
    call a26
    cmp di,[si]
    jne a20
    mov al,es:[di+2]
    sub al,48
    cmp al,10
    jae a23
    mov b es:[di+2],0
a23:mov b[a21-1],43
    mov b[a22-1],43
    mov b es:[di],43
    lodsw
    ret
a24:mov al,b[a30]
    mul ch
    add al,cl
    adc ah,0
    mov bp,ax
    mov b[bp+100h],43
a25:mov dx,[a22-3]
    mov ah,9
    int 21h
    ret
a26:pusha
a27:mov cx,0ffffh
a28:loop a28
    dec bp
    jnz a27        
    popa
    ret
a29:dw -162,92,-1,-1,-2,45,-1,0,158,47,-1,1,0,0,0,0,-160,124,0,-1
a30:dw 0
a31:dw 0,0,0,160,124,0,1,0,0,0,0,-158,47,1,-1,2,45,1,0,162,92,1,1
a32:

Interesting features: self modifying code, animated output (the second example works, but is too big to display), abuse of 'ret' to implement a loop counter, interesting way of determining line/movement direction.

Skizz
Wow, must have been quite some work, nice job !
ChristopheD
+6  A: 

Rebmu: 218 chars

Ma L{-|\/}Qb|[sg?SBaB]Da|[feSm[TfiSrj[spAsp]iT[++Tbr]]t]Xa|[i?A]Ya|[i?FImHDa]Ca|[skPCmSCaBKfsA]wh[Jd++N][roG[xJyJ]]Bf+GwhB[JcB Ff+GiF[KcF HqXkXj VqYkYju[chCbPClEZv1[ezH2[eeHv3 4]]e?A+bRE[hV]f]]chJeFIlSCj{+}{+ }Jk Bf]wM

I'm getting pretty good at reading and editing it natively in its pig-latin form. (Though I do use line breaks!!) :)

But here's how the dialect is transformed by the interpreter when the case-insensitive "mushing" trick is boiled away, and one gets accustomed to it. I'll add some comments. (Tips: fi is find, fe is foreach, sp is a space character, i? is index, hd is head, ch is change, sk is skip, pc is pick, bk is break, i is if, e is either, ee is either equal, ad nauseum)

; copy program argument into variable (m)atrix
m: a

; string containing the (l)etters used for walls
l: {-|\/} 

; q is a "b|function" (function that takes two parameters, a and b)
; it gives you the sign of subtracting b from a (+1, -1, or 0)
q: b| [sg? sb a b]

; d finds you the iterator position of the first digit of a two digit
; number in the matrix
d: a| [fe s m [t: fi s rj [sp a sp] i t [++ t br]] t]

; given an iterator position, this tells you the x coordinate of the cell
x: a| [i? a]

; given an iterator position, this tells you the y coordinate of the cell
y: a| [i? fi m hd a]

; pass in a coordinate pair to c and it will give you the iterator position
; of that cell
c: a| [sk pc m sc a bk fr a]

; n defaults to 1 in Rebmu.  we loop through all the numbers up front and
; gather their coordinate pairs into a list called g
wh [j: d ++ n] [ro g [x j y j]]

; b is the (b)eginning coordinate pair for our stroke. f+ returns the
; element at G's current position and advances G (f+ = "first+")
; advance g's iteration position
b: f+ g
wh b [
    ; j is the iterator position of the beginning stroke
    j: c b 

    ; f is the (f)inishing coordinate pair for our stroke
    f: f+ g

    ; if there is a finishing pair, we need to draw a line 
    i f [
        ; k is the iterator position of the end of the stroke
        k: c f

        ; the (h)orizontal and (v)ertical offsets we'll step by (-1,0,1)
        h: q x k x j 
        v: q y k y j 

        u [
            ; change the character at iterator location for b (now our
            ; current location) based on an index into the letters list
            ; that we figure out based on whether v is zero, h is zero,
            ; v equals h, or v doesn't equal h.
            ch c b pc l ez v 1 [ez h 2 [ee h v 3 4]]

            ; if we update the coordinate pair by the offset and it 
            ; equals finish, then we're done with the stroke
            e? a+ b re [h v] f
        ]
    ] 

    ; whether we overwrite the number with a + or a plus and space
    ; depends on whether we detect one of our wall "letters" already
    ; one step to the right of the iterator position
    ch j e fi l sc j {+} {+ }

    ; update from finish pair to be new begin pair for next loop iteration
    j: k
    b: f
] 

; write out m
w m

Both the language and sample are new and in an experimental stage. For instance, ad couldn't be used to add together vectors and matrices before I changed it to help with this sample. But I think that's just the sort of thing that a language designed specifically for code golf has to have anyway. It's a subtle line between "language" and "library".

Hostile Fork