One of the issues I have with python, even though this particular issue becomes helpful at times, is the lack of compiling going on. To make a program it’s a matter of grabbing the .py files, running them, and off you go. On the other hand, when working with such open code like that, cheating tends to be a bit of an issue. Why bother with levelling up a character to take down a creature Y, if all you have to do is open up the configs and take a few significant figures off of the creature’s health stat?
Now obviously there’s no such thing as an unbreakable code, and I don’t want to discourage modification of my code–if someone wants to give themselves God Mode at the start for some reason, then go for it, but it would be nice if there were a way to make sure that, if a player gets a high score, the game knows to not include that run in the scoring table because he or she cheated. Cheating itself I’m fine with, but if I can I’d like to discourage circumventing the game’s logic that ensures a fair playthrough was, in fact, a fair one.
Looking around, there seems to be a few different ways to combat cheating of this sort with a language like python.
The first is to hand out only the .pyc files. As far as I can tell, they function exactly the same as .py files, but they’re unreadable unless if decompiled. However, I plan on releasing the source anyway (as I have been), and it’s trivial to just replace the .pyc files with .py ones and make changes there. And I don’t want to hide code from people, at least in such a blunt way.
Option two, is the use obfuscation. Python has a nice little function called “lambda“, that can be used to allow one to repeatedly nest a function inside a function, and the net result is it gets really hard to read after a short while. A quick example:
“lambda [variables]: [formula] (optional input if not being assigned to a variable)” is the general syntax, with the result of formula being returned. An equivalent def statement would be something like “def lambda(variables): return formula”.
So, if we wanted to make a lambda function that would return a value of 2*input, we’d have:
“val = lambda x: 2*x”
Then you could just call val(5) and it would return a value of 10. Where does obfuscation come into play?
“val = (lambda x: 2*(lambda y: y)(x))
val(5) will still return 10, but I replaced the second “x” with “lambda y: y”. In other words, I nested another inline function that simply returns the input asis, and that returned input is then multiplied by 2.
for added fun, one can assign extra dummy variables:
“val = (lambda x,a=1,b=3: 2*(lambda y:y)(x))”
then, say, instead of “a = 1” I could’ve written “a = lambda c: c (1)” and still get the same result, and then the code looks like:
“val = (lambda x,a=(lambda c:c(1)),b=3: 2*(lambda y:y)(x))”
what if we wanted to return 4*input? We can take the y:y and inline inside of that the previous line:
“val = (lambda x,a=(lambda c:c(1)),b=3: 2*(lambda y:((lambda x,a=(lambda c:c(1)),b=3: 2*(lambda y:y)(x)))(x))(x))”
and so on. A few other functions like map() can also be used within lambdas to make things even more confusing and throw in arrays.
So what’d be the point? If I, for example, added in a calculation that checks to make sure the .py files are original via a hash, it’d be very simple for the player to just change wherever I put “if baseFileHash == calculatedFileHash” to “if basefile == basefile” and as a result allow the player to, for example have a high score at the end. But if I’ve got the comparison split across a gigantic nested lambda in various places, well then if the player’s going to cheat to make the game think it’s a legitimate playthrough, he’s at least going to have to work for it.
That and intentionally trying to make working yet incomprehensible python code is in some ways pretty fun, hah.