Spin the Calculator

16

1

Introduction:

Let's take a look at a standard Calculator in Windows:
enter image description here
For this challenge, we'll only look at the following buttons, and ignore everything else:

7 8 9 /
4 5 6 *
1 2 3 -
0 0 . +

Challenge:

Input:
You will receive two inputs:

  • One is something to indicate the rotation in increments of 90 degrees
  • The other is a list of coordinates representing the buttons pressed on the rotated calculator.

Based on the first input, we rotate the layout mentioned above clockwise in increments of 90 degrees. So if the input is 0 degrees, it remains as is; but if the input is 270 degrees, it will be rotated three times clockwise (or once counterclockwise). Here are the four possible lay-outs:

Default / 0 degrees:
7 8 9 /
4 5 6 *
1 2 3 -
0 0 . +

90 degrees clockwise:
0 1 4 7
0 2 5 8
. 3 6 9
+ - * /

180 degrees:
+ . 0 0
- 3 2 1
* 6 5 4
/ 9 8 7

270 degrees clockwise / 90 degrees counterclockwise:
/ * - +
9 6 3 .
8 5 2 0
7 4 1 0

The second input is a list of coordinates in any reasonable format . For example (0-index 2D integer-array):

[[1,2],[2,3],[0,3],[1,0],[1,1]]

Output:
We output both the sum, as well as the result (and an equal sign =).

Example:
So if the input is 270 degrees and [[1,2],[2,3],[0,3],[1,0],[1,1]], the output will become:

517*6=3102

Challenge rules:

  • The inputs can be in any reasonable format. The first input can be 0-3, 1-4, A-D, 0,90,180,270, etc. The second input can be a 0-indexed 2D array, 1-indexed 2D array, a String, list of Point-objects, etc. Your call. It's even possible to swap the x and y coordinates compared to the example inputs given. Please state which input formats you've used in your answer!
  • You are allowed to add spaces (i.e. 517 * 6 = 3102) if you want to.
  • You are allowed to add trailing zeros after the comma, to a max of three (i.e. 3102.0/3102.00/3102.000 instead of 3102 or 0.430 instead of 0.43).
  • You are not allowed to add parenthesis in the output, so (((0.6+4)-0)/2)/4=0.575 is not a valid output.
  • You are allowed to use other operand-symbols for your language. So × or · instead of *; or ÷ instead of /; etc.
  • Since a calculator automatically calculates when inputting an operand, you should ignore operator precedence! So 10+5*3 will result in 45 ((10+5)*3=45), not 25 (10+(5*3)=25)
    (i.e. 10+5* (it now displays 15 in the display) → 3= (it now displays the answer 45)). Keep this in mind when using eval and similar functions on the resulting sum.
  • There won't be any test cases for division by 0.
  • There won't be any test cases with more than three decimal digits as result, so no need for rounding the result.
  • There won't be any test cases where multiple operands follow each other, or where two dots follow each other.
  • There won't be any test cases for negative numbers. The minus-sign (-) will only be used as operand, not as negative.
  • There won't be any test cases for .## without a leading number before the comma (i.e. 2+.7 will not be a valid test case, but 2+0.7 could be).

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code.
  • Also, please add an explanation if necessary.

Test cases:

Input:   270 degrees & [[1,2],[2,3],[0,3],[1,0],[1,1]]
Output:  517*6=3102

Input:   90 degrees & [[3,1],[0,0],[0,1],[3,3],[2,0],[0,3],[0,0],[0,2],[3,0],[2,1]]
Output:  800/4+0.75=200.75

Input:   0 degrees & [[0,0],[1,0],[2,0],[3,0],[1,2],[2,1],[2,2]]
Output:  789/263=3

Input:   180 degrees & [[3,0],[1,0],[1,2],[0,0],[3,2],[0,1],[2,0],[0,3],[2,1],[0,3],[3,2]]
Output:  0.6+4-0/2/4=0.575

Kevin Cruijssen

Posted 2017-06-22T14:41:26.037

Reputation: 67 575

Can I output the equation with (proper) parentheses? (e.g. ((((0.6)+4)-0)/2)/4=0.575) – dzaima – 2017-06-22T15:00:46.793

1The test cases have many errors (e.g. the 3rd and 4th has X and Y swapped (1st doesn't) and I don't even know what happened to the 2nd) – dzaima – 2017-06-22T15:14:39.107

2Should the program handle weird button presses? 1+-*/+-*/2 will gives 0.5 on Windows (10) calculator. – user202729 – 2017-06-22T15:21:56.887

1second test case should start with [1,3], – Uriel – 2017-06-22T15:37:32.843

1Do we have to handle decimals less than 1 without leading 0s, like in 2+.7? – Tutleman – 2017-06-22T15:37:38.093

1In Windows 8 calculator, 3 * - 5 is equivalent to 3-5. Does it indicate that the code needn't handle negative numbers in the input (though negatives are possible during calculation)? – Keyu Gan – 2017-06-22T15:55:16.163

@KevinCruijssen Yes, the post has been in sandbox for 7 days, but was in there for only 5 hours with the precedence rule and test-cases added. – dzaima – 2017-06-22T16:10:25.793

Inb4 Mr. Mendo provides us a 13 byte MATL answer. – Magic Octopus Urn – 2017-06-22T16:12:31.517

4Operator precedence is why I never use Windows Calculator in standard mode. – Neil – 2017-06-22T17:03:37.827

@dzaima Thanks for pointing this out, and you are indeed right I shouldn't have posted it as soon after editing.. I was impatient and eager to post it I guess. Test cases should all be fixed now (I hope). As for your very first question: no, you can't have parenthesis in the output. – Kevin Cruijssen – 2017-06-22T17:24:26.393

1@user202729 I've added a rule that there won't be two operands following each other, so you won't have to deal with that. – Kevin Cruijssen – 2017-06-22T17:25:15.933

@Tutleman I've added a rule for this: all decimals will have a leading number before the comma, so 2+.7 won't be a valid test case, but 2+0.7 could be a valid test case. – Kevin Cruijssen – 2017-06-22T17:26:03.397

@KeyuGan The minus-sign will only be used as operand in this challenge, so you won't have to deal with negative numbers (except in the form of a test case like 5-9=-4). – Kevin Cruijssen – 2017-06-22T17:27:06.160

It seems like the 1st test case is given with [x,y] coordinates and the other ones with [y,x]. Could you please clarify? (+what @Uriel already said: the first entry of the 2nd test case is not consistent) – Arnauld – 2017-06-22T17:48:47.370

@Neil : The worst part is that the windows calculator displays "1 + 2 * 3" but calculates "(1 + 2) * 3" – Eric Duminil – 2017-06-22T19:38:35.833

Answers

4

SOGL V0.12, 70 69 67 bytes

i⅛⁸Νο;⌡░▼Y6γj±²‘1n4n.⌡Iø,→{_≤whwιh:"/*-+”;W? )Κ; (Κ;}+}:Ƨ)(čøŗoļ=→p

Try it Here, or try a version which takes inputs as given in the test-cases

uses SOGLs I operator, which rotates the array. Then reads a string as a JavaScript array, and where an operation is used, encase previous result in parentheses, evaluates as JavaScript and then removes the parentheses.

dzaima

Posted 2017-06-22T14:41:26.037

Reputation: 19 048

3

Dyalog APL, 94 88 86 85 bytes

{o,'=',⍎('('\⍨+/'+-×÷'∊⍨o),'[×÷+-]'⎕R')&'⊢o←(((⌽∘⍉⍣⍺)4 4⍴'789÷456×123-00.+')⊃⍨⊂∘⊢)¨⍵}

Try it online!

Takes the rotations as left argument, 0-3, and the 1-based indices as right argument, as a list of y x coordinates, like (1 1)(2 3)(4 5) etc.

This got quite messy because of the right-handed evaluation of expressions in APL.

Uriel

Posted 2017-06-22T14:41:26.037

Reputation: 11 708

3

C (gcc), 282 294 295 296 300 304 306 310 bytes

All optimizations need to be turned off and only work on 32-bit GCC.

float r,s;k,p,l,i;g(d,x,y){int w[]={y,x,3-y,3-x,y};d=w[d+1]*4+w[d];}f(x,y,z)int**z;{for(i=0;i<=y;i++)putchar(k=i-y?"789/456*123-00.+"[g(x,z[i][0],z[i][1])]:61),57/k*k/48?p?r+=(k-48)*pow(10,p--):(r=10*r+k-48):k-46?s=l?l%2?l%5?l&4?s/r:s+r:s-r:s*r:r,r=p=0,l=k:(p=-1);printf("%.3f",s);}

1 byte thanks to @Orion!

Try it online!

Function prototype:

f(<Direction 0-3>, <Number of entries>, <a int** typed array in [N][2]>)

Input format (as on TIO):

<Direction 0~3> <Number of entries>
<Entries 0 Row> <Entries 0 Column>
<Entries 1 Row> <Entries 1 Column>
....
<Entries N Row> <Entries N Column>

Ungolfed version with comments:

float r, s;
k, p, l, i;
g(d, x, y) {
  int w[] = {
    y,
    x,
    3 - y,
    3 - x,
    y
  };
  d = w[d + 1] * 4 + w[d];
}
f(x, y, z) int **z; {
  for (i = 0; i <= y; i++)
  {
      putchar(k = i - y ? 
      "789/456*123-00.+"[g(x, z[i][0], z[i][1])] : 61),     // Print character, otherwise, '='
      57 / k * k / 48 ?                                     // If the character is from '0'~'9'
        p ?                                                 // If it is after or before a dot
            r += (k - 48) * pow(10., p--)                   // +k*10^-p
        :
            (r = 10 * r + k - 48)                           // *10+k
      :
          k - 46 ?                                          // If the character is not '.', that is, an operator, + - * / =
            s = l ?                                         // Calculate the result of previous step (if exist)
                    l % 2 ?                                 // If + - /
                        l % 5 ?                             // If + /
                            l & 4 ?
                                s / r
                            :
                                s + r
                        :
                            s - r
                    :
                        s * r
                 :
                    r,
                    r = p = 0, l = k                        // Reset all bits
          :
            (p = -1);                                       // Reverse the dot bit
  }
  printf("%.3f", s);
}

The code can handle cases such as 1+.7 or -8*4.

Very sad C doesn't have an eval .

Keyu Gan

Posted 2017-06-22T14:41:26.037

Reputation: 2 028

You can indeed deem cases like 3*-5 invalid. I've specified this in the rules. – Kevin Cruijssen – 2017-06-22T17:31:30.323

Considering the required precision in the rules is only 3 places, you could replace double with float for a free byte. Also, isn't putc() identical to putchar()? I could be wrong, though. – Orion – 2017-06-23T00:10:39.083

@Orion I remember putc needs a second argument to specify which stream you are writing to? – Keyu Gan – 2017-06-23T05:20:55.077

292 bytes – ceilingcat – 2019-09-30T15:59:49.877

2

JavaScript (ES6), 162 160 157 bytes

Takes input as orientation o and array of (y,x) coordinates a in currying syntax (o)(a).

The orientation is an integer in [0..3]:

  • 0 = 0°
  • 1 = 90° clockwise
  • 2 = 180° clockwise
  • 3 = 270° clockwise
o=>a=>(s=a.map(([y,x])=>'789/456*123-00.+'[[p=y*4+x,12+(y-=x*4),15-p,3-y][o]]).join``)+'='+eval([...x=`0)+${s}`.split(/(.[\d.]+)/)].fill`(`.join``+x.join`)`)

Test cases

let f =

o=>a=>(s=a.map(([y,x])=>'789/456*123-00.+'[[p=y*4+x,12+(y-=x*4),15-p,3-y][o]]).join``)+'='+eval([...x=`0)+${s}`.split(/(.[\d.]+)/)].fill`(`.join``+x.join`)`)

console.log(f(3)([[2,1],[3,2],[3,0],[0,1],[1,1]])) // 517*6=3102
console.log(f(1)([[1,3],[0,0],[1,0],[3,3],[0,2],[3,0],[0,0],[2,0],[0,3],[1,2]])) // 800/4+0.75=200.75
console.log(f(0)([[0,0],[0,1],[0,2],[0,3],[2,1],[1,2],[2,2]])) // 789/263=3
console.log(f(2)([[0,3],[0,1],[2,1],[0,0],[2,3],[1,0],[0,2],[3,0],[1,2],[3,0],[2,3]])) // 0.6+4-0/2/4=0.575

Arnauld

Posted 2017-06-22T14:41:26.037

Reputation: 111 334

2

Ruby, 135 133 132 bytes

->r,c{a="";c.map{|x,y|a=((w="789/456*123-00.+"[[y*4+x,12-x*4+y,15-y*4-x,x*4+3-y][r]])=~/[0-9.]/?a:"#{eval a}")+w;w}*""+"=#{eval a}"}

Try it online!

Orientation as integer: 0 for 0°, 1 for 90° and so on.

G B

Posted 2017-06-22T14:41:26.037

Reputation: 11 099

1

Python 3, 235 234 230 bytes

Bit ugly but it works for all the test cases except the first, which doesn't seem to match the example calculator. I take the rotation as 0-3 (0-270) and multiply by 16 to offset.

eval() is a built-in which attempts to compile strings as code and handles converting the text symbols to operators.

import re
def f(r,c,J=''.join):
 b='789/456*123-00.+01470258.369+-*/+.00-321*654/987/*-+963.85207410'
 s=t=J([b[r*16+x*4+y]for y,x in c]);t=re.split('([\+\-\/\*])',s)
 while len(t)>2:t=[str(eval(J(t[0:3])))]+t[3:]
 print(s+'='+t[0])

Alternate method, it turned out a little bit longer but I really like this SO tip for rotating the array.

import re
def f(r,c):
 L=list;b=L(map(L,['789/','456*','123-','00.+']))
 while r:b=L(zip(*b[::-1]));r-=1
 s=''.join([b[x][y]for y,x in c]);t=re.split('([\+\-\/\*])',s)
 while len(t)>2:t=[str(eval(''.join(t[0:3])))]+t[3:]
 print(s+'='+t[0])

nocturama

Posted 2017-06-22T14:41:26.037

Reputation: 111

1

Java 10, 418 380 378 bytes

d->a->{String r="",g=d>2?"/*-+963.85207410":d>1?"+.00-321*654/987":d>0?"01470258.369+-*/":"789/456*123-00.+",n[],o[];for(var i:a)r+=g.charAt(i[1]*4+i[0]);n=r.split("[-/\\+\\*]");o=r.split("[[0-9]\\.]");float s=new Float(n[0]),t;for(int i=1,O,j=0;++j<o.length;O=o[j].isEmpty()?99:o[j].charAt(0),s=O<43?s*t:O<44?s+t:O<46?s-t:O<48?s/t:s,i+=1-O/99)t=new Float(n[i]);return r+"="+s;}

Decided to answer my own question as well. I'm sure it can be golfed some more by using a different approach.
Input as int (0-3) and int[][] (0-indexed / the same as in the challenge description). Output as float with leading .0 if the result is an integer instead of decimal number.

-2 bytes thanks to @ceilingcat.

Explanation:

Try it here.

d->a->{                       // Method with int & 2D int-array parameters and String return
  String r="",                //  Result-String, starting empty
    g=d>2?                    //  If the input is 3:
       "/*-+963.85207410"     //   Use 270 degree rotated String
      :d>1?                   //  Else if it's 2:
       "+.00-321*654/987"     //   Use 180 degree rotated String
      :d>0?                   //  Else if it's 1:
       "01470258.369+-*/"     //   Use 90 degree rotated String
      :                       //  Else (it's 0):
       "789/456*123-00.+",    //   Use default String
    n[],o[];                  //  Two temp String-arrays
  for(var i:a)                //  Loop over the coordinates:
    r+=g.charAt(i[1]*4+i[0]); //   Append the result-String with the next char
  n=r.split("[-/\\+\\*]");    //  String-array of all numbers
  o=r.split("[[0-9]\\.]");    //  String-array of all operands (including empty values unfortunately)
  float s=new Float(n[0]),    //  Start the sum at the first number
        t;                    //  A temp decimal
  for(int i=0,                //  Index-integer `i`, starting at 0
      O,                      //  A temp integer
      j=0;++j<o.length        //  Loop `j` over the operands
      ;                       //    After every iteration:
       O=o[j].isEmpty()?      //     If the current operand is an empty String
          99                  //      Set `O` to 99
         :                    //     Else:
          o[j].charAt(0),     //      Set it to the current operand character
       s=O<43?                //     If the operand is '*':
          s*t                 //      Multiply the sum with the next number
         :O<44?               //     Else-if the operand is '+':
          s+t                 //      Add the next number to the sum
         :O<46?               //     Else-if the operand is '-':
          s-t                 //      Subtract the next number from the sum 
         :O<48?               //     Else-if the operand is '/':
          s/t                 //      Divide the sum by the next number
         :                    //     Else (the operand is empty):
          s,                  //      Leave the sum the same
       i+=1-O/99)             //     Increase `i` if we've encountered a non-empty operand
    t=new Float(n[i]);        //   Set `t`  to the next number in line
  return r+"="+s;}            //  Return the sum + sum-result

Kevin Cruijssen

Posted 2017-06-22T14:41:26.037

Reputation: 67 575

1

PHP, 267 235 229 225 221 213 bytes

<?php $t=[1,12,15,4,1];$j=$t[$k=$argv[1]];($o=$i=$t[$k+1])<5&&$o--;foreach(json_decode($argv[2])as$c){echo$s='/*-+963.85207410'[($c[0]*$i+$c[1]*$j+$o)%16];strpos(' /*-+',$s)&&$e="($e)";$e.=$s;}eval("echo'=',$e;");

Try it online!

6 bytes thanks to @Kevin Cruijssen.

Input directions are: 0 is 0, 1 is 90, 2 is 180 and 3 is 270 degrees.

Ungolfed

The rotation is tricky, my idea is to re-map the symbols depending on orientation. The re-mapping is done by multiplying the x and y coordinates by different amounts and offsetting the result. So, there is a triple per orientation (stored in $rot). There is probably a better way, but I can't think of it at the moment!

$coords = json_decode($argv[2]);
$symbols = '789/456*123-00.+';
$r=$argv[1];
$rot = [1,4,0,12,1,12,15,12,15,4,15,3];    
list($xs,$ys,$o)=array_slice($rot,$r*3,3);

$expression='';

foreach($coords as $coord) {
    $symbol=$symbols[($coord[0]*$xs+$coord[1]*$ys+$o)%16];

    if (!is_numeric($symbol) && $symbol!='.') {
        $expression = "($expression)";
    }
    $expression .=$symbol;
    echo $symbol;
}

eval ("echo '='.($expression);");

?>

Guillermo Phillips

Posted 2017-06-22T14:41:26.037

Reputation: 561

I'm not too familiar with PHP, but I think you can golf the && to & to save a byte. And nice answer, +1 from me! :) – Kevin Cruijssen – 2020-01-16T13:10:52.540

Actually, I think if(!is_numeric($s)&$s!='.') can be golfed further to if(strpos(' /*-+',$s)) (if I understand this SO answer correctly).

– Kevin Cruijssen – 2020-01-16T13:17:15.637

Of course! And thanks. There I was staring at that if statement, but nothing came. – Guillermo Phillips – 2020-01-16T14:11:59.760

0

05AB1E, 57 54 bytes

εžm3ôí…/*-ø"00.+"ª€SIFøí}yθèyнè}JD'=s.γžh'.«så}R2ôR».VJ

Uses 0-based coordinates similar as the challenge description, and [0,1,2,3] for [0,90,180,270] respectively as inputs.

Try it online or verify all test cases.

Explanation:

ε                    # Map over each of the (implicit) input-coordinates:
 žm                  #  Push builtin "9876543210"
   3ô                #  Split it into parts of size 3: [987,654,321,0]
     í               #  Reverse each inner item: [789,456,123,0]
      …/*-           #  Push string "/*-"
          ø          #  Zip the lists together: [["789","/"],["456","*"],["123","-"]]
           "00.+"ª   #  Append string "00.+": [["789","/"],["456","*"],["123","-"],"00.+"]
                  €S #  Convert each inner item to a flattened list of characters
                     #   [[7,8,9,"/"],[4,5,6,"*"],[1,2,3,"-"],[0,0,".","+"]]
 IF                  #  Loop the input-integer amount of times:
   ø                 #   Zip/transpose the character-matrix; swapping rows/columns
    í                #   And then reverse each inner row
 }                   #  Close the loop (we now have our rotated character matrix)
  yθèyнè             #  Index the current coordinate into this matrix to get the character
}J                   # After the map: join all characters together to a single string
  D                  # Duplicate the string
   '=               '# Push string "="
     s               # Swap to get the duplicated string again
      .γ             # Group it by (without changing the order):
        žh           #  Push builtin "0123456789"
          1/         #  Divide it by 1, so it'll become a float with ".0": "0123456789.0"
                     #  (1 byte shorter than simply appending a "." with `'.«`)
            så       #  And check if this string contains the current character
       }             # Close the group-by, which has separating the operators and numbers
                     #  i.e. "800/4+0.75" → ["800","/","4","+","0.75"]
        R            # Reverse this list
                     #  i.e. ["0.75","+","4","/","800"]
         2ô          # Split it into parts of size 2
                     #  i.e. [["0.75","+"],["4","/"],["800"]]
           R         # Reverse the list again
                     #  i.e. [["800"],["4","/"],["0.75","+"]]
            »        # Join each inner list by spaces, and then those strings by newlines
                     #  i.e. "800\n4 /\n0.75 +"
             .V      # Execute this string as 05AB1E code
               J     # Join this with the earlier string and "=" on the stack
                     # (after which the result is output implicitly)

Kevin Cruijssen

Posted 2017-06-22T14:41:26.037

Reputation: 67 575

0

Burlesque, 122 bytes

pe'1'9r@3co{".-"".*""./"}z[)FL{'0'0'.".+"}+]j{{}{tp}{)<-<-}{tp)<-}}j!!e!<-Ppra{pPj<-d!}msJ'=_+j{><}gB3co{rtp^}mwpe?+'.;;++

Try it online!

Takes args as: "[[1,2],[2,3],[0,3],[1,0],[1,1]]" 3

pe                  # Push args to stack
'1'9r@              # Range of characters [1,9]
3co                 # Split into chunks of 3
{".-" ".*" "./"}z[  # Zip in the operators
)FL                 # Flatten the resulting list (giving us the first 3 rows)
{'0 '0 '. ".+"}+]   # Add the 4th row
j                   # Swap rotation index to top of stack
{
 {}                 # NOP
 {tp}               # Transpose
 {)<- <-}           # Reverse each column and row
 {tp )<-}           # Transpose and reverse each row
}
j!!e!               # Select which based on rotation spec and eval
<-                  # Reverse the result
Pp                  # Push to hidden stack
ra                  # Read the coords as array
{
 pP                 # Pop from hidden stack
 j<-                # Reverse the coords (tp'd out)
 d!                 # Select this element
}ms                 # For each coord and accumulate result as string
J                   # Duplicate said string
'=_+j               # Put the equals at the end of the other string
{><}gB              # Group by whether digits
3co                 # Split into chunks of 3
{
 rt                 # rotate (putting op in post-fix position)
 p^                 # Ungroup
}mw                 # Map intercalating spaces
pe                  # Evaluate the result
?+                  # Combine the results
'.;;++              # Change .* ./ etc to * /

DeathIncarnate

Posted 2017-06-22T14:41:26.037

Reputation: 916