Print the phrase
And she said, 'But that's his.' using only the following characters:
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ No punctuation or non-alphabetic characters whatsoever. You can use whatever programming language you want. Whitespace is completely allowed. Shortest program wins.
Print the phrase
Husk, 408 bytes
Well, that was an adventure.
The code was generated with this Python script. Not sure it's optimal, but it's good enough for me.
So, limiting Husk to using only letters turned out to be a real pain... My first idea was to use
s to convert a value to string, use
t (drop first element) and
D (double) to change the length of this string, then convert it to a number by computing its length with
L: I could do this to generate the ASCII code of each character in the output string, and the convert them to characters with
That was complex enough for my tastes, but then I realized that all the builtins associated to letters in Husk are functions (which cannot be converted to string), except for
N... which is the infinite list of natural numbers, good luck getting the length of that. Fortunately,
i can take a string and return the first integer found in that string, which for
N would be 1, so with
sisN I can get my string of length 1 and I can manipulate that to get all other lengths.
I wrote a python script to bruteforce the shortest way to build strings of every needed length starting from
sisN and using
P (permutations, works like a factorial), and
Q (substrings, works like n*(n+1)/2). I also realized that some values could be built more efficiently by working directly on numbers rather than strings, so
isN is 1,
DisN is 2,
iDsisN is 11 (double the string "1" and read that as a number) and so on... those values were not too many, so I just hardcoded them as alternatives and kept for each the shortest between the hardcoded string and the bruteforced one.
Only after I did all that I realized that I didn't have a way to combine different characters into an output string. Strings are just lists of characters, and
e can build a list of two arguments, but to build longer lists I would need
This time I was saved by
J, join, which (among other things) can take a value and a list and insert that value between each pair in the list. If I want to concatenate three strings a b c I can do it with
Jbeac. The script does this recursively starting from the desired output by choosing
b as a single central element and
c as the two halves separated by
b. This was my first attempt an miraculously it worked (if the length of the string was different I could have to join only two strings together, but this doesn't happen here). I have tried other ways to compose the string, but this remains the shortest working result I could produce.
BRASCA, 213 bytes
At first I wanted to port the ><> answer, before realizing that stack length wont work with only alphabetical characters. That's when I remembered that BRASCA has some nifty things ><> does not have: The
lLeEdDhHK built-ins for pushing numbers,
u to seperate them into individual digits,
M to rotate and shift the stack,
x to discard digits, and then
g to put them back together I can create and output any string using only the alphabet.
DoluRlugoHoEoDuRxluxluxMgoldUxgollUxgoEoDuRxluxluxMgoholDUmxMgoHoduxduxgoEoEuxhuxgoDuxDuxgoluxluxhumxMgoluxluxDuxgoEoluxluxDuxgoldUxgoholuxluxDuxgoEuxhuxgoDuRxluxluxMgoEoldUxgolDUmxMgoDuRxluxluxMgoduxDuxgoEuxhuxgo +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- A| Do n| luRlugo d| Ho | Eo s| DuRxluxluxMgo h| ldUxgo e| llUxgo | Eo s| DuRxluxluxMgo a| ho i| lDUmxMgo d| Ho ,| duxduxgo | Eo '| Euxhuxgo B| DuxDuxgo u| luxluxhumxMgo t| luxluxDuxgo | Eo t| luxluxDuxgo h| ldUxgo a| ho t| luxluxDuxgo '| Euxhuxgo s| DuRxluxluxMgo | Eo h| ldUxgo i| lDUmxMgo s| DuRxluxluxMgo .| duxDuxgo '| Euxhuxgo
2679 2618 2561 bytes
PPuPuPDDc uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuck D cHj
I'll golf each character one by one. So far I've golfed up to
I'll golf pushing the numbers over time, so I'll only explain
k D cHj for now.
k # Put all the characters into the list D cH # Push the empty string j # Join the list using the empty string
80186 machine code + DOS, 91 bytes
hm j j PPjzjzjgaAAA JSJJ RU Sq ReAA JdJJJ RfiJElK JEiS GtI And she said But that s his
Text version, with tabs (code 9) replaced by
9 and spaces (code 32) replaced by
68 6D 09 6A 09 6A 09 50 50 6A 7A 6A 7A 6A 67 61 41 41 41 09 4A 53 4A 4A 09 52 55 09 53 71 09 52 65 41 41 09 4A 64 4A 4A 4A 09 52 66 69 4A 45 6C 4B 09 4A 45 69 53 20 47 74 49 20 41 6E 64 20 73 68 65 20 73 61 69 64 20 20 20 42 75 74 20 74 68 61 74 20 73 20 68 69 73 20 20 20
The machine code appears in a file with extension
.com. When I run it, it prints the required message and then hangs (executing random data).
High-level explanation on what it does:
- Initializes registers with constant values
- Replaces spaces in the message by the required special symbols (
- Patches the code to generate the
int 21instruction, which prints the message
- Calls DOS
Assembly code (can be compiled with
my_bp equ 7ah my_si equ 7ah my_di equ 67h my_msg equ 13bh .model tiny .code .startup .186 org 100h push 96dh ; ax (ah = 0; al = don't care, but see below) push 9 ; cx push 9 ; dx push ax ; bx = don't care push ax ; don't care push my_bp push my_si push my_di popa inc cx inc cx inc cx or [bp+si+my_msg-my_bp-my_si+12], cx ; , dec dx dec dx or [bp+si+my_msg-my_bp-my_si+14], dx ; ' or [bp+di+my_msg-my_bp-my_di+23], dx ; ' or [bp+si+my_msg-my_bp-my_si+30], dx ; ' inc cx inc cx or [bp+si+my_msg-my_bp-my_si+29], cx ; . dec dx dec dx dec dx or [bp+si+my_msg-my_bp-my_si+31], dx ; $ ; 0x2049 * 0x4b6c = 0x98301cc ; So this sets cx to 1cc (a temporary constant used to patch code) imul cx, [bp+si+my_msg-my_bp-my_si-2], 4b6ch ; 0x1cc | 0x2049 = 0x21cd (the instruction which calls DOS int 21) ; Here ah = 9 ("print" mode) or [bp+si+my_msg-my_bp-my_si-2], cx ; At address 101, there is the constant 96d, which was loaded into ax ; 0x96d * 0x7447 = 0x448013b ; So the following sets dx to 13b (adddress of the message) imul dx, [bp+di+101h-my_bp-my_di], 7447h int21: dw 2049h db 'And she said But that s his ' end
It uses the
popa instruction to pop all registers, because regular
pop cannot fill all needed registers (e.g.
pop di is a forbidden opcode).
Addresses of bytes to patch are in the range 0x100...0x160. By luck, they can be represented as a sum of 3 bytes with allowed values:
- 0x7a in
- 0x7a or 0x67 in
- Immediate value
Patching of bytes in the message works by doing logical
OR on 0x20 (space character) and a small constant (4, 7, 12 or 14). The small constant is obtained by initializing
dx to 9 (tab character) and doing
DEC as needed.
Patching of code uses the
IMUL instruction. I found the needed 16-bit constants to multiply using brute-force search.
Finally, the address of the message (0x13b) is obtained by multiplication. To save space, I took one of the constants from one of the instructions, which contains an immediate value
0x96d. Here the
9 part chooses a DOS print function, and the
6d part is a free parameter. It turns out that
6d is the only possibility which can give 0x13b after multiplication.
Disassembly of the code part:
06BA:0100 686D09 PUSH 096D 06BA:0103 6A09 PUSH +09 06BA:0105 6A09 PUSH +09 06BA:0107 50 PUSH AX 06BA:0108 50 PUSH AX 06BA:0109 6A7A PUSH +7A 06BA:010B 6A7A PUSH +7A 06BA:010D 6A67 PUSH +67 06BA:010F 61 POPA 06BA:0110 41 INC CX 06BA:0111 41 INC CX 06BA:0112 41 INC CX 06BA:0113 094A53 OR [BP+SI+53],CX 06BA:0116 4A DEC DX 06BA:0117 4A DEC DX 06BA:0118 095255 OR [BP+SI+55],DX 06BA:011B 095371 OR [BP+DI+71],DX 06BA:011E 095265 OR [BP+SI+65],DX 06BA:0121 41 INC CX 06BA:0122 41 INC CX 06BA:0123 094A64 OR [BP+SI+64],CX 06BA:0126 4A DEC DX 06BA:0127 4A DEC DX 06BA:0128 4A DEC DX 06BA:0129 095266 OR [BP+SI+66],DX 06BA:012C 694A456C4B IMUL CX,[BP+SI+45],4B6C 06BA:0131 094A45 OR [BP+SI+45],CX 06BA:0134 6953204774 IMUL DX,[BP+DI+20],7447 06BA:0139 CD21 INT 21 (after the code patches itself)
Fun fact: Normally, I would use
offset message instead of the hard-coded
13bh, but in this case, because at the time of parsing its address is unknown, tasm generates 16-bit immediate offset, wasting 1 code byte:
06BA:0131 098A4600 OR [BP+SI+0046],CX
CJam, 162 bytes
Each letter is outputted individually and all the source codes are concatenated. The individual codes are:
S e# Space
' (6 bytes)
HAmfbc HA e# Push 17 and 10 17 10 mf e# Factorization 17 [2 5] b e# Base conversion 39 c e# Convert to char ''
, (6 bytes)
DFmfbc DF e# Push 13 and 15 13 15 mf e# Factorization 13 [3 5] b e# Base conversion 44 c e# Convert to char ',
. (8 bytes)
SciBbIbc Sci e# Push space as an integer 32 Bb e# Convert to base 11 [2 10] Ib e# Convert from base 18 44 c e# Convert to char '.
A (6 bytes)
KFmfbc KF e# Push 20 and 15 20 15 mf e# Factorization 20 [3 5] b e# Base conversion 65 c e# Convert to char 'A
B (7 bytes)
BsXbceu Bs e# Push 11 as a string "11" (i.e. [49 49]) Xb e# Convert from base 1 98 c e# Convert to char 'b eu e# Uppercase 'B
a (5 bytes)
AsXbc As e# Push 10 as a string "10" (i.e. [49 48]) Xb e# Convert from base 1 97 c e# Convert to char 'a
The codes for
i work exactly the same way as for
a. The codes are:
DsXbc -> 'd EsXbc -> 'e HsXbc -> 'h IsXbc -> 'i
n (6 bytes)
ACZbbc ACZ e# Push 10, 12 and 3 10 12 3 b e# Base conversion 10 [1 1 0] b e# Base conversion 110 c e# Convert to char 'n
s (7 bytes)
Fseesic Fs e# Push 15 as a string "15" ee e# Enumerate [[0 '1] [1 '5]] s e# Convert to string "0115" ic e# Convert to char 's
The codes for
u do the same thing but staring with
Gseesic -> 't Hseesic -> 'u
Most of these codes were found using another Cjam program to brute force all possible outputs for a given formula. If you want to use it, type some formula such as
RTsbc, and it will show you how to get to each possible output by replacing
U in the input by some 1 byte constant.
a-zA-Z. In theory I could use
Eval to create the necessary instructions, but none of
+-*,%'"can be constructed without using (at least) one of
(programmer-of (language 'lisp))dislikes this. \$\endgroup\$