36
\$\begingroup\$

Overview

Given a 3 line string, figure out if the structure falls to the left, balances, or falls to the right.

Input structure

You can imagine the structure as metal rod with stuff on top, all being balanced on top of a vertical rod.

1  7 4        a
===============
        |

The first line is the items. Each item's weight is calculated as the ascii value of the character minus 32. (Characters under 32 aren't considered and spaces weigh 0). Keep in mind that an item's force on the rod is its weight times the distance to the pivot point.

The second line is the rod. Each length of rod weighs 1 unit by itself. This line is exclusively equals signs (=).

The third line is the pivot point. This can be placed anywhere, and is represented by a number of spaces followed by a single pipe (|) character.

Examples

Input:


=====
  |

Output: Balance

Input:


=====
   |

Output: Falls left

Input:

    %
=====
   |

Output: Balance (Because % weighs enough to counteract the weight of the left side of the rod)

Input:

 a    a
=======
   |

Output: Falls right (because the a on the right is further away from the pivot point)

Input:

1  7 4        A
===============
        |

Output: Falls left

Input:

1  7 4        a
===============
        |

Output: Falls right (lower case letters are heavy!)

Input:

            $ ~
===============
             |

Output: Balance

Notes

  • Trailing whitespace is permitted, leading whitespace is not.
  • Your program may output in whatever format you like, as long as there are 3 distinct outputs for left, balance, and right.
  • Your program must accept the format shown as input.
\$\endgroup\$
  • 1
    \$\begingroup\$ Closely related \$\endgroup\$ – Luis Mendo Jul 3 '17 at 23:23
  • \$\begingroup\$ May the program take the three lines as three separate strings (e.g. as three arguments to a function or as a three element list)? \$\endgroup\$ – notjagan Jul 3 '17 at 23:39
  • \$\begingroup\$ @notjagan The input must be a single string separated by new line characters. \$\endgroup\$ – Daffy Jul 3 '17 at 23:40
  • \$\begingroup\$ Related, possible dupe. \$\endgroup\$ – xnor Jul 4 '17 at 0:08
  • \$\begingroup\$ @xnor Not a dupe because that question deals with only uppercase letters, and its goal is to find the pivot. My question is about all ascii characters >= 32, and mine supplies the pivot and asks if the structure will fall over. Essentially the reverse of the one you linked. \$\endgroup\$ – Daffy Jul 4 '17 at 0:18

15 Answers 15

8
\$\begingroup\$

JavaScript (ES6), 116 111 108 106 bytes

-5 bytes by summing via eval(array.join`+`) instead of array.reduce().
-3 bytes by defaulting to 1 instead of 32 - 31, allowing parentheses to be removed.
-2 bytes since pivot point is the length of the last line - 1

(s,[t,r,b]=s.split`
`)=>Math.sign(eval([...r].map((_,i)=>(t.charCodeAt(i)-31||1)*(i-b.length+1)).join`+`))

Outputs -1, 0, or 1, for left, balanced, or right, respectively. Ended up similar to Chas Brown's python answer, so credit goes there.

Can save 4 bytes if the first line is padded to match the length of the rod by using
(31-t.charCodeAt(i))*(b.length+~i).

Test Snippet

Includes additional output (Left/Balanced/Right) along with the number.

f=
(s,[t,r,b]=s.split`
`)=>Math.sign(eval([...r].map((_,i)=>(t.charCodeAt(i)-31||1)*(i-b.length+1)).join`+`))
<textarea id=I rows=3 cols=20></textarea><br><button onclick="O.value=I.value?`${x=f(I.value)} (${['Left','Balanced','Right'][x+1]})`:''">Run</button> <input id=O disabled>

Another 106 byte method

(s,[t,r,b]=s.split`
`)=>Math.sign(eval(r.replace(/./g,(_,i)=>"+"+(t.charCodeAt(i)-31||1)*(i-b.length+1))))

Instead of joining an array on +s, we create a string of numbers, each prefixed by +. The leading + gets ignored.

\$\endgroup\$
  • 1
    \$\begingroup\$ I think (b.length+~i) might help to save a byte. (Also I don't understand why you have the ||1.) \$\endgroup\$ – Neil Jul 4 '17 at 10:31
  • 1
    \$\begingroup\$ @Neil b.length+~i returns the negative of i-b.length+1; that could help if I could negate the other part. As for the ||1, that was because I was assuming the first line wasn't padded to match the length of the rod, so t.charCodeAt(i) would return NaN beyond the end of the first line. \$\endgroup\$ – Justin Mariner Jul 4 '17 at 16:20
  • \$\begingroup\$ I hadn't though to try a non-padded test case; thanks for explaining. \$\endgroup\$ – Neil Jul 4 '17 at 16:33
3
\$\begingroup\$

Python 2, 112 110 bytes

def f(s):w,b,p=s.split('\n');return cmp(sum((ord((w+' '*-~i)[i])-31)*(i-p.find('|'))for i in range(len(b))),0)

Try it online!

EDIT: Finally managed to eliminate the enumerate and rjust for a measly 2 bytes... meh!

Takes in a string; outputs -1,0, or 1 for falls left, balances, falls right, respectively.

First pass at 112 bytes was:

def f(s):w,b,p=s.split('\n');return cmp(sum((ord(c)-31)*(i-p.find('|'))for i,c in enumerate(w.rjust(len(b))),0)
\$\endgroup\$
  • \$\begingroup\$ (ord(c)-31) Took me a while to realize that this is actually incorporating the weight of the rod itself along with the items. Very clever! \$\endgroup\$ – Daffy Jul 3 '17 at 23:54
  • 1
    \$\begingroup\$ As per meta, you can replace return with print for -1 byte (although it doesn't really play nicely with the current TIO code). \$\endgroup\$ – notjagan Jul 3 '17 at 23:58
3
\$\begingroup\$

Haskell, 212 171 bytes (188 if take input as one string)

o!p=map(fst)(zip[p-0,p-1..]o)
x#p=sum(zipWith(\c w->(max(fromEnum c-32)0)*w)x(x!p))+sum(x!p)
x?c=length(takeWhile(==c)x)

171 bytes variant

r a b c=signum(take(b?'=')(a++repeat ' ')#(c?' '))

188 bytes variant

x%y=lines x!!y
r i=signum(take(i%1?'=')(i%0++repeat ' ')#(i%2?' '))

Explanation

o!p=map(fst)(zip[p-0,p-1..]o)        Creates weights coefs list. 
                                     o - list, p - pivot position
                                     for list "abcdf" and p=3 (pivot under 'd')
                                     outputs [3,2,1,0,-1]

x#p                                  Calculates total balance
                                     x-list of "objects" on lever, p-pivot place
  sum(zipWith                        sum of zipped lists
   (\c w->(max(fromEnum c-32)0)*w)   weight of ascii "object" times
                                     distance from pivot
    x(x!p))                          x-ascii objects, 
                                     (x!p)-distances list(weight coefs)
  +sum(x!p)                          balance of lever ("==") itself

x?c=length(takeWhile(==c)x)          length of list before non c element met
                                     used to find '|' position
                                     and length of "===" lever
                                     before right whitespaces met

r a b c=                             Sums it all up =)
                                     a-ascii objects, b-lever, c-pivot line
   signum(                           1-tips left, 0-balance, -1-tips right
     take(b?'=')(a++repeat ' ')      takes all object on lever 
                                     plus whitespaces up to length of the lever
      #                              calculate the balance
       (c?' ')                       determine place of pivot
\$\endgroup\$
  • 1
    \$\begingroup\$ You can use fromEnum instead of ord and drop the import. c can be simplified to c p=max(ord p-32)0 (or with fromEnum) and as you are using it only once, inline it. \$\endgroup\$ – nimi Jul 6 '17 at 17:18
  • \$\begingroup\$ Or you could add (Lambdabot) to your title, this imports pretty much everything you need, see here. \$\endgroup\$ – ბიმო Jul 7 '17 at 2:10
  • 1
    \$\begingroup\$ The function c can even be simplified (characters under 32 aren't considered) further to c p=ord p-32. Also p is basically length (minus 1), so p x=length x-1 would work too (and you can inline it too). Also have a look at my solution, how I use signum - you could do r o l s = signum $ 2 * z ... which returns 0,1,-1 for B,L,R. \$\endgroup\$ – ბიმო Jul 7 '17 at 2:27
  • 1
    \$\begingroup\$ Apart from, that this solution seems to fail test cases [3,4,7] and takes 3 Strings instead of one. (see lines). \$\endgroup\$ – ბიმო Jul 7 '17 at 2:33
  • 1
    \$\begingroup\$ Here's a version with a few tips applied (saves you 29 bytes ;)). \$\endgroup\$ – ბიმო Jul 7 '17 at 16:26
2
\$\begingroup\$

Jelly, 30 bytes

O_31×J_¥A+\sṪ€µ÷ḢṪ_2Ṡ
ỴṪLç@ỴḢ$

Test Suite

Outputs 0 for balanced, 1 for right, and -1 for left.

How it Works

O_31×J_¥A+\sṪ€µ÷ḢṪ_2Ṡ - helper function. Arguments position of pivot and first line
O                        - char codes of first line
 _31                     - subtract 31 to get weight
    ×                    - elementwise product with:
     J_¥                 - distances from the pivot
        A                - absolute value
         +\              - cumulative sum
           s             - split to get [[...,left weight],...,[..., right + left weight]]
            Ṫ€           - last element of each sublist: [left weight, ... right weight]
              µ÷Ḣ        - get ratio of each element over left weight: ratio n indicates
                              right + left = n × left ===> right = left if n = 2
                 _2      - subtract 2: positive is right>left and negative is right<left
                   Ṡ     - return the sign of this


ỴṪLç@ỴḢ$              - main link. Argument: 3 line string.
   ç@                  - apply helper function with arguments:
Ỵ                        - split by linefeeds
 Ṫ                       - last line
  L                      - length (returns position of pivot)
       $               - and
     Ỵ                   - split by linefeeds
      Ḣ                  - first line              
\$\endgroup\$
2
\$\begingroup\$

Jelly, 24 bytes

ṪO_31
ỴµṪLạЀṪL$×Çṣ0S€IṠ

Try it online!

-1 for falling left, 0 for balancing, 1 for falling right (full program).
[-1] for falling left, [0] for balancing, [1] for falling right (function).

First line must have trailing spaces, last line must not.

Explanation (we start with bottom line):

First of all, we're working with individual lines, so we need to somehow get them. That's a job for . Then, we need to treat the \n-split version of the input as if it was the original input, so we use µ to make a monadic chain applied to the current value.

Now we begin real work, and our first job would be computing the factors of the weights. Essentially this is a range [distance from far left to pivot..0..distance from pivot to far right]. First of all, we have to find the 1-based index of the pivot, which is essentially the length of the last line without trailing spaces. So we pop the last line (pivot line) from our original list with , since we won't need it anymore, and then we take its length with L. We then need to take the length of the rod, for which we do the same thing to the now-last line (rod line) with ṪL$. Finally, to get the range, we map |x - y| to [1..rod length], where x is the pivot index and y is each element of the list we map upon. We do this using ạЀ, where calculates |x - y| and Ѐ makes a range from 1 up to and including the rod length. Now we'll have the range we want.

After that, we have to multiply each integer, representing a piece of the rod, with its corresponding weight. To calculate the weights, we use Ç, going to the top line of our code. We take the remaining line with , its charcodes with O, and then we calculate x - 31 using _31, x being each charcode. We then assign space to weight 1 (0 + rod piece = 1), ! to weight 2 (1 + 1) etc. We're done with the top line, so now Ç would return the list of weights, which we multiply with the corresponding integers representing the rod pieces with ×.

After that, we split with ṣ0 on the pivot point, represented by a 0 (since any weight there won't affect the result), resulting in a list of the form [[1st weight, 2nd weight...weight just before pivot], [weight just after pivot, weight after the one before...last weight]]. Those lists represent the sides of the rod, left and right. We now sum each of the lists using S€ to get the total weights on each side, and use I to take the delta, which will be negative if left side is heavier, zero if they're equal-weighted, and positive if right side is heavier. So, to return the final result using this properly to our advantage, we take the sign with .

\$\endgroup\$
2
\$\begingroup\$

APL (Dyalog), 43 bytes*

{×(¯31+⎕UCS⊃⍵)+.×(⍳≢⊃⍵)-'|'⍳⍨⊃⌽⍵}⎕TC[2]∘≠⊆⊢

Try it online!

⊆⊢ partition the argument into runs of characters that are

⎕TC[2]∘≠ different from the 2nd Terminal Control character (Linefeed)**

{} apply the following anonymous function on the list of strings:

⊃⌽⍵ in the first string of the reversed list (i.e. the last)

'|'⍳⍨ find the ɩndex of the pivot point

()- subtract that from the following list:

  ⊃⍵ the first string

   its length

   all the ɩndices of that

()+.× weighted sum with those weights and the following values:

  ⊃⍵ the first string

  ⎕UCS code points in the Universal Character Set

  ¯31+ add negative thirty-one (32 for the required offset minus one for the rod)

× signum of that


* For 1 byte per char, use {×(¯31+⎕UCS↑⍵)+.×(⍳≢↑⍵)-'|'⍳⍨↑⌽⍵}⎕TC[3]∘≠⊂⊢ with ⎕ML←3. Try it online!
** ⎕TC is deprecated and used here only for golfing purposes. In production code, one should use ⎕UCS 10.

\$\endgroup\$
2
\$\begingroup\$

Haskell (Lambdabot), 142 bytes

l=length
g[a,c,b]=splitAt(l b)$a++(' '<$[1..l c-l a])
k e=sum$zipWith((*).(-31+).ord)e[1..]
f=signum.uncurry(-).(k.drop 1.reverse***k).g.lines

Try it online!

Ungolfed version:

-- for readability, allows reading top-down/left-right
(.>) = flip (.)

ungolfed =
     lines                                 -- separate out lines into..
  .> (\[a,b,c] ->                          -- a,b,c (first,second,third)
                                           -- ' ' pad the first line & split on pivot
       splitAt (length c) (a ++ replicate (length b - length a) ' ')
     )
  .> (weight.drop 1.reverse *** weight)    -- reverse left half, drop element above pivot & get weight for both
  .> uncurry (-)                           -- subtract right from left
  .> signum                                -- get sign

-- get ord of the character subtract 31 ('=' char from bar),
-- then multiply with scales ([1..]) and sum it all up
weight es = sum $ zipWith (ord .> subtract 31 .> (*)) es [1..]
\$\endgroup\$
2
\$\begingroup\$

Python 2, 90 bytes

def f(s):L=len(s)/3;print cmp(sum((ord(s[i])-31)*(i-s[-L:].find('|'))for i in range(L)),0)

Expects input lines to be padded (with spaces) to the correct length. Outputs -1 for falls left, 0 for balanced, and 1 for falls right.

Try it online!


94 bytes

For +4 bytes we can have a version which, using a while loop, requires stripped lines rather than padded lines:

def f(s):
 i=r=0
 while''<s[i]:r+=(ord(s[i])-31)*(i-s[-3::-1].find('='));i+=1
 print cmp(r,0)

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Ruby, 543 bytes

def willittip(s)
leftw=0;
rightw=0;
arr=[];
fields=s.split("\n")
pos=fields[2].index("|")
fields[0].split("").each do |i|
arr << i.ord-32
end
arr[pos+1..-1].each_with_index do |x,y|
rightw=rightw+1
if x>0
if pos>0
rightw=rightw+x*(pos-y).abs
else
rightw=rightw+x
end
end
end
arr[0..pos-1].each_with_index do |x,y|
leftw=leftw+1
if x>0
if pos>0
leftw=leftw+x*(pos-y).abs
else
leftw=leftw+x
end
end
end
if leftw==rightw
return "Equal"
elsif leftw<rightw
return "Right"
elsif leftw>rightw
return "Left"
end
end
\$\endgroup\$
  • 10
    \$\begingroup\$ Welcome to PPCG! :D The goal for code-golf challenges is to make your code as small as possible. You can reduce your code size by making all variables and function names a single character, and by deleting whitespace wherever possible. \$\endgroup\$ – Daffy Jul 4 '17 at 8:40
1
\$\begingroup\$

C (gcc), 106 107 121 123 124 129 131 bytes

c,b,l,i;f(char*a){l=strlen(a)/3;for(i=b=c=0;32/a[l*2+c];++c);for(;i<l-1;b+=(a[i]-31)*(i++-c));a=b>0?2:!b;}

Return 0 for falling left, 1 for balance and 2 for falling right.

Require all three lines to have same length and finish with \n to determine the length of string.

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Mathematica, 91 92 bytes

Sign[(v=(g=ToCharacterCode)@#&@@(d=#~StringSplit~"
")-31).(Range@(l=Length)@v-l@g@Last@d)]&

The first line should have the same length with the rod. The third line should contain no trailing spaces.

Return -1, 0, 1 for falling left, balance and falling right.

\$\endgroup\$
1
\$\begingroup\$

C# (.NET Core), 127 95 90 + 18 = 108 bytes

For this function the first line must be right padded with spaces to be the same length as the rod and the third line must not have trialing spaces. This conditions are allowed (see comments of the question).

s=>s.Split('\n')[0].Select((c,i)=>(c-31)*(i-s.Split('\n')[2].Length+1)).Sum().CompareTo(0)

Try it online!

Outputs:

-1 for tip left
0 for balance
1 for tip right

\$\endgroup\$
1
\$\begingroup\$

Python 3, 217 bytes

Also works in Python 2.7

def f(s):i,b,p=s.split('\n');c=p.find('|');l=sum((ord(e)-32)*(c-i.find(e))for e in i[:c])+sum(x for x in range(1,c+1));r=sum((ord(e)-32)*i[c:].find(e)for e in i[c:])+sum(x for x in range(len(b[c:])));return(l>r)-(r>l)

Returns 1 for left-side, -1 for right-side, or zero if balanced.

Readable version:

def f(s):
    i,b,p = s.split('\n')
    c = p.find('|')

    l = sum((ord(e)-32)*(c-i.find(e))for e in i[:c])+sum(x for x in range(1, c+1))
    r = sum((ord(e)-32)*i[c:].find(e)for e in i[c:])+sum(x for x in range(len(b[c:])))

    return(l>r)-(r>l)
\$\endgroup\$
  • 1
    \$\begingroup\$ You don't need sum([...]), you can simply have sum(...) \$\endgroup\$ – Mr. Xcoder Jul 4 '17 at 5:26
  • \$\begingroup\$ @Daffy this should be 100% conformant to your specification and all given example inputs. If you agree please let me know so that I might further optimize it. Thank you. \$\endgroup\$ – veganaiZe Jul 6 '17 at 3:08
  • \$\begingroup\$ @veganaiZe Passed all of my tests, looks good! :) \$\endgroup\$ – Daffy Jul 6 '17 at 3:20
  • 1
    \$\begingroup\$ Stuff to shorten it: i[c:].find(e) can be i.find(e,c), use i,m,n=s.split('\n') and avoid the need for s at all, use return 2*(r>l) or l>r to dramatically reduce test cost at end (return value is numerically equivalent, but it's True instead of 1 and False instead of 0), or really, use a different set of return values and do return (l>r)-(r>l) to return the 1, 0 or -1 the way the old cmp function did. \$\endgroup\$ – ShadowRanger Jul 6 '17 at 4:30
  • \$\begingroup\$ Thanks ShadowRanger , Mr. Xcoder, and Daffy! @ShadowRanger I had to stick with the i[c:] because the shorter way caused a weird off-by-one issue for some corner-case input (try placing a | exactly in the middle -- above the bar). \$\endgroup\$ – veganaiZe Jul 7 '17 at 2:03
1
\$\begingroup\$

PHP, 105 bytes

for([$a,$b,$c]=explode("
",$argn);$b[$i];)$w+=(strpos($c,"|")-$i++)*8*(max(1,ord($a[$i])-31));echo$w<=>0;

prints -1/0/1 for left/balance/right. Run as pipe with -nR or try it online.

breakdown

for([$a,$b,$c]=explode("\n",$argn); # import input
    $b[$i];)                        # loop through bar
    $f+=                                # add to force:
        ($i-strpos($c,"|"))             # distance (<0 if left, >0 if right of pivot)
        *8                              # *8
        *(max(1,ord($a[$i++])-31));     # *weight
echo$f<=>0;                         # print -1 if $f<0, 1 if $f>0, 0 if $f==0
\$\endgroup\$
1
\$\begingroup\$

Charcoal, 31 bytes

A⁰ξFLθA⁺ξ×⁻ι⌕ζ|⁻℅§θι³¹ξI⁻›ξ⁰‹ξ⁰

Try it online! Link is to verbose version of code. Outputs 0 for balance or -1 or 1 for falling left or right. Edit: Changes in Charcoal now mean that ≔ΣEθ×⁻κ⌕ζ|⁻℅ι³¹ξI⁻›ξ⁰‹ξ⁰ works for 24 bytes: Try it online! Link is to verbose version of code. Note: Both answers require padded input, but can be adapted to accept unpadded input at a cost of 3 bytes: ≔⁰ξFLη≔⁺ξ×⁻ι⌕ζ|⁻℅§◨θLηι³¹ξI⁻›ξ⁰‹ξ⁰ Try it online! ≔ΣE◨θLη×⁻κ⌕ζ|⁻℅ι³¹ξI⁻›ξ⁰‹ξ⁰ Try it online! Links are to verbose version of code.

\$\endgroup\$
  • \$\begingroup\$ You may want to mention this this expects the input lines to be padded to the correct length with spaces, so an unpadded input might not work. \$\endgroup\$ – FlipTack Dec 26 '17 at 10:59
  • \$\begingroup\$ @FlipTack Better still, I've devised versions that accept unpadded input. \$\endgroup\$ – Neil Dec 26 '17 at 11:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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