#!/usr/bin/env python
import sys
from sys import stdin, stdout
from types import FunctionType

# TODO move all non-icfp-related prints to log functions
# TODO validate vitality, and throw errors
# TODO count applications of functions
# TODO apply slots before each turn, for every dead slot; implement the special
#      behaviours for inc, dec, attack and help

def I(x):
    return x
I.name = "I"

def succ(x):
    return x + 1
succ.name = "succ"

def dbl(x):
    return 2 * x
dbl.name = "dbl"

def to_str(x):
    if type(x) is FunctionType:
        return x.name
    if x == 0:
        return "zero"
    return str(x)

def K(x):
    def a(y):
        return x
    a.name = "K(%s)" % to_str(x)
    return a
K.name = "K"

def S(x):
    def a(y):
        def b(z):
            _ = x(z)
            __ = y(z)
            return _(__)
        b.name = "%s(%s)" % (a.name, to_str(y))
        return b
    a.name = "S(%s)" % to_str(x)
    return a
S.name = "S"

def get(i):
    global curr_player
    rv = curr_player.get_slot_val(i)
    return rv
get.name = "get"

def put(i):
    global curr_player
    curr_player.set_slot_val(i, I)
    return I
put.name = "put"

def inc(i):
    global curr_player
    curr_player.inc(i)
    return I
inc.name = "inc"

def dec(i):
    global opponent
    opponent.dec(255-i)
    return I
dec.name = "dec"

def attack(i):
    def a(j):
        def b(n):
            global curr_player, opponent
            curr_player.dec_by_n(i,n)
            opponent.dec_by_n(255-j,(n*9/10))
            return I
        b.name = "%s(%s)" % (a.name, to_str(j))
        return b
    a.name = "attack(%s)" % to_str(i)
    return a

attack.name = "attack"

def help(i):
    global curr_player
    def a(j):
        def b(n):
            curr_player.dec_by_n(i,n)
            curr_player.inc_by_n(j,(n*11/10))
            return I
        b.name = "%s(%s)" % (a.name, to_str(j))
        return b
    a.name = "help(%s)" % to_str(i)
    return a

help.name = "help"

def copy(i):
    global opponent
    return opponent.get_slot_val(255-i)

copy.name = "copy"

def revive(i):
    global curr_player
    slot=curr_player.slots[i]
    if not slot.is_alive():
        slot.vitality=1
    return I

revive.name = "revive"

def zombie(i):
    global opponent
    def a(x):
        sv=opponent.slots[255-i]
        if not sv.is_alive():
            sv.v=x
            sv.vitality=-1
            return I
    a.name = "zombie(%s)" % to_str(i)
    return a

zombie.name = "zombie"



zero = 0

cards = dict(zero=zero, I = I, succ = succ, dbl=dbl, get=get, put=put,
             inc=inc, dec=dec, S=S, K=K, attack=attack, help=help,
             copy=copy, revive=revive, zombie=zombie)

class Slot:
    def __init__(self):
        self.vitality = 10000
        self.v = I

    def is_alive(self):
        return self.vitality > 0

class Player:
    def __init__(self, name):
        self.slots = [Slot() for i in range(256)]
        self.name = name
    
    def move(self, _move, opponent):
        global cards
        i = _move.slot_no
        cname = _move.card_name
        val = self.slots[i].v
        card = cards[cname]
        if _move.ap_type == Move.CARD_TO_SLOT:
            rv = card(val)
        else:
            rv = val(card)
        self.set_slot_val(i, rv)

    def current_val(self, slot):
        """for the copy card"""
        return self.slots[slot].v

    def inc(self, i):
        self.slots[i].vitality += 1

    def inc_by_n(self, i,n):
        self.slots[i].vitality += n

    def dec(self, i):
        self.slots[i].vitality -= 1

    def dec_by_n(self, i,n):
        self.slots[i].vitality -= n

    def set_slot_val(self, i, val):
        self.slots[i].v = val

    def get_slot_val(self, i):
        return self.slots[i].v

    def used_slots_str(self):
        used_slots = ["%d={%d,%s}" % (i, s.vitality, to_str(s.v))
                        for i, s in enumerate(self.slots)
                            if s.vitality != 10000 or s.v is not I]
        return "\n".join(used_slots)

class Move:
    CARD_TO_SLOT = 1
    SLOT_TO_CARD = 2

    def __init__(self, ap_type=None, card_name=None, slot_no=None):
        self.ap_type = ap_type
        self.card_name = card_name
        self.slot_no = slot_no

    def write(self):
        stdout.write("%d\n" % self.ap_type)
        if self.ap_type == Move.CARD_TO_SLOT:
            stdout.write("%s\n" % self.card_name)
            stdout.write("%d\n" % self.slot_no)
        else:
            stdout.write("%d\n" % self.slot_no)
            stdout.write("%s\n" % self.card_name)
        stdout.flush()


def ask_move(turn, player):
    # TODO FIXME remove print and anything that pollutes the stdout
    print "###### turn %d" % turn
    print "*** player %s's turn, with slots:" % player.name
    print "(slots {10000,I} are omitted)"
    s = player.used_slots_str()
    if s.strip():
        print s
    move = Move()
    move.ap_type = input("(1) apply card to slot, or (2) apply slot to card?\n")
    if move.ap_type == Move.CARD_TO_SLOT:
        move.card_name = raw_input("card name?\n")
        move.slot_no = int(input("slot no?\n"))
    else:
        move.slot_no = int(input("slot no?\n"))
        move.card_name = raw_input("card name?\n")
    return move
    

curr_player = None
opponent = None

def game(only=False):
    global curr_player, opponent
    p0 = Player("0")
    p1 = Player("1")
    curr_player = p0
    opponent = p1
    turn = 1
    while True:
        move =  ask_move(turn, curr_player)
        curr_player.move(move, opponent)
        if not only:
            curr_player, opponent = opponent, curr_player
        turn = turn + 1

def read_move():
    """reads a move of out opponent from stdin"""
    move = Move()
    move.ap_type = int(stdin.readline().strip())
    if move.ap_type == Move.CARD_TO_SLOT:
        move.card_name = stdin.readline().strip()
        move.slot_no = int(stdin.readline().strip())
    else:
        move.slot_no = int(stdin.readline().strip())
        move.card_name = stdin.readline().strip()
    return move

class Bot:
    def __init__(self, player, opponent):
        self.player = player
        self.opponent = opponent
        self.zeroed = False

    def move(self):
        # TODO WRITEME
        if not self.zeroed:
            m = Move(ap_type=Move.SLOT_TO_CARD, slot_no=0, card_name="zero")
            self.zeroed = True
        else:
            m = Move(ap_type=Move.CARD_TO_SLOT, slot_no=0, card_name="dec")
            self.zeroed = False
        return m

def play_bot(player_turn):
    i_am_p0 = player_turn == '0'
    global curr_player, opponent
    p0 = Player("0")
    p1 = Player("1")
    turn = 1
    if not i_am_p0:
        curr_player = p1
        opponent = p0
        move = read_move()
        curr_player, opponent = opponent, curr_player
        curr_player.move(move, opponent)
        curr_player, opponent = opponent, curr_player
    else:
        curr_player = p0
        opponent = p1
    bot = Bot(curr_player, opponent)
    while True:
        # me
        m = bot.move()
        curr_player.move(m, opponent)
        m.write()

        # the other jerk
        m = read_move()
        # this is required for the cards to work properly
        curr_player, opponent = opponent, curr_player
        curr_player.move(m, opponent)
        curr_player, opponent = opponent, curr_player

        turn = turn + 1

if __name__ == '__main__':
    if len(sys.argv) == 1:
        game()
    elif len(sys.argv) == 2 and sys.argv[1] in ('1', '0'):
        play_bot(sys.argv[1])
    elif len(sys.argv) == 2 and sys.argv[1] == 'only':
        game(only=True)

