Counting the Endpoints of an ASCII Art

14

1

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.

randomra

Posted 2015-09-19T07:09:44.460

Reputation: 19 909

Answers

11

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 '+'

feersum

Posted 2015-09-19T07:09:44.460

Reputation: 29 566

5

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>

edc65

Posted 2015-09-19T07:09:44.460

Reputation: 31 086

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 – edc65 – 2015-09-19T23:17:42.177

Ah, right, sorry about that. – ETHproductions – 2015-09-20T16:22:37.860

3

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.

xnor

Posted 2015-09-19T07:09:44.460

Reputation: 115 687

3

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.

Dennis

Posted 2015-09-19T07:09:44.460

Reputation: 196 637