22
\$\begingroup\$

Intro

Find the result of number cannibalism.

Why was 6 scared of 7? Becase 7 8 9.

Your program will have to find the result of applying the 8 operation(defined below) on a number, repeatedly.

Related, Sandbox

Challenge

Your input will be a single positive integer, n.

You need to find each 8 in the integer, and substitute it for + or - based on the following conditions.

  • remove all 8s at the beginning or end of the string.
  • If there are multiple 8s, then collapse them into a single 8.
  • \$a\$ and \$b\$ are the chunks to the left and right of the 8 respectively. Since 8 is an operator, the numbers are delimited by 8.
  • If \$ a ≥ b,\$ 8 becomes +.
  • else, it becomes -.

Then evaluate the expression from left to right, and take the absolute value.

If the result has any 8s in it, the repeat the above steps again till there are none.

For example, 12384568789 becomes:

123 456 7 9
123 < 456: 123 - 456 7 9
456 ≥ 7  : 123 - 456 + 7 9
7 < 9    : 123 - 456 + 7 - 9 = -335
= abs(-335)
= 335

Hence 335 is the final answer.

Example Input and Output

789 → 2
180 → 1
42312348 → 4231234
2389687 → 66
12384568789 → 335
13749547874394873104972349 → 7309154
808 → 0
7800482 → 13
14458883442 → 1997
88888 → indeterminate
388182 → 2
08182 → 3

Here are the testcases as an array:

[789,180,42312348,2389687,12384568789,13749547874394873104972349,808,7800482,14458883442,88888]

Scoring Criteria

This is . Shortest answer in each language wins.

\$\endgroup\$
10
  • \$\begingroup\$ We can't take input as a string I assume? \$\endgroup\$
    – Dion
    Commented Oct 16, 2020 at 6:37
  • \$\begingroup\$ @Dion as said in the question, input will always be a single positive integer. \$\endgroup\$
    – Razetime
    Commented Oct 16, 2020 at 6:38
  • \$\begingroup\$ @Razetime: what am I doing wrong with the 6th test case? split: 13749547, 74394, 73104972349; with operators: 13749547 + 74394 - 73104972349; abs sum: abs(-73091148408)=73091148408. Why don't I get the same value as you? \$\endgroup\$ Commented Oct 16, 2020 at 10:00
  • 2
    \$\begingroup\$ @DominicvanEssen 7309114 + 40 = 7309154. \$\endgroup\$
    – Razetime
    Commented Oct 16, 2020 at 10:03
  • \$\begingroup\$ Got it! Thanks! I didn't notice the 'If the result has any 8s in it...' bit. Will update my answer... \$\endgroup\$ Commented Oct 16, 2020 at 10:07

18 Answers 18

8
\$\begingroup\$

05AB1E, 18 14 bytes

Δ8¡þDü@1š·<*OÄ

Try it online or verify all test cases.

Explanation:

Δ               # Loop until it no longer changes:
 8¡             #  Split the integer on 8s
                #  (which will use the implicit input-integer in the first iteration)
   þ            #  Remove all empty strings by only leaving digits
    D           #  Duplicate this list
     ü          #  For each overlapping pair [a,b]:
      @         #   Check if a>=b (1 if truthy; 0 if falsey)
       1š       #  Prepend a 1 to this list
         ·      #  Double each value
          <     #  Decrease each by 1 (0 has become -1; 1 is still 1)
           *    #  Multiply the values at the same positions in the two lists
            O   #  Sum the list
             Ä  #  And take the absolute value of this sum
                # (after which the result is output implicitly)
\$\endgroup\$
3
  • \$\begingroup\$ TIO linke seems to miss the triangle... \$\endgroup\$ Commented Oct 16, 2020 at 14:23
  • \$\begingroup\$ @DominicvanEssen Oops, thanks for noticing. I added that one later when I realized we should continue until no 8s are left, but forgot to update the regular TIO apparently. \$\endgroup\$ Commented Oct 16, 2020 at 14:29
  • 2
    \$\begingroup\$ I assumed so. Annoyingly, when I realized that it cost me 33 bytes, or more than one-third of my code-length in R. When you realised, it cost you one byte in 05AB1E. Grr... \$\endgroup\$ Commented Oct 16, 2020 at 14:32
7
\$\begingroup\$

Jelly, 20 18 bytes

IŻṠo-×
ṣ8LƇḌÇSAµÐL

Try it online!

Went through 5 or 6 other approaches before eval turned out to be the only one I could get to work... and then I tried a different spin on my second approach and it's 2 bytes shorter.

Old version: ṣ8LƇḌ<Ɲị⁾_+ż@ƲFVAµÐL

IŻṠo-×         Monadic helper link:
  Ṡ            signs of
I              deltas
 Ż             with prepended 0,
   o-          replace all 0s with -1s,
     ×         pairwise multiply with argument.

ṣ8LƇḌÇSAµÐL    Main link:
ṣ8             split (implicitly converted digit list) on 8s,
  LƇ           remove empty slices,
    Ḍ          convert digit lists from decimal,
     Ç         apply helper link,
      S        sum,
       A       abs.
        µÐL    Loop that whole thing until it doesn't change.
\$\endgroup\$
7
\$\begingroup\$

JavaScript (ES6), 76 bytes

f=n=>n.replace(p=/[^8]+/g,x=>t-=p<(p=+x)?x:-x,t=0)&&n-(t=t<0?-t:t)?f(t+''):t

Try it online!

Commented

f = n =>               // f is a recursive function taking n as a string
  n.replace(           // we look for ...
    p = /[^8]+/g,      // ... all groups of consecutive non-eight digits
    x =>               // for each group x:
      t -=             //   update t:
        p < (p = +x) ? //     if the previous value is less than x:
          x            //       subtract x from t
        :              //     else:
          -x,          //       add x to t
    t = 0              //   start with t = 0
  ) &&                 // end of replace()
  n - (                // if n is not equal to t,
    t = t < 0 ? -t : t // where t is first updated to its absolute value:
  ) ?                  //
    f(t + '')          //   do a recursive call with t (coerced back to a string)
  :                    // else:
    t                  //   success: return t
\$\endgroup\$
5
\$\begingroup\$

Husk, 27 26 25 bytes

Edit: -1 byte by using S combinator to recycle function argument, and moving helper function in-line, and then -1 more byte by a bit of rearrangement to be able to use o combinator instead of ()

ω(aΣSz*o:1Ẋȯ`^_1¬<mdfIx8d

Try it online!

A slightly different approach to Unrelated String's Husk answer, also 27 bytes.

I'd held-off posting this for a bit, to give any new Husker a clean slate from which to try out this challenge... but now the Husketition is open...

How?*

mdfIx8d                 # helper function: splits input on 8s
      d                 # get digits of input
    x8                  # split on 8  
  f                     # remove elements that are falsy for
   I                    # the identity function (so, remove empty elements)
m                       # map over each element of the list 
 d                      # combining digits as decimal number
 
ωλaΣz*₁¹m`^_1ΘẊo¬<₁     # main program
ω                       # repeatedly apply function until results are constant
 λ                      # lambda function taking one argument:
  a                     # get the absolute value of
   Σ                    # the sum of all elements of
    z*                  # element-wise multiplication of
      ₁¹                # (1) helper function applied to input
                        #     (so: the input split on 8s)
        m               # (2) map this function to all elements of
                  ₁     #     helper function applied to input
             Θ          #     (with 0 prepended at the start)                 
         `^_1           #     minus one to the power of
              Ẋ         #     the results for each pair of elements
               o        #     combine 2 functions
                ¬       #     NOT
                 <      #     second is greater than first    
\$\endgroup\$
5
  • \$\begingroup\$ o¬< can be converted to , right? \$\endgroup\$
    – Razetime
    Commented Oct 17, 2020 at 10:06
  • 1
    \$\begingroup\$ @Razetime - I don't think so, because the result of truthy > is the greater element, rather than simply 1 (and I need to adjust the result to get -1,1)... That's why I did the comparison 'the wrong way around' and then NOTed it. \$\endgroup\$ Commented Oct 17, 2020 at 10:10
  • \$\begingroup\$ Ah then it would be o±≥ which is the same bytes. \$\endgroup\$
    – Razetime
    Commented Oct 17, 2020 at 10:58
  • 1
    \$\begingroup\$ @Razetime - that's really good, but you ought to post it yourself (even if it's your own challenge). It's quite a different approach - and clearly golfier! I need to practice olding... \$\endgroup\$ Commented Oct 17, 2020 at 14:03
  • 2
    \$\begingroup\$ @Razetime That fails on 2389687. You want to choose the sign by comparing to the previous list element, not the partial sum so far. \$\endgroup\$
    – Zgarb
    Commented Oct 17, 2020 at 19:01
4
\$\begingroup\$

R, 89 122 bytes

Edit: +33 bytes to convert to recursive function when I realized that it should repeat the 8 operation on its own output until there are no more 8s. Doh!

f=function(s)`if`(grepl(8,a<-abs(sum(c(1,-sign(diff(n<-sapply((n=el(strsplit(s,8)))[n>-1],as.double))))*n))),f(c(a,'')),a)

Try it online!

Accepts argument n as a string. Errors for ns containing no non-8 digits/characters.

\$\endgroup\$
2
  • \$\begingroup\$ Can't the <- assignment be converted to =? \$\endgroup\$
    – Razetime
    Commented Oct 17, 2020 at 2:34
  • 1
    \$\begingroup\$ @Razetime - not in this case. Inside a function definition (as here), '=' only assigns to the local function argument: so, it needs to correspond to one of the formal argument names, and it'll only be available within the function. In contrast (in this setting), '<-' will assign to any named variable, not limited to the function scope, and the function argument will be assigned by position in the function definition. So, in this case n<- assigns to the (global) variable n, and - at the same time - the first argument of diff (which is x) gets the same value. \$\endgroup\$ Commented Oct 17, 2020 at 9:06
4
\$\begingroup\$

Python 3, 158 148 145 138 bytes

x=input()
while'8'in x:
 y=[int(f)for f in x.split('8')if f];x=str(sum([-1,1][y[i-1]<y[i]]*y[i]for i in range(len(y))))
print(abs(int(x)))

Try it online!

-10 bytes thanks to @pavi2410 and to me not being an idiot and accidentally leaving an extra space from the golfing suggestion

and another -3 thanks to @pavi2410

and another -7 thanks to @pavi2410

\$\endgroup\$
7
  • 1
    \$\begingroup\$ And I thought mine was big... :p \$\endgroup\$
    – Dion
    Commented Oct 16, 2020 at 6:27
  • \$\begingroup\$ -10 bytes by using ternary operator for i in range(len(y)):t+=y[i]if y[i-1]<y[i]else-y[i] \$\endgroup\$
    – pavi2410
    Commented Oct 16, 2020 at 6:46
  • 1
    \$\begingroup\$ @Lyxal It is 148 bytes! You left a space after else-y[i] :) \$\endgroup\$
    – pavi2410
    Commented Oct 16, 2020 at 7:57
  • \$\begingroup\$ Oh lol I just went off the TIO byte counter \$\endgroup\$
    – lyxal
    Commented Oct 16, 2020 at 7:59
  • \$\begingroup\$ Replacing the ternary with t+=[-1,1][y[i-1]<y[i]]*y[i] saves 3 bytes 🥳 \$\endgroup\$
    – pavi2410
    Commented Oct 16, 2020 at 8:06
4
\$\begingroup\$

C (gcc), 149 \$\cdots\$ 139 137 bytes

Saved 2 bytes thanks to ceilingcat!!!
Saved 8 bytes thanks to AZTECCO!!!

R;t=10;m;s;p;c;f(n){R=n;for(s=p=0;n;){for(m=1,c=0;n&&n%t-8;n/=t,m*=t)c+=n%t*m;s+=c>p?p:-p;for(p=c;n%t==8;)n/=t;}p=abs(s+c);R=p-R?f(p):p;}

Try it online!

Explanation (before some golfs)

t=10;                                 // golf by storing 10 in t
g(n){                                 // helper function takes int n  
     for(     ;n;){                   // loops until n is zero  
         s=p=0                        // init sum s and previous p to 0  
       for(                           // inner loop calculates next rh value  
           m=1,                       // init multiplier m to 1  
               c=0;                   // and current c to 0   
                   n&&                // loop until n is 0   
                      n%t-8;          // or most rh digit is 8     
                            n/=t,     // knock most rh digit off n      
                               m*=t)  // and bump m by 10 each loop  
         c+=n%t*m;                    // add next digit to current 
                                      // building up number after  
                                      // most right-hand 8  
      s+=c>p?p:-p;                    // after that's done update sum s  
      for(                            // loop to strip off all rh 8's  
          p=c;                        // also make previous = current  
              n%t==8;)                // loop until most rh digit isn't an 8  
                      n/=t;           // knock most rh 8 off n   
        }                             //   
 p=abs(s+c);                          // return abs value of sum with   
                                      // positive most lh value  
}                                     //  
f(n){                                 // recursive main function  
     x=g(n);                          // calc first iteration
            x=x-g(x)?                 // is it different to next iteration?  
                     f(x):            // if so iterate  
                          x;          // else return value   
}                                     //  
\$\endgroup\$
0
4
\$\begingroup\$

Perl 5 -p, 59 bytes

y/8/ /;s/\d+/$&<$'?"$&-":"$&+"/ge;$_=abs eval$_.0;/8/&&redo

Try it online!

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

J, 53 bytes

|@('8'+/@(*_1x^0,2</\])@(,(*@#;._1#".;._1)~[=,)":)^:_

Try it online!

How it works

  • f^:_: until the result does not change, do f.
  • '8'…(,g[=,)":: convert the input to a string, prepend , the character 8, and execute g with this as the left argument, and a bit mask of 8 characters as the right argument.
  • (…".;._1)~: split the string into groups u;._1, which start with 1 in the bit mask and convert them back to numbers ". (excluding the 8).
  • *@#;._1#: because we could have empty groups (888), take # only those, whose length's # signum * is 1. (There might be a nicer approach.)
  • 2</\]: compare < each group with its following, resulting a bit-mask.
  • *_1x^0,: prepend a 0 to the bit mask, and calculate x*(-1)^y for each group x with y being the result of the comparing. So we get 3 1 2 -> 3 1 _2.
  • |@…+/: sum +/ the result and take the absolute value |.
\$\endgroup\$
3
\$\begingroup\$

Scala, 94 90 bytes

i=>"[^8]+".r.findAllIn(""+i).map(_.toInt).scanRight(0){(a,b)=>if(a<b.abs)a else-a}.sum.abs

Try it online!

-4 bytes by not using Java's cursed split method.

i=>                           //The input
  "[^8]+".r                   //Regex for operands
  .findAllIn(""+i)            //Get all separate numbers in the input
  .map(_.toInt)               //Turn them into integers
  .scanRight(0){(a,b)=>       //Starting at the right,
    if(a<b.abs)a else-a}      //Replace a with -a if a >= b.abs (this is different from the challenge, but it works because we call abs again later)
  .sum                        //Sum them up
  .abs                        //Take the absolute value
\$\endgroup\$
3
\$\begingroup\$

Husk, 27 bytes

ω(aΣSz*(Ẋȯ`^_1±>Ṡ:←)mdfIx8d

Try it online!

This isn't quite my first Husk answer, but it's a pretty clumsy direct translation of one of my attempted Jelly answers, so it could at least serve as a serviceable starting point for someone to swoop in and earn the bounty.

ω(                             Iterate until periodic:
  a                            absolute value of
   Σ                           sum of
     z*                        zipwith multiplication on:
                          d    decimal digits
                        x8     split on 8s
                      fI       with empty slices removed
                    md         and converted back from decimal,
    S                          and the result of that
       (        Ṡ:←)           with its first element duplicated
        Ẋ                      and with neighboring pairs mapped to
          `^_1                 -1 to the power of
         ȯ    ±>               if the first is less than the second.
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Very nice! But, I would say that, because apart from the program structure, the bits-and-pieces are remarkably similar to my own answer! \$\endgroup\$ Commented Oct 17, 2020 at 10:13
3
\$\begingroup\$

Bash, 177 bytes

A=($(sed s/8/\ /g<<<$1));B=;while [ ${#A[@]} -ge 2 ];do [ ${A[0]} -ge ${A[1]} ]&&C=+||C=-;B=$B${A[0]}$C;A=(${A[@]:1});done;R=$(bc<<<$B$A|tr -d -);grep -cq 8 <<<$R&&f $R||echo $R

It has to be stored as function f or in file f in the current working directory.

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ 110 bytes \$\endgroup\$ Commented Oct 18, 2020 at 17:07
  • \$\begingroup\$ @NahuelFouilleul your answer is so different I think you shold go ahead and post it as your own answer \$\endgroup\$ Commented Oct 22, 2020 at 13:45
  • \$\begingroup\$ I started by removing external commands $(sed ..) with variable expansion ${1//8/ } and $(bc ..|tr ..) with arithmetic expansions $[..]. \$\endgroup\$ Commented Oct 22, 2020 at 13:53
3
\$\begingroup\$

PHP, 290 bytes

(Guess you shouldn't golf in php. :) )

<?php $n=$_SERVER["argv"][1];while(false!==strpos($n,'8')){$n=abs(array_reduce(str_split(preg_replace('|8+|','8',trim($n,'8')).'8'),function($c,$i){if($i!=8){$c[1]*=10;$c[1]+=$i;}else{$c[2]+=$c[1]*((!isset($c[0])||$c[0]>=$c[1])?1:-1);$c[0]=$c[1];$c[1]=0;}return$c;},[null,0,0])[2]);}echo$n;

Try it online

Ungolfed:

<?php

$n = $_SERVER["argv"][1];

$f=function($c, $i){
    if($i!=8) {
        $c[1]*=10;
        $c[1]+=$i;
    } else {
        $c[2] += $c[1] * ( (!isset($c[0]) || $c[0]>=$c[1])?1:-1);
        $c[0]=$c[1];
        $c[1]=0;
    }
    return $c;
};

while(false!==strpos($n,'8')) {
    $n = trim($n, '8');
    $n = preg_replace('|8+|', '8', $n);
    $a = str_split($n.'8');
    $n = abs(array_reduce($a, $f, [null, 0, 0])[2]);
}

echo $n;

Explanation:

I use array_reduce to walk over every digit, and use an array as carry to have 3 datapoints carried on: the variables $a, $b and $sum, though they are unnamed as the elements of $c.

If the current digit is a non-8, I "add" it to my "$b", otherwise I first compare $b to $a, add/subtract $b from $sum, and move the content of $b to $a.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ The general goal here is to have the best answer in your language, so feel free to keep golfing in PHP. \$\endgroup\$
    – Razetime
    Commented Oct 18, 2020 at 12:28
  • 1
    \$\begingroup\$ I don't know PHP, but can you replace false with something like 1<1? \$\endgroup\$
    – user
    Commented Oct 18, 2020 at 19:07
2
\$\begingroup\$

Red, 142 bytes

func[n][while[find to""n"8"][b: to[]load form split to""n"8"forall b[if b/2[b/1:
reduce[b/1 pick[- +]b/1 < b/2]]]n: absolute do form b]to 1 n]

Try it online!

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

GolfScript, 32 bytes

.,{8`%(~:x\{~.x\:x<2*(*-}/abs`}*

Try it online!

Every time the algorithm is executed, the number either stays the same or it gets at least 1 digit shorter. This means we can execute the algorithm once for every byte instead of testing if it has an 8.

.,                                 # Get the number of bytes in the input
  {                           }*   # Execute this block that many times
   8`%                             # Split the string around the 8s and remove the empty strings
                                   # This simultaneously executes the first 3 steps
      (~                           # Get the first number
        :x                         # Store it in the variable x
          \{~           }/         # For each number left in the array
             .x                    # Copy it and push x
               \:x                 # Store the current number in x
                  <                # Compare the two numbers
                   2*(             # 1 if previous<current and -1 if previous>=curret
                      *-           # Multiply and subtract
                          abs`     # Parse the absolute value to a string
\$\endgroup\$
2
\$\begingroup\$

Japt, 37 bytes

@=q8 f;=äÈn ¨Y?Y:-Y}Ug)x a s ,Uø8}f U

Try it

  • takes input as a string, it can be easily modified to take input as a number but it won't work with big numbers

'''

@=q8 f;=äÈn ¨Y?Y:-Y}Ug)x a s ,Uø8}f U
@....}f    - return first number which return false when passed through @...
=q8 f;     - input splitted on 8 and empties removed
=äÈ...}Ug) - pass each consecutive values through È , but prepend first value before so that they are >=
n ¨Y?Y:-Y  - 1st to number compared to 2nd: return 2nd (negated eventually)
x a s      - reduce->abs->to string

The above is assigned to U while the funxtion returns..
Uø8        - contains 8? => repeat
Finally we return U

'''

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

K (oK), 60 43 38 bytes

-22 bytes thanks to Traws

{a|-a:+/{x*1-2*>':x}.:'(~^.)#"8"\$:x}/

Try it online!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ It's nice that you reimplemented split but you can use "8"\ directly to split on "8". This simplifies the solution to: {{0>x}{-x}/+/{x*1-2*>':x}.:'(~^.)#"8"\$:x}/ \$\endgroup\$
    – Traws
    Commented Oct 20, 2020 at 12:10
  • \$\begingroup\$ @Traws Thank you, I feel stupid... \$\endgroup\$ Commented Oct 20, 2020 at 12:42
  • 1
    \$\begingroup\$ You can also simplify the abs part with {a|-a:+/{x*1-2*>':x}.:'(~^.)#"8"\$:x}/ \$\endgroup\$
    – Traws
    Commented Oct 20, 2020 at 15:23
  • \$\begingroup\$ @Traws Thanks, that's much better. \$\endgroup\$ Commented Oct 20, 2020 at 15:41
1
\$\begingroup\$

Retina 0.8.2, 68 bytes

{`8+
-
^-|-$

\d+
$*
(?=-(1*))(?<=\1)-
+
O`\D1*
\+

1>`-

(1+)-\1

1

Try it online! Link includes smaller test cases (Retina has to do subtraction in unary, which is too slow for the larger cases). Explanation:

{`

Repeat until there are no 8s left.

8+
-

Convert each runs of 8s to a -.

^-|-$

Delete leading and trailing -s.

\d+
$*

Convert the remaining numbers to unary.

(?=-(1*))(?<=\1)-
+

Replace each - with a + unless the following number is greater.

O`\D1*

Sort the numbers to be added to the beginning and the numbers to be subtracted to the end.

\+

Add all the numbers to be added together.

1>`-

Add all the numbers to be subtracted together.

(1+)-\1
   

Take the absolute value of the difference.

1

Convert it to decimal.

\$\endgroup\$

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