14
\$\begingroup\$

You should write a program or function which receives a string representing an ASCII art as input and outputs or returns the number of endpoints in the input.

The input will consist of the characters space - | + (with 0, 2, 2 and 4 endpoints respectively) and linebreaks. E.g.:

-|++-
  +

Two adjacent characters are connected and hence lose 1 endpoint each in the following cases:

--  -+  +- |  |  +  +  ++
           |  +  |  +

The first example has

2+2+2+2+1+
    3        = 12

endpoints.

Input

  • Input will be a string consisting of the characters space, -, |, + and newline.
  • Input length can be 0 length and any input matching the above description is valid (in regex input is [ -+|\n]*).
  • Trailing newline is optional.

Output

  • A single non-negative integer, the number of endpoints.

Examples

Outputs are after the last row of their inputs.

+
4 

-|++-
  +
12 

+--+
|  |
+--+
8 

  |  |
  +--+-- |||
12 

--++
 |||--
10 

<empty input>
0 


|
|     
2 

--
++--
 ++
   --+
  +++ || 

 ----
30 

This is code golf so the shortest entry wins.

\$\endgroup\$

4 Answers 4

11
\$\begingroup\$

Snails, 29

A
\+|\-)lr!\-|(\+|\|)n!\|}!\+

I added line comments with ,,, in order to make a commented version.

A                    ,, Count all accepting paths
        \+ | \- )    ,, Literal '+' or '-'        
        lr           ,, Set direction to left or right
        !\-          ,, Assert next char is not '-'
    |                ,, Or...
        ( \+ | \| )  ,, Literal '+' or '|'
        n            ,, Turn 90 degrees right or left (from initial direction right)
        !\|          ,, Assert next char is not '|'
}                    ,, Group everything previous
!\+                  ,, Assert next char is not '+'
\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES6), 168

Using template strings, all newlines are significant and counted.

Test running the snippet below in Firefox. (Chrome still does not support ...)

f=s=>`
${s}
`.split`
`.map((r,y,s,v=c=>c>' '&c!='-',h=c=>c>' '&c<'|')=>[...r].map((c,x)=>t+=(v(c)?2-v(s[y-1][x])-v(s[y+1][x]):0)+(h(c)?2-h(r[x-1])-h(r[x+1]):0)),t=0)&&t

// Less golfed
u=s=>{
  s = ('\n' + s + '\n').split('\n'); // split in rows, adding a blank line at top and one at bottom
  t = 0; // init counter
  v = c => c>' ' & c!='-'; // function to check if a character has vertical end points
  h = c => c>' ' & c<'|'; // function to check if a character has horizontal end points
  s.forEach( (r,y) =>
    [...r].forEach( (c,x) => {
     if (v(c)) // if current character has vertical endpoints, check chars in previous and following row
        t += 2 - v(s[y-1][x]) - v(s[y+1][x]); 
     if (h(c))  // if current character has horizontal endpoints, check previous and following chars in row
        t += 2 - h(r[x-1]) - h(r[x+1]);
    })
  )  
  return t
}

//TEST
out=x=>O.innerHTML+=x+'\n'

;[
 [`+`,4]
,[`-|++-
  +`,12]
,[`+--+
|  |
+--+`,8]
,[`  |  |
  +--+-- |||`,12]
,[`--++
 |||--`,10]
,[``,0]
,[`
|
|`,2]
,[`
--
++--
 ++
   --+
  +++ || 

 ----`,30]
].forEach(t=>{ r=f(t[0]),k=t[1],out('Test '+(r==k?'OK':'Fail')+'\n'+t[0]+'\nResult:'+r+'\nCheck:'+k+'\n') })
<pre id=O></pre>

\$\endgroup\$
2
  • \$\begingroup\$ What I need to do is splitting s in rows, then add an empty row at top and an empty row at bottom. Your code does not split s at all. You could do ["",...s.split("\n"),""] that is longer @ETHproductions \$\endgroup\$
    – edc65
    Sep 19, 2015 at 23:17
  • \$\begingroup\$ Ah, right, sorry about that. \$\endgroup\$ Sep 20, 2015 at 16:22
3
\$\begingroup\$

Python 2, 123

l=[]
i=p=t=0
for c in input():
 l+=0,;h=c in'-+';t+=h>p;p=h;v=c in'|+';t+=v>l[i];l[i]=v;i+=1
 if' '>c:l=l[:i];i=0
print t*2

A one-pass method. Takes as input a string with linebreaks.

For the horizontals, the idea is to count the number of horizontal segments, each of which has two endpoints. A segment starts whenever a character is one of +- (boolean h) but the previous one isn't (boolean p).

For the verticals, we'd like to do the same thing on the transposed input, looking at runs of +|. Unfortunately, Python's transposing is really clunky. It requires something like map(None,*s.split('\n')) to fill in the blanks with None, which are also themselves to deal with.

Instead, we do the vertical count while iterating horizontally. We keep a list l of which columns indices are still "running", i.e. where that the previous character in that column connects down. Then, we do the same thing as with the horizontal, counting newly starting vertical segments. When we hit a newline, we cut off the list right of where we are, since all segments to the right were broken, and reset the current index to 0.

\$\endgroup\$
3
\$\begingroup\$

CJam, 66 62 61 bytes

q_N/_z,S*f.e|zN*"-|++"2$fe=1b"|-"{'++:R:a@+2ew{aR2m*&},,-}/2*

Try it online in the CJam interpreter.

Idea

We can compute the endpoints as follows:

  1. Count the number of -s, |s and +s in the input.
  2. Multiply the last one by 2 and add the results.
  3. Count the number of --s, -+s, +-s and ++s in the rows.
  4. Count the number of ||s. |+s, +|s and ++s in the columns.
  5. Subtract the results from 3 and 4 from the result from 2.
  6. Multiply the result from 5 by 2.

Code

q        e# Read all input from STDIN.
_N/      e# Push a copy and split it at linefeeds.
_z,      e# Count the number of rows of the transposed array.
         e# This pushes the length of the longest row.
S*       e# Push a string of that many spaces.
f.e|     e# Perform vectorized logical OR with the rows.
         e# This pads all rows to the same length.
zN*      e# Transpose and join, separating by linefeeds.
"-|++"   e# Push that string.
2$       e# Copy the original input.
fe=      e# Count the occurrences of '-', '|', '+' and '+' in the input.
1b       e# Add the results.
"|-"{    e# For '|' and '-':
  '++    e#   Concatenate the char with '+'.
  :R     e#   Save the resulting string in R.
  :a     e#   Convert it into an array of singleton strings.
  @      e#   Rotate one of the two bottom-most strings on top of the stack.
         e#   This gets the transposed input for '|' and the original input for '-'.
  +      e#   Concatenate both arrays.
         e#   This pads the input with nonsense to a length of at least 2.
  2ew    e#   Push a overlapping slices of length 2.
  {      e#   Filter the slices; for each:
    a    e#     Wrap it in an array.
    R2m* e#     Push the second Cartesian power of R.
         e#     For '|', this pushes ["||" "|+" "+|" "++"].
    &    e#     Intersect.
  },     e#   If the intersection was non-empty, keep the slice.
  ,      e#   Count the kept slices.
  -      e#   Subtract the amount from the integer on the stack.
}/       e#
2*       e# Multiply the result by 2.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.