Make your language *mostly* unusable! (Cops' thread)
Haskell, cracked by Christian Sievers
import Prelude(getLine,print)
a=a
Full program, reading two integers (including negative ones) from stdin and writing to stdout.
I've just disabled the Prelude so almost nothing is in scope, and then added a definition; further imports are syntactically invalid. I gave you getLine
and print
though.
Edited to add my original solution. Christian's crack was different, but exploits the same basic features (you can get a surprising amount of computation done by accessing functions that have syntactic sugar, even when you can't call anything builtin directly or even name the types involved).
import Prelude(getLine,print)
a=a
(x:l)!0=x
(x:l)!n=l!d[0..n]
d[x,y]=x
d(x:l)=d l
x^y=[x..]!y
x+y=f[0..y](x^y)(-((-x)^(-y)))
f[]x y=y
f _ x y=x
f.g= \x->f(g x)
f&0= \x->x
f&n=f.(f&d[0..n])
x*y=((+x)&y)0
x%[]=x
x%('-':s)= -(x%s)
x%(c:s)=x*10+i c%s
i c=l['1'..c]
l[]=0
l(x:s)=1+l s
main=do
x<-getLine
y<-getLine
print((0%x)+(0%y))
Which probably isn't super-golfed anyway, but here it is more readibly:
import Prelude(getLine,print)
a=a
-- List indexing
(x : _) !! 0 = x
(_ : xs) !! n = xs !! (sndLast [0..n])
-- sndLast [0..n] lets us decrement a positive integer
sndLast [x, _] = x
sndLast (_ : xs) = sndLast xs
-- Pseudo-addition: right-operator must be non-negative
x +~ y = [x..] !! y
-- Generalised addition by sign-flipping if y is negative
x + y = switch [0..y] (x +~ y) (-((-x) +~ (-y)))
where switch [] _ empty = empty -- [0..y] is null if y is negative
switch _ nonempty _ = nonempty
f . g = \x -> f (g x)
-- compose a function with itself N times
composeN f 0 = \x -> x
composeN f n = f . (composeN f (sndLast [0..n]))
-- multiplication is chained addition
x * y = composeN (+x) y 0
strToNat acc [] = acc
strToNat acc ('-' : cs) = -(strToNat acc cs)
strToNat acc (c : cs) = strToNat (acc * 10 + charToDigit c) cs
charToDigit c = length ['1'..c]
length [] = 0
length (_ : xs) = 1 + length xs
main = do
x <- getLine
y <- getLine
print (strToNat 0 x + strToNat 0 y)
Python 2, Cracked
Implements addition as a named function
import sys
c="".join(open(__file__).read().split('\n')[4:])
if set(c)-set(' &)(,.:[]a`cdfijmonrt~')or"import"in c:sys.setrecursionlimit(1)
f=lambda\
Try it online!
What does this do?
For the purposes of helping you out a bit I'll explain what this does. This code opens the source file and checks if the remainder of the code fits the following criteria:
- Does not contain the string
import
- Is made solely of the characters
&)(,.:[]a`cdfijmonrt~
If it fails either criterion the recursion limit is set to 1
meaning that any code you write will hit the recursion limit.
There are no tricks here, I have written a solution that uses only these characters and no imports, I'm not doing anything subversive, but I will say that I think this will be pretty hard to crack.
To save you some time here is a short list of useful things you cannot do with this restriction
+
well duh,eval
/exec
Wasn't going to let you get away with thatNumbers, They might be more useful than you think
String literals
len
=
, No assigning variables>
,<
,==
. . . I have left you with no comparisons*
,-
,/
,%
,^
,|
,>>
,<<
The only operators available are~
and&
__foo__
, None of those fancy double underscore methods are allowed.
Python 2, Cracked
This is the fourth iteration of this answer. Each of the last answers has was cracked via reseting the recursion depth.
Implements addition as a named function
import sys
if set("".join(open(__file__).read().split('\n')[4:]))-set(' &)(,.:[]a`cdfijmonrt~'):sys.setrecursionlimit(1)
for m in sys.modules:sys.modules[m]=None
del sys;f=lambda\
Try it online!
What does this do?
For the purposes of helping you out a bit I'll explain what this does. This code opens the source file and checks if the remainder of the code
is made solely of the characters &)(,.:[]a`cdfijmonrt~
If it fails the recursion limit is set to 1
meaning that any code you write will hit the recursion limit.
I've also disabled all of the modules, so you can't import anything.
There are no tricks here, I have written a solution that uses only these characters and no imports, I'm not doing anything subversive, but I will say that I think this will be pretty hard to crack.
To save you some time here is a short list of useful things you cannot do with this restriction
+
well duh,eval
/exec
Wasn't going to let you get away with thatNumbers, They might be more useful than you think
String literals
len
=
, No assigning variables>
,<
,==
. . . I have left you with no comparisons*
,-
,/
,%
,^
,|
,>>
,<<
The only operators available are~
and&
__foo__
, None of those fancy double underscore methods are allowed.
My solution
So now that xnor has cracked it in a way I am sufficiently satisfied with I am going to reveal my solution
r,o:(o and f(~(~r&~o)&~(r&o),int(`r`[:r&~r].join([`dict()`[r&~r],`r&~r`,`dict([(r&~r,r&~r)])`[int(`~([]in[[]])`[[]in[[]]:])],`min`[[]in[[]]],`dict()`[~(r&~r)],`r&~r`]).format(r&o),int(`~([]in[[]])`[[]in[[]]:]))))or r
Surprise, surprise its a hulking pile of gibberish. Rather than break this down I'm going to go through the process of how I made this.
I started with a pretty standard addition algorithm
r,o:(o and f(r^o,r&o<<1))or r
Then I used a bitwise trick for representing ^
with |
,&
,~
.
r,o:(o and f((r|o)&~(r&o),r&o<<1))or r
I used another bitwise trick to get rid of the |
r,o:(o and f(~(~r&~o)&~(r&o),r&o<<1))or r
Now all thats left is the <<
, shouldn't be too hard, right? Well get ready for a bumpy ride. To replace the bitshift I used strings to append a zero to the end of its binary representation
r,o:(o and f(~(~r&~o)&~(r&o),int(bin(r&o)[2:]+"0",2)))or r
This has a few problems but the primary one is using addition, so I worked around this by using a format instead
r,o:(o and f(~(~r&~o)&~(r&o),int("{}0".format(bin(r&o)[2:]),2)))or r
We are not allowed to use bin, so I used string formatting to convert to binary.
r,o:(o and f(~(~r&~o)&~(r&o),int("{0:b}0".format(r&o),2)))or r
Since string literals are forbidden I have to build the string {0:b}0
out of parts made with back ticks and join
them together.
r,o:(o and f(~(~r&~o)&~(r&o),int("".join(["{","0",":","b","}","0"]).format(r&o),2)))or r
The empty string is pretty easy, you can just do
`r`[:0]
The zeros were
`0`
and the {:}
were all grabbed from dictionaries.
r,o:(o and f(~(~r&~o)&~(r&o),int("".join([`dict()`[0],`0`,`dict([(0,0)])`[2],"b",`dict()`[-1],`0`]).format(r&o),2)))or r
b
seems pretty hard to get, its not in our character set, so how are we supposed to get an object that has a b
in its repr
? Well here's how: When you use repr
on a builtin function you get something that looks like
<built-in function name>
And thats from where we'll get our b
.
r,o:(o and f(~(~r&~o)&~(r&o),int("".join([`dict()`[0],`0`,`dict([(0,0)])`[2],`min`[1],`dict()`[-1],`0`]).format(r&o),2)))or r
Now all thats left are numbers, I only need -1, 0, 1, and 2 so here's how I represented them:
-1 = ~(r&~r)
0 = r&~r
1 = []in[[]]
2 = `~([]in[[]])`[[]in[[]]:]
2 could actually be a byte shorter as
```r&~r```.find(`r&~r`)
based on @Blender's suggestions in the comments, but I didn't think of this until after the fact.
So we substitute these numbers in
r,o:(o and f(~(~r&~o)&~(r&o),int(`r`[:r&~r].join([`dict()`[r&~r],`r&~r`,`dict([(r&~r,r&~r)])`[int(`~([]in[[]])`[[]in[[]]:])],`min`[[]in[[]]],`dict()`[~(r&~r)],`r&~r`]).format(r&o),int(`~([]in[[]])`[[]in[[]]:]))))or r
And thats the crack.