Vague. The only limitations are obvious stuff like hacking the game client to get around information restrictions, using so much compute you slow the game itself to a crawl, installing anything that takes me significant effort to set up and anything I consider a security risk. You may PM me for clarification on your specific situation. I can easily perform speed tests as I have already written the game client.
What do you mean by information restrictions? What exactly is restricted? If I were to use some side-channel to determine that I was being simulated by an opponent and forkbomb if so, would I be eliminated for hacking? Would they be eliminated for overusing compute? (after all, it wasn’t my code per say that forkbombed, but their code simulating my code) Would I be eliminated for being a dick? (Which would be self-evident, at any rate)
You say in another comment, “Simulator bots are legal for programmers to write provided they never crash, slow the game to a crawl, etc. Any bot (even a non-simulator) that does will be disqualified.” I’m just concerned about the edge case where someone writes the troll code described above, and suddenly every person who submitted a simulator bot (i.e. Zak M. Davis) gets DQ’d. The rules as written suggest that such a bot (presuming it does not preserve information across rounds) would be perfectly legal and it’s Zak’s own fault for executing untrusted code, but that seems to effectively prohibit simulators as there’s an infinite plurality of side-channels through which to do this.
A few possible resolutions would be:
Whoever wrote the code that contains the instructions to forkbomb is at fault. Essentially, don’t be a dick.
Any information not explicitly given is forbidden. Essentially, you don’t know if you’re being simulated, and you’re not allowed to find out.
Untrusted code is untrusted code; simulate at your own risk. Essentially, if Zak executes my forkbomb, that’s his problem. (Well, your problem as well to fix it, but his fault.)
Resolution 2 seems the most reasonable to me, but if you choose it I humbly request a way to locally overwrite get_opponent_source so you simulate your opponent’s response without risking infinite recursion. (Such an overwrite is probably standard python, but being ignorant of your implementation I’d be concerned about screwing something up and not being able to detect the mistake in testing)
EDIT: Upon further consideration it seems inevitable that Zak will get DQ’d for infinite recursion. So I must be missing something.
Hacking the game engine is against the rules. If your opponent decides to simulate your code then hacking them from inside your opponents’ simulation is allowed. Your code is allowed to peek at the game engine for the purposes of figuring out if it is being simulated by someone else’s code. Peeking at the game engine for other reasons, like figuring out how many of each opponents remain or attempting to modify the game engine itself or attempting to modify your opponents’ code from anywhere outside their own simulation of you is against the rules.
Anything that could damage the game’s execution environment is a security risk and grounds for disqualification. Forkbombing your opponent is really pushing things…but technically legal. I would prefer something more mundane like calling exit() or an infinite loop. Anything more damaging than a forkbomb constitutes a security risk and is not allowed under any circumstances. If you are going to write any malware-like then code (including forkbombs) then please draw my attention to it in some way what the intended behavior is so I can mitigate any unintended consequences to my own systems. Even better would be to PM me for explicit permission.
Any code which gets disabled by malware will get disqualified from the tournament. I will then restart the tournament with the broken competitor(s) removed.
The resolution is #3 “Untrusted code is untrusted code; simulate at your own risk.”
The function get_opponent_source returns source code as a string. You can trivially overwrite it in your local environment with the command get_opponent_source = my_custom_get_opponent_source. (This is standard Python.) If you execute your opponent’s code in a simulation where get_opponent_source has been rebound[1] to a new function (and have not otherwise left alternate attack vectors open) I see no reason why this would would trigger infinite recursion.
If you rebind the get_opponent_source function in your local environment in a reasonable way that is itself obviously not an attempt to breach anything then I will consider that a bug in the game engine and attempt to engineer a fix.
I have not yet read over Zak’s code in detail nor have I attempted to execute it. If it infinitely recurses then it will be disqualified.
Edit: Do note that an unobfuscated simulator bot is likely to have a statement something along the lines of from extra import get_opponent_source. If you overwrite the get_opponent_source function and then execute your opponent’s code, it is possible your opponent’s from extra import get_opponent_source may un-overwrite your rebinding.
In what order do programs get disqualified? For example, if I submit a program with an infinite loop, every other program using simulation will also go into infinite loop when meeting with my program as detecting infinite loops generally isn’t theoretically feasible. Is my program disqualified before the others? What is the general principle?
EDIT: An unrelated question: Do round numbers start from 0 or 1? In the post you write “Unlike Zvi’s original game, you do get to know what round it is. Rounds are indexed starting at 0.”, but also: “Your class must have an __init__(self, round=1) [..]”. Why not have the default initializer also use 0 if the round numbers start from zero?
First a run your program against one or more simple programs without any simulations. If your program hits an infinite loop there then you will be disqualified before you get to infect any other programs. In this way you can be disqualified before the others.
If your program passes the simple tests then it will join the pool and against all other programs which have passed the initial test. All programs which fail to terminate at this stage will be removed simultaneously.
Thank you for the bug report. I have corrected __init__(self, round=1) [..] to __init__(self, round=0) [..]
Hm. Can we get a “you can use at least this amount of time per move and not be disqualified”? Without wanting to say too much, I have a strategy in mind that would rely on knowing a certain runtime is safe. (Allowing for some amount of jankiness, so that if the limit was 5s I’d consider 4s safe.)
My previous answer was a mistake. It is actually 100 move calls in 0.05 seconds. Sorry.
The numbers are brutal.
If a game goes for 50 rounds and 10 different bots each use 5[1] seconds per move and there are 550 moves per bot per pairing then it would take 4.36 years to run this tournament.
However, do note that the guarantee is “0.05 seconds per 100 moves” as opposed to “0.0005 seconds per move”. If you only have to run expensive computations once then, depending on your algorithm, the right caching system might get you close to 100× performance.
I can add caching functions to the extra package if that would help. The computer in question has 4 processors and runs Linux so multiprocessing can get you up to a 4× speedup.
Oh, geez. I figured it would be too long, but I didn’t think about just how much too long. Yeah, with these constraints, even 5s per hundred moves I agree is unreasonable.
Caching seems easy enough to implement independently, I think. No need for you to add it.
The rules are that, from the game engine’s perspective, everyone’s code is going to be written in Python 3 or Hy. It is theoretically possible that someone’s code might, say, include some code in a different language that is then executed from the Python runtime.
Vague. The only limitations are obvious stuff like hacking the game client to get around information restrictions, using so much compute you slow the game itself to a crawl, installing anything that takes me significant effort to set up and anything I consider a security risk. You may PM me for clarification on your specific situation. I can easily perform speed tests as I have already written the game client.
If you are a programmer then you may request reasonable features such as reading your opponent’s source code.
Edit #1: In order to maximize resource use, you may provide an adjustable constant such as
TREE_DEPTH
.Edit #2: Any code that can always complete 10,000
move
calls within 5 seconds is guaranteed to be “fast enough”.What do you mean by information restrictions? What exactly is restricted? If I were to use some side-channel to determine that I was being simulated by an opponent and forkbomb if so, would I be eliminated for hacking? Would they be eliminated for overusing compute? (after all, it wasn’t my code per say that forkbombed, but their code simulating my code) Would I be eliminated for being a dick? (Which would be self-evident, at any rate)
You say in another comment, “Simulator bots are legal for programmers to write provided they never crash, slow the game to a crawl, etc. Any bot (even a non-simulator) that does will be disqualified.” I’m just concerned about the edge case where someone writes the troll code described above, and suddenly every person who submitted a simulator bot (i.e. Zak M. Davis) gets DQ’d. The rules as written suggest that such a bot (presuming it does not preserve information across rounds) would be perfectly legal and it’s Zak’s own fault for executing untrusted code, but that seems to effectively prohibit simulators as there’s an infinite plurality of side-channels through which to do this.
A few possible resolutions would be:
Whoever wrote the code that contains the instructions to forkbomb is at fault. Essentially, don’t be a dick.
Any information not explicitly given is forbidden. Essentially, you don’t know if you’re being simulated, and you’re not allowed to find out.
Untrusted code is untrusted code; simulate at your own risk. Essentially, if Zak executes my forkbomb, that’s his problem. (Well, your problem as well to fix it, but his fault.)
Resolution 2 seems the most reasonable to me, but if you choose it I humbly request a way to locally overwrite get_opponent_source so you simulate your opponent’s response without risking infinite recursion. (Such an overwrite is probably standard python, but being ignorant of your implementation I’d be concerned about screwing something up and not being able to detect the mistake in testing)
EDIT: Upon further consideration it seems inevitable that Zak will get DQ’d for infinite recursion. So I must be missing something.
Hacking the game engine is against the rules. If your opponent decides to simulate your code then hacking them from inside your opponents’ simulation is allowed. Your code is allowed to peek at the game engine for the purposes of figuring out if it is being simulated by someone else’s code. Peeking at the game engine for other reasons, like figuring out how many of each opponents remain or attempting to modify the game engine itself or attempting to modify your opponents’ code from anywhere outside their own simulation of you is against the rules.
Anything that could damage the game’s execution environment is a security risk and grounds for disqualification. Forkbombing your opponent is really pushing things…but technically legal. I would prefer something more mundane like calling
exit()
or an infinite loop. Anything more damaging than a forkbomb constitutes a security risk and is not allowed under any circumstances. If you are going to write any malware-like then code (including forkbombs) then please draw my attention to it in some way what the intended behavior is so I can mitigate any unintended consequences to my own systems. Even better would be to PM me for explicit permission.Any code which gets disabled by malware will get disqualified from the tournament. I will then restart the tournament with the broken competitor(s) removed.
The resolution is #3 “Untrusted code is untrusted code; simulate at your own risk.”
The function
get_opponent_source
returns source code as a string. You can trivially overwrite it in your local environment with the commandget_opponent_source = my_custom_get_opponent_source
. (This is standard Python.) If you execute your opponent’s code in a simulation whereget_opponent_source
has been rebound[1] to a new function (and have not otherwise left alternate attack vectors open) I see no reason why this would would trigger infinite recursion.If you rebind the
get_opponent_source
function in your local environment in a reasonable way that is itself obviously not an attempt to breach anything then I will consider that a bug in the game engine and attempt to engineer a fix.I have not yet read over Zak’s code in detail nor have I attempted to execute it. If it infinitely recurses then it will be disqualified.
Edit: Do note that an unobfuscated simulator bot is likely to have a statement something along the lines of
from extra import get_opponent_source
. If you overwrite theget_opponent_source
function and then execute your opponent’s code, it is possible your opponent’sfrom extra import get_opponent_source
may un-overwrite your rebinding.In what order do programs get disqualified? For example, if I submit a program with an infinite loop, every other program using simulation will also go into infinite loop when meeting with my program as detecting infinite loops generally isn’t theoretically feasible. Is my program disqualified before the others? What is the general principle?
EDIT: An unrelated question: Do round numbers start from 0 or 1? In the post you write “Unlike Zvi’s original game, you do get to know what round it is. Rounds are indexed starting at 0.”, but also: “Your class must have an __init__(self, round=1) [..]”. Why not have the default initializer also use 0 if the round numbers start from zero?
First a run your program against one or more simple programs without any simulations. If your program hits an infinite loop there then you will be disqualified before you get to infect any other programs. In this way you can be disqualified before the others.
If your program passes the simple tests then it will join the pool and against all other programs which have passed the initial test. All programs which fail to terminate at this stage will be removed simultaneously.
Thank you for the bug report. I have corrected
__init__(self, round=1) [..]
to__init__(self, round=0) [..]
Hm. Can we get a “you can use at least this amount of time per move and not be disqualified”? Without wanting to say too much, I have a strategy in mind that would rely on knowing a certain runtime is safe. (Allowing for some amount of jankiness, so that if the limit was 5s I’d consider 4s safe.)
I will not disqualify anyone who can complete 100move
calls in 5 seconds (0.05 seconds per call on average) on my T450s.I will not disqualify anyone who can complete 100
move
calls in 0.05 seconds on my T450s.Edit: Corrected time limit downward by 100×.
Thanks. I confess I’d been hoping for more like 100x that, but not really expecting it :p
My previous answer was a mistake. It is actually 100
move
calls in 0.05 seconds. Sorry.The numbers are brutal.
If a game goes for 50 rounds and 10 different bots each use 5[1] seconds per move and there are 550 moves per bot per pairing then it would take 4.36 years to run this tournament.
5secondsmove×550movesspawning×100spawningsbot×round×50roundstournament×10bots×1tournament31,536,000secondsyear=4.36years
However, do note that the guarantee is “0.05 seconds per 100 moves” as opposed to “0.0005 seconds per move”. If you only have to run expensive computations once then, depending on your algorithm, the right caching system might get you close to 100× performance.
I can add caching functions to the
extra
package if that would help. The computer in question has 4 processors and runs Linux so multiprocessing can get you up to a 4× speedup.500 seconds per move is 100× my original limit which equals 10,000× the corrected limit.
Oh, geez. I figured it would be too long, but I didn’t think about just how much too long. Yeah, with these constraints, even 5s per hundred moves I agree is unreasonable.
Caching seems easy enough to implement independently, I think. No need for you to add it.
Is everybody’s code going to be in Python?
The rules are that, from the game engine’s perspective, everyone’s code is going to be written in Python 3 or Hy. It is theoretically possible that someone’s code might, say, include some code in a different language that is then executed from the Python runtime.
I don’t know how likely it is to make a difference, but what version of python 3?
Python 3.6.9 or Hy 0.18.0.