Keep the unique characters down

It's very simple: Your program or function should generate the following text:

Elizabeth obnoxiously quoted (just too rowdy for my peace): "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG," giving me a look.


Fine print

You may write a program or function, which returns the output as a string or printing it to STDOUT (or closest alternative). You may optionally include a single trailing newline in the output.

Scoring

The number of bytes in your code multiplied by the number of unique bytes in your code

Lowest score wins.

For example, a brainfuck or a whitespace answer would have a huge advantage here, as the multiplier would be very low (8 and 3 respectively). However, generally, writing programs in those languages produces much longer code which may negate that advantage.

• I'm assuming casing has to be exact? Mar 4, 2015 at 16:43
• @crayzeedude yes, that's sort of the point Mar 4, 2015 at 16:47
• Alright. Just clarifying. :p Mar 4, 2015 at 16:48
• @CarpetPython There was some debate about this in the sandbox, and ultimately I decided there was no way to TRULY level the playing field. Notice that whitespace and insomnia have the highest scores anyway! Mar 6, 2015 at 6:14
• As I wrote somewhere, if we assume that using more bytes to compensate for less unique characters behaves similarly to converting numbers into different bases, then approximately bytecount~Log(X,unique), with X some constant for this problem. Thus unique^bytecount ~ constant. Computing this score (log2) gives python2_mbomb007 728, python2_carpetpython 744, ruby 756, Fish 825, Insomnia 1148, cjam 1277, whitespace 1484, brainfuck 3546. So except for the brainfuck answer, it is relatively constant... Mar 6, 2015 at 11:23

CJam, 266281 456 bytes * 1412 7 unique = 37243372 3192

Try it online.

14301201124204202034420112034224204431020210101232301021240204310431312122132100240400222324402030223103420431324402222132223233141443401210314023001122320404112224314302132421403301243334313000011124244441400003310332301330220022110121411122100040310110020040121444302100143202204330334033211334242120304123121024200421121232100303121022431044444423243331440434010014400~~10~~100~~1~43c~c~4~~41c~100~~1~43c~c~123~~100~~1~43c~c~44~~14~~43c~c100~~40c~c43c~~


Explanation

The strategy I've used is to treat each character in the string as a base-123 digit and encode that as a decimal number in the program. The program then converts that number back to base 123 and maps each base-123 digit back to a character. Because it's hard to explain why the program is in its current state, I'll explain each version of it.

Here's what the end of the program looked like in the first version:

...2068438725 123b:c


This implements the strategy in the most straightforward way possible. The number, encoded normally in base 10, is converted back to base 123 and each base-123 digit is mapped back to a character. But this uses 4 unique non-digit characters, and being able to get rid of any one of them would likely be worth the size hit due to having to use less straightforward code.

First, I realized that I could get rid of the b and the : operators by creating them at runtime as their ASCII character values converted back to a character (with the already present c operator) and evaluating them with the ~ operator. It turned out to be a little tricky to do this with the : operator, since it has to be parsed together with the following c operator. I solved this by producing the characters : and c and then producing and evaluating the character +, which concatenates the former two characters into the string :c which can then be evaluated properly.

Second, I realized that the ~ operator I just introduced had a handy new overloaded variant: when given a number, it produces the bitwise complement. By using this twice in a row after a number, I could introduce a token break in the source with no resultant computational effect, allowing me to replace the spaces used to separate numbers with ~~.

The final result is 15 more bytes of code at the end, but this cost is greatly outweighed by the benefit of eliminating 2 unique characters out of 14. Here's a comparison of the end of the first version with the end of the second version:

...2068438725  123[ b  ][   :c    ]
...2068438725~~123~~98c~58c99c43c~~


Using any fewer than the 2 operators I was using would be impossible, but I still wanted fewer unique characters. So the next step was to eliminate digits. By changing the number's encoding so that each decimal digit was really a base-5 digit, I could potentially eliminate the digits 6-9. Before eliminating anything from the end of the prgoram, it looked like this:

...4010014400 10b5b123b:c


As mentioned before, eliminating the space is easy. But the b, :, and c would not be so easy, as their character codes are 98, 58, and 99, respectively. These all contained digits marked for elimination, so I had to find ways to derive them all. And the only useful numeric operators with character values not containing 5-9 were decrement, increment, multiply, and add.

For 98, I initially used 100~~40c~40c~, which decrements 100 twice. But then I realized I could make yet another use of the ~ operator, as bitwise complement lets me get negative numbers which, when added, let me emulate subtraction. So I then used 100~~1~43c~, which adds 100 and -2 and is 2 bytes smaller. For 58, I used 44~~14~~43c~, which adds 44 and 14. And for 99, I used 100~~40c~, which decrements 100.

The final result is pretty big and obfuscated, but the cost of the significantly larger number and processing code were slightly outweighed by the big benefit of eliminating 5 unique characters out of 12. Here's a comparison of the final end of the program before eliminations and after eliminations:

...4010014400  10[      b      ][  5  ][     b     ]123[      b      ][            :c            ]
...4010014400~~10~~100~~1~43c~c~4~~41c~100~~1~43c~c~123~~100~~1~43c~c~44~~14~~43c~c100~~40c~c43c~~

• Ok, now I'm impressed. How do base 6 digits, base 7 digits, etc. compare to base 5? Mar 4, 2015 at 19:21
• @durron597 They don't really make the computations any easier, as the character codes of the operators I need (98, 58, and 99) are still out of range. And incrementing the base only decreases the total size of the program by 8-10%, which isn't enough to make up for the 10-15% score gained by having a new unique character. Re-including the b operator similarly isn't worth it. Mar 4, 2015 at 20:38
• Take a number x, and convert it to base b. Its length will be floor(log_b(x)+1), and it will contain b different symbols. So the score is b*floor(log_b(x)+1). x is a given large number, and if you plot this for b, you'll find the minimum is pretty much at b=3. Ie, the length reduces slightly as you use higher bases (log), but size of the charset increases linearly, so it isn't worth it. Made a simple whitespace program, but it gets only a score of 4134. Mar 4, 2015 at 22:54
• @blutorange You should put up the whitespace program. 4134 is enough for second place Mar 4, 2015 at 23:00

Whitespace, 1157 937 bytes * 3 unique = 3471 2811

By popular(?) request, I'm posting my whitespace solution.

In order to reduce the code needed, I hardcoded the entire string as one binary number (7 bits for each byte). A simple loop extracts the characters and prints them.

Source code on filebin.ca.

NOTE: The specifications allow for arbitrary large integers, but the Haskell interpreter on the official page is limited to 20 bits. Use, for example, this ruby interpreter on github/hostilefork/whitespaces.

The ruby script to create the whitespace program (l=WHITESPACE, t=TAB, u=NEWLINE, everything after // ignored, writes to a file prog.h):

str = 'Elizabeth obnoxiously quoted (just too rowdy for my peace): "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG," giving me a look.'

def b2ws(bin)
bin.chars.map{|x|x=='0' ? "l" : "t"}.join
end
end

bin = str.reverse.chars.map do |x|
"  " + d2ws(x.getbyte(0),7) + " // char <#{x=="\n" ? "LF" : x}>"
end
data = "lll // pushes string as one large number\n#{bin.join("\n")}\nu"

File.open('prog.h','w') do |fout|
fout << data << "\n"
fout << <<-eos

// output string loop
ulllu // label 0

// extract, print, and remove the last 7 bits of the data
lul   // dup
llltlllllllu // push 128
tltt // mod
tull  // print as char
llltlllllllu // push 128
tltl // div

// break loop if EOS
lul   // dup

ulltu // label 1
uuu
eos
end


For illustration, the whitespace program in human-readable form. See below for a simple script to convert it to an actual whitespace program.

lll // pushes string as one large number
ltltttl // char <.>
ttltltt // char <k>
ttltttt // char <o>
ttltttt // char <o>
ttlttll // char <l>
ltlllll // char < >
ttllllt // char <a>
ltlllll // char < >
ttlltlt // char <e>
ttlttlt // char <m>
ltlllll // char < >
ttllttt // char <g>
ttltttl // char <n>
ttltllt // char <i>
tttlttl // char <v>
ttltllt // char <i>
ttllttt // char <g>
ltlllll // char < >
ltllltl // char <">
ltlttll // char <,>
tlllttt // char <G>
tlltttt // char <O>
tllltll // char <D>
ltlllll // char < >
tlttllt // char <Y>
tlttltl // char <Z>
tlllllt // char <A>
tllttll // char <L>
ltlllll // char < >
tllltlt // char <E>
tlltlll // char <H>
tltltll // char <T>
ltlllll // char < >
tltlltl // char <R>
tllltlt // char <E>
tltlttl // char <V>
tlltttt // char <O>
ltlllll // char < >
tltlltt // char <S>
tltllll // char <P>
tllttlt // char <M>
tltltlt // char <U>
tlltltl // char <J>
ltlllll // char < >
tlttlll // char <X>
tlltttt // char <O>
tlllttl // char <F>
ltlllll // char < >
tlltttl // char <N>
tltlttt // char <W>
tlltttt // char <O>
tltlltl // char <R>
tlllltl // char <B>
ltlllll // char < >
tlltltt // char <K>
tlllltt // char <C>
tlltllt // char <I>
tltltlt // char <U>
tltlllt // char <Q>
ltlllll // char < >
tllltlt // char <E>
tlltlll // char <H>
tltltll // char <T>
ltllltl // char <">
ltlllll // char < >
ltttltl // char <:>
ltltllt // char <)>
ttlltlt // char <e>
ttllltt // char <c>
ttllllt // char <a>
ttlltlt // char <e>
tttllll // char <p>
ltlllll // char < >
ttttllt // char <y>
ttlttlt // char <m>
ltlllll // char < >
tttlltl // char <r>
ttltttt // char <o>
ttllttl // char <f>
ltlllll // char < >
ttttllt // char <y>
ttlltll // char <d>
tttlttt // char <w>
ttltttt // char <o>
tttlltl // char <r>
ltlllll // char < >
ttltttt // char <o>
ttltttt // char <o>
tttltll // char <t>
ltlllll // char < >
tttltll // char <t>
tttlltt // char <s>
tttltlt // char <u>
ttltltl // char <j>
ltltlll // char <(>
ltlllll // char < >
ttlltll // char <d>
ttlltlt // char <e>
tttltll // char <t>
ttltttt // char <o>
tttltlt // char <u>
tttlllt // char <q>
ltlllll // char < >
ttttllt // char <y>
ttlttll // char <l>
tttlltt // char <s>
tttltlt // char <u>
ttltttt // char <o>
ttltllt // char <i>
ttttlll // char <x>
ttltttt // char <o>
ttltttl // char <n>
ttllltl // char <b>
ttltttt // char <o>
ltlllll // char < >
ttltlll // char <h>
tttltll // char <t>
ttlltlt // char <e>
ttllltl // char <b>
ttllllt // char <a>
ttttltl // char <z>
ttltllt // char <i>
ttlttll // char <l>
tllltlt // char <E>
u

// output string loop
ulllu // label 0

// extract, print, and remove the last 7 bits of the data
lul   // dup
llltlllllllu // push 128
tltt // mod
tull  // print as char
llltlllllllu // push 128
tltl // div

// break loop if EOS
lul   // dup

ulltu // label 1
uuu


Basically, the string to output is a long integer, and you need to reduce its score.

Take a number x, and convert it to base b. Its length will be floor(log_b(x)+1), and it will contain b different symbols. So the score is b*floor(log_b(x)+1). x is a given large number, and if you plot this for b, you'll find the minimum is pretty much at b=3 (and b=2 is almost as good). Ie, the length reduces slightly as you use higher bases (log), but size of the charset increases linearly, so it isn't worth it.

Thus I looked for a language with only 0/1's, but I didn't find any, and then I remembered there was whitespace and tried it. In whitespace, you can enter binary numbers with 0's and 1's directly.

Old code, worse score but more interesting

Old code on filebin.

The ruby script I used for creating the program (l=WHITESPACE, t=TAB, u=NEWLINE, everything after // ignored, writes to a file prog.h):

str = 'Elizabeth obnoxiously quoted (just too rowdy for my peace): "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG," giving me a look.' + "\n"

def shift(x)
(x-97)%128
end

EOS = "lttltl" #26
STACK = []

bin = str.reverse.chars.map do |x|
byte = shift(x.getbyte(0))
rf = STACK.index(byte)
STACK.unshift(byte)

y = byte.to_s(2).chars.map{|y|y=='0' ? 'l' : 't'}.join
ph = "lll#{y}u" # pushing directly
if rf
bn = rf.to_s(2).chars.map{|y|y=='0' ? 'l' : 't'}.join
cp = "ltll#{bn}u" # copying from stack
end

if STACK.size>0 && STACK[0]==STACK[1]
"lul // dup #{x.inspect}"
elsif cp && cp.size < ph.size
"#{cp} // copy <#{x.inspect}> (from #{rf})"
else
"#{ph} // push <#{x.inspect}> (#{shift(x.getbyte(0))})"
end
end

File.open('prog.h','w') do |fout|
fout << "ll#{EOS}u // push EOS" << "\n"
fout << bin.join("\n") << "\n"
fout << <<-eos
// output string
ullu // label 0
// add 97 (128) before printing
lllttlllltu // push 97
llltlllllllu // push 128
tltt // mod
tull  // print top of stack

// break loop if EOS
lul   // dup
ll#{EOS}u // push EOS
tllt // subtract

ulltu // label 1
uuu
eos
end


For illustration, the whitespace program in human-readable form. See below for a simple script to convert it to an actual whitespace program.

lllttltlu // push EOS
llltltlltu // push <"\n"> (41)
llltllttltu // push <"."> (77)
llltltlu // push <"k"> (10)
llltttlu // push <"o"> (14)
lul // dup "o"
llltlttu // push <"l"> (11)
lllttttttu // push <" "> (63)
llllu // push <"a"> (0)
ltlltu // copy <" "> (from 1)
llltllu // push <"e"> (4)
lllttllu // push <"m"> (12)
ltlltlu // copy <" "> (from 2)
lllttlu // push <"g"> (6)
lllttltu // push <"n"> (13)
llltlllu // push <"i"> (8)
llltltltu // push <"v"> (21)
ltlltu // copy <"i"> (from 1)
lllttlu // push <"g"> (6)
ltllttlu // copy <" "> (from 6)
llltllllltu // push <"\""> (65)
llltlltlttu // push <","> (75)
lllttllttlu // push <"G"> (102)
lllttltttlu // push <"O"> (110)
lllttlllttu // push <"D"> (99)
ltlltltu // copy <" "> (from 5)
lllttttlllu // push <"Y"> (120)
lllttttlltu // push <"Z"> (121)
lllttlllllu // push <"A"> (96)
lllttltlttu // push <"L"> (107)
ltlltllu // copy <" "> (from 4)
lllttlltllu // push <"E"> (100)
lllttlltttu // push <"H"> (103)
llltttllttu // push <"T"> (115)
ltllttu // copy <" "> (from 3)
llltttllltu // push <"R"> (113)
ltlltllu // copy <"E"> (from 4)
llltttltltu // push <"V"> (117)
ltlltttlu // copy <"O"> (from 14)
ltlltllu // copy <" "> (from 4)
llltttlltlu // push <"S"> (114)
lllttlttttu // push <"P"> (111)
lllttlttllu // push <"M"> (108)
llltttltllu // push <"U"> (116)
lllttltlltu // push <"J"> (105)
ltlltltu // copy <" "> (from 5)
llltttltttu // push <"X"> (119)
ltlltlllu // copy <"O"> (from 8)
lllttlltltu // push <"F"> (101)
ltllttu // copy <" "> (from 3)
lllttlttltu // push <"N"> (109)
llltttlttlu // push <"W"> (118)
ltlltllu // copy <"O"> (from 4)
ltlltllltu // copy <"R"> (from 17)
lllttlllltu // push <"B"> (97)
ltlltltu // copy <" "> (from 5)
lllttltltlu // push <"K"> (106)
lllttllltlu // push <"C"> (98)
lllttltlllu // push <"I"> (104)
ltllttttu // copy <"U"> (from 15)
llltttllllu // push <"Q"> (112)
ltlltltu // copy <" "> (from 5)
ltllttlltu // copy <"E"> (from 25)
ltllttttlu // copy <"H"> (from 30)
ltllttttlu // copy <"T"> (from 30)
llltllllltu // push <"\""> (65)
ltlltllu // copy <" "> (from 4)
llltlttlltu // push <":"> (89)
llltlltlllu // push <")"> (72)
llltllu // push <"e"> (4)
llltlu // push <"c"> (2)
llllu // push <"a"> (0)
llltllu // push <"e"> (4)
lllttttu // push <"p"> (15)
ltlltttu // copy <" "> (from 7)
lllttlllu // push <"y"> (24)
lllttllu // push <"m"> (12)
ltlltlu // copy <" "> (from 2)
llltllltu // push <"r"> (17)
llltttlu // push <"o"> (14)
llltltu // push <"f"> (5)
ltllttu // copy <" "> (from 3)
ltllttlu // copy <"y"> (from 6)
lllttu // push <"d"> (3)
llltlttlu // push <"w"> (22)
llltttlu // push <"o"> (14)
ltlltttu // copy <"r"> (from 7)
ltlltltu // copy <" "> (from 5)
ltlltlu // copy <"o"> (from 2)
lul // dup "o"
llltllttu // push <"t"> (19)
ltllttu // copy <" "> (from 3)
ltlltu // copy <"t"> (from 1)
llltlltlu // push <"s"> (18)
llltltllu // push <"u"> (20)
llltlltu // push <"j"> (9)
llltllltttu // push <"("> (71)
ltlltltu // copy <" "> (from 5)
lllttu // push <"d"> (3)
llltllu // push <"e"> (4)
ltlltttu // copy <"t"> (from 7)
llltttlu // push <"o"> (14)
ltlltttu // copy <"u"> (from 7)
llltllllu // push <"q"> (16)
ltllttlu // copy <" "> (from 6)
lllttlllu // push <"y"> (24)
llltlttu // push <"l"> (11)
llltlltlu // push <"s"> (18)
ltlltltu // copy <"u"> (from 5)
llltttlu // push <"o"> (14)
llltlllu // push <"i"> (8)
llltltttu // push <"x"> (23)
ltlltlu // copy <"o"> (from 2)
lllttltu // push <"n"> (13)
llltu // push <"b"> (1)
ltlltlu // copy <"o"> (from 2)
ltlltlttu // copy <" "> (from 11)
llltttu // push <"h"> (7)
llltllttu // push <"t"> (19)
llltllu // push <"e"> (4)
llltu // push <"b"> (1)
llllu // push <"a"> (0)
lllttlltu // push <"z"> (25)
llltlllu // push <"i"> (8)
llltlttu // push <"l"> (11)
lllttlltllu // push <"E"> (100)
// output string
ullu // label 0
// add 97 (128) before printing
lllttlllltu // push 97
llltlllllllu // push 128
tltt // mod
tull  // print top of stack

// break loop if EOS
lul   // dup
lllttltlu // push EOS
tllt // subtract

ulltu // label 1
uuu


This whitespace program itself is rather simple, but there are three golfing optimizations:

• use lul to clone the stack when there's a duplicate character
• use ltl to clone the n-th entry of the stack if its shorter than pushing the char directly
• shift down all bytes by 97 (mod 128), makes the binary numbers smaller

A simple ruby script to convert my human readable whitespace code to an actual whitespace program (read a file prog.h and writes to a file prog.ws):

WHITESPACE = "l"
NEWLINE = "u"
TAB = "t"

File.open('prog.ws','w') do |fout|
code = ""
fin.each_line do |line|
line.gsub!(/\/\/.*/,'')
line.scan(/#{NEWLINE}|#{WHITESPACE}|#{TAB}/i) do |x|

code << case x.downcase
when NEWLINE.downcase
"\n"
when WHITESPACE.downcase
" "
when TAB.downcase
"\t"
else
""
end
end
end
fout << code
end

• The trailing newline is optional, you can save a few characters by not printing it. Mar 5, 2015 at 13:59
• Thanks, changed it. Originally, I included it because the Haskell interpreter adddd some debug output, but with the ruby one, it looks clean. Mar 5, 2015 at 14:22

Insomnia, 575 bytes * 4 = 2300

dyedd=dyyyye=d=yd=dddde=ded=deey=eyy=de=edd=eydyee=yeyy=yey==ddyee=eydee=y=eyeedd=dde=yddd=eede=yedde=eeyd==yeyy=yyddy=eyy=dy=dddye=dyey=ede=yddd=dde=de=yyye=yyye=ddyyy=dyy=yeyddy=eyyyde=eed=ddd=yd=edyyd=eeydd=yedd=eye=eee=eeyy=yyyedde=d=eyyyedy=d=yddyye==dyyede=ddd=eydd=yd=ddde==eeeeyy=yeyd=deyy=yyy=ydy=dy=dd=e=ey=dddddy=yeey=ey=eed=ddd=eedy=ddddde=ydeyy=edd=ydy=ddy=yeedd=ydde=dde=edyye=ded=yeed=dddddde=eedy=eyde=ydy=ydydd=eyydd=dd=dddd=ddyye=ydeey=ed=eeeye=y=ydydde=eyyd=eyee=dy=edeyed=dyed==eyyddey=dd=yddd=yddd=de=ddedy=eyydd=eeedy==eyyydeee=d=d=eydd=yey=dyy==yyyedy=


Uses 4 characters dye=.

Solution 1b (unpublished): 783 bytes * 3 = 2349

eeeeyeeyy=eeyy=yeyye=yyyeeyye=eeeye=yeyey=eyeyyy=eyy=eeyeye=yeyeyyy=yyeeye=yeyy=yey==yyeyeyeye=eyyey=y=yyyyyy=y=eyyeyyyyyyye=eeye=yyeeyye=eyeyeyyy==yeyy=yeyyyeyye=yyyeeyy==eeyyeee==eeyye=yyyyeyyeyyye=yyyyyyy=yeye=eyyyyy=eeye=eeyeeeeey=eyeye=eeee=yeyyye=eyyyyye=y=eeeyyy=eyyeyee=yyy=eyye=yeyeeee=yyeyeyyyy=eeyy=yyyyy=eyyyy=yyeeyye=e=eyyyyy=eyey==eyeyy=yyyyyeyeyeye=ye=yyyyyy==eeeeyy=yeye=yeyyey=yyy=eyyyeeeye==yeeyyeyy=e==yeeeyyyeyy=yeey=ey=eee=eyeyy=yyeeee=yeeyyyyy=yee=yeyyy=yeeeeyy=eyeeeeeye=eeeyy=yyeeeeyyye=yey=yyyyeeyye=yyyyyyy=yeee=eeeeyyeye=eyeeye=eeyyeyyy=eeyee=yyyyyeeyy=eee=eeyyeyyyyy=eeey=eyyeeyy=eyyeyyy=eeeeyeee=eeeye=y=yyyeyy=yyeeee=yyeyey=ey=eyyyyyeyyy=yeye==yeeyyyeyy=yeye=yyyyeeyy=eeeyyeeyyy=yeeyeyyy=eeyye=eee=eyyye==eyeyeyyyyy=e=eeyeeye=yeey=eyeyy=eyeye=ey=eyyeye=


Only uses 3 characters ey=.

Solution 1: 826 bytes * 3 = 2478

yoyoy~oyoyo~oyooyy~ooyyoyy~ooyyyyoy~~yoyyo~ooyoooyooo~yoyyo~oyoyoyyy~yyo~oyyoo~~ooooy~oyyyyy~oyoyoo~~oyyyoooyy~~oyoyooyyy~yyooo~yoyo~yoyyyyo~yooyo~y~yyoyyyooyy~oyyooy~ooyoy~oyyooy~ooyyo~yoyyoo~o~ooyo~yooyyoo~yoyoy~ooyyyyooo~oyoyyo~yyoyyyoo~yyo~yyooyyo~yoyyoooy~~yoyyyooyoyy~oyoyyy~yoooyyoy~yooyy~yooyyo~yyooyyoo~oyoyo~oyyy~oyyo~oyyoyo~oyyyoo~oyoyyoo~yooyo~ooooo~~yoyyoyyoyoo~yoyyooo~y~ooooy~yyyoy~ooooyyoyyoy~oyyoo~oyyoyo~yoyyyooy~oyooo~yoyyoyy~yyyyyo~oyooy~oyoyy~yoyyyoy~ooyoy~yoyyoooy~~oyyyo~ooooooy~yooyoyyoyo~yooyyo~oooyyo~yoyooyyo~oyyy~o~yooooy~oyoyyooooy~~yoyyooyyo~oy~yyyoyyyyy~yoyyyoy~yooo~oooy~oooooyyoyyyo~yoyyo~y~oooyyyoyoo~yyooooyoyy~y~oooyyyoy~~yoy~oyyoyoyyy~yoyyyyy~oyooo~yyyyooo~yooyyoy~yooyo~yoyyyo~yyoyyyo~o~ooooooyoy~oyoyyoy~yyoooo~ooooyy~oyoyoy~ooyyo~yyoooyo~yyyyyo~oyyo~yooyyooyyoy~~ooyyyyoyyy~ooyyo~yyooo~


Only uses 3 characters: yo~. A program is used to generate this.

Currently, all programs only use instructions 0, 1, 2, 6. In other words, they manipulate the bits in a single byte and print out the result.

Ruby 144 Bytes * 39 Unique = 5616

puts"Elizabeth obnoxiously quoted (just too rowdy for my peace): \"#{'the quick brown fox jumps over the lazy dog,'.upcase}\" giving me a look."


Sometimes the simplest is the best.

• You don't use any simplifications in the data. The cjam and whitespace examples got impressive scores along with some explanations. Even with the bonus for using tidy code in a sane language, it's too much ground to make up!!! Maybe you can re-enter in the length + unique scoring contest. Mar 5, 2015 at 8:30

Brainfuck, 1264 bytes * 7 unique = 8848

Yeah, it's a terrible score.

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

• I found that it's possible to use fewer unique characters, but the length would be 10795, times 5 = 53975. Mar 4, 2015 at 20:53
• you could do it with only +. not sure it would be any smaller though. Mar 5, 2015 at 6:06
• Using only + and . I get 15018 * 2 = 30,036. Mar 5, 2015 at 6:17

><> (Fish) - 578 bytes * 8 unique = 4624

14121412412431421124321110441421130402401433401430421141143114240333221142303303210321143224241321121343212340321131301320302344221132340304221130322340321313221100323234202311321143224241341121131213110444324310422421114441421142412420421114400443412442421112412413421113403423411403121100410413412423432421114431403423412401404412402434312421140413410434324324401431443243*3*3+3*1+00p44*4+3*2+01p44*3*4+1+11p44*3*4+1+21p43+3*2*31p43+3*2*41p43*3*41+1p44*3*4+1+42+1p43+3*2*43+1p43+3*2*1+44+1p43+3*2*1+33*1p43*3*3*3+33*1+1p43*3*3*33*2+1p43+3*3*43*1p44*2*1+43*1+1p44*3+3*2+43*2+1p


My score isn't as competitive as I was hoping, but I thought this solution was still interesting enough to post.

Explanation

The first section of code is a long string of the digits 0-4 that represent a 3 digit base 5 representation for each character in the string. The remaining code takes advantage of the p operator in Fish that allows you to edit the program's source code while the program is running. By using that operator, I was able to generate the Fish code necessary to convert the base 5 characters back to base 10 and output them, and then put that code back into the source code at the beginning of the file before the interpreter reached the end of the line and wrapped around. When the interpreter reaches the end of the line, the code has been modified to look like this:

v4121412412431421124321110441421130402401433401430421141143114240333221142303303210321143224241321121343212340321131301320302344221132340304221130322340321313221100323234202311321143224241341121131213110444324310422421114441421142412420421114400443412442421112412413421113403423411403121100410413412423432421114431403423412401404412402434312421140413410434324324401431443243*3*3+3*1+00p44*4+3*2+01p44*3*4+1+11p44*3*4+1+21p43+3*2*31p43+3*2*41p43*3*41+1p44*3*4+1+42+1p43+3*2*43+1p43+3*2*1+44+1p43+3*2*1+33*1p43*3*3*3+33*1+1p43*3*3*33*2+1p43+3*3*43*1p44*2*1+43*1+1p44*3+3*2+43*2+1p
>55**\$5*++ol?!;


When the code wraps around and hits the v operator it goes down to the second line, hits the > operator and proceeds to loop, each time converting the base 5 encoding back to a base 10 ascii value and then outputting that value. When there are no more values on the stack the ? operator will skip to the ; and the program will end.

7, 273 bytes × 7 unique bytes = 1911, noncompeting (language postdates challenge)

501513010250341012432044041204045104011053033043511402401130443235151151105240424404004144031133304214014044433043401032201433022042551243201043511102223044114403120042140450415114434050440500114042432010430032533041340424320250451514210501020420444010403043040402343447403


Try it online!

Unfortunately, this is an old challenge, meaning that I don't get to win with one of my newest languages. However, the language was created with no knowledge of the challenge (I just randomly stumbled across it in "related questions"), but nonetheless pretty much turned out to be a perfect fit.

7 programs on PPCG normally get submitted in packed format, packing eight commands into three bytes (the language has twelve commands, but only eight can appear in a source file, meaning that three bits are enough). However, the language also supports an octal format in which each command is written as an ASCII digit, and that's what I've used here, meaning that only seven distinct bytes are used (the 6 command isn't necessary in a program that just prints a simple string).

The program is very simple; it consists of two stack elements, a long string that just gets printed verbatim, a 7 to separate the elements (unfortunately unavoidable), and 403 which is a simple way of printing a constant string in 7 (40 escapes the second stack element, whilst moving it to the top of the stack, then 3 prints it and discards the old first stack element, i.e. the 403 itself).

So how did I get the string as short as 269 bytes? 7 supports multiple I/O formats, and one of its formats is US-TTY, a character set (specifically, a variant of Baudot) which was widely used before ASCII was invented. (The 5 at the start of the second stack element, i.e. the start of the program, specifies the string's encoding; the rest is the string content itself.) It's a five-bit character set, and the commands 0 to 5 can safely be stored in strings whilst being unescaped consistently (so that escaping it once will exactly reproduce the original), so the language uses pairs of commands (36 options) to encode characters of US-TTY (32 options, with 4 being used for directives to the 7 interpreter itself). Of course, there are more than 32 unique characters that people might want to output (and more than 32 characters appear in the string), so two of the characters are "shift codes" that switch between four character sets (uppercase letters, lowercase letters, figures, and a user-defined "figures extension" which 7 uses for the remaining ASCII characters that aren't in the other character sets; however, all the characters in the string are "natively" in US-TTY so the details of the extension didn't matter). Here's the program's output, with non-ASCII characters added to show where shifts occur (and a line break added to make reading easier, which isn't in the original string):

Ełlizabeth obnoxiously quoted Ø(Łłjust too rowdy for my peaceØ):
"ŁTHE QUICK BROWN FOX JUMPS OVER THE LAZY DOGØ," Łłgiving me a lookØ.


I count ten shifts added to 124 characters of input, a fairly negligible ratio. As such, the ability to use only just over two bytes of input per input character, times 7 unique bytes, means that the score on this challenge is incredibly good. (I guess a language that was designed specifically for this challenge would use some sort of string compression, rather than a pre-existing character set, but even though Baudot and US-TTY weren't designed for golfing, they're still fairly concise.)

• That loophole is to prevent writing a language for the purpose of answering a challenge, which this is clearly not. Still worthy of an upvote from me, though I can't accept it despite having the lowest score to date. Dec 14, 2016 at 21:25

Python 2, 163147145 143 bytes * 3536 35 unique = 570552925220 5005

That's probably about as good as I'm going to get it.

Edits:

1. Removed .capitalize() in favor of using E.
2. Changed to use ' instead of backslash escaping quotes.
3. Removed + and some spaces to use commas in the print statement.
print"Elizabeth obnoxiously quoted (just too rowdy for my peace):",'"the quick brown fox jumps over the lazy dog,"'.upper(),"giving me a look."

• I tried looking into starting with binary or hex and converting to the string, but the benefits are lost in the conversion process. Mar 5, 2015 at 1:42

Python 2, 14508 11700 11088 10164 9486 9746 7860 145 bytes * 36 unique = 5220

I saw the title and thought this was an interesting challenge for the rather wordy Python. These are my notes as I tackled this problem.

My first try reduced the uniques to 31:

print''.join(chr([69,108,105,122,97,98,101,116,104,32,111,98,110,111,120,105,111,117,115,108,121,32,113,117,111,116,101,100,32,40,106,117,115,116,32,116,111,111,32,114,111,119,100,121,32,102,111,114,32,109,121,32,112,101,97,99,101,41,58,32,34,84,72,69,32,81,85,73,67,75,32,66,82,79,87,78,32,70,79,88,32,74,85,77,80,83,32,79,86,69,82,32,84,72,69,32,76,65,90,89,32,68,79,71,44,34,32,103,105,118,105,110,103,32,109,101,32,97,32,108,111,111,107,46][r])for r in range(124))


I thought I could do better. By using map, uniques came down to 26:

print''.join(map(chr,(69,108,105,122,97,98,101,116,104,32,111,98,110,111,120,105,111,117,115,108,121,32,113,117,111,116,101,100,32,40,106,117,115,116,32,116,111,111,32,114,111,119,100,121,32,102,111,114,32,109,121,32,112,101,97,99,101,41,58,32,34,84,72,69,32,81,85,73,67,75,32,66,82,79,87,78,32,70,79,88,32,74,85,77,80,83,32,79,86,69,82,32,84,72,69,32,76,65,90,89,32,68,79,71,44,34,32,103,105,118,105,110,103,32,109,101,32,97,32,108,111,111,107,46)))


At about this time, I noticed in the question text that the score was uniques * bytes, not just uniques! That meant my scores for the above were 14508 and 11700. Not very competitive. So I now reduce the bytes by storing the text as a hex string:

# 308*36 = 11088
print''.join(chr(int('456c697a6162657468206f626e6f78696f75736c792071756f74656420286a75737420746f6f20726f77647920666f72206d79207065616365293a202254484520515549434b2042524f574e20464f58204a554d5053204f56455220544845204c415a5920444f472c2220676976696e67206d652061206c6f6f6b2e'[i*2:i*2+2],16)) for i in range(124))


Size was reduced but more unique characters. But if I used a packed 2 digit decimal string with a 32 offset:

# 308*33 = 10164
print''.join(chr(int('37767390656669847200796678798873798583768900818579846968000874858384008479790082798768890070798200778900806965676909260002524037004953413543003450475546003847560042534548510047543750005240370044335857003647391202007173867378710077690065007679797514'[i*2:i*2+2])+32) for i in range(124))


This has the same number of bytes but saves 3 uniques.

I hatch a new plan. If I pack a Python long integer with 7 bit characters, I could extract each one by shifting:

# 306*31 = 9486
h=1073974643401006528619595312441225198653732186368270382545648881135648217524502741093886285232362673460172159947573049818819511630304840724474679255867143965214892747087773876949021986013520804726327302180335979259392708372721217579101211940864406962137554744750
w=''
while h:w=chr(h&127)+w;h>>=7
print w


Well that reduced the score to 9486. An interesting experiment, but nowhere near good enough. Now what if I get rid of the function names and rely on string formatting?

# 443 * 22 = 9746
print('%c'*124)%(69,108,105,122,97,98,101,116,104,32,111,98,110,111,120,105,111,117,115,108,121,32,113,117,111,116,101,100,32,40,106,117,115,116,32,116,111,111,32,114,111,119,100,121,32,102,111,114,32,109,121,32,112,101,97,99,101,41,58,32,34,84,72,69,32,81,85,73,67,75,32,66,82,79,87,78,32,70,79,88,32,74,85,77,80,83,32,79,86,69,82,32,84,72,69,32,76,65,90,89,32,68,79,71,44,34,32,103,105,118,105,110,103,32,109,101,32,97,32,108,111,111,107,46)


I now have only 22 uniques, but the score does not improve.

Ok, What if I took the obvious way and just printed the string:

# 131*60 = 7860
print'Elizabeth obnoxiously quoted (just too rowdy for my peace): "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG," giving me a look.'


Score of 7860. I should have done this first. But I would not have learned so much.

I guess I could reduce uniques by 26 if I dynamically produced the upper case parts, so:

# 145*36 = 5220
print'Elizabeth obnoxiously quoted (just too rowdy for my peace): '+'"the quick brown fox jumps over the lazy dog,"'.upper()+' giving me a look.'


I think Python will not get much better than 5220. The task of minimising the unique characters in Python sure was instructive though.

Update: mbomb007 has a better Python solution scoring 5005. Nice work.

• Nice optimisations in your solution +1. I have edited the title as you have suggested. Mar 6, 2015 at 0:39

><> (Fish) - 138 Bytes * 65 Unique = 8970

The simple route, i.e. Hello World:

!v'Elizabeth obnoxiously quoted (just too rowdy for my peace): "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG," giving me a look.'r!
>l?!;o


Well, for my first foray into ><> this was a challenge. I think I see some improvements, but it was fun learning it :)

Or to make it overly complicated - 1567 bytes * 27 Unique = 42309

!\f8+3*:o2*f2*-:o3-:of+2+:ofa+-:o1+:o3+:of+:oc-:o\7+:od-:oc+:o1+:o9+: of-:o6+:o6+:o2-:o7-: od+:o\8-:o4+:o6-:o5+:of -:o1-:o\\6+:ob+ :o2-:o1+:o\:o5-:o:o\3+ :o3-:o8+:of4+-:oab ++:o\f4+-:o9+ :o3+: o\!
!5:of+a+:o1-:off+2+o:f-6-oa-:o8-:of3*1-:oa-:o2-:!fo3*7+:o2+:o:d+o:o5!f+:o7-:off+2+o6+:o8-!f:off!f+2+o4-:off+2+ob+!f:o3+::\ff    f f          f        f  f                  f    f        f     \!f-
-                                               f                   f                    f     f                 f       ff    3 f          f        f  f                  f    f        f       f
:                                               +                   +                    +     +                 +       ++    * +          +        +  +                  +    +        +       +
o                                               2                   2                    2     2                 2       2a    1 2          2        2  2                  2    2        2       2
c                                               +                   +                    +     +                 +       ++    + +          +        +  +                  +    +        +       +
+                                               o                   o                    o     o                 o      \oo4-o \ o;         o        o  o                  o    o        o       o
\:off+2+o9-:ob-:o4-:o2+:o2+:of4*%:of2++:o2,3+:o2 +:o3*f-3-:oc-:o3-:o\c+:o4+:oc-:o6-:o8+:o\9-:of 1++:o3-:o8+:o9-:o\8-:o9+:  o9+: o\e-:ob+:o8- :o3+:o3+ :o\4-:o7+:o2-f-:od+:o\2+:o c-:o3-:o\7+:ob\

• If you write the string backwards you can drop the r stack reversing :) Also you can move the final o to the blank space at the start of the second line. Mar 5, 2015 at 7:47
• @Sp3000 Thanks for the tips! Mar 5, 2015 at 14:25
• Any way you could avoid having f f + 2 + o so many times in the complicated one? Mar 6, 2015 at 19:36
• In the best one, is there an easy way to upper case? Loop through a subroutine that adds 32 to each in that part of the string, saving you some unique chars, maybe? Mar 6, 2015 at 19:38
• @mbomb007 I could optimize that out using the register, or I could even read a space from the source code itself. I'm super new to Fish, so I don't know if that's possible Mar 6, 2015 at 20:08

05AB1E, 380 bytes * 11 chars = 4180 score

This is potentially non-competing

"234413410442342343401431404112421343420421440410421432430413441112423432421431401400112130411432430431112431421421112424421434400441112402421424112414441112422401342344401131213112114314242234112311320243232300112231312304322303112240304323112244320302310313112304321234312112314242234112301230330324112233304241134114112403410433410420403112414401112342112413421421412141"3ô5öçJ


Try it online!

Pushes base 5 representation of ASCII chars joined together.
Splits in pieces of 3, converts back to decimal.
Converts ASCII integer back to character.
Joins back together.

Perl 6, 139 bytes * 36 unique = 5004

say 'Elizabeth obnoxiously quoted (just too rowdy for my peace): "'~"the quick brown fox jumps over the lazy dog".uc~'," giving me a look.'


Java 8, 141 bytes * 64 unique characters = 9,024

()->{return"Elizabeth obnoxiously quoted (just too rowdy for my peace): \"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG,\" giving me a look.";}


141 bytes, 64 unique characters. Approaches other than the simple "return the string" increase the byte count without saving much on characters used.

Perl 5, 137 bytes * 35 unique = 4795

say'Elizabeth obnoxiously quoted (just too rowdy for my peace): "',uc'the quick brown fox jumps over the lazy dog','," giving me a look.'


Try it online!

Tcl, 345 bytes, 345 x 13 = 4485

\160\165\164\163 [\163\165\142\163\164 [\162\145\147\163\165\142 \55\141\154\154 \50\\\144\\\144\51 "055451724142456450 5742565770515765635471 616557644544 \5052656364 645757 6257674471 465762 5571 6045414345\51\72 \"241005 2125110313 0222172716 061730 1225152023 17260522 241005 14013231 041707\"\54 475166515647 5545 41 54575753" \\\\1\\1]]\56


Try it online!

Tcl, 337 bytes, 337 x 15 = 5055

\160\165\164\163 [\163\165\142\163\164 [\162\145\147\163\165\142 \55\141\154\154 (\\\144\\\144) "055451724142456450 5742565770515765635471 616557644544 (52656364 645757 6257674471 465762 5571 6045414345)\72 \"241005 2125110313 0222172716 061730 1225152023 17260522 241005 14013231 041707\"\54 475166515647 5545 41 54575753" \\\\1\\1]]\56


Try it online!

Tcl, 329 bytes, 329 x 16 = 5264

\160\165\164\163 [\163\165\142\163\164 [\162\145\147\163\165\142 \55\141\154\154 (\\d\\d) "055451724142456450 5742565770515765635471 6165576445d (52656364 645757 625767d71 465762 5571 6045414345)\72 \"241005 2125110313 0222172716 061730 1225152023 17260522 241005 14013231 041707\"\54 475166515647 5545 41 54575753" \\\\1\\1]]\56


Try it online!

Tcl, 333 bytes, 333 x 16 = 5328

\160\165\164\163 [\163\165\142\163\164 [\162\145\147\163\165\142 \55\141\154\154 (\\d\\d) "055451724142456450 5742565770515765635471 6165576445d \5052656364 645757 625767d71 465762 5571 6045414345\51\72 \"241005 2125110313 0222172716 061730 1225152023 17260522 241005 14013231 041707\"\54 475166515647 5545 41 54575753" \\\\1\\1]]\56


Try it online!

Tcl, 148 bytes, 148 x 37 = 5476

puts "Elizabeth obnoxiously quoted (just too rowdy for my peace): \"[string tou "the quick brown fox jumps over the lazy dog"],\" giving me a look."


Try it online!

Tcl, 277 bytes, 277 x 21 = 5817

\160u\164s [subs\164 [\162\145\147sub \55\141\154\154 (\\d\\d) "0554517241b456450 ob56o7051ous5471 61uo6445d(52us64 64oo 62o67d71 46o62 5571 6045414345): 241005 2125110313 0222172716 061730 1225152023 17260522 241005 14013231 041707\" 475166515647 5545 41 54oo53" \\\\1\\1]]\56


Try it online!

Tcl, 371 bytes, 371 x 16 = 5936

puts "\105\154\151\172\141\142\145t\150 o\142\156o\170\151ous\154\171 \161uot\145\144 \50\152ust too \162o\167\144\171 \146o\162 \155\171 p\145\141\143\145\51\72 \"\124\110\105 \121\125\111\103\113 \102\122\117\127\116 \106\117\130 \112\125\115\120\123 \117\126\105\122 \124\110\105 \114\101\132\131 \104\117\107\54\" \147\151\166\151\156\147 \155\145 \141 \154oo\153\56"


Try it online!

Tcl, 401 bytes, 401 x 16 = 6416

puts "\105\154\151\172\141\142\145\164\150 o\142\156o\170\151o\165\163\154\171 \161\165o\164\145\144 \50\152\165\163\164 \164oo \162o\167\144\171 \146o\162 \155\171 \160\145\141\143\145\51\72 \"\124\110\105 \121\125\111\103\113 \102\122\117\127\116 \106\117\130 \112\125\115\120\123 \117\126\105\122 \124\110\105 \114\101\132\131 \104\117\107\54\" \147\151\166\151\156\147 \155\145 \141 \154oo\153\56"


Try it online!

Tcl, 403 bytes, 403 x 16 = 6448

puts "\105\154\151\172\141\142\145\164\150 o\142\156o\170\151o\165\163\154\171 \161\165o\164\145\144 \50\152\165\163\164 \164oo \162o\167\144\171 \146o\162 \155\171 \160\145\141\143\145\51\72 \42\124\110\105 \121\125\111\103\113 \102\122\117\127\116 \106\117\130 \112\125\115\120\123 \117\126\105\122 \124\110\105 \114\101\132\131 \104\117\107\54\42 \147\151\166\151\156\147 \155\145 \141 \154oo\153\56"


Try it online!

Tcl, 433 bytes, 433 x 15 = 6495

puts "\105\154\151\172\141\142\145\164\150 \157\142\156\157\170\151\157\165\163\154\171 \161\165\157\164\145\144 \50\152\165\163\164 \164\157\157 \162\157\167\144\171 \146\157\162 \155\171 \160\145\141\143\145\51\72 \42\124\110\105 \121\125\111\103\113 \102\122\117\127\116 \106\117\130 \112\125\115\120\123 \117\126\105\122 \124\110\105 \114\101\132\131 \104\117\107\54\42 \147\151\166\151\156\147 \155\145 \141 \154\157\157\153\56"


Try it online!

Tcl, 473 bytes, 473 x 14 = 6622

puts \105\154\151\172\141\142\145\164\150\40\157\142\156\157\170\151\157\165\163\154\171\40\161\165\157\164\145\144\40\50\152\165\163\164\40\164\157\157\40\162\157\167\144\171\40\146\157\162\40\155\171\40\160\145\141\143\145\51\72\40\42\124\110\105\40\121\125\111\103\113\40\102\122\117\127\116\40\106\117\130\40\112\125\115\120\123\40\117\126\105\122\40\124\110\105\40\114\101\132\131\40\104\117\107\54\42\40\147\151\166\151\156\147\40\155\145\40\141\40\154\157\157\153\56


Try it online!

Tcl, 133 bytes, 133 x 60 = 7980

puts "Elizabeth obnoxiously quoted (just too rowdy for my peace): \"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG,\" giving me a look."


Try it online!