Molding ASCII art

18

You are given a single printable ASCII string containing no newlines, and a multiline "mold", containing spaces (), and hashes (#).

You must go character by character in the string, and replace hashes using the characters from the string in left-right, top-bottom order. If the string is too short to fill the mold, you stop outputting, if the string is too long you truncate the string to exactly fill the mold.


Example string/mold (string too long, truncated):

Loremipsumdolorsitamet,consecteturadipiscingelit.Namsuscipitmagnanoneratgravidacondimentum.Vestibulumnecnisllorem.Fuscemolestieviverranibh,eueleifendnislplaceratnon.Namblanditturpislacus,vitaemolestielacusimperdietquis.Nullapulvinar,exquissollicitudinfacilisis,eratnullavolutpatlectus,etluctusenimvelitegetex.Inhachabitasseplateadictumst.Donecinterdumnullalacinia,sodalesloremin,eleifendturpis.Pellentesqueanisimi.Aeneannonlobortisdiam,quisaliquetquam.Aeneanaugueipsum,imperdietsedaliquetnon,volutpategetsapien.Nullampharetranullaquispretiumornare.Aliquamfermentumvestibulummassavitaevehicula.
###########################################################
##### ##############   ###### ###### ######################
#####  ##   ######   #  ##### ###### ########        ######
###### #  #  ####  #### ##### ###### #######  ######  #####
######   ###  ###       ##### ###### ####### #######  #####
######  ##### ### ########### ###### #######   ###   ######
###### ###### ###  ########## ######      #####   #########
##################       ####    ##########################
###########################################################

Example output:

Loremipsumdolorsitamet,consecteturadipiscingelit.Namsuscipi
tmagn anoneratgravid   acondi mentum .Vestibulumnecnisllore
m.Fus  ce   molest   i  evive rranib h,euelei        fendni
slplac e  r  atno  n.Na mblan dittur pislacu  s,vita  emole
stiela   cus  imp       erdie tquis. Nullapu lvinar,  exqui
ssolli  citud inf acilisis,er atnull avolutp   atl   ectus,
etluct usenim vel  itegetex.I nhacha      bitas   seplatead
ictumst.Donecinter       dumn    ullalacinia,sodalesloremin
,eleifendturpis.Pellentesqueanisimi.Aeneannonlobortisdiam,q

Example string/mold (string too short, output stopped):

This probably won't look good.
### ### ### ###
# # # # #   #
### ### #   # #
#   #   #   # #
#   #   ### ###

Corresponding output:

Thi s p rob abl
y   w o n   '
t l ook     g o
o   d   .   

Shortest code in bytes wins.

Credit for the idea to this website.

orlp

Posted 2016-02-08T09:55:47.130

Reputation: 37 067

Can the input line contain hashes? (If so, that could use a test case.) – Martin Ender – 2016-02-08T10:17:18.060

Can the input line contain spaces? – manatwork – 2016-02-08T10:42:32.197

Can the input have leading/trailing spaces/newlines? – Sp3000 – 2016-02-08T10:51:57.317

@manatwork In the second test case, it does. – Martin Ender – 2016-02-08T10:57:29.797

@MartinBüttner Yes, the input case can contain hashes. – orlp – 2016-02-08T12:45:42.440

@Sp3000 The input can have (leading/trailing) spaces, but not newlines. – orlp – 2016-02-08T12:46:01.980

Can the output have trailing whitespace if the string is too short? – PurkkaKoodari – 2016-02-08T19:10:17.317

@Pietu1998 No, the specification is exact. Only the standard single trailing newline is allowed. – orlp – 2016-02-08T19:18:15.260

Answers

5

CJam, 16 14 bytes

Thanks to Sp3000 for saving 2 bytes.

lq{s'#-\+(\}/;

Terminates with an error if the string is too short, but the error is printed to STDERR.

Try it online!

Alternatively (same byte count):

lq{SN+&\+(\}/;

Explanation

l       e# Read the input line.
q       e# Read the grid.
{       e# For each character in the grid...
  s     e#   Convert to string.
  '#-   e#   Remove "#" from it.
  \+    e#   Prepend it to the input line (this is a no-op for "#"s in the grid).
  (     e#   Pull off the first character from the input line. This will terminate the
        e#   program with an error once the input line is empty.
  \     e#   Swap the character with the input line.
}/
;       e# Discard the remainder of the input line if there is any.

Martin Ender

Posted 2016-02-08T09:55:47.130

Reputation: 184 808

7

LabVIEW, 37 LabVIEW Primitives

splits the string into text and mold then transforms them into an array. Checks the mold if theres a # and puts a char from text else it does nothing. If either the text or the mold are empty exit the loop

Eumel

Posted 2016-02-08T09:55:47.130

Reputation: 2 487

The fact that you did this with LabView blows – Brain Guider – 2016-02-08T19:39:37.580

AND its fun to look at! – Draco18s no longer trusts SE – 2016-02-08T21:41:15.953

6

Haskell, 48 bytes

called like "(replace with string)#(hashmark string)":

[]#_=[]
(r:t)#('#':s)=r:t#s
r#(c:s)=c:r#s
_#_=[]

Less golfed:

combine replacements slots | null replacements = []
                           | null slots        = []
                           | head slots == '#' = head replacements : combine (tail replacements) (tail slots)
                           | otherwise         = head slots        : combine       replacements  (tail slots)

Michael Klein

Posted 2016-02-08T09:55:47.130

Reputation: 2 111

Cool, but isn't this invalid unless you add I/O? E.g import Control.Applicative;main=liftA2(#)getLine getContents>>=putStrLn – Cubic – 2016-02-09T14:53:28.207

@Cubic The problem statement does not require IO (or even a full program) and other solutions, including one in Python 3, do not include IO. – Michael Klein – 2016-02-10T00:20:49.373

5

Retina, 42 40 bytes

Byte count assumes ISO 8859-1 encoding.

T`#`×`¶.+
+`^(.)([^×]+)×
$2$1
^.*¶|×\D*

The trailing linefeed is significant.

Try it online!

Explanation

T`#`×`¶.+

We first replace # which are part of the grid with the non-ASCII (but extended ASCII) character × so we don't confuse them with any # that might appear on the first line.

+`^(.)([^×]+)×
$2$1

Now we fill as many × as possible from the first line by repeatedly replacing the first × we can find with the first character on the first line (which is removed in the process).

^.*¶|×\D*

Finally, we get rid of anything that's left on the first line as well as anything from the first × to truncate the input in both directions.

Martin Ender

Posted 2016-02-08T09:55:47.130

Reputation: 184 808

4

JavaScript (ES6), 57 56 55 bytes

(s,m)=>m.replace(x=/[^]/g,c=>c-1?x&&c:x=s[i++]||"",i=0)

Saved 1 byte thanks to @Neil!

Explanation

Works with hashes in the input string and retains trailing white-space after the input string has finished.

var solution =

(s,m)=>
  m.replace(x=/[^]/g,c=> // for each character c of the mold, initialise x to true
    c-1?                 // if c is a space or newline:
      x&&c               // if x is truthy, return c
                         // else if the string has ended, x will equal "" (false), return x
    :                    // else if c is a hash:
      x=                 // set x to the character of the input string
        s[i++]||"",      // return the input string character (or "" if finished)
    i=0                  // initialise i to 0
  )
<input type="text" id="input" value="This probably won't look good." /><br>
<textarea id="mold" rows="6" cols="40">### ### ### ###
# # # # #   #
### ### #   # #
#   #   #   # #
#   #   ### ###</textarea><br>
<button onclick="result.textContent=solution(input.value,mold.value)">Go</button>
<pre id="result"></pre>

user81655

Posted 2016-02-08T09:55:47.130

Reputation: 10 181

1Nice algorithm, but m.replace(/./g,c=>...) is shorter. – Neil – 2016-02-08T10:44:54.270

@Neil You're right. I was trying too hard for it to be different to your answer haha! – user81655 – 2016-02-08T10:49:48.877

1Not any more, since you can use /[^]/ instead of /.|\n/. (Also apologies for wrongly suggesting /./.) – Neil – 2016-02-08T19:23:26.360

3

Python 3, 69 68 67 bytes

def f(s,m):s=iter(s);return''.join(n<'#'and n or next(s)for n in m)

Ideone

Thanks to FryAmTheEggman, Chiel ten Brinke for the byte off. Alternatively, I could have used Python 2 for an extra one (print without ()).

301_Moved_Permanently

Posted 2016-02-08T09:55:47.130

Reputation: 401

You can save one byte by replacing the print with return. – Chiel ten Brinke – 2016-02-10T17:11:57.030

2

pb, 359 bytes

^w[B!0]{w[B=10]{t[T+1]b[0]}>}<w[T!0]{w[B!0]{<}>^b[T]vw[B!0]{t[B]b[0]^v[B]v<[X]w[B!0]{>}b[T]<[X]^[Y+2]w[B=0]{>}t[B]b[0]>b[T]v}^t[B-1]vw[B=0]{<}}>b[10]t[X]w[X!-2]{w[B!0]{v}<}w[X!T]{b[35]>}^[Y]^<[X]w[B!10]{t[B]b[0]w[T=35]{t[10]}v<[X]w[B!35]{w[B=0]{v<[X]<b[1]}>}b[T]^[Y]^<[X]w[B=0]{>}}<[X+2]w[B=0]{v}w[B!0]{b[0]>}w[Y!-1]{<[X]^w[B!0]{w[B=35]{b[0]}w[B=10]{b[35]}>}}

In pb, input is strictly one dimensional. It doesn't understand that you're drawing a shape with your input, it just sees one long line with some bytes with a value of 10 thrown in there. The first thing this program does is copy all but the first "line" of input onto Y=0, Y=1, etc, to create the shape of the mold.

Something I've noticed a lot in code golf, but especially when golfing esoteric languages, is that you often don't want to have two branches to deal with; you just set yourself up that you do the same thing in either case. The naive way to solve this problem would probably check the length of the string against the number of hashes in the rest of the input and do something depending on the result, because it has to behave differently depending on what gets cut off. But that's a lot of bytes.

Instead, after completing the mold, an extra line is added to the bottom. It is simply n hashes in a row, where n is the length of the string. Now the string is guaranteed to fit! After inserting all the characters of the string, that extra line that was added is unconditionally destroyed. Any leftover hashes in the mold proper are erased as well, and that's the necessary output!

Of course, it would violate the spec to simply destroy all hashes. After all, there could be a hash in the input string! To handle this, I refer to another part of the spec:

You are given a single printable ASCII string containing no newlines

(Emphasis mine.) By the time we're dealing with the string, we don't really care if there are newlines in it, but we do know that there aren't any. So, all hashes are replaced with newlines before being put into the mold! After all hashes are destroyed, all newlines are replaced with hashes again. This doesn't turn the entire output into a single hash-delimited line because the nature of pb's 2D output means that it never actually put a newline at the end of each line, it just went on to the next line.

Ungolfed:

# Start by copying down the mold
^
# (B means "the character under the cursor")
w[B!0]{       # While B isn't a null byte:
    w[B=10]{    # While B IS a newline:
            t[T+1]    # Increase T by 1
                      # (`T` is the only variable that can be modified directly)
            b[0]      # Overwrite with 0 to break out of inner loop
    }
    >           # Move to the right
                # (dodge the 0 we wrote and progress towards the end of input)
}

# Brush is now at the end of the input, T contains number of lines

<             # Make sure the brush is actually /on/ the input
w[T!0]{       # While T isn't 0:
    w[B!0]{<}>  # Go to the first character of the last line
    ^b[T]       # Place a flag above current character
                # Also a convenient way to get the value of T back later
    vw[B!0]{    # While the character under the flag isn't 0:
            t[B]b[0]  # Put it in T, overwrite with 0
            ^v[B]v    # Go down by the amount written in the space above
            <[X]      # Go left by the amount right the brush is (i.e. go to X=0)
            w[B!0]{>} # Find first empty space
            b[T]      # Write the value of T
            <[X]      # Go left by the amount right the brush is
            ^[Y+2]    # Go up by the amount down the brush is plus 2 (above input)
            w[B=0]{>} # Find flag
            t[B]b[0]  # Pick it up, overwrite with 0
            >b[T]     # Place it to the right
    v}
    ^t[B-1]v    # Collect flag - 1
    w[B=0]{<}   # Go to end of previous line
}

# Mold is placed, all that's left is placing the string
>b[10]        # Put a newline at the end, guaranteed to not be in the string
t[X]          # Save current X value in T

# Add more hashes, guaranteed to fit the input and findable later
w[X!-2]{       # While X!=-2:
    w[B!0]{v}    # Move down until hitting a null byte
    <            # Move left
}
w[X!T]{        # While not at the X value we saved earlier:
    b[35]>       # Travel right, leaving hashes
}

^[Y]^<[X]     # Go to (0, -1)

w[B!10]{      # Until hitting the newline at the end:
    t[B]b[0]    # Pick up character, overwrite with 0
    w[T=35]{    # If it's a hash...
            t[10]     # Make it a newline so we remember, deal with it later
    }
    v<[X]       # Go to (0, 0)
    w[B!35]{    # While B is not a hash:
            w[B=0]{   # While B IS null:
                    v       # Go down
                    <[X]<   # Go to X=-1
                    b[1]    # Print a 1 to break loop (it won't be rendered anyway)
            }
            >           # Go right, either ignore a non hash or go to X=0
    }
    b[T]        # Overwrite hash with picked up character
    ^[Y]^<[X]   # Go to (0, -1)
    w[B=0]{>}   # Go to first character of it to restart loop
}

<[X+2]        # Go to (-2, -1)
w[B=0]{v}     # Go down until finding the row of added hashes
w[B!0]{b[0]>} # Wipe it out unconditionally
w[Y!-1]{      # For every remaining line on the screen:
    <[X]^       # Go to the beginning
    w[B!0]{     # For each character in it:
            w[B=35]{  # If it's a hash:
                    b[0]    # Destroy it
            }
            w[B=10]{  # If it's a newline:
                    b[35]   # Write a hash (after the check to destroy hashes!)
            }
    >}
}

undergroundmonorail

Posted 2016-02-08T09:55:47.130

Reputation: 5 897

Nice, that looks like a lot of work. – Rɪᴋᴇʀ – 2016-02-08T15:17:40.730

1

ES6, 59 bytes

(t,m)=>m.replace(/#/g,h=>t[i++]||h,i=0).replace(/#[^]*/,'')

70 bytes if the text can contain hashes:

(t,m)=>m.replace(/#/g,(_,n)=>t[i]&&(j=++n)&&t[i++],i=0,j=0).slice(0,j)

Neil

Posted 2016-02-08T09:55:47.130

Reputation: 95 035

Do not remove trailing whitespace, exactly replicate the mold, with exactly the input string replacing the hash characters. – orlp – 2016-02-08T10:11:41.760

@orlp Thanks I'll edit that version back out again. – Neil – 2016-02-08T10:16:29.343

1

Perl, 53 51 42 + 2 = 44 bytes

@a=/./g;$/="";$_=<>;s/#/shift@a/ge;s/\s+$//

Requires -p to run. Explanation:

@a=/./g; # Split first line into an array of characters
$/=""; # Enable slurp mode (read the rest of lines in one go)
$_=<>;
s/#/shift@a/ge;
s/\s+$//
# With '-p' auto print is enabled

andlrc

Posted 2016-02-08T09:55:47.130

Reputation: 1 613

I get some ugly “1”s at the beginning of the output lines. Try this for a clean output: $a=<>;$/="";say<>=~s/#/substr$a,$i++,1/ger – manatwork – 2016-02-08T10:59:55.447

@manatwork I realized that as well, clever with the use of $/ instead of join – andlrc – 2016-02-08T11:08:30.120

1

Jelly, 10 8 bytes

¹ƈ<”#$?€

Try it here!

Lynn

Posted 2016-02-08T09:55:47.130

Reputation: 55 648

2This seems to print too much trailing whitespace when the input line is shorter than the number of # in the input. – Martin Ender – 2016-02-08T14:34:46.877

1

Perl 6, 72 bytes

my@a=get.comb;say(S:g/\#/{@a.shift//''}/~~{S/\s+$//}),@a||last for lines

Brad Gilbert b2gills

Posted 2016-02-08T09:55:47.130

Reputation: 12 713

1

ES6, 47 bytes

Probably the most straightforward solution.

(S,R)=>(i=-1,S.replace(/#/g,_=>R[++i]||R[i=0]))

This code creates an anonymous function that receives 2 parameters and returns the final result.

The first parameter S is the "map" string with your "#", while the second parameter R is the "replacement" for those "#".

Ismael Miguel

Posted 2016-02-08T09:55:47.130

Reputation: 6 797

0

Python 3

152 127 bytes

A full program.

from sys import stdin as S
s=list(S.readline())
print(''.join([''if not s else(s.pop(0)if m=='#'else m)for m in S.read()]))

106 bytes

A function that takes the stream as input.

def f(S):s=list(S.readline());return''.join([''if not s else(s.pop(0)if m=='#'else m)for m in S.read()])

Chiel ten Brinke

Posted 2016-02-08T09:55:47.130

Reputation: 201

We already have a Python answer which is much shorter and uses the same method to construct the output.

– Mego – 2016-02-09T08:31:05.250

I see. I wrote it yesterday though, when that answer wasn't there yet. Sorry for posting so late – Chiel ten Brinke – 2016-02-09T08:32:08.543

You're not posting late, lots of people probably haven't seen this question yet (I certainly didn't until I saw this post) – Blue – 2016-02-09T12:19:23.163