27
\$\begingroup\$

I love BATCH, despite its shocking lack of functional commands, despite even due to its lack of non-integer support. Why? Because this works:

SET var=SET %var% i=0 

This would evaluate to:

SET var=SET SET i=0 

Fantastic, isn't it? I've used this technique in a BATCH program before, because it saves bytes!

Your challenge, should you accept it, would be to "golf" BATCH programs in this way. You are to decrease the byte size of an input BATCH program by including SET statements that would evaluate to portions of the program, and in no other way modify the program. (This disallows, say, renaming a variable name to something shorter. Keep in mind that BATCH, asides from variables, is case insensitive.) Your score is calculated as thus:

score = # of characters in your program + 5*(net result bytes in test cases below) 

I reserve the right to add more test cases, so as to discourage working to optimize the program for the test cases.

For the sake of this challenge, your SET statements cannot contain control characters (|, <, >, %) or linebreaks. You may not modify the code other than to move pieces of code inside a set statement. (That is, you may not remove unnecessary whitespace, replace EQU with ==, etc.) We will assume that the lines end with \n.

Test cases

Each test case is in a separate code block, and each test case is self-contained, meaning you should golf only assuming what's given within it. (I.e., if you SET d=SET in one program, that statement will not be automatically given to any other program). Each example result can be found after each test case. There is a line between test cases.

@ECHO OFF SET increment=10 :loop IF %increment% EQU 0 GOTO end ECHO %increment% SET /A %increment%-=1 GOTO loop :end EXIT

@ECHO OFF SET /p INPUT=Enter input here: SET R=%1 ECHO Last char of input here: %R:~-1%

@ECHO OFF SET increment=10 :e GOTO f ECHO f :f GOTO g ECHO g :g GOTO h ECHO h :h GOTO i ECHO i :i GOTO j ECHO j :j IF 3==4 ( ECHO 4 ) ELSE ( ECHO 5 ) IF 5==3 ( GOTO l ) ELSE ( GOTO k ) :k ECHO Done. ECHO BATCH OUT!! EXIT :l GOTO g

ECHO Hello, Hello, Hello, hello, hello, Hello, Hello!, hello, ello!, Lello.

Example outputs:

@ECHO OFF SET increment=10 :loop IF %increment% EQU 0 GOTO end ECHO %increment% SET /A %increment%-=1 GOTO loop :end EXIT
(0 bytes saved)

@ECHO OFF SET %i%= input here: SET /p INPUT=Enter%i% SET R=%1 ECHO Last char of %i%%R:~-1%
(3 bytes gained)

@ECHO OFF SET increment=10 SET g=GOTO SET e=ECHO :e %g%f %e%f :f %g%g %e%g :g %g%h %e%h :h %g%i %e%i :i %g%j %e%j :j IF 3==4 ( %e%4 ) ELSE ( %e%5 ) IF 5==3 ( %g%l ) ELSE ( %g%k ) :k %e%Done. %e%BATCH OUT!! EXIT :l %g%g
(10 chars saved)

SET %h%=ello, ECHO H%h% H%h% H%h% h%h% h%h% H%h% Hello!, h%h% ello!, Lello.
(1 character saved)

\$\endgroup\$
6
  • 2
    \$\begingroup\$ Shortening batch for fun and profit! \$\endgroup\$ Commented Feb 23, 2016 at 21:49
  • \$\begingroup\$ You need some more specifications. Of course AAA %increment%set a=increment¶AAA %%a%% is invalid, and AAA %1 BBB %2set a= BBB ¶AAA %1%a%%2 is valid. (iirc) So you need to formalize it. ( represents a newline) \$\endgroup\$ Commented May 23, 2018 at 7:43
  • \$\begingroup\$ Do we need to handle code that has delayed expansion enabled, percent-sign escapes, or multi-line for / if statements? As per the last test case (which produces additional output as echo is on and there's no @ before the SET) is extraneous output acceptable from the golfed program? \$\endgroup\$ Commented Feb 24, 2019 at 22:49
  • 1
    \$\begingroup\$ Tcl all over again \$\endgroup\$ Commented Mar 19, 2019 at 13:19
  • 1
    \$\begingroup\$ despite even due to? \$\endgroup\$ Commented Aug 29, 2019 at 7:38

1 Answer 1

6
+400
\$\begingroup\$

Java 8, Java 10, 3884 799/795 program + 484 output = 4368 1283/1279 total

There are two limitations of this code:

  • It assumes that variables from A to Z are free. (uppercase)
  • It assumes that there are no more than 27 substitutions.
  • Oh, and because Scanner doesn't quite cut it, empty input dumps out stacktrace.

But hey - there is a pro!

  • Outputs best code. Always.

The code manages to perform better than examples provided by challenge author.

This golfed version has been made by Kevin.

Java 8

c->{List<String>S=new Stack();HashMap<String,Integer>h=new HashMap(),s=new HashMap();int v=65,l=c.length(),b,e;do{for(b=0,l=c.length(),s.clear();b!=l;b++)for(e=b;++e<=l;)S.add(c.substring(b,e));S.removeIf(t->t.length()<5|t.matches(".*[\n|<%>].*"));S.forEach(t->h.merge(t,1,Integer::sum));S.clear();h.entrySet().removeIf(t->t.getValue()==1);String Y=c;int L=l;char V=(char)v;h.forEach((k,x)->{String i=Y,t;for(int j,I,q;i.contains(k);i=t+"%"+V+"%"+i.substring(j+k.length(),i.length())){for(I=-1,t=i.substring(q=0,j=i.indexOf(k));(I=t.indexOf("%",++I))>=0;q++);if(q%2>0)return;}i="SET "+V+"="+k+"\n"+i;if(i.length()<L)s.put(i,L-i.length());});h.clear();v++;c=s.isEmpty()?c:s.entrySet().stream().max((x,y)->x.getValue()>y.getValue()?1:-1).get().getKey();}while(l>c.length());return c;} 

Try it online!

Java 10

c->{var S=new Stack<String>();HashMap<String,Integer>h=new HashMap(),s=new HashMap();int v=65,l=c.length(),b,e;do{for(b=0,l=c.length(),s.clear();b!=l;b++)for(e=b;++e<=l;)S.add(c.substring(b,e));S.removeIf(t->t.length()<5|t.matches(".*[\n|<%>].*"));S.forEach(t->h.merge(t,1,(x,y)->x+y));S.clear();h.entrySet().removeIf(t->t.getValue()==1);var Y=c;int L=l;var V=(char)v;h.forEach((k,x)->{String i=Y,t;for(int j,I,q;i.contains(k);i=t+"%"+V+"%"+i.substring(j+k.length(),i.length())){for(I=-1,t=i.substring(q=0,j=i.indexOf(k));(I=t.indexOf("%",++I))>=0;q++);if(q%2>0)return;}i="SET "+V+"="+k+"\n"+i;if(i.length()<L)s.put(i,L-i.length());});h.clear();v++;c=s.isEmpty()?c:s.entrySet().stream().max((x,y)->x.getValue()>y.getValue()?1:-1).get().getKey();}while(l>c.length());return c;} 

Try it online!.

Original version

It's not golfed at all, I just wanted to have some fun, not to suffer. If you, dear reader, would like to golf this answer, please do it.

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; public class Main { 	List<String> substrings = new ArrayList<String>(); 	HashMap<String, Integer> hm = new HashMap<String, Integer>(); 	HashMap<String, Integer> scores = new HashMap<String, Integer>(); 	 	private int v1 = 65; 	 	public static String rfos(String inputString, String stringToReplace, 	 String stringToReplaceWith) { 	 int length = stringToReplace.length(); 	 int inputLength = inputString.length(); 	 int startingIndexofTheStringToReplace = inputString.indexOf(stringToReplace); 	 if(count(inputString.substring(0, startingIndexofTheStringToReplace), "%") % 2 == 1) 	 	return null; 	   	 String finalString = inputString.substring(0, startingIndexofTheStringToReplace) + stringToReplaceWith 	 + inputString.substring(startingIndexofTheStringToReplace + length, inputLength); 	 return finalString; 	} 	 	public static int count(String text, String find) { int index = 0, count = 0, length = find.length(); while( (index = text.indexOf(find, index)) != -1 ) {   index += length; count++; } return count; 	} 	 	private String process(String program) { 		int begin = 0, end, il = program.length(); 		 		scores.clear(); 		 		while(begin != program.length()) { 			for(end = begin + 1; end < program.length() + 1; end++) 				substrings.add(program.substring(begin, end)); 			begin++; 		} 		 		substrings.removeIf(new Predicate<String>() { 			@Override 			public boolean test(String arg0) { 				return arg0.length() <= 4 || arg0.contains("\n") 						|| arg0.contains("|") 						|| arg0.contains("<") 						|| arg0.contains("%") 						|| arg0.contains(">"); 			} 		}); 		 		substrings.forEach(new Consumer<String>() { 			@Override 			public void accept(String t) { 				if(hm.containsKey(t)) { 					hm.replace(t, hm.get(t) + 1); 				} else { 					hm.put(t, 1); 				} 			} 			 		}); 		 		substrings.clear(); 		 		hm.entrySet().removeIf(new Predicate<Map.Entry<String, Integer>>() { 			@Override 			public boolean test(Map.Entry<String, Integer> t) { 				return t.getValue() == 1; 			} 			 		}); 		 		hm.forEach(new BiConsumer<String, Integer>() { 			 			@Override 			public void accept(String arg0, Integer arg1) { 				String iteration = program; 				boolean between = false; 				while(iteration.contains(arg0)) { 					iteration = rfos(iteration, arg0, "%" + Character.toString((char) v1) + "%"); 					if(iteration == null) 						return; 				} 				iteration = "SET " + Character.toString((char) v1) + "=" + arg0 + "\n" + iteration; 				if(iteration.length() < program.length()) 					scores.put(iteration, program.length() - iteration.length()); 			} 			 		}); 		 		hm.clear(); 		v1++; 		 		if(scores.isEmpty()) 			return program; 		else 			return scores.entrySet().stream().max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1).get().getKey(); 	} 	public static void main(String[] args) { 		Main processor = new Main(); 		int genid = 0, before = 0, after = 0; 		String currentCode = new Scanner(System.in).useDelimiter("\\Z").next(); 		 		System.out.println("Calculating first generation..."); 		 		do { 			String cc = processor.process(currentCode); 			before = currentCode.length(); 			after = cc.length(); 			 			currentCode = cc; 			 			if(before > after) { 				System.out.println("Generation " + genid++); 				System.out.println(before + " -> " + after); 				System.out.println("***\n" + cc + "\n***"); 			} else { 				System.out.println("Generation FAIL " + genid++); 				System.out.println(before + " -> " + after); 				System.out.println("***\n" + cc + "\n***"); 			} 		} while(before > after); 		 		 	} } 

Example output:

SET B=GOTO SET A=ECHO @%A%OFF SET increment=10 :e %B%f %A%f :f %B%g %A%g :g %B%h %A%h :h %B%i %A%i :i %B%j %A%j :j IF 3==4 ( %A%4 ) ELSE ( %A%5 ) IF 5==3 ( %B%l ) ELSE ( %B%k ) :k %A%Done. %A%BATCH OUT!! EXIT :l %B%g 

Try it online!

\$\endgroup\$
6
  • \$\begingroup\$ I think all that "java.util." is repetitive. You may wany to simplify your code to import java.util.*. \$\endgroup\$ Commented Aug 29, 2019 at 8:22
  • \$\begingroup\$ I thought jdk imports do not count? \$\endgroup\$ Commented Aug 29, 2019 at 8:52
  • 1
    \$\begingroup\$ "If you, dear reader, would like to golf this answer, please do it." 799 bytes in Java 8 or 795 bytes in Java 10+. You're welcome. :) Can definitely be golfed some more, but this will do for now. \$\endgroup\$ Commented Aug 29, 2019 at 13:28
  • 3
    \$\begingroup\$ @KevinCruijssen Thanks for the contribution. I've added your version to the post. Feel free to edit it without asking me if you find something better. \$\endgroup\$ Commented Aug 29, 2019 at 13:32
  • 1
    \$\begingroup\$ Suggest e++<l instead of ++e<=l \$\endgroup\$ Commented Sep 16, 2019 at 20:03

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.