# exit code 0  == solved
# exit code >0 == not solved, counts as a wrong submit
# stdout should be shown to the contestants
from sys import stdin, argv, exit
from os import path as os_path
from random import seed, randint

k_easy, p_easy, k_hard, p_hard = 3, 9, 9, 10

def random_key(k,p): return ''.join([str(randint(0,k-1)) for i in range(p)])

#state_dir='././states/'
state_dir='/home/ipsc/ipsc2012real/repo/key/states/'
if state_dir == '././states/': print("WARNING: state_dir has to be configured before using this script for grading")

assert os_path.exists(state_dir), "The directory used to store states does not exist."

usage = "Correct usage: python {} tis {{easy,hard}} < submitted_commands".format(argv[0])
assert len(argv)==3, usage
tis, mode = argv[1], argv[2]
assert mode == "easy" or mode == "hard", usage
seed(tis+'zaJac')
state_file = state_dir+'/'+tis+'.'+mode

def generate_new_state():
    global mode, p_easy, p_hard, masterkey, roomkey719, roomkey723, profiles
    if mode == "easy":
        masterkey = random_key(k_easy,p_easy)
        while True:
            roomkey719 = random_key(k_easy,p_easy)
            if roomkey719 != masterkey: break
        while True:
            roomkey723 = random_key(k_easy,p_easy)
            good = True
            if roomkey723 == roomkey719: good = False
            for i in range(p_easy):
                if masterkey[i]==roomkey723[i]: good = False
            if good: break
        profiles = [ "0"*p_easy for i in range(160) ]
    else:
        masterkey = random_key(k_hard,p_hard)
        while True:
            roomkey719 = random_key(k_hard,p_hard)
            if roomkey719 != masterkey: break
        while True:
            roomkey723 = random_key(k_hard,p_hard)
            if roomkey723 != masterkey and roomkey723 != roomkey719: break
        profiles = [ "0"*p_hard for i in range(23) ]

def load_state():
    global state_file, masterkey, roomkey719, roomkey723, profiles
    try:
        state = [ x.strip() for x in open(state_file,"r").readlines() ]
        masterkey, roomkey719, roomkey723 = state[:3]
        profiles = state[3:]
    except:
        print("Something is horribly broken. Please contact the organizers. Sorry!")

def save_state():
    global state_file, masterkey, roomkey719, roomkey723, profiles
    try:
        f = open(state_file,"w")
        f.write(masterkey+"\n"+roomkey719+"\n"+roomkey723+"\n")
        for pp in profiles: f.write(pp+"\n")
        f.close()
    except:
        print("Something is horribly broken. Please contact the organizers. Sorry!")

# load the state from a file OR generate a new state
if os_path.isfile(state_file): load_state()
else: generate_new_state()

if mode == "easy": k, p = k_easy, p_easy
else: k, p = k_hard, p_hard

commands = stdin.readlines()
if len(commands)==0:
    print("WARNING: No commands found, file empty.")
if len(commands)>20:
    print("WARNING: More than 20 lines. Lines 21+ will be ignored.")
    commands = commands[:20]

if mode == "hard" and len(commands) != 1:
    terminal = False
    for cmd in commands:
        tokens = cmd.split()
        if len(tokens)==3 and tokens[0]=="try" and tokens[2]=="723": terminal = True
    if terminal:
        print("ERROR: The command 'try A 723' may only be sent as the only command in the file.")
        print("All commands in your submission were ignored.")
        exit(1)

for cmdid, cmd in enumerate(commands):
    tokens = cmd.split()
    processed = False
    if len(tokens)==1 and tokens[0]=="restart":
        print("restarted")
        seed(tis+'zaJac'+'@'.join(commands)+str(cmdid)+masterkey)
        generate_new_state()
        processed = True
    if len(tokens)==1 and tokens[0]=="checkin":
        print("room key: {}".format(roomkey719))
        processed = True
    if len(tokens)==3 and tokens[0]=="file":
        try:
            A = int(tokens[1])
            assert A >= 1
            assert A <= len(profiles)
            newbit = tokens[2]
            assert len(newbit)==p
            for x in newbit:
                assert x.isdigit()
                assert ord(x)-ord('0') < k
            newkey = ''
            for i in range(p):
                if newbit[i] < profiles[A-1][i]:
                    newkey += profiles[A-1][i]
                else:
                    newkey += newbit[i]
            profiles[A-1] = newkey
            print("key {} new bitting {}".format(A,newkey))
        except:
            print("filing failed -- incorrect syntax")
        processed = True
    if len(tokens)==3 and tokens[0]=="try":
        try:
            A = int(tokens[1])
            assert A >= 1
            assert A <= len(profiles)
            R = tokens[2]
            assert R=="719" or R=="723"
            fits = True
            if R=="719":
                for i in range(p):
                    if profiles[A-1][i] != masterkey[i] and profiles[A-1][i] != roomkey719[i]: fits = False
            else:
                for i in range(p):
                    if profiles[A-1][i] != masterkey[i] and profiles[A-1][i] != roomkey723[i]: fits = False
            if fits:
                print("success")
                if R=="723": exit(0) # <----- data set was just solved
            else:
                print("failure")
        except Exception:
            print("try failed -- incorrect syntax")
        processed = True
    if not processed:
        print("unknown command")

save_state()
# if we got here, we correctly processed all commands, but the data set was not solved yet -- so the exit code is 1
exit(1)
