import random
#from client_runner import play
from uuid import uuid4

def analyze_list(info,left_to_win, curr_rnd, items):
     #dictionary that stores each painting as a key and the round when in which the third painting of the same artist will be on the list
    artist_reps={}
    painter_repitition=0
    for i,painter in enumerate(info["items"]):
        painter_repitition=0
        #check the first 12 elements as it is enough to find the first painting that is repeated three times
        for j in range(curr_rnd,curr_rnd+11):
            if(i>=j):
                if(info["items"][i]==info["items"][j] and (painter not in artist_reps) and (painter in items)):
                    painter_repitition+=1
                    #left_to_win refers to how many paintings do i still need to win the game
                    if(painter_repitition==left_to_win):
                        painter_repitition=0
                        artist_reps[painter]=j+1
    #rearrange the dictionary and store it as a tuple in a list then sort it based on which painter has three repeated paintings first
    # the dictionary will have this format: (picasso:8,da_vinci:7)
    # the sorted new_list will have this format: {(7,da_vinci),(8,picasso)}
    lst=[(v,k) for k,v in artist_reps.items()]
    new_list= sorted(lst)
    return new_list

#function to return two lists, one for the paintings that i have only 1 from, and one for the paintings that i possess two from
def myPaintingsCnt(info):
    singlePaintingsList=[]
    twosPaintingsList=[]
    for x,y in info["self"]["item_count"].items():
        if(y==1):
            singlePaintingsList.append(x)
        elif(y==2):
            twosPaintingsList.append(x)
    return singlePaintingsList,twosPaintingsList

def avgBudget_Others(info, playersNum):
    sum=0
    for x in range(playersNum):
        sum+=info["others"][x]["budget"]
    avg=sum//playersNum
    return avg

def block_player(info, painting, original_bid):
     bid=original_bid
     for x in range (len(info["others"])):
        if(info["others"][x]["item_count"][painting]==2):
            new_bid = info["others"][x]["budget"]+1
            if new_bid <= info['self']['budget']:
                bid=max(bid, new_bid)
                
     return bid

# for concurrent runs, dont use global state
def compute_bid_state(info, prev_state=None):
    if prev_state is None:
        prev_state = 0
    next_state = prev_state + 1
    
    #I will create a list of the four painters based on their priority, for the painting with the lowest priority I bid only 3
    min_bid=2
    bid=min_bid
    round=info["cur_round"]
    numOfPlayers=len(info["others"])
    

    #First step: We should analyze the list and decide the priorities of each painting based on the list
    initial_priority=analyze_list(info,3,round,info["item_types"]) 
   
    #give initial priorities based on values stored in new_list
    #assign the first painting that is repeated three times to be of highest priority
    #the highest priority will be a set that might be updated after each round
    highest_priority=initial_priority[0][1]
    
    painting=info["items"][round]
    
    mySinglePaintingsList,myDoublePaintingsList=myPaintingsCnt(info) 
    #assign highest priority to the paintings that I already possess one piece of
    if(len(mySinglePaintingsList)>=1):
        updated_priority=analyze_list(info,2,round,mySinglePaintingsList)
        print(updated_priority)
        #we need to check whether the priority list is empty to avoid errors
        if(len(updated_priority)!=0):
            highest_priority=updated_priority[0][1]
    
    
    #assign bids based on priority
    if((painting==highest_priority) and (len(myDoublePaintingsList)==0)):
        #if i still have no paintings I bid small amount for first painting to have money left for other paintings
        if(len(mySinglePaintingsList)==0):
            
            #if there is only one player, my first bid will be 1/3 of the total money he owns plus 1 to get slight advantage
            if(numOfPlayers==1):
                otherPlayerBudget=info["others"][0]["budget"]
                bid=((otherPlayerBudget)//3) + 1
            else:
            #when playing against many people my first bid will be based on their average
                bid=(avgBudget_Others(info, numOfPlayers)//3) + 1
                new_bid=0
            #when people possess my priority painting then my bid will be based on their remaining budget 
                for x in range(numOfPlayers):   
                    if(info["others"][x]["item_count"][painting]>=1):
                        bid2=((info["others"][x]["budget"])//2)
                        if(bid2>=new_bid):
                            new_bid=bid2
                            bid2=0
                            
                if new_bid:
                    bid = new_bid
            #in these calculations i might get a small value of bid but to stay safe the lowest i want to get is 20
            if (bid<20): 
                bid=20
                
        else:
            bid=0.4*info['self']['budget']
    
    #if one other player has two paintings of the same type, 
    
    if(len(info["others"])<=2 or (block_player(info,painting,bid)<=(info['self']['budget']//2))):
        bid=block_player(info, painting, bid)
    

    #We will spend all of the money left on a painting if we already purchased two of the same painting
    if(info['self']['item_count'][painting]==2):
        bid=info['self']['budget']
    

    print(f"bidding ${bid} for a", info["items"][round])

    return bid, None


# memorize state between rounds
store = None


def compute_bid(info):
    global store
    if info["cur_round"] == 0:
        # first round, we init the store
        store = {}
        store["mysecret"] = 3
    # update store
    store["mysecret"] += 1
    return round(info["self"]["budget"] * random.random())


if __name__ == "__main__":
    my_name = "matt-" + uuid4().hex[:6]
    server = "tcp://localhost:50018"
    print(my_name)
    play(my_name, server, compute_bid)




from copy import deepcopy
from pprint import pprint
import zmq
import time
import random
     
p=0
v=0
d=0
r=0
counter=0
counterf=0
beton=""
strat=1
blocking=False
init_state = p, v, d, r, counter, beton, counterf, strat, blocking

def dennis_fmt(info):
    """
    converts info to Dennis' format
    itemsinauction is a list where at index "rd" the item in that round is being sold is displayed.
    winnerarray is a list where at index "rd" the winner of the item sold in that round is displayed.
    winneramount is a list where at index "rd" the amount of money paid for the item sold in that round is displayed.
    example: I will now construct a sentence that would be correct if you substituted the outputs of the lists:
    In round 5 winnerarray[4] bought itemsinauction[4] for winneramount[4] dirhams/dollars/money unit.
    numberbidders is an integer displaying the amount of people playing the auction game.
    players is a list containing all the names of the current players.
    mybidderid is a string that contains your name.
    artists is a list containing all the names of the artists (paintings) that are for sale in our auction.
    standings is a set of nested dictionaries (standings is a dictionary that for each person has another dictionary
    associated with them). standings[name][artist] will return how many paintings "artist" the player "name" currently has
    standings[name]['money'] (remember quotes for string, important!) returns how much money the player "name" has left.
    If you want to access information about yourself use standings[mybidderid][(name of artist)/'money']
    rd is the current round in 0 based indexing.
    """
    itemsinauction = info["items"]
    winnerarray = [x["player"] for x in info["history"]]
    winneramount = [x["bid"] for x in info["history"]]
    numberbidders = len(info["others"]) + 1
    all_p = info["others"] + [info["self"]]
    players = [p["name"] for p in all_p]
    mybidderid = info["self"]["name"]
    artists = info["item_types"]
    standings = []
    for p in all_p:
        d1 = {artist: p["item_count"][artist] for artist in artists}
        d1.update({"money": p["budget"]})
        standings.append(d1)
    rd = info["cur_round"]
    return (
        itemsinauction,
        winnerarray,
        winneramount,
        numberbidders,
        players,
        mybidderid,
        artists,
        standings,
        rd,
    )
def compute_bid_state(info,  prev_state=None):
    if prev_state is None:
        prev_state = deepcopy(init_state)
    p,v,d,r,counter,beton, counterf,strat, blocking= prev_state
    """
    TODO: complete this function to determine the best bid.
    >>> compute_bid(sample_info)
    2
    """
    # convert info to Dennis' format
    # you are free to apply further preprocessing to make your life easier
    (
        itemsinauction,
        winnerarray,
        winneramount,
        numberbidders,
        players,
        mybidderid,
        artists,
        standings,
        rd,
    ) = dennis_fmt(info)
    def calcNumPaint(itemsinauction):
        nonlocal v,p,r,d,beton,counter
        i=rd
        p=info["self"]["item_count"]["Picasso"]
        r=info["self"]["item_count"]["Rembrandt"]
        d=info["self"]["item_count"]["Da_Vinci"]
        v=info["self"]["item_count"]["Van_Gogh"]
        while v<3 and p<3 and r<3 and d<3:
            if itemsinauction[i]=="Picasso":
                p=p+1
            elif itemsinauction[i]=="Van_Gogh":
                v=v+1
            elif itemsinauction[i]=="Rembrandt":
                r=r+1
            elif itemsinauction[i]=="Da_Vinci":
                d=d+1
            i=i+1
        if p==3:
            beton="Picasso"
        if d==3:
            beton="Da_Vinci"
        if r==3:
            beton="Rembrandt"
        if v==3:
            beton="Van_Gogh"
        counter=counter+1       
    bid = 0
    if numberbidders>2:
        if counter==0 or counterf>info["self"]["item_count"][beton]:
            calcNumPaint(itemsinauction)
            #print("Final--",beton, " is what i choose")
            #print("")
            counterf=info["self"]["item_count"][beton]
        if itemsinauction[rd]==beton:
            #print("")
            #print("Final--i own ",info["self"]["item_count"][beton], " of ", beton)
            #print("")
            if (info["self"]["item_count"][beton])==0:
                
                bid=round((info["self"]["budget"])/3)
                counterf=counterf+1
            elif (info["self"]["item_count"][beton])==1:
                bid=round((info["self"]["budget"])/2)
                counterf=counterf+1
            elif (info["self"]["item_count"][beton])==2:
                bid= (info["self"]["budget"])
                counterf=counterf+1
            else:
                bid=0
    if numberbidders==2:
        if counter==0:
            calcNumPaint(itemsinauction)
            #print("Final--",beton, " is what i choose")
            #print("")
        elif counterf>(info["self"]["item_count"][beton]):
            calcNumPaint(itemsinauction)
            #print("Final--",beton, " is what i choose")
            #print("")
            counterf=info["self"]["item_count"][beton]   
        if itemsinauction[rd]==beton:
            #print("")
            #print("Final--i own ",info["self"]["item_count"][beton], " of ", beton)
            #print("")
            if (info["self"]["item_count"][beton])==0:
                bid=round((info["self"]["budget"])/3)
                counterf=counterf+1
            elif (info["self"]["item_count"][beton])==1:
                bid=round((info["self"]["budget"])/2)
                counterf=counterf+1
            elif (info["self"]["item_count"][beton])==2:
                bid= (info["self"]["budget"])
                counterf=counterf+1
            else:
                bid=0
        for player in info["others"]:
            pnum=player["item_count"]["Picasso"]
            vnum=player["item_count"]["Van_Gogh"]
            rnum=player["item_count"]["Rembrandt"]
            dnum=player["item_count"]["Da_Vinci"]
            if pnum==2:
                if itemsinauction[rd]=="Picasso":
                    bid=player["budget"]+1
            if vnum==2:
                if itemsinauction[rd]=="Van_Gogh":
                    bid=player["budget"]+1
            if rnum==2:
                if itemsinauction[rd]=="Rembrandt":
                    bid=player["budget"]+1
            if dnum==2:
                if itemsinauction[rd]=="Da_Vinci":
                    bid=player["budget"]+1
    #print(f"Final--bidding ${bid} for a", info["items"][rd]) 
    next_state=p,v,d,r,counter,beton,counterf,strat,blocking
    return bid, next_state

from copy import deepcopy

 

#global variables

no_of_steps={}

target_list=[]

targetlist=[]

dtarget_list=[]

#succ_bidno=0

targetP=""

update_req="yes"

my_paintings={}

want_to_buy="no"

no_wanted = 0

blocked_p=0

blocked_b=0

blockingbid=0

round_no=0

my_rivals={}

red_rivals={}

backup_p=[]

 

 

init_state = (

   

#global variables

no_of_steps,

target_list,

targetlist,

dtarget_list,

#succ_bidno=0

targetP,

update_req,

my_paintings,

want_to_buy,

no_wanted,

blocked_p,

blocked_b,

blockingbid,

round_no,

my_rivals,

red_rivals,

backup_p   

    )

 

def compute_bid_state(info, prev_state):

    """

    TODO: complete this function to determine the best bid.

    >>> compute_bid(sample_info)

    2

    """

    # convert info to Dennis' format

    # you are free to apply further preprocessing to make your life easier

    my_name=info["self"]["name"]

    if prev_state is None:

        prev_state = deepcopy(init_state)

  

    (

   

        #global variables

        no_of_steps,

        target_list,

        targetlist,

        dtarget_list,

        #succ_bidno=0

        targetP,

        update_req,

        my_paintings,

        want_to_buy,

        no_wanted,

        blocked_p,

        blocked_b,

        blockingbid,

        round_no,

        my_rivals,

        red_rivals,

        backup_p

    ) = prev_state

 

    my_budget=info["self"]["budget"]

 

    list_auc = info["items"]

    no_of_types = len(info["item_types"])

    round_no= info["cur_round"]

    '''

    #computing blocking budget

    if (len(info["others"]))<2:

        blocked_b=my_budget

    else:

        blocked_b=10

    '''

    blocked_b=my_budget

    #calculate how many paintings i have (calculated every round)

    my_paintings={}

    count=0

    for x in range (len(info["item_types"])):

        key = info["item_types"][x]

        count=0

        for y in range (len(info["history"])):

 

            if info["history"][y]["player"]==my_name and info["history"][y]["item"]== info["item_types"][x]:

                count=count+1

 

        my_paintings[key]=count

  

    print("I have ", my_paintings)

    print("My target p was", targetP)

 

    #check if i got my painting

    print("want to buy (old) ", want_to_buy)

    print("no of wanted paintings ",no_wanted )

    if want_to_buy =="yes":

        if my_paintings[targetP]==no_wanted:

            want_to_buy="no"

        else:

            print("I lost this painting")

            no_wanted=my_paintings[targetP]

            if my_paintings[targetP]==0:

                update_req="yes"

 

    print("update req before checking if stay ", update_req)

 

    #update_req conditions

    if targetP!="":

        if my_paintings[targetP]!=0:

            update_req="no"

   

 

    print("update req after checking if stay ", update_req)

 

 

    #computing target list

    print("computing target list now")

    print("We are on round no and the painting is ", round_no, " ", info["items"][round_no])

    if update_req =="yes":
        for x in range(no_of_types):
            key = info["item_types"][x]
            counter=0
            noSteps=round_no
            while noSteps<10:
                if info["items"][noSteps]== key:
                    counter=counter+1
                noSteps=noSteps+1
            no_of_steps[key]= counter
        dtarget_list= [(v,k) for k,v in no_of_steps.items()]

        dtarget_list = sorted(dtarget_list)

        dtarget_list.sort(reverse=True)

       

        

    print("target_list is ", dtarget_list)

 

    #check whether same number and put in list

    same_r=[]

    max_r= dtarget_list[0][0]

    for x in range(len(dtarget_list)):

        check=0

        if dtarget_list[x][0]==max_r and x!=0:

            for y in range(len(same_r)):

                if same_r[y]==dtarget_list[x][1]:

                    check=check+1

 

            if check==0:

                same_r.append(dtarget_list[x][1])

 

    same_r.append(dtarget_list[0][1])

 

    print("These paintings have the same r ", same_r)

 

    #sorting according to shortest steps

    updatereq="no"

    if len(same_r)>=1 and update_req=="yes":

        updatereq="yes"

        update_req="no"

        print("computing updated target list now ")#, len(same_r))

 

    no_of_steps={}

 

    if updatereq =="yes":

        for x in range(len(same_r)):

            key = same_r[x]

 

            counter=0

            noSteps=round_no

 

            while counter<3 and noSteps<len(info["items"]):

 

                if info["items"][noSteps]== key:

                    counter=counter+1

 

                noSteps=noSteps+1

 

            no_of_steps[key]= noSteps-round_no

 

        targetlist= [(v,k) for k,v in no_of_steps.items()]

 

        targetlist = sorted(targetlist)

        updatereq="no"

       

    print("minimum steps  ", targetlist)

    target_listc={}

    #updating target_p

    for x in range(len(same_r)):

        key=targetlist[x][1]

        target_listc[key]=max_r

 

    print("Most updated target list ", target_listc)

 

    target_list= [(v,k) for k,v in target_listc.items()]

    print("MOST ", target_list)

 

 

 

    #changing targetP

    targetP = target_list[0][1]

    #if backup paintings are now 2
    for x in range (len(info["item_types"])):
        key = info["item_types"][x]
        check=0
        if my_paintings[key]==2:
            for y in range(len(backup_p)):
                if backup_p[y]==key:
                    check=check+1
            if check==0:
                backup_p.append(key)

    print("My backup paintings are ", backup_p)

    #now checking if we are bidding on backupp
    bidonbackup="no"

    for x in range(len(backup_p)):

        if backup_p[x]== info["items"][round_no]:

            bidonbackup="yes"

 

    print("update req is now", update_req)

   

    #blocking block

 

    #compute rival and their paintings

    counter=0

   

    for x in range (len(info["others"])):

 

        key = info["others"][x]["name"]

 

        my_rivals[key]=info["others"][x]["item_count"]

 

    print("Information about my rivals: ", my_rivals)

 

    #check if rivals have 2 paintings

 

    for x in range(len(my_rivals)):

       

        for y in range (len(info["item_types"])):

 

            if my_rivals[info["others"][x]["name"]][info["item_types"][y]]==2:

                key = info["others"][x]["name"]

                red_rivals[key]= info["item_types"][y]

    print("my red rivals are ", red_rivals)

   

    #do i block now?

    rivals=list(red_rivals)

 

    budget_r=0

    block="no"

    for x in range (len(rivals)):

        if info["items"][round_no]==red_rivals[rivals[x]]:

             block="yes"

             print("block is now set to yes")

 

             for y in range(len(info["others"])):

                 if info["others"][y]["name"]== rivals[x]:

                     budget_r= info["others"][y]["budget"]

                     print("found him and his budget is ",budget_r )

       

            

            

                

                

    print("my blocking b was ", blocked_b)

    bid=0

    blockingbid=0

    if budget_r<blocked_b and block=="yes":

        blockingbid =budget_r+1

        blocked_b=blocked_b-blockingbid

    elif budget_r==blocked_b and block=="yes":

        blockingbid =budget_r

        blocked_b=blocked_b-blockingbid

 

    print("my b budget is now ", blocked_b)

    print("my blocking bid is ", blockingbid )

    #now we start with the main computation

   

    

    print("round number ", round_no)

    if info["items"][round_no]==targetP:

        print("I need to bid this round")

        want_to_buy="yes"

        no_wanted=no_wanted+1

    else:

        want_to_buy="no"

 

    if bidonbackup=="yes":

        bid = my_budget

        print("Bidding on backup")

    else:

        if block=="yes":

            bid = blockingbid

            block="no"

           

        elif want_to_buy=="yes":

            if my_paintings[targetP]==0:

                bid= (1/3)*my_budget

                bid = round(bid, 0)

           

            elif my_paintings[targetP]==1:

                bid=0.5*my_budget

                bid = round(bid,0)

            elif my_paintings[targetP]==2:

                bid=my_budget

 

        else:

            #print("bidding 1")

            bid=1

 

 

 

   

 

    next_state = (

   

        #global variables

        no_of_steps,

        target_list,

        targetlist,

        dtarget_list,

        #succ_bidno=0

        targetP,

        update_req,

        my_paintings,

        want_to_buy,

        no_wanted,

        blocked_p,

        blocked_b,

        blockingbid,

        round_no,

        my_rivals,

        red_rivals,

        backup_p

    )

   

    print(f"bidding ${bid} for a", info["items"][round_no])

    return bid, next_state

 
from pprint import pprint
import zmq
import time
import random

PORT = 50018
context = zmq.Context()
socket = context.socket(zmq.REQ)
# socket.connect("tcp://localhost:%d" % PORT)
socket.connect("tcp://0.tcp.ngrok.io:15171")
my_name = input("what's your name? ")
is_bot = "n" not in input("are you a bot? (Y/n) ").lower()

happy_bot = [
    "Why aren't you cheering louder?",
    "Aren't you proud of me?",
    "Damn I'm good, and I don't even have a brain!",
]
sad_bot = [
    "I'm doing my best, okay?",
    "And do you think you could do any better?",
    "I feel like it's me doing all the work, you're just chilling in your chair",
    "If I lose this it's your fault not mine... I'm doing EXACTLY what you told me to do!",
]

sample_info = {
    "cur_round": 10,
    "history": [
        {"bid": 76, "item": "Van_Gogh", "player": "charlie"},
        {"bid": 79, "item": "Da_Vinci", "player": "bob"},
        {"bid": 11, "item": "Van_Gogh", "player": "bob"},
        {"bid": 92, "item": "Da_Vinci", "player": "alice"},
        {"bid": 2, "item": "Picasso", "player": "bob"},
        {"bid": 10, "item": "Picasso", "player": "charlie"},
        {"bid": 9, "item": "Van_Gogh", "player": "charlie"},
        {"bid": 6, "item": "Picasso", "player": "bob"},
        {"bid": 6, "item": "Rembrandt", "player": "alice"},
        {"bid": 3, "item": "Picasso", "player": "charlie"},
    ],
    "item_types": ["Picasso", "Van_Gogh", "Rembrandt", "Da_Vinci"],
    "items": [
        "Van_Gogh",
        "Da_Vinci",
        "Van_Gogh",
        "Da_Vinci",
        "Picasso",
        "Picasso",
        "Van_Gogh",
        "Picasso",
        "Rembrandt",
        "Picasso",
        "Picasso",
        "Da_Vinci",
        "Da_Vinci",
        "Van_Gogh",
        "Van_Gogh",
        "Van_Gogh",
        "Rembrandt",
        "Van_Gogh",
        "Rembrandt",
        "Van_Gogh",
        "Rembrandt",
        "Rembrandt",
        "Picasso",
        "Picasso",
        "Picasso",
        "Rembrandt",
        "Rembrandt",
        "Da_Vinci",
        "Da_Vinci",
        "Rembrandt",
        "Van_Gogh",
        "Da_Vinci",
        "Picasso",
        "Da_Vinci",
        "Rembrandt",
        "Picasso",
        "Rembrandt",
    ],
    "others": [
        {
            "budget": 2,
            "item_count": {"Da_Vinci": 1, "Picasso": 2, "Rembrandt": 0, "Van_Gogh": 1},
            "name": "bob",
        },
        {
            "budget": 2,
            "item_count": {"Da_Vinci": 1, "Picasso": 0, "Rembrandt": 1, "Van_Gogh": 0},
            "name": "alice",
        },
    ],
    "self": {
        "budget": 2,
        "item_count": {"Da_Vinci": 0, "Picasso": 2, "Rembrandt": 0, "Van_Gogh": 2},
        "name": "charlie",
    },
    "type": "info",
}


def dennis_fmt(info):
    """
    converts info to Dennis' format

    itemsinauction is a list where at index "rd" the item in that round is being sold is displayed.

    winnerarray is a list where at index "rd" the winner of the item sold in that round is displayed.

    winneramount is a list where at index "rd" the amount of money paid for the item sold in that round is displayed.

    example: I will now construct a sentence that would be correct if you substituted the outputs of the lists:
    In round 5 winnerarray[4] bought itemsinauction[4] for winneramount[4] dirhams/dollars/money unit.

    numberbidders is an integer displaying the amount of people playing the auction game.

    players is a list containing all the names of the current players.

    mybidderid is a string that contains your name.

    artists is a list containing all the names of the artists (paintings) that are for sale in our auction.

    standings is a set of nested dictionaries (standings is a dictionary that for each person has another dictionary
    associated with them). standings[name][artist] will return how many paintings "artist" the player "name" currently has
    standings[name]['money'] (remember quotes for string, important!) returns how much money the player "name" has left.
    If you want to access information about yourself use standings[mybidderid][(name of artist)/'money']

    rd is the current round in 0 based indexing.
    """
    itemsinauction = info["items"]
    winnerarray = [x["player"] for x in info["history"]]
    winneramount = [x["bid"] for x in info["history"]]
    numberbidders = len(info["others"]) + 1
    all_p = info["others"] + [info["self"]]
    players = [p["name"] for p in all_p]
    mybidderid = info["self"]["name"]
    artists = info["item_types"]
    standings = []
    for p in all_p:
        d1 = {artist: p["item_count"][artist] for artist in artists}
        d1.update({"money": p["budget"]})
        standings.append(d1)
    rd = info["cur_round"]
    return (
        itemsinauction,
        winnerarray,
        winneramount,
        numberbidders,
        players,
        mybidderid,
        artists,
        standings,
        rd,
    )


def compute_bid(info):
    """
    TODO: complete this function to determine the best bid.
    >>> compute_bid(sample_info)
    2
    """
    # convert info to Dennis' format
    # you are free to apply further preprocessing to make your life easier
    (
        itemsinauction,
        winnerarray,
        winneramount,
        numberbidders,
        players,
        mybidderid,
        artists,
        standings,
        rd,
    ) = dennis_fmt(info)
    rd = info["cur_round"]
    bid = info["self"]["budget"] * random.random()
    bid = round(bid)
    print(f"bidding ${bid} for a", info["items"][rd])
    return bid


def ask_bid(info):
    """
    >>> ask_bid(sample_info)
    5
    """
    rd = info["cur_round"]
    info["items"] = info["items"][rd : rd + 10]  # too long to print
    pprint(info)
    return int(input("place your bid (integer): "))


def done_msg(winner):
    if my_name == winner:
        return f"Congrats {winner}, you won!"
    else:
        return f"Too bad, you lost to {winner}."


def round_msg(info):
    if info["history"]:
        last = info["history"][-1]
        print("auction won by %s at $%d" % (last["player"], last["bid"]))
        if is_bot:
            print(
                random.choice(
                    happy_bot if info["history"][-1]["player"] == my_name else sad_bot
                )
            )
            print()
    else:
        print("Let's go!")


while True:
    request = {"type": "info", "name": my_name}
    socket.send_json(request)
    response = socket.recv_json()

    if response["type"] == "info":
        info = response.copy()
        round_msg(info)
        # we recieved the latest info, we can compute and place our bid
        if is_bot:
            bid = int(compute_bid(info))
        else:
            bid = ask_bid(info)

        bid_request = {"type": "bid", "name": my_name, "bid": bid}
        socket.send_json(bid_request)
        bid_response = socket.recv_json()
        # check that there was no error
        assert bid_response["type"] == "bid"

    elif response["type"] == "wait":
        time.sleep(0.1)

    elif response["type"] == "done":
        print(done_msg(response["winner"]))
        break

    else:
        print("ERROR:", response)


import numbers
import random
import time
import zmq
import sys

num_bidders = int(sys.argv[1]) # HOW MANY CONTESTANTS
needed_to_win = 3
init_budget = 100
item_types = ["Picasso", "Van_Gogh", "Rembrandt", "Da_Vinci"]

# time delay
between_rounds = 0.01 # How much delay between per painting auctions.

PORT = 50018
context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:%d" % PORT)


def init_player_info(player_name):
    return {
        "name": player_name,
        "budget": init_budget,
        "item_count": {it: 0 for it in item_types},
    }


def answer_phase1(req):
    """
    In phase 1, we wait for num_players to connect.
    >>> answer_phase1({"type": "info", "name": "alice"})
    {"type": "wait", "msg": "ok, waiting for all players to join"}
    """
    global players
    player = req["name"]
    if req["type"] == "info":
        if player not in player_names:
            print(player, "joined")
            player_names.append(player)
        return {"type": "wait", "msg": "ok, waiting for all players to join"}
    else:
        print("Error wait", player)
        return {"type": "error", "msg": "wait for everyone!"}


player_names = []  # player_name -> player_info
# phase1: waiting for players to join
print(f"waiting for {num_bidders} players")
while len(player_names) < num_bidders:
    request = socket.recv_json()
    response = answer_phase1(request)
    socket.send_json(response)
print()


def answer_phase2(req, cur_round, bids):
    """
    In phase 2, we accept info and bid requests.
    To info we reply with the current state if the player needs to play
    or with wait if the player should wait for the next round.
    Bids are capped by the player's budget.
    >>> answer_phase2({"type": "info", "name": "alice"})
    sample_info
    >>> answer_phase2({"type": "info", "name": "alice"})
    {"type": "wait", "msg": "others are placing their bids"}
    >>> answer_phase2({"type": "bid", "name": "alice", "bid": 3.14})
    {"type": "bid", "msg": "got it", "bid": 3.14}
    >>> answer_phase2({"type": "bid", "name": "alice", "bid": 999999999})
    {"type": "bid", "msg": "entire budget", "bid": 42}
    """
    global players, items, item_types, history
    cur_player = req["name"]
    if req["type"] == "info":
        if cur_player in bids:
            return {"type": "wait", "msg": "others are placing their bids"}
        return {
            "type": "info",
            "item_types": item_types,
            "items": items,
            "cur_round": cur_round,
            "history": history,
            "self": players[cur_player],
            "others": [info for name, info in players.items() if name != cur_player],
        }
    if req["type"] == "bid":
        budget = players[cur_player]["budget"]
        bid = max(0, min(budget, int(req["bid"])))
        bids[cur_player] = bid
        return {
            "type": "bid",
            "msg": ("got it" if bid < budget else "all in"),
            "bid": bid,
        }


game_winners = []
for game in range(100): # number of games
    print('game', game)
    items = random.choices(item_types, k=num_bidders * needed_to_win * len(item_types) + 1)
    history = []
    players = {}
    for player in player_names:
        players[player] = init_player_info(player)
    for cur_round, item in enumerate(items):
        print(f"round {cur_round}, competing for: {item}")
        bids = {}
        while len(bids) < len(players):
            request = socket.recv_json()
            response = answer_phase2(request, cur_round, bids)
            socket.send_json(response)
            print(bids, end="\r")

        print("final bids:", bids)

        # bid, _, winner = max((bid, random.random(), player) for player, bid in bids.items())
        bid, _, winner = max(
            (bid, -i, player) for i, (player, bid) in enumerate(bids.items())
        )
        print(f"game {game}: round won by {winner}, at ${bid}")
        print()

        players[winner]["budget"] -= bid
        players[winner]["item_count"][item] += 1
        history.append({"item": item, "bid": bid, "player": winner})

        if players[winner]["item_count"][item] == needed_to_win:
            print('auction game won by', winner)
            time.sleep(2)
            game_winners.append(winner)
            break  # keep last value for winner

        time.sleep(between_rounds)

from collections import Counter
print(Counter(game_winners))
for i in range(num_bidders):
    request = socket.recv_json()
    response = {"type": "done", "winners": dict(Counter(game_winners))}
    socket.send_json(response)

import sys
from pprint import pprint
import zmq
import time
import random
import uuid

# IMPORT THE PLAYER PROGRAMS
import randomclientstrat
import ahmad_sarah
import arnav_diyan
import arnesh_tala
import hunwoo_belaussa
import phil_phil
import safal_dusan
my_name = sys.argv[1]

# REPEAT THE PLAYER PROGRAMS
strat = {
	'randomclientstrat' :  randomclientstrat,
	'ahmad_sarah'  :   ahmad_sarah,
	'arnav_diyan' :   arnav_diyan,
	'arnesh_tala':   arnesh_tala,
	'hunwoo_belaussa':hunwoo_belaussa,
	'phil_phil':      phil_phil,
	'safal_dusan':    safal_dusan,
}[my_name]
my_name += '_' + uuid.uuid4().hex[:5]

context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:50018")
# socket.connect("tcp://4.tcp.ngrok.io:14031")


def round_msg(info):
    if info["history"]:
        last = info["history"][-1]
        # print("auction won by %s at $%d" % (last["player"], last["bid"]))



while True:
    request = {"type": "info", "name": my_name}
    socket.send_json(request)
    response = socket.recv_json()

    if response["type"] == "info":
        info = response.copy()
        if info['cur_round'] == 0:
            state = None
        round_msg(info)
        # we recieved the latest info, we can compute and place our bid
        bid, state = strat.compute_bid_state(info, state)
        bid = int(bid)

        bid_request = {"type": "bid", "name": my_name, "bid": bid}
        socket.send_json(bid_request)
        bid_response = socket.recv_json()
        # check that there was no error
        assert bid_response["type"] == "bid"

    elif response["type"] == "wait":
        time.sleep(0.01)

    elif response["type"] == "done":
        print('done', response)
        break

    else:
        print("ERROR:", response)


from collections import Counter

"""
def closest_three(info):
    counts = Counter(info['items'])
    # -> { "Picasso": # of picasso, VG: ... }
    by_count = []
    for artist, count in counts.items():
        ratio = count / len(info['items'])
        by_count.append((ratio, artist))
    by_count.sort(reverse=True)
    # print(by_count)
    ratio2, artist2 = by_count[1]
    # print(artist2)
    return artist2
"""

def indices_of_artists(info):
    artists = info['item_types']
    indices = {a: [] for a in artists}
    for i, item in enumerate(info['items']):
        indices[item].append(i)
    # print(indices)
    return indices

sample_info = {
    "cur_round": 10,
    "history": [
        {"bid": 76, "item": "Van_Gogh", "player": "charlie"},
        {"bid": 79, "item": "Da_Vinci", "player": "bob"},
        {"bid": 11, "item": "Van_Gogh", "player": "bob"},
        {"bid": 92, "item": "Da_Vinci", "player": "alice"},
        {"bid": 2, "item": "Picasso", "player": "bob"},
        {"bid": 10, "item": "Picasso", "player": "charlie"},
        {"bid": 9, "item": "Van_Gogh", "player": "charlie"},
        {"bid": 6, "item": "Picasso", "player": "bob"},
        {"bid": 6, "item": "Rembrandt", "player": "alice"},
        {"bid": 3, "item": "Picasso", "player": "charlie"},
    ],
    "item_types": ["Picasso", "Van_Gogh", "Rembrandt", "Da_Vinci"],
    "items": [
        "Van_Gogh",
        "Da_Vinci",
        "Van_Gogh",
        "Da_Vinci",
        "Picasso",
        "Picasso",
        "Van_Gogh",
        "Picasso",
        "Rembrandt",
        "Picasso",
        "Picasso",
        "Da_Vinci",
        "Da_Vinci",
        "Van_Gogh",
        "Van_Gogh",
        "Van_Gogh",
        "Rembrandt",
        "Van_Gogh",
        "Rembrandt",
        "Van_Gogh",
        "Rembrandt",
        "Rembrandt",
        "Picasso",
        "Picasso",
        "Picasso",
        "Rembrandt",
        "Rembrandt",
        "Da_Vinci",
        "Da_Vinci",
        "Rembrandt",
        "Van_Gogh",
        "Da_Vinci",
        "Picasso",
        "Da_Vinci",
        "Rembrandt",
        "Picasso",
        "Rembrandt",
    ],
    "others": [
        {
            "budget": 2,
            "item_count": {"Da_Vinci": 1, "Picasso": 2, "Rembrandt": 0, "Van_Gogh": 1},
            "name": "bob",
        },
        {
            "budget": 2,
            "item_count": {"Da_Vinci": 1, "Picasso": 0, "Rembrandt": 1, "Van_Gogh": 0},
            "name": "alice",
        },
    ],
    "self": {
        "budget": 2,
        "item_count": {"Da_Vinci": 0, "Picasso": 2, "Rembrandt": 0, "Van_Gogh": 2},
        "name": "charlie",
    },
    "type": "info",
}

# closest_three(sample_info)
indices_of_artists(sample_info)


def dennis_fmt(info):
    """
    converts info to Dennis' format

    itemsinauction is a list where at index "rd" the item in that round is being sold is displayed.

    winnerarray is a list where at index "rd" the winner of the item sold in that round is displayed.

    winneramount is a list where at index "rd" the amount of money paid for the item sold in that round is displayed.

    example: I will now construct a sentence that would be correct if you substituted the outputs of the lists:
    In round 5 winnerarray[4] bought itemsinauction[4] for winneramount[4] dirhams/dollars/money unit.

    numberbidders is an integer displaying the amount of people playing the auction game.

    players is a list containing all the names of the current players.

    mybidderid is a string that contains your name.

    artists is a list containing all the names of the artists (paintings) that are for sale in our auction.

    standings is a set of nested dictionaries (standings is a dictionary that for each person has another dictionary
    associated with them). standings[name][artist] will return how many paintings "artist" the player "name" currently has
    standings[name]['money'] (remember quotes for string, important!) returns how much money the player "name" has left.
    If you want to access information about yourself use standings[mybidderid][(name of artist)/'money']

    rd is the current round in 0 based indexing.
    """
    itemsinauction = info["items"]
    winnerarray = [x["player"] for x in info["history"]]
    winneramount = [x["bid"] for x in info["history"]]
    numberbidders = len(info["others"]) + 1
    all_p = info["others"] + [info["self"]]
    players = [p["name"] for p in all_p]
    mybidderid = info["self"]["name"]
    artists = info["item_types"]
    standings = []
    for p in all_p:
        d1 = {artist: p["item_count"][artist] for artist in artists}
        d1.update({"money": p["budget"]})
        standings.append(d1)
    rd = info["cur_round"]
    return (
        itemsinauction,
        winnerarray,
        winneramount,
        numberbidders,
        players,
        mybidderid,
        artists,
        standings,
        rd,
    )


def compute_bid_state(info, prev_state):
    """
    TODO: complete this function to determine the best bid.
    >>> compute_bid(sample_info)
    2
    """
    # convert info to Dennis' format
    # you are free to apply further preprocessing to make your life easier
    (
        itemsinauction,
        winnerarray,
        winneramount,
        numberbidders,
        players,
        mybidderid,
        artists,
        standings,
        rd,
    ) = dennis_fmt(info)
    
    rd = info["cur_round"]
    biddinghistory = []

    # if no budget we withdraw early from the bid
    if info['self']['budget'] == 0:
        biddinghistory.append(0)
        bid = 0
        return bid, None
    
    # if i have two of one artist bid all my money on the next round
    if info["self"]["item_count"][info["items"][rd]]  == 2:
        bid = info["self"]["budget"]
        return bid, None

    # FPSB: if there are two bidders we just bet half our budget every round then we should win the first three rounds so we win no?
    
    if numberbidders == 2:
        
            # do we want to bed half every round? or do we want to choose which round to bet on
            bid = info["self"]["budget"] * 0.5
            bid = round(bid)
            print(f"bidding ${bid} for a", info["items"][rd])
            return bid, None

    # if there's more than two players we don't bet on the first round
    else:
        if rd != 0:
        
        # if there are more than two we bet on the items that come after the earliest recurring artist: 
        # for instance in sample info every time after Van Gogh we bet some amount of money : so on items of index 1 3 7 14 
        # otherwise we don't participate on the bet 

        # need to decide the set of items we want to buy
            artistindex = indices_of_artists(info)
            third_artists = []
            for artist, indices in artistindex.items():
                third = indices[2] if len(indices) >= 3 else 1234
                third_artists.append((third, artist))
            min_third, min_artist = min(third_artists)
            before_indices = artistindex[min_artist]
            
            if rd-1 in before_indices:
                # explain why we chose 20
                bid = 20
                return bid, None
            
    return 0, None

import random
from uuid import uuid4
from collections import Counter

def compute_bid_state(info, prev_state=None):
    next_state=prev_state
    bid=0
    if prev_state is None:
        next_state={}
        next_state["rankey"]=0
        next_state["rankey"]=random.randint(0,10)
        if next_state["rankey"]<=7: #go for first most common
            biggerFirst=info["items"][0:(len(info["others"])*4)]
            next_state["biggerFirstcounts"] = Counter(biggerFirst)
            mostcommonBig=next_state["biggerFirstcounts"].most_common(1)[0][0] # get the most common item
            smallerFirst=info["items"][0:(len(info["others"])*3)]
            next_state["smallerFirstcounts"] = Counter(smallerFirst)
            mostcommonSmall=next_state["smallerFirstcounts"].most_common(1)[0][0] # get the most common item
            if next_state["smallerFirstcounts"][mostcommonSmall]>=3:
                next_state["target"]=mostcommonSmall
            else:
                next_state["target"]=mostcommonBig
        else: # go for second most common
            if (len(info["others"])*4)<15:
                biggerFirst=info["items"][0:(len(info["others"])*6)]
            else:
                biggerFirst=info["items"][0:(len(info["others"])*4)]
            next_state["biggerFirstcounts"] = Counter(biggerFirst)
            # get the second most common item
            mostcommonBig=next_state["biggerFirstcounts"].most_common(2)[1][0]  # get the second most common item
            if (len(info["others"])*3)<12:
                smallerFirst=info["items"][0:(len(info["others"])*5)]
            else:
                smallerFirst=info["items"][0:(len(info["others"])*3)]
            next_state["smallerFirstcounts"] = Counter(smallerFirst)
            mostcommonSmall=next_state["smallerFirstcounts"].most_common(2)[1][0] # get the second most common item
            if next_state["smallerFirstcounts"][mostcommonSmall]>=3:
                next_state["target"]=mostcommonSmall
            else:
                next_state["target"]=mostcommonBig
        next_state['bid_times']=0
    round_num=info["cur_round"]
    if info["items"][round_num]==next_state["target"]:
        bid=33
        next_state['bid_times']=next_state['bid_times']+1
        if next_state['bid_times']>=2:
            bid=34
    else:
        bid=0
    return bid, next_state

import random

def compute_bid_state(info, prev_state):
    bid = random.random() * info["self"]['budget']
    bid = round(bid)
    next_state = (prev_state or 0) + 1
    return bid, next_state

import random
# from client_runner import play
from uuid import uuid4


# for concurrent runs, dont use global state
def compute_bid_state(info, prev_state=None):
    if prev_state is None:
        prev_state = 0
    next_state = prev_state + 1
    bid = compute_bid(info)
    return bid, next_state


order_early = []

# these variables keep track of your priority and opponent's priority
mypick = ""
opppick = ""

# helper function that calculates the order in which the paintings end
# for paintings to end, you need 3 in succession
def calculate_pick(order, dict={}):
    dict = {}
    order_early = []
    
    for el in order:
        if el in dict: dict[el] += 1
        else: dict[el] = 1

        if dict[el] == 3:
            order_early.append(el)
    
    return order_early

    # print(order_early)

# we look at a fixed number of paintings in the beginning of sequence to pick our priority and also opponent's
# using the helper function above
def initial_analysis(info):
    global mypick, opppick, order_early

    perm = info["items"][:20]
    order_early = calculate_pick(perm, info)
    
    mypick = opppick = order_early[0]

def choose_opponent(info):
    opp = ''
    if len(info["others"]) == 1:
        opp = info["others"][0]
    else:
        os = []

        # we take all the players who are going for our pick, and we claim that's our opponent
        for o in info["others"]:
            os.append(o["item_count"][mypick])
        i = os.index(max(os))

        opp = info["others"][i]

    return opp

# main analysis function for subsequent rounds
def analyze(info):
    global opppick, mypick
    round = info["cur_round"]   # current round number
    curr = info["items"][round]     # current painting

    # get what we can almost win
    me_almost_wins = []
    my_count = info["self"]["item_count"]
    for p in my_count:
        if my_count[p] == 2:
            me_almost_wins.append(p)
    
    # bid everything if we're about to win
    # if info["self"]["item_count"][mypick] in me_almost_wins and curr == mypick: return info["self"]["budget"]
    if curr in me_almost_wins: return info["self"]["budget"]

    # early return if it's the first round and we find what we want
    if round == 0 and curr == mypick: return 33
    
    # who's my opponent? for multiplayer
    opp = choose_opponent(info)
    
    # getting opponent's almost wins
    almost_wins = []
    opp_paintings = opp["item_count"]
    for e in opp_paintings:
        if opp_paintings[e] == 2:
            almost_wins.append(e)
    
    #-----HELPER VARIABLES ------
    mybudget = info["self"]["budget"]
    # mypaintings = 3 - info["self"]["item_count"][mypick]
    opp_rem_budget = opp["budget"]
    opp_rem_paintings = 3 - opp["item_count"][opppick]

    # block = int(mybudget - ((opp_rem_budget/opp_rem_paintings)*mypaintings) ) +1
    block = opp_rem_budget + 1
    #------------------------------

    # if opponent close to winning, block with +1
    if (curr in almost_wins):
        # print("im blocking")
        if block <= mybudget:
            return block
    
    # if they aren't about to win
    # change my pick
    # if they have more of their priority painting than us...
    if opp["item_count"][opppick] > info["self"]["item_count"][opppick]:
        # keep outbidding opponent if they spend a lot on the first round
        if curr == mypick and int(opp_rem_budget / opp_rem_paintings) < int(mybudget / (3-info["self"]["item_count"][opppick])):
            bid = int(opp_rem_budget / opp_rem_paintings) + 1
            return bid
        else:
            # if they haven't spent a lot on the first painting, change your pick
            for p in order_early:
                if p != opppick:
                    mypick = p
                    break
    
    
    # some programs might use a specific 'low bid', we can try to outbid that
    low_bid = 1
    filtered = list(filter(lambda h: h["bid"] < 5, info["history"]))
    if len(filtered) >= 1: low_bid = max(filtered, key=(lambda x: x["bid"]))["bid"] + 1

    # if current painting isn't what we want, make a low bid
    if curr != mypick:
        return low_bid
    else:
        # if we want the current painting, but opponent doesn't want it, make a low bid
        if mypick != opppick:
            return low_bid
        else:
            # if both of us want, make max bid
            return 33
      

def compute_bid(info):
    if info["cur_round"] == 0:
        initial_analysis(info)

    # if can't find paintings that end earlier, just don't bother bidding
    if info["items"][info["cur_round"]] not in order_early: return 0
    return analyze(info)


if __name__ == "__main__":
    my_name = "matt-" + uuid4().hex[:6]
    server = "tcp://localhost:50018"
    print(my_name)
    play(my_name, server, compute_bid)


