<reponame>uvammm/uvammm.github.io
###
### auction.py - code for simulating user clicks and running the auction
###
### Project 4
### Markets, Mechanisms, and Machines
### University of Virginia
### Spring 2019
### https://uvammm.github.io/
###
### <NAME>
###

import redis
import json
import random
from threading import Timer

N_BIDS = 2500   # total number of bidding rounds
MIN_BID = 0.25 # minimum bid amount required to win a slot

def load_teams(r):
    """
    Load all team ids from the redis database, and 
    return as a list of team_ids
    """

    team_ids = r.get('teams')
    if not team_ids:
        return []
    team_ids = json.loads(team_ids)
    return team_ids

def load_team_info(r, team_id):
    """
    Given a team_id, load the information for that team
    from the database and return it
    """

    team_info = r.get(team_id)
    if not team_info:
        return None
    team_info = json.loads(team_info)
    return team_info

def set_message(r, team_id, message):
    """ 
    Appends message to team_id's message list in the db
    """

    team_info = load_team_info(r, team_id)
    if not team_info:
        return
    team_info['messages'].append(message)
    r.set(team_id, json.dumps(team_info))

def reduce_budget(r, team_id, amount):
    """
    Reduces team_id's budget by amount
    """

    team_info = load_team_info(r, team_id)
    if not team_info:
        return
    team_info['budget_left'] -= amount
    r.set(team_id, json.dumps(team_info))

def run_auction(r, auction_id):
    """
    Returns top 3 winning slots for the round auction_id. 
    If there were fewer than 3 bids of at least 
    MIN_BID amount, then returns fewer than 3 slots.
    """

    team_ids = load_teams(r)
    if not team_ids:
        return []

    bids = {}
    for team_id in team_ids:
        team_info = load_team_info(r, team_id)
        if not team_info:
            bids[team_id] = 0.0
            continue
        if not auction_id in team_info['bids']:
            bids[team_id] = 0.0
            continue
        if team_info['bids'][auction_id] >= MIN_BID:
            bids[team_id] = team_info['bids'][auction_id]


    bids_lst = [(bids[k], random.random(), k) for k in bids]
    bids_lst.sort(reverse=True)

    winning_slots = [MIN_BID, MIN_BID, MIN_BID]
    slots = []
    if len(bids_lst) > 0:
        slots.append((bids_lst[0][2], bids_lst[0][0]))
        winning_slots[0] = max(bids_lst[0][0], MIN_BID)
    if len(bids_lst) > 1:
        slots.append((bids_lst[1][2], bids_lst[1][0]))
        winning_slots[1] = max(bids_lst[1][0], MIN_BID)
    if len(bids_lst) > 2:
        slots.append((bids_lst[2][2], bids_lst[2][0]))
        winning_slots[2] = max(bids_lst[2][0], MIN_BID)
    if len(bids_lst) > 3:
        slots.append((bids_lst[3][2], bids_lst[3][0]))

    wb = r.get('winning_bids')
    if wb:
        wb = json.loads(wb)
        wb.append(winning_slots)
        r.set('winning_bids', json.dumps(wb))

    return slots

def simulate_click(r):
    """
    First, determines the top 3 slots for current round of 
    bidding. Then simulates the process of user clicking on 
    one of the 3 slots (or none). Reduces the winning team's 
    budget, and stores results for all teams.
    """

    auction_id = r.get('current_auction_id')
    if not auction_id:
        return
    auction_id = auction_id.decode()
    slots = run_auction(r, auction_id)
    team_ids = load_teams(r)

    t = random.random()
    if t < 0.9:
        winner = None
    elif t < 0.96:
        winner = None
        if len(slots) >= 1:
            winner = slots[0][0]
            if len(slots) >= 2:
                price = max(MIN_BID, slots[1][1])
            else:
                price = MIN_BID

            reduce_budget(r, winner, price)
            msg = {'auction_id': auction_id,
                   'info': 'slot 1',
                   'price': price }
            set_message(r, winner, msg)
    elif t < 0.99:
        winner = None
        if len(slots) >= 2:
            winner = slots[1][0]
            if len(slots) >= 3:
                price = max(MIN_BID, slots[2][1])
            else:
                price = MIN_BID

            reduce_budget(r, winner, price)
            msg = {'auction_id': auction_id,
                   'info': 'slot 2',
                   'price': price }
            set_message(r, winner, msg)
        for team_id in team_ids:
            if team_id == winner:
                continue
            msg = {'auction_id': auction_id,
                    'info': 'user did not click on your ad',
                    'price': 0.0 }
            set_message(r, team_id, msg)
    else:
        winner = None
        if len(slots) >= 3:
            winner = slots[2][0]
            if len(slots) >= 4:
                price = max(MIN_BID, slots[3][1])
            else:
                price = MIN_BID

            reduce_budget(r, winner, price)
            msg = {'auction_id': auction_id,
                   'info': 'slot 3',
                   'price': price }
            set_message(r, winner, msg)

    for team_id in team_ids:
        if team_id == winner:
            continue
        msg = {'auction_id': auction_id,
                'info': 'user did not click on your ad',
                'price': 0.0 }
        set_message(r, team_id, msg)

    r.set('current_auction_id', int(auction_id) + 1)

def main():
    r = redis.StrictRedis(host='localhost', port=6379, db=0)

    i = 0
    while i < N_BIDS:
        # Runs every 1 second till N_BIDS number of rounds is conducted
        t = Timer(1, simulate_click, args=[r])
        t.start()
        t.join()
        i += 1

if __name__ == '__main__':
    main()


###
### auction.py - code for simulating user clicks and running the auction
###
### Project 4
### Markets, Mechanisms, and Machines
### University of Virginia
### Spring 2019
### https://uvammm.github.io/
###
### <NAME>
###

import redis
import json
import random
from threading import Timer

N_BIDS = 2500   # total number of bidding rounds
MIN_BID = 0.25 # minimum bid amount required to win a slot

def load_teams(r):
    """
    Load all team ids from the redis database, and 
    return as a list of team_ids
    """

    team_ids = r.get('teams')
    if not team_ids:
        return []
    team_ids = json.loads(team_ids)
    return team_ids

def load_team_info(r, team_id):
    """
    Given a team_id, load the information for that team
    from the database and return it
    """

    team_info = r.get(team_id)
    if not team_info:
        return None
    team_info = json.loads(team_info)
    return team_info

def set_message(r, team_id, message):
    """ 
    Appends message to team_id's message list in the db
    """

    team_info = load_team_info(r, team_id)
    if not team_info:
        return
    team_info['messages'].append(message)
    r.set(team_id, json.dumps(team_info))

def reduce_budget(r, team_id, amount):
    """
    Reduces team_id's budget by amount
    """

    team_info = load_team_info(r, team_id)
    if not team_info:
        return
    team_info['budget_left'] -= amount
    r.set(team_id, json.dumps(team_info))

def run_auction(r, auction_id):
    """
    Returns top 3 winning slots for the round auction_id. 
    If there were fewer than 3 bids of at least 
    MIN_BID amount, then returns fewer than 3 slots.
    """

    team_ids = load_teams(r)
    if not team_ids:
        return []

    bids = {}
    for team_id in team_ids:
        team_info = load_team_info(r, team_id)
        if not team_info:
            bids[team_id] = 0.0
            continue
        if not auction_id in team_info['bids']:
            bids[team_id] = 0.0
            continue
        if team_info['bids'][auction_id] >= MIN_BID:
            bids[team_id] = team_info['bids'][auction_id]


    bids_lst = [(bids[k], random.random(), k) for k in bids]
    bids_lst.sort(reverse=True)

    winning_slots = [MIN_BID, MIN_BID, MIN_BID]
    slots = []
    if len(bids_lst) > 0:
        slots.append((bids_lst[0][2], bids_lst[0][0]))
        winning_slots[0] = max(bids_lst[0][0], MIN_BID)
    if len(bids_lst) > 1:
        slots.append((bids_lst[1][2], bids_lst[1][0]))
        winning_slots[1] = max(bids_lst[1][0], MIN_BID)
    if len(bids_lst) > 2:
        slots.append((bids_lst[2][2], bids_lst[2][0]))
        winning_slots[2] = max(bids_lst[2][0], MIN_BID)
    if len(bids_lst) > 3:
        slots.append((bids_lst[3][2], bids_lst[3][0]))

    wb = r.get('winning_bids')
    if wb:
        wb = json.loads(wb)
        wb.append(winning_slots)
        r.set('winning_bids', json.dumps(wb))

    return slots

def simulate_click(r):
    """
    First, determines the top 3 slots for current round of 
    bidding. Then simulates the process of user clicking on 
    one of the 3 slots (or none). Reduces the winning team's 
    budget, and stores results for all teams.
    """

    auction_id = r.get('current_auction_id')
    if not auction_id:
        return
    auction_id = auction_id.decode()
    slots = run_auction(r, auction_id)
    team_ids = load_teams(r)

    t = random.random()
    if t < 0.9:
        winner = None
    elif t < 0.96:
        winner = None
        if len(slots) >= 1:
            winner = slots[0][0]
            if len(slots) >= 2:
                price = max(MIN_BID, slots[1][1])
            else:
                price = MIN_BID

            reduce_budget(r, winner, price)
            msg = {'auction_id': auction_id,
                   'info': 'slot 1',
                   'price': price }
            set_message(r, winner, msg)
    elif t < 0.99:
        winner = None
        if len(slots) >= 2:
            winner = slots[1][0]
            if len(slots) >= 3:
                price = max(MIN_BID, slots[2][1])
            else:
                price = MIN_BID

            reduce_budget(r, winner, price)
            msg = {'auction_id': auction_id,
                   'info': 'slot 2',
                   'price': price }
            set_message(r, winner, msg)
        for team_id in team_ids:
            if team_id == winner:
                continue
            msg = {'auction_id': auction_id,
                    'info': 'user did not click on your ad',
                    'price': 0.0 }
            set_message(r, team_id, msg)
    else:
        winner = None
        if len(slots) >= 3:
            winner = slots[2][0]
            if len(slots) >= 4:
                price = max(MIN_BID, slots[3][1])
            else:
                price = MIN_BID

            reduce_budget(r, winner, price)
            msg = {'auction_id': auction_id,
                   'info': 'slot 3',
                   'price': price }
            set_message(r, winner, msg)

    for team_id in team_ids:
        if team_id == winner:
            continue
        msg = {'auction_id': auction_id,
                'info': 'user did not click on your ad',
                'price': 0.0 }
        set_message(r, team_id, msg)

    r.set('current_auction_id', int(auction_id) + 1)

def main():
    r = redis.StrictRedis(host='localhost', port=6379, db=0)

    i = 0
    while i < N_BIDS:
        # Runs every 1 second till N_BIDS number of rounds is conducted
        t = Timer(1, simulate_click, args=[r])
        t.start()
        t.join()
        i += 1

if __name__ == '__main__':
    main()
### ### auction.py - code for simulating user clicks and running the auction ### ### Project 4 ### Markets, Mechanisms, and Machines ### University of Virginia ### Spring 2019 ### https://uvammm.github.io/ ### ### <NAME> ### # total number of bidding rounds # minimum bid amount required to win a slot Load all team ids from the redis database, and      return as a list of team_ids Given a team_id, load the information for that team     from the database and return it Appends message to team_id's message list in the db Reduces team_id's budget by amount Returns top 3 winning slots for the round auction_id.      If there were fewer than 3 bids of at least      MIN_BID amount, then returns fewer than 3 slots. First, determines the top 3 slots for current round of      bidding. Then simulates the process of user clicking on      one of the 3 slots (or none). Reduces the winning team's      budget, and stores results for all teams. # Runs every 1 second till N_BIDS number of rounds is conducted