Add weight to one side of a see-saw to balance it

13

Balancing Act

A see-saw (supposedly from the French 'ci-ça', meaning 'this-that') forms a third of the holy trinity of playground equipment, along with the similarly ubiquitous slide and swing. A see-saw is in perfect balance if, and only if, the sum of the moments on each side are equivalent. A see-saw can therefore be balanced by adding a specific quantity of weight to the side with the lower moment sum; achieving this is your goal for this challenge.

Challenge

Your challenge is to take a depiction of a see-saw as input and output it again, with weight added to one end of the see-saw to balance it.

Input

Your program must take, in any reasonable format, an ASCII see-saw such as the following:

100             100
-------------------
         ^         

The first line contains two numbers, each representing weights on the see-saw. Exactly one weight is present on each side, each acting on the very end of its side of the plank. Weights are guaranteed to be integers, and always align with their corresponding end of the plank. These numbers will never overlap the fulcrum (^).

The second line represents the 'plank' of the see-saw. Each dash (-) represents an equal length to each other dash, with the sole exception of the dash directly over the fulcrum (^), which has no length.

The third line represents the fulcrum of the see-saw. This fulcrum is marked by the only character that is not a space on this line, a circumflex ('^'). The fulcrum can be positioned anywhere along the length of the plank in a valid input so long as enough space is left so that the numbers representing weights do not overlap the fulcrum in either the input or the output.

The input is guaranteed to have three lines, and have no white-space prior to or after the characters that constitute the see-saw (excepting, of course, the third line, which requires it).

Output

For output, the same see-saw depiction should be printed to stdout, but with one (and only one) of the weights replaced with a larger weight, so as to balance the see-saw. Inputs are guaranteed to make this possible using integers alone. Therefore, weights must be shown without decimal points or any other similar notations. If your language does not use stdout you should go by community / meta consensus on output. Trailing newlines are fine but any other changes to the depiction format are probably not OK.

Exemplification

Test Inputs and Corresponding Outputs

Input 1

12                22
--------------------
             ^      

Output 1

12                26
--------------------
             ^      

Input 2

42       42
-----------
     ^     

Output 2

42       42
-----------
     ^     

Input 3

3             16
----------------
        ^      

Output 3

14            16
----------------
        ^      

Input 4

1                56
-------------------
    ^              

Output 4

196              56
-------------------
    ^              

Reference Implementation - Python 3

# Takes a list of strings as input
def balance_seesaw(lines):
    weights = [int(w.strip()) for w in lines[0].split()]

    length  = len(lines[1])
    pivot   = lines[2].find("^")
    left_length    = pivot
    right_length   = length - 1 - pivot

    left_torque  = weights[0] * left_length
    right_torque = weights[1] * right_length

    if left_torque > right_torque:
        weights[1] = left_torque // right_length
    elif right_torque > left_torque:
        weights[0] = right_torque // left_length

    weights = [str(w) for w in weights]

    string_gap = " " * (length - sum(len(w) for w in weights))
    lines[0] = weights[0] + string_gap + weights[1]

    print("\n".join(lines))

balance_seesaw(["1                56",
                "-------------------",
                "    ^              "])

Rules

  • This is , so the shortest code wins counted in bytes. Check meta if counting bytes is awkward in your language.

  • Standard rules/loopholes apply.

  • Input must be taken in a reasonable format. A non-exhaustive list of appropriate formats are given as follows:

    • A single string with lines separated by newline characters
    • A list of strings, each string represented a line
    • A 2D Array or Matrix of characters

Related Challenges


FourOhFour

Posted 2016-11-12T12:38:25.763

Reputation: 241

Is there any reason you want output to stdout? We generally allow functions to output via their return value. – corvus_192 – 2016-11-12T19:40:24.810

@corvus_192 I envisaged this as a 'display' type challenge, like an ASCII art one or 'Draw a flag' or whatever. A list of strings as output isn't really 'human friendly' as such. If a language has no inbuilt stdout support other output forms are allowed though. – FourOhFour – 2016-11-12T19:57:31.230

Welcome to PPCG! Nice first challenge. (and props for using the sandbox on it, too!) – AdmBorkBork – 2016-11-13T19:42:29.187

@TimmyD thanks, it's been good fun seeing how people tackle the problem. – FourOhFour – 2016-11-13T19:51:19.590

Answers

5

05AB1E, 60 51 50 49 47 45 bytes

Saved 10 bytes thanks to Emigna and 1 byte thanks to Adnan.

All input lines must have the same amount of characters.

#õKD³'^¡€gDŠ*¬-Os÷1®‚*D0›*+¬?DJg²gs-ð×?¤,²,³,

#                                             Split the first input line on spaces
 õKD                                          Push [first weight, second weight] twice
    ³'^¡€gD                                   Push both lengths from either side of the pivot '^' as an array [left, right] twice
           Š*                                 Multiply by weights to get torque
             ¬-O                              Evaluate rightTorque-leftTorque
                s÷                            Divide by each side's length to get the weights to add: [deltaLeft, deltaRight], keep integer values
                  1®‚                         Push [1,-1]
                     *D                       Yield [deltaLeft, -deltaRight]
                       0›*                    Replace the negative value by 0
                          +                   Add weights: old + deltaWeight
                           ¬?                 Print left weight
                             DJg              Take the size of total decimal representation
                                ²gs-ð×?       Print a string composed of filler spaces between both new weights
                                       ¤,     Print right weight and newline
                                         ²,³, Print the last two lines from input (unchanged)

Try it online!

There should be a rule of thumb, like "if your 05AB1E code is longer than 40 bytes, you're probably doing it wrong". It seems so golfable, any idea is welcome!

Osable

Posted 2016-11-12T12:38:25.763

Reputation: 1 321

Good answer! It's pretty unintelligible to me, but it passes every test I could think of :) – FourOhFour – 2016-11-12T14:30:14.220

1For a start ¬s¤s\‚ can be õK. – Emigna – 2016-11-12T14:34:26.630

I'm currently editing my answer to add an explanation, but it's still like dark magic. I'm waiting for a two-byte long answer in another esolang. And welcome by the way :) . – Osable – 2016-11-12T14:35:32.087

@Osable Well, I checked esolang wiki and I'm pretty sure there isn't a language in which an empty file balances an ASCII see-saw.... but one can never be certain! – FourOhFour – 2016-11-12T14:38:57.047

1kD²g->(‚ can be ¡€g if you add the missing spaces in bottom row of the test case – Emigna – 2016-11-12T14:39:58.657

Thanks for golfing my code, I'm not very familiar with accented letters and euro symbol in 05AB1E – Osable – 2016-11-12T15:00:14.083

You have a typo: tumb – user41805 – 2016-11-12T15:21:20.907

1Thanks for the explanation. I see it's quite similar to the reference algorithm (no bad thing) but there's some clever tricks in there too. Something about 05AB1E means that it seems to promote cleverer answers than some other golfing languages - it's perhaps my favourite especially when there's an included explanation. – FourOhFour – 2016-11-12T16:54:35.510

With 05AB1E I'm used to avoiding conditional statements as often as possible because they generally increase code size. So I have to think about other ways to mathematically encompass the if/else statements. However I haven't tried to use such statements - like in the reference algorithm -. It might have been shorter but it isn't very likely. I'm no 05AB1E pro though, I always write code as it comes to my mind. – Osable – 2016-11-12T18:27:21.667

1Nice answer! You can replace 31SÍ with 1®‚ :) – Adnan – 2016-11-12T22:17:15.073

Thanks! I didn't know that register_c evaluates to -1 when left uninitialized. – Osable – 2016-11-12T22:44:52.460

1Can you perhaps also replace / ï with ÷.? – Emigna – 2016-11-13T10:27:57.243

Correct! I don't know why I didn't think about it when I wrote the code. Now we are below 50 bytes! – Osable – 2016-11-13T14:48:54.523

I also just figured out that at least two bytes can be saved if the first line is built in order, instead of first computing the filler spaces. It saves two swap operations. – Osable – 2016-11-13T14:56:14.913

5

JavaScript (ES6), 136

Probably not working in Chrome,as it uses destructured assignment and default parameters.

Note that the standard JS output method alert is particularly unsuited for the task, because of the proportional font used.

(m,n,o,[p,q]=m.split(/ +/),l=n.length,h=o.indexOf`^`,g=l-h-1,c=p*h<q*g?q*g:p*h)=>alert((c/h+o).slice(0,h)+(o+c/g).slice(h-l)+`
${n}
`+o)

Less golfed

( m,n,o, // input parameters, 3 strings
  // default parameters used as local variables
  [p,q] = m.split(/ +/), // left and right weight
  l = n.length, // bar length
  h = o.indexOf`^`, // left length
  g = l-h-1, // right length
  // p*h left torque
  // q*g right torque
  c = p*h<q*g ? q*g : p*h // max torque
) => alert( (c/h+o).slice(0,h)+(o+c/g).slice(h-l) // o has enough spaces to pad left and right
     +`\n${n}\n`+o )

Test

F=
(m,n,o,[p,q]=m.split(/ +/),l=n.length,h=o.indexOf`^`,g=l-h-1,c=p*h<q*g?q*g:p*h)=>alert((c/h+o).slice(0,h)+(o+c/g).slice(h-l)+`
${n}
`+o)

function go()
{
  var [a,b,c]=I.value.split('\n')
  if(a.length!=b.length || a.length < c.length)
    alert('The strings are not of the same length')
  else 
  {  
    if (a.length > c.length)
      c = c+' '.repeat(a.length-c-length)
    F(a,b,c)
  }  
}
<textarea id=I>3             16
----------------
        ^      </textarea>
<button onclick='go()'>go</button>

edc65

Posted 2016-11-12T12:38:25.763

Reputation: 31 086

According to http://kangax.github.io/compat-table/es6/, Chrome 54 fully supports default parameters and destructuring, so I don't think you have to worry too much.

– ETHproductions – 2016-11-13T04:22:44.717

Works on Chrome for me. – DLosc – 2016-11-15T02:29:15.267

3

Perl, 149+2=151 characters

Requires command line options -p0 (this gives me a 2 byte penalty on top of the 149 bytes in the program itself).

($_,$b,$c,$d)=map length,/(\d+) +(.+)
(-+)
( +)/;$r=$d/($c-$d-1);($x,$y)=$1*$r>$2?($1,$1*$r):($2/$r,$2);$_="$x$,$y",$,.=$"while$c>length;$\="
$3
$4^"

Explanation:

  • The -p0 switch reads the entire input up to the first NUL byte or EOF. This problem doesn't allow NULs, so we'll get the entire input in the variable $_ that's used for regexes, etc., by default.
  • We start with a regex that parses the input (between the first and second slash). There are several ways we could parse the first weight (e.g. .+?), but I can't get it below 3 characters so I may as well use the obvious \d+. The second number is at the end of the line so it can be parsed as .+ (2 characters). The central line is used to determine how wide the scales are; it's parsed as -+ (many other representations would work). The spaces before the caret on the last line are +. Once the caret (or indeed any nonspace) appears, we ignore the rest of the input.
  • Perl automatically captures the four groups of the regex (first weight, second weight, row of hyphens, spaces before the caret) into $1, $2, $3, $4. Giving a regex as an argument to map additionally uses an array of those groups as the array to map over. We therefore take their lengths; this is a convenient way to store the lengths of $3 and $4 without having to write length twice. We also overwrite $_ with the length of $1; we don't really care about the value of this (the number of digit in the left input is kind-of useless), but the fact that it's short ($_'s length is now the number of digits in the number of digits in the first weight, which is necessarily very small compared to the width of the scales).
  • We measure the ratio $r in which the scales are divided.
  • $1*$r>$2 checks to see which side is heavier. We store the new weights in $x and $y; these have very simple calculations once the ratio of weights is known.
  • We concatenate $x, $,, and $y into $_ to produce the top row, then keep adding spaces ($" contains a single space by default, and is shorter than a literal space ' ' would be) onto $, until it's the same length as the middle row (i.e. has length $c). (I chose the variable $, as it's a built-in variable that can safely be changed in this context and starts empty by default.) As length operates on $_ by default, we don't need to give it an argument explicitly. I used a Yoda conditional because it needs considerably less disambiguating syntax to parse correctly.
  • Finally, I redefine Perl's idea of the output line ending convention ($\) to contain the rest of the set of scales (which is the same as in the input, so I can simply use $3 and $4 directly to produce the bulk of it). Note that this means that there's no trailing whitespace on the third line; adding it would make the program slightly longer and doesn't seem to serve any purpose, so I left it out.
  • At the end of the program, the -p switch triggers again; this time, it outputs $_ followed by a "newline" ($\). Because I redefined the output newline, these two implicit prints generate the new set of scales between them (although as a side effect, there's no newline on the output).
  • The -p switch now tries to read input again, but we already slurped the entire file, so it reads EOF and ends the program.

user62131

Posted 2016-11-12T12:38:25.763

Reputation:

1

PHP, 212 209 205 bytes

probably golfable

preg_match("#(\d+)( +)(\d+)\s*(-+)[\r\n]+( +)\^#",$s=$argv[1],$m);echo preg_replace("#\d+( +)\d+#",(($r=$m[3])>($q=$m[1]*($p=strlen($m[5]))/(-$p-1+$e=strlen($m[4])))?$r*$e/($p+1)-$q=$r:$m[1]).$m[2].$q,$s);

Takes input from command line argument; escape newlines. Run with -r.


Replacing with a placeholder did not work as expected; so I had to add more parens to the first regex.

Titus

Posted 2016-11-12T12:38:25.763

Reputation: 13 814

1

Python 2, 184 183 bytes

Definitely golfable

i=raw_input
j=int
w=map(j,i().split())
W=len(i())
I=i().find('^')
R=W-I-1
a=[w[1]*R/I,w[0]*I/R]
h=a[1]>w[1]
w[h]=j(a[h])
k='\n'
print(' '*(W-len(str(w))+4)).join(map(str,w))+k+'-'*W+k+' '*I+'^'

Pretty straightforward. Just take the adjusted weights for adjusting both sides, see which one's larger than the original, and change that one, and output.

EDIT Switched multiplication and division because integer division is evile (thanks to @JonathanAllan for noticing this)

EDIT -1 byte Changed i().index('^') to i().find('^') (thanks to @JonathanAllan [again!])

HyperNeutrino

Posted 2016-11-12T12:38:25.763

Reputation: 26 575

You should swap the multiplication and division since the division is integer division - i.e. a=[w[1]*R/I,w[0]*I/R] (a simple example that would not work would be a 1 and 2 with I and R both 3). Currently 194 not 184 by the way since the newlines count as a byte each, but j and k are costing more bytes than they save. – Jonathan Allan – 2016-11-13T10:17:26.133

You can use I=i().find('^'), and the short form of __repr__, backticks to make the last line print`w[0]`+' '*(W-len(`w`)+4)+`w[1]`+'\n'+'-'*W+'\n'+' '*I+'^' and get down to 182 - https://repl.it/EW8f

– Jonathan Allan – 2016-11-13T10:41:40.923

1

Befunge, 223 217 bytes

&:00p&10p~$0>~#<2#+%#1_:20p0~>8#~%#+!#1_:3v
v\g01/g03*g01_v#!\g04`*g01g04:*g03p04-1-p0<
>#g>#0>#0>#/>#<:.2\5>5>#\+/#1:#\_$50p:50g\5>5>#\+/#1:#\_$20g\-v>
1#,>#*-#4:#8_$.55+,20g>:#,1#*-#9\#5_55+,30g>:#,1#*-#8\#4_"^",@>>

Try it online!

James Holderness

Posted 2016-11-12T12:38:25.763

Reputation: 8 298

215 bytes, I think – Zacharý – 2018-12-07T14:30:46.450

@Zacharý I'm afraid not. At least one of those arrows is needed, otherwise it'll fail whenever left torque > right torque (the first test case for example). The other > I think was just left for aesthetic reasons. That said, I do seem to have a 215 byte solution in my notes, so it may be possible (it my also have bugs which would explain why I never submitted it - don't have time to test it now). – James Holderness – 2019-04-23T01:38:55.173

0

C++ 14, 482 bytes

include<iostream>#include<string>#include<math.h>usingnamespacestd;intmain(){stringa,b,c,d;intj=0;inte[2];getline(cin,a);getline(cin,b);getline(cin,c);for(inti=0;i<a.size();i){if(isdigit(a.at(i))){while(i<a.size()&&isdigit(a.at(i))){d=a.at(i);i;}e[j]=stoi(d);d="";}}strings(b.size()-(int)log10(e[0])-(int)log10(e[1])-2,'');intl1=(c.size()-1);intl2=(b.size()-c.size());intl=e[0]*l1;intr=e[1]*l2;if(l>r)e[1]=l/l2;elsee[0]=r/l1;cout<<e[0]<<s<<e[1]<<endl;cout<<b<<endl;cout<<c;return0;}

more readable version:

#include <iostream>
#include <string>
#include <math.h>
using namespace std;
int main() {
    string a,b,c,d;
    int j=0;
    int e[2];
    // input
    getline(cin,a);// 1st line
    getline(cin,b);// 2nd line
    getline(cin,c);// 3rd line
    for (int i=0;i<a.size();i++) {
        if(isdigit(a.at(i))){
            while(i<a.size() && isdigit(a.at(i))){
                d+=a.at(i);
                i++;
            }
            e[j++]=stoi(d);
            d="";
        }
    }
    // amount of white space in between 2 numbers
    string s(b.size()-(int)log10(e[0])-(int)log10(e[1])-2,' ');
    int l1 = (c.size()-1);
    int l2 = (b.size()-c.size());
    int l = e[0]*l1;
    int r = e[1]*l2;
    // change the side with smaller torque
    if (l>r)
        e[1]=l/l2;
    else
        e[0]=r/l1;
    // output
    cout<<e[0]<<s<<e[1]<<endl;// 1st line
    cout<<b<<endl;// 2nd line
    cout<<c;// 3rd line
    return 0;
}

Bobas_Pett

Posted 2016-11-12T12:38:25.763

Reputation: 965

0

Python 3, 235 230 bytes (minimized reference)

I just minimized the reference, since I'm very new to code-golfing.

def s(l):
 w,i,t=[int(z.strip())for z in l[0].split()],len(l[1]),l[2].find("^");k,o=i-1-t,w[0]*t;p=w[1]*k
 if o>p:w[1]=o//k
 else:w[0]=p//t
 w=[str(z)for z in w];s=" "*(i-sum(len(z)for z in w));l[0]=w[0]+s+w[1];print("\n".join(l))

You use it exactly the same as the example, but the function is s instead of balance_seesaw.

ender_scythe

Posted 2016-11-12T12:38:25.763

Reputation: 249

Lines 5 and 6 could become w[o>p]=[o//k,p//t][o>p]. Also, most of the lines could be joined to get rid of some extra whitespace. – James – 2016-11-14T17:59:41.727

Thanks, as I said, I'm very new, so I overlook even the simplest fixes. – ender_scythe – 2016-11-14T18:05:55.027

Except, it doesn't work, instead providing 0,56 instead of 196,56. – ender_scythe – 2016-11-14T18:09:05.707