Minify Brainfuck

22

4

Your challenge is to minify Brainfuck code, according to these rules:

  • Remove anything that is not one of +-><[].,.
  • For any group of consecutive + or - characters, if the amount of +s and -s is the same, remove them.
  • Do the same as above, but with > and <.
  • Remove sequences of the +->< characters if they do nothing. For example, you should remove +>-<->+<. (This may be the trickiest and hardest one to implement.) Make sure you don't get any false positives, like +>-<+>-<, which should not be removed.

Test cases:

Input

++++++[->++++++<]>.   prints a $
[-]<                  resets tape
>,[>,]<[.<]           reverses NUL terminated input string
++-->><<              does nothing

Output

++++++[->++++++<]>.[-],[>,]<[.<]

Input

Should disappear: ++>>+<+++<->-->-<<->-<
Should disappear: +++>-<--->+<
Should stay: +++>-<+>---<

Output

+++>-<+>---<

You may accept input and output however you would like - stdin/stdout, a function, etc., but input may not be hardcoded.

This is , so the shortest code in character count will win.

Doorknob

Posted 2013-12-29T22:19:03.287

Reputation: 68 138

4I know this is an old challenge, but the test cases are inadequate. ++>>++<<-- should output >>++<<, and that wasn't covered. Please add more test cases. – mbomb007 – 2016-02-29T22:20:26.617

@mbomb007 did you examine the last test case, +++>-<+>---<? It can be shortened to avoid unnecessary pointer movement, but the expected output leaves it unchanged. My understanding based on looking at both the question and the answers is that Doorknob is cool with the spec being taken loosely; we must eliminate any no-op contiguous +->< sequences as explicitly stated, and beyond that it is permissible to do extra minifying as in your example ++>>++<<--, and we can also make rearrangements as long as they don't change the functionality of the code, e.g. >+<+ into +>+<. – Mitch Schwartz – 2016-10-26T04:26:38.797

@MitchSchwartz "Remove sequences of the +->< characters if they do nothing. For example, you should remove +>-<->+<. (This may be the trickiest and hardest one to implement.) Make sure you don't get any false positives, like +>-<+>-<, which should not be removed." - this is kind of vague – mbomb007 – 2016-10-26T13:40:16.517

@mbomb007 And the second and third bullet points are redundant and unnecessary because they are included in the fourth bullet point. So what? It's a cool task. My comment was meant to be constructive and provide clarification, not to attack you. Please take it the way I intended, or tell me how I should have said it differently. Because you did not really address what I wrote; it just seems like you're trying to defend yourself without really being constructive. In what way do you find it vague? How would you rewrite it? Do you want to edit the question? Do you want to ask Doorknob? – Mitch Schwartz – 2016-10-26T14:07:39.630

1Oh, so we only have to remove contiguous sequences? – mbomb007 – 2016-10-26T14:19:03.387

Hmm, that's how I read it in order to fit with the test cases, but now I see that there is a fundamental difference between (1) +++>-<+>---< and (2) ++>>++<<--: the first case requires rearrangement in order to minify it, but the second case doesn't. In other words, the second case has a removable non-contiguous subsequence, but the first case doesn't. I think Doorknob would have to clarify this, because there are two different interpretations that both satisfy all the test cases in the problem statement. (But most answers took liberties with the spec, and were not penalized for it.) – Mitch Schwartz – 2016-10-26T14:51:43.460

Part of why I inferred contiguous may be that it would immediately exclude minifying +>.<- (on phone, can't input backticks), which seems to fit with what is intended, whereas with non-contiguous you might think that you have to minify that case. – Mitch Schwartz – 2016-10-26T15:30:40.510

Answers

10

REBEL - 104

_/^_$/$</([^][<>.,+-]|\+-|-\+|<>|><)//((?<X>(<|>))+[+-]+(?!\2)(?<-X><|>)+(?(X)(?!)))([+-]+)/$3$1/.+/$>$&

Usage:

Input: Reads one line from stdin.

Output: Prints one line to stdout.

Anomalies*:

  • Entering _ causes another line to be read and used, rather than outputting nothing.
  • The second test outputs ++++>----< instead of +++>-<+>---<. But that's OK, right? ;)
  • >-<+ etc. are replaced with +>-< etc.

Spoiler:

Implementing anomaly #3 makes things quite trivial.

* It's not a bug, it's a feature!

Kendall Frey

Posted 2013-12-29T22:19:03.287

Reputation: 2 384

"it's not a bug its a feature" +1! – Rohan Jhunjhunwala – 2016-08-01T02:18:51.197

37

Brainfuck, 579 bytes

,[<<+>>>>+<<[[<+>>+<-]++++++[>-------<-]>-[-[-[-[--------------[--[<+++++[>-----
-<-]>+[--[<<[-]>>-]]<]>[>>-<<<<<[-]<[<]<<<[<]>>>>>>>>[<]<-[+>]+[->+]>>>>+>[<-]<[
>+<-<]>]<]>[<<<[-]-[<]>>>>>>>>>>>[<]<<<<<<[<]<-[+>]+[-<+]<<<+<[>-<<<]>[-<+<]]]<]
>[+>[-<]<[<<]<[-]>>]]<]+>[-[<-]<[>+>+<<-<]<[-]>+>]<<[>-]>[,>]<]<+<[>]>[>>>[<<<<[
-<]<<<]>>>+>>>>[<<<<->>>>[>>>[-<]>>>>]]]>[<<<[<+[-->>]]>[-[.[-]]]>[<]>[<<++++++[
>+++++++<-]>+>>[<<.>>-]<<++>-[<.>-]+++[<+++++>-]+<<<<<<+>[<<[>->>>>>.[[-]<<<<]<<
<+>>>]>[->->>>>[-]]]<[->+[>>>>>]>>[<]<<<<<<<<[[-]<]>[++.[-]>>>>>>>]<]]>>]<[>>>>>
>>]+[-<<<<<[-]<<],]

With formatting and some comments:

,
[
  <<+>> >>+<<
  [
    [<+> >+<-]
    ++++++[>-------<-]
    >-
    [
      not plus
      -
      [
        not comma
        -
        [
          not minus
          -
          [
            not period
            --------------
            [
              not less than
              --
              [
                not greater than
                <+++++[>------<-]>+
                [
                  not open bracket
                  --
                  [
                    not close bracket
                    <<[-]>>-
                  ]
                ]
                <
              ]
              >
              [
                greater than
                >>-<<
                <<<[-]<[<]<<<[<]
                >>>>>>>>[<]
                <-[+>]
                +[->+]
                >>>>+>[<-]
                <[>+<-<]
                >
              ]
              <
            ]
            >
            [
              less than
              <<<[-]-[<]
              >>>> >>>>>>>[<]
              <<<<<<[<]
              <-[+>]
              +[-<+]
              <<<+<[>-<<<]
              >[-<+<]
            ]
          ]
          <
        ]
        >
        [
          minus
          +>[-<]
          <[<<]
          <[-]>>
        ]
      ]
      <
    ]
    +>
    [
      plus
      -[<-]
      <[>+>+<<-<]
      <[-]>+>
    ]
    <<
    [
      comma or period or bracket
      >-
    ]
    >[,>]
    <
  ]
  comma or period or bracket or eof
  <+<
  [
    start and end same cell
    >
  ]
  >
  [
    >>>
    [
      <<<<[-<]<<<
    ]
    >>>+>>>>
    [
      start right of end
      <<<<->>>>
      [>>>[-<]>>>>]
    ]
  ]
  >
  [
    <<<
    [
      <+[-->>]
    ]
    >[-[.[-]]]
    >[<]
    >
    [
      <<++++++[>+++++++<-]>+>>
      [<<.>>-]
      <<++>-[<.>-]
      +++[<+++++>-]
      +<<<<< <+>
      [
        <<
        [
          go left
          >->>>>>.
          [[-]<<<<]
          <<<+>>>
        ]
        >
        [
          toggle left right
          ->->>>>[-]
        ]
      ]
      <
      [
        toggle right left
        ->+[>>>>>]>>[<]
        <<<<<<<<
        [
          [-]<
        ]
        >
        [
          go right
          ++.[-]
          >>>>>>>
        ]
        <
      ]
    ]
    >>
  ]
  <[>>>>>>>]
  +[-<<<<<[-]<<]
  ,
]

This uses the same approach as Keith Randall's solution, minifying all contiguous sequences of +-<> optimally by simulation. For example, +++>-<+>---< becomes ++++>----< and >+<+<<+>+<->>>> becomes +<+>>+>.

Try it online. (If a simulated cell's absolute value gets close to 256, there will be overflow issues.)

The overall structure is

while not EOF:
  while not EOF and next char not in ",.[]":
    process char
  print minified sequence (followed by the char in ",.[]" if applicable)

The tape is divided into 7-cell nodes; at the beginning of the inner loop, the memory layout is

0 s 0 c 0 a b

where s is a boolean flag for start cell, c is the current character, a is the negative part of the simulated cell value (plus one), and b is the positive part of the simulated cell value.

When the minified sequence is being printed, the memory layout is

d n e 0 0 a b

where d is a boolean flag for direction, a and b are as before (but become one/zero when printed), and n and e are only nonzero for the end node; n is related to how many times the node has been seen, and e is the value of the char that halted the inner loop (plus one).

Originally I considered keeping track of more information per node: leftmost and rightmost node as boolean flags, and node's position in relation to the start and end nodes. But we can avoid that by looking at neighboring cells when needed, and by doing left and right scans in order to find the start node.

When printing the minified sequence and deciding how to move the simulated pointer, we can take a general approach: start by moving away from the end node (in an arbitrary direction if start and end nodes are the same), turn around at leftmost and rightmost nodes, and stop based on the number of times the end node has been seen: 3 times if the start and end nodes are the same, otherwise 2.

Mitch Schwartz

Posted 2013-12-29T22:19:03.287

Reputation: 4 899

2Source: brainfuck. Target: brainfuck. +1 – Erik the Outgolfer – 2016-10-28T08:36:26.063

2

This fulfills an indefinite bounty

– Destructible Lemon – 2016-10-28T09:47:20.460

1I'll let this attract some attention and award the bounty in a day or two – lirtosiast – 2016-10-29T23:04:22.250

1@MitchSchwartz Did you happen to test your code against your code? You may actually shorten it! #meta – WallyWest – 2016-10-30T00:08:17.000

1@WallyWest (That appears to save 7 bytes!) Nevermind, the code in the permalink has linebreaks. – Dennis – 2016-10-30T00:11:32.173

7

Python, 404 chars

This code does a perfect optimization of all subsequences of +-<>. A bit more than you asked for, but there you go.

M=lambda n:'+'*n+'-'*-n                                                           
def S(b):                                                                         
 s=p=0;t=[0];G,L='><'                                                             
 for c in b:                                                                      
  if'+'==c:t[p]+=1                                                                
  if'-'==c:t[p]-=1                                                                
  if G==c:p+=1;t+=[0]                                                             
  if L==c:s+=1;t=[0]+t                                                            
 if p<s:k=len(t)-1;t,p,s,G,L=t[::-1],k-p,k-s,L,G                                  
 r=[i for i,n in enumerate(t)if n]+[s,p];a,b=min(r),max(r);return(s-a)*L+''.join(M(n)+G for n in t[a:b])+M(t[b])+(b-p)*L                                           
s=b=''                                                                            
for c in raw_input():                                                             
 if c in'[].,':s+=S(b)+c;b=''                                                     
 else:b+=c                                                                        
print s+S(b) 

It works by simulating the +-<> operations on the tape t. s is the starting position on the tape and p is the current position. After simulation, it figures out the extent [a,b] that needs to be operated on and does all the +/- in one optimal pass.

Keith Randall

Posted 2013-12-29T22:19:03.287

Reputation: 19 865

1

CoffeeScript - 403 397

i=prompt().replace /[^\]\[,.+-><]/g,''
x=(c)->
 t={};p=n=0
 for d in c
  t[p]?=0;switch d
   when'+'then n=1;t[p]++;when'-'then n=1;t[p]--;when'<'then p--;when'>'then p++
 (n=0if v!=0)for k,v of t;n
s=e=0;((e++;(i=(i.substr 0,s)+i.substr e;e=s=0)if x (i.substr s,e-s).split "")while(i[e]||0)!in['[',']',0];e=++s)while s<=i.length
r=/(\+-|-\+|<>|><|^[<>]$)/g
i=i.replace r,'' while r.test i
alert i

Demo (please forgive the use of bit.ly here, the whole URL would break the markdown)

Uncompressed version (w/ debug code):

console.clear()
input = """Should disappear: ++>>+<+++<->-->-<<->-<
Should disappear: +++>-<--->+<
Should stay: +++>-<+>---<"""

input = input.replace /[^\]\[,.+-><]/g, ''
console.log input

execute = (code) ->
  stack = {}
  item = 0
  console.log code
  nop = false
  for char in code
    switch char
      when '+' then nop = true; stack[item]?=0;stack[item]++
      when '-' then nop = true; stack[item]?=0;stack[item]--
      when '<' then item--
      when '>' then item++
  console.debug stack
  (nop = false if v != 0) for k,v of stack
  nop
start = 0
end = 0

while start <= input.length
 while input.charAt(end) not in [ '[', ']', '' ]
  end++
  if execute (input.substring start, end).split("")
    input = (input.substring 0, start) + input.substring end
    end = start = 0
    console.log input
 end = ++start
input = input.replace /(\+-|-\+|<>|><|^(<|>)$)/g, '' while /(\+-|-\+|<>|><)/.test input
console.log 'Result: ' + input

TimWolla

Posted 2013-12-29T22:19:03.287

Reputation: 1 878

This fails on >+.-<, producing the empty string instead of leaving it unchanged. – Mitch Schwartz – 2016-11-03T15:46:47.907

An alternative way of posting Coffeescript demos is to use JSFiddle. In the left margin there's a "Languages" config pane which lets you use CoffeeScript instead of JS.

– Peter Taylor – 2013-12-30T14:04:54.507

@PeterTaylor Thanks, i knew about JSFiddle before, but not that it is able to use CoffeeScript – TimWolla – 2013-12-30T15:07:05.267