Vyxal \$\le\$ 2.10.3, score 252, safe
λƛ¬∧⟑∨⟇÷׫
»°•ß†€½∆ø↔¢⌐æʀʁɾɽÞƈ∞¨ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOQRSTUVWXYZ[\]`^_abcdefghijlmnpqrstuvwxyz{|}~↑↓∴∵›‹∷¤ð→←βτȧḃċḋėḟġḣḭŀṁṅȯṗṙṡṫẇẋẏż√⟨⟩‛₀₁₂₃₄₅₆₇₈¶⁋§ε¡∑¦≈µȦḂĊḊḞĠḢİĿṀṄȮṖṘṠṪẆẊẎŻ₌₍⁰¹²∇⌈⌊¯±₴…□↳↲⋏⋎꘍ꜝ℅≤≥≠⁼ƒɖ∪∩⊍£¥⇧⇩ǍǎǏǐǑǒǓǔ⁽‡≬⁺↵⅛¼¾Π„‟
(Or, in other words, everything except koPĖ
is disallowed)
If you're wondering where to start, some of the ideas from here may be applicable. However, Vyxal has changed a lot since that competition. Good luck!
Btw, my intended solution is 37 yottabytes.
Solution
note: Due to bug fixes, some of the links here won't work anymore
This character set is quite powerful. We have these builtins:
ko
- octal digits as a string, 01234567
kP
- printable ASCII
o
- remove string a from string b
P
- strip string a from the outsides of string b. Has interesting (and very helpful for us) behaviour on lists/ints
Ė
- Evaluate string as Vyxal / take reciprocal
In older versions of Vyxal, regular python floats were used, making it a lot easier. Now, we have sympy floats.
The significance of this is that 1/1234567 used to be represented as 8.100005913004317e-07
, allowing us to easily get the characters e and -. Now, it's represented as 1/1234567
.
In older versions of Vyxal, a leading zero on a number did nothing. Now, it pushes a zero before said number. This is a problem, as our only real way of getting numbers is with 1234567, and since the zero is before the number using o
or P
will just leave a zero.
koĖ
pushes a zero as well, but appending a P
(strip) results in a zero, which we can then remove from the string 01234567
leaving 1234567, which we can eval.
Getting strings
Following this, we can get the /
character by taking the reciprocal (resulting something that stringified gives 1/1234567
) and stripping 01234567
. We can evaluate this to:
- Divide two numbers (because we have reciprocal, also multiply)
- Split a string into n parts
- Split a string by another string
Because of this, we can get 1 by dividing 1234567 by itself.
We can get the range from 2 to 7 by removing 1 although this seems useless for now. We can also get 1-7.
Taking a step back, we can stringify 1/1234567 by stripping
/
. We can get 1/
with this although it doesn't seem to help much right now.
List trickery
For reasons that I don't quite understand, we can get 7654321 with something like this. In general, we can reverse a string/number by calling P
with it and [0]
. This will prove very helpful.
Taking a step back to numbers, we can get 234567
with what we already have.
For some reason, stripping a string from certain types of lists seems to return (wrapped) the first item of said list. But how do we get that item?
I'm not sure yet, but using some very confusing stuff lets us get the singleton list containing 2.
Removing that from [2..7] yields 7...3. We can repeat these techniques to get every element of [2...7] in a singleton list.
Since we can simulate multiplication with use of division and reciprocals, we can finally get the elusive 8 and 9!
Printable ASCII
By removing both of these + the octal digits from printable ASCII, we can get something usable?
Vyxal's split into pieces builtin doesn't actually give you the right amount of strings. Instead, it tries to make the strings all equal in length.
We can split this into 64 pieces, but our trick to get the first character doesn't work since it's wrapped in a list...
And anyway, executing a list produces a horrific mess...
However, there's another way we can get numbers! By removing select digits (in lists) from 01234567
we can get quite a few numbers, including all the digits as individual numbers. We can construct 56 with this method.
Using this method, we can split the string into 56 pieces instead of 64, cutting this down into a much more manageable result, with a single unnested list.
This can be represented with a much shorter piece of code.
So, we can get the first item of a list of strings as a singleton list, and we can strip a singleton list from a string to yield a string, and we can split a string into a list.
Letters
By stripping /
to get the first item, we can get ["a"]
. Then we can get ["b"]
.
We can remove a
from the string form of printable ascii (and reverse it as a side effect, but that's easily fixable). We can then remove b
. You can probably see where this is going, can't you.
With this method, we can get c
in a list, remove abc, get d, remove abcd, etc. The cost for this method grows exponentiially, but it works.
Then, we can get the string with n characters removed, and remove it from the string with (n-1) characters removed, leaving just the character itself.
Finally, we can evaluate this and execute arbitrary ASCII characters.
Scoring individual letters
Whew, that was a lot of work. Now let's put it all together.
The overhead for splitting a string into a character list is 941 (56) + 14 (division) = 955 bytes.
The overhead for getting the first character of a string as a singleton list is 955 + 3 = 958 bytes (referred to as head
)
The overhead for removing a character and then de-reversing the string is only six bytes - PkoĖoP
. (referred to as strip
)
The overhead each time we push printable ascii - digits is 2052 bytes (referred to as ascii
). The overhead for pushing this as a character list is 3007 bytes.
Then, we can generate each character of ascii in sequence by removing all the previous ones and head
ing. For example, to get b
we remove a
from ascii
and head
.
a
as a singleton list costs 1 ascii + 1 head = 3010 bytes.
b
as a singleton list costs 2 ascii + 2 head + 1 strip = 6026 bytes.
c
as a singleton list costs 4 ascii + 4 head + 3 strip = 12058 bytes.
Can you see the pattern? The formula is \$2^n\text{ascii} + 2^n\text{head} + (2^n - 1)\text{strip} = 3016 * 2^n - 6\$ for a 0-indexed n representing the position.
Putting it all together
To get say a
as a single character, we need the overhead for getting b
as a singleton list, minus one head , and the overhead for getting a
as a singleton list, minus one head. We also need a single o
.
With this, we can take the string bcdef...
used to get b
before it is head
ed, do the same with the string abcdef...
, and remove the former from the latter leaving only a
.
So, we need 3 ascii, 1 head, 1 strip and a single o
= 7121 bytes to get the single character a
. Simpler version.
For b
, we need 6 ascii + 4 head + 4 strip + o
= 16169 bytes. Simpler version.
The formula here is \$(2^n + 2^{n+1})\text{ascii} + (2^n + 2^{n+1} - 2)\text{head} + (2^n + 2^{n+1} - 2)\text{strip} = 3016(2^n + 2^{n+1}) - 1927\$.
What we're interested in are the characters C
and +
. I've made a calculator for the overhead of a single printable ASCII character, and for C
it spits out 2428804003961
. For +
it gives 41726535094731005753465
. Of course, we must increment these due to the need to evaluate them.
Because I'm lazy, to create a certain character we're going to go 1 1+ 1+ 1+ ...C
. This has a total cost of \$41726535094731005753495 * (\text{ord}(c) - 1) + 2428804003991 \$. calculator.
Finally, we need to get this over all characters in the Vyxal codepage except the four we're using. We need 251 more +
to concatenate it all together, and we're done!
The final size of the program is 37,775,866,751,962,486,187,362,186,532 bytes, or 37 yottabytes.