win a virtual fencing match (against your fellow stack exchangers) WARNING: this is a fairly complex problem, in a king-of-the-hill combat style, with an added randomness to it, the best code may not always win. Please read all rules in full, as it is quite complex!

FLAVOR TEXT


Bill and Steve  decided to have a "friendly" duel, however being so rich and smart, decided to let their best programmers try their hand at coming up with the code to beat each other. You are said programmer.

OBJECTIVE


In fencing, your goal is to score the most hits on your opponent, while getting hit the least yourself

MOVES


Your code will have the following choices of "moves"

Attack
Parry
Block
Lunge
AND
Head
Chest
Feet

SCORING POINTS


Attacking beats Parrying, for 1 point
Lunging beats Blocking, for 1 point
Parrying Beats Lunging, for 1 point
Blocking beats Attacking, for 1 point
Lunging ties Attacking, with the lunging player unable to block or parry the next round, and the attacking player unable to attack or lunge the next round
Blocking ties Parrying, with the parrying player unable to block or parry the next round, and the blocking player unable to attack or lunge the next round

HEIGHT OF ACTION


you will also choose a "height" for your action, whereupon the outcomes above only take place if the heights of both players match the attack height. if the heights do not match, both players may no longer select the same action (height is not restricted) as the previous tying rounds, until a point is scored, or all 4 actions have been filled (once a tie is broken, all actions are available again)

CODE REQUIREMENTS


for each round, it should prompt the move of the opponent the previous round (barring round 1), compare it with its own, determine the outcome of the previous round, then output the next round number, the score, and its choice / position for that round

ex:
  INPUT: LC (lunge chest)
  OUTPUT: previous round: PM vs LC - PM scores! score is now 2-1, action for next round is AH (attack head)

WINNER


the game ends after 50 rounds, or after 3 points have been scored

AGAINST OTHER PLAYERS


the first answer will be given an instant guaranteed win, as long as it functions to actually work/play. Each answer will be evaluated, in order of posting, against the previous winner, and if winning, will be declared the new winner. I ask that while winning, or waiting to compete, that you do NOT change your code. Once being defeated, you may no longer compete for championship status with that same language, you may however submit a different language answer (must be significantly different, no using variations of the same base stuff).

I will attempt to run each challenge and will post the results in the comments of the champion and challenger, as well as declare a new winner - as I may not be able to run every language, especially some of the more obscure ones, I ask any possible help you can give to make sure your answer gets run be taken into consideration. Thank you!
Python

En garde!

My warrior combines unpredictability with a keen eye for weakness in his opponent's stance.  He's pretty confident he'll be able to dispose of aggressive foes but his trainer (me) may have failed to anticipate certain scenarios or, perhaps more worrying, may have misinterpreted the rules (bugs!!).

Anyway I'm new so hopefully this is an ok format for the code:

from random import choice, random

def cleverly_pick_move(me_allowed,op_allowed,opp_last_move=None) :
    """ Behold the genius you're up against!
    Pretty much everything else is just flavour text or match rules
    so you'll probably only want to read this...
    """
    heights = ['head','chest','feet']
    rand_choice = lambda a,h : {'type':choice([t for t in a if a[t]]),
                                'height':choice(h)}

    if opp_last_move is None or feeling_like_a_lucky_punk():
        return rand_choice(me_allowed,heights)

    if sum(1 for x in op_allowed if op_allowed[x]) == 3 :
        for i in op_allowed:
            if not op_allowed[i] :
                weakness = i
                break
        return {'type':exploit_weakness(weakness,me_allowed),
                'height':choice(heights)}
    return rand_choice(me_allowed,heights)

def exploit_weakness(weakness,me_allowed) :
    moves = ['attack','parry','lunge','block']
    for i,move in enumerate(moves) :
        if move == weakness :
            if me_allowed[moves[(i+1) % 4]] :
                return moves[(i+1) % 4]
            break
    if me_allowed[weakness] :
        return weakness
    return choice([x for x in me_allowed if me_allowed[x]])

def feeling_like_a_lucky_punk() :
    return random() &gt; 0.8

def main():

    this_round = 1
    opp_last_move = None
    score   = {'myself':0, 'the blaggard':0}
    quips   = ['blaggard', 'fool', 'scum', 'raggamuffin']
    adverbs = ['deftly', 'skillfully', 'gracefully', 'clumsily']

    me_allowed = {'attack':True,'block':True,'lunge':True,'parry':True}
    op_allowed = {'attack':True,'block':True,'lunge':True,'parry':True}

    while (this_round &lt;= 50 and
           all([points &lt; 3 for points in score.values()])) :

        if this_round == 1 :
            move = cleverly_pick_move(me_allowed,op_allowed) 
        else:
            move = cleverly_pick_move(me_allowed,op_allowed,
                                      opp_last_move=opp_last_move)

        print "Our hero %s %ss at the %s's %s" % (
            choice(adverbs),
            move['type'],
            choice(quips),
            move['height']
            )
        print "We await the %s's response..." % choice(quips)
        print "Our hero's move: " + (move['type'][0]+move['height'][0]).upper()

        opp_move = parse_move(raw_input("Opponent's move: "))

        outcome,me_allowed,op_allowed = get_outcome(move,opp_move,me_allowed,
                                                    op_allowed)
        if outcome == 'WIN' :
            print "Our hero pulls off an excellent round!"
            score['myself'] += 1
        elif outcome == 'LOSE' :
            print "Never before have we seen such blatant cheating!"
            score['the blaggard'] += 1
        else :
            print "Our hero is clearly toying with his opponent as he allows \
a drawn round."

        print ("""The score after round %d:\nOur hero:\t%d\nHis opponent:\t%d""" 
                % (this_round, score['myself'], score['the blaggard']))
        opp_last_move = opp_move
        this_round += 1

    print "Match over, surely the victory is mine!"
    print """Final score:\n
             Our hero:\t%d\nOpponent:\t%d""" % (score['myself'],
                                                score['the blaggard'])

    if score['myself'] &gt; score['the blaggard'] :
        print "My victory was inevitable!"
    elif score['myself'] == score['the blaggard'] :
        print "An even match! Huzzar!"
    else :
        print ""    
    return

def reset_allowed(dictionary) :
    return dict((x,True) for x in dictionary)

def get_outcome(mymove,opmove,me_allowed,op_allowed) :
    result = ''

    if not me_allowed[mymove['type']] :
        print "Whoops, I forgot I couldn't do that..."
        result = 'LOSE'

    if not op_allowed[opmove['type']] :
        print "Haha! What a clutz!"
        result = 'WIN'

    if mymove['height'] != opmove['height'] :
        print "The combatants flail at each other with little effect!"
        print "They'll have to try something else next round!"
        result = 'DRAW'

    if mymove['type'] == opmove['type'] :
        if mymove['type'] in ['attack','lunge']:
            print "The combatants' blades clash dramatically!"
        else :
            print "Both combatants take a moment to practice their \
defensive stance..."
        result = 'DRAW'

    if result :
        me_allowed, op_allowed = (reset_allowed(me_allowed),
                                  reset_allowed(op_allowed))
        if mymove['height'] != opmove['height'] :
            me_allowed[mymove['type']] = op_allowed[opmove['type']] = False
        return (result, me_allowed,op_allowed)
    else :
        return compare_attacks(mymove,opmove,me_allowed,op_allowed)

def compare_attacks(mymove,opmove,me_allowed,op_allowed) :
    """
    0 A &gt; P 1
     ^  x  v
    3 B &lt; L 2
    """
    print "Our hero %ss, his opponent %ss!" % (mymove['type'],opmove['type'])

    move_val = {'attack':0,'parry':1,'lunge':2,'block':3}
    result_num = (move_val[opmove['type']] - move_val[mymove['type']]) % 4
    results = ['DRAW','WIN','DRAW','LOSE']

    me_allowed, op_allowed = (reset_allowed(me_allowed),
                              reset_allowed(op_allowed))    
    if result_num == 1 :
        print "Our hero easily outwits his foe! *Huge cheers from crowd*"
        return ('WIN',me_allowed,op_allowed)
    elif result_num == 3 :
        print "Our hero graciously allows his opponent a charity point.\
*A torrent of boos from the crowd*"
        return ('LOSE',me_allowed,op_allowed)
    else:
        # Combatants drew and will have their moves restricted next round.
        if mymove['type'] in ['attack','parry'] :
            me_allowed['attack'] = me_allowed['lunge'] = False
            me_allowed['parry']  = me_allowed['block'] = True
            op_allowed['parry']  = op_allowed['block'] = False
            op_allowed['attack'] = op_allowed['lunge'] = True
        else :
            me_allowed['parry']  = me_allowed['block'] = False
            me_allowed['attack'] = me_allowed['lunge'] = True 
            op_allowed['attack'] = me_allowed['lunge'] = False
            op_allowed['parry']  = op_allowed['block'] = True
        return ('DRAW',me_allowed,op_allowed)

def parse_move(move_string) :
    m_types = {'A':'attack','B':'block','L':'lunge','P':'parry'}
    m_heights = {'C':'chest','H':'head','F':'feet'}

    move_string = move_string.strip().upper()
    if not move_string :
        print "Couldn't understand your input: %s" % move_string
        return parse_move(raw_input("Opponent's move: "))

    if move_string[0] not in m_types :
        move_string = move_string[::-1] 

    try :
        move = {'type':m_types[move_string[0]],
                'height':m_heights[move_string[1]]}
        return move
    except KeyError :
        print "Couldn't understand your input: %s" % move_string
        return parse_move(raw_input("Opponent's move: "))

if __name__ == '__main__' :
    main()