39
\$\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.
  • This is so the shortest code in bytes wins
\$\endgroup\$
7
  • 1
    \$\begingroup\$ Closely related \$\endgroup\$
    – Luis Mendo
    Commented Jul 3, 2017 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
    Commented Jul 3, 2017 at 23:39
  • \$\begingroup\$ @notjagan The input must be a single string separated by new line characters. \$\endgroup\$
    – Daffy
    Commented Jul 3, 2017 at 23:40
  • \$\begingroup\$ Related, possible dupe. \$\endgroup\$
    – xnor
    Commented Jul 4, 2017 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
    Commented Jul 4, 2017 at 0:18

16 Answers 16

9
\$\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\$
3
  • 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
    Commented Jul 4, 2017 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\$ Commented Jul 4, 2017 at 16:20
  • \$\begingroup\$ I hadn't though to try a non-padded test case; thanks for explaining. \$\endgroup\$
    – Neil
    Commented Jul 4, 2017 at 16:33
5
\$\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\$
2
  • \$\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
    Commented Jul 3, 2017 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
    Commented Jul 3, 2017 at 23:58
4
\$\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\$
4
\$\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\$
8
  • 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
    Commented Jul 6, 2017 at 17:18
  • \$\begingroup\$ Or you could add (Lambdabot) to your title, this imports pretty much everything you need, see here. \$\endgroup\$ Commented Jul 7, 2017 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\$ Commented Jul 7, 2017 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\$ Commented Jul 7, 2017 at 2:33
  • 1
    \$\begingroup\$ Here's a version with a few tips applied (saves you 29 bytes ;)). \$\endgroup\$ Commented Jul 7, 2017 at 16:26
4
\$\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\$
0
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\$

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\$
1
  • 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
    Commented Jul 4, 2017 at 8:40
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\$
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
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\$
5
  • 1
    \$\begingroup\$ You don't need sum([...]), you can simply have sum(...) \$\endgroup\$
    – Mr. Xcoder
    Commented Jul 4, 2017 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
    Commented Jul 6, 2017 at 3:08
  • \$\begingroup\$ @veganaiZe Passed all of my tests, looks good! :) \$\endgroup\$
    – Daffy
    Commented Jul 6, 2017 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\$ Commented Jul 6, 2017 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
    Commented Jul 7, 2017 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\$
2
  • \$\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
    Commented Dec 26, 2017 at 10:59
  • \$\begingroup\$ @FlipTack Better still, I've devised versions that accept unpadded input. \$\endgroup\$
    – Neil
    Commented Dec 26, 2017 at 11:19
0
\$\begingroup\$

Jelly, 18 bytes

ỴµḢO_31×Ẉạ€/$ṣ0§IṠ

Try it online!

This started as a golf of Erik's answer (so be sure to go upvote it), but it developed to be different enough, and Erik is no longer active on the site, that I thought posting a separate answer would be fine.

Outputs [0] for balanced, [1] for tipping right and [-1] for tipping left

How it works

ỴµḢO_31×Ẉạ€/$ṣ0§IṠ - Main link. Takes a string S on the left
Ỵ                  - Split S into a list of lines [I, R, P]
 µ                 - Use this list as the arguments
  Ḣ                - Pop and yield I
   O               - Convert to ordinals
    _31            - Subtract 31, mapping space to 1, ! to 2 etc.
            $      - Previous 2 links as a monad f([R, P]):
        Ẉ          -   Get the length of each
           /       -   Reduce by:
          €        -     Convert len(R) to a range and over each element:
         ạ         -       Take the absolute difference with len(P)
       ×           - Multiply these lists together
             ṣ0    - Split at 0, the pivot point
               §   - Sum of each side
                I  - Difference between the sides
                 Ṡ - Sign
\$\endgroup\$

Your Answer

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

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