Beispiel #1
0
def anchor_management():
    np = NeoPage(path)
    if np.contains('form-fire-cannon'):
        action = np.search('<input name="action" type="hidden" value="(.*?)">')[1]
        np.post(path, action=action)
        if np.contains('prize-item-name'):
            prize = np.search('<span class="prize-item-name">(.*?)</span>')[1]
            print(f'Blasted krawken; got {prize}')
        else:
            print('Blasted krawken; got unknown prize')
    elif np.contains('safe from sea monsters for one day'):
        print('Already did anchor management.')
    else:
        print("Couldn't find anchor management.")
Beispiel #2
0
def anchor_management():
    np = NeoPage(path)
    if np.contains('form-fire-cannon'):
        action = np.search(
            '<input name="action" type="hidden" value="(.*?)">')[1]
        np.post(path, action=action)
        if np.contains('prize-item-name'):
            prize = np.search('<span class="prize-item-name">(.*?)</span>')[1]
            print(f'Blasted krawken; got {prize}')
        else:
            print('Blasted krawken; got unknown prize')
    elif np.contains('safe from sea monsters for one day'):
        print('Already did anchor management.')
    else:
        print("Couldn't find anchor management.")
Beispiel #3
0
def scorchy_slots():
    np = NeoPage()
    np.get(path)
    _ref_ck = np.search(r"<input type='hidden' name='_ref_ck' value='(.*?)'>")[1]
    np.post(path, f'_ref_ck={_ref_ck}', 'play=yes')

    while True:
Beispiel #4
0
def bank_interest():
    path = '/bank.phtml'
    np = NeoPage(path)
    if np.contains('You have already collected your interest today.'):
        print('Already collected interest today.')
    elif np.contains('Collect Interest ('):
        amount = np.search(r'Collect Interest \((.*?)\)')[1]
        print(f"Collecting {amount} interest.")
        np.post('/process_bank.phtml', 'type=interest')
    else:
        print("Error collecting bank interest.")
Beispiel #5
0
def apple_bobbing():
    np = NeoPage(path)
    if np.contains('Give it a shot!'):
        np.get(path, bobbing=1)
        message = np.search("<div id='bob_middle'>(.*?)</div>")[1].strip()
        message = strip_tags(message)
        print(message)
    elif np.contains('blind underneath this hat'):
        print('Already apple bobbed today.')
    else:
        print("Couldn't find apple bobbing.")
Beispiel #6
0
def bank_interest():
    path = '/bank.phtml'
    np = NeoPage(path)
    if np.contains('You have already collected your interest today.'):
        print('Already collected interest today.')
    elif np.contains('Collect Interest ('):
        amount = np.search(r'Collect Interest \((.*?)\)')[1]
        print(f"Collecting {amount} interest.")
        np.post('/process_bank.phtml', 'type=interest')
    else:
        print("Error collecting bank interest.")
Beispiel #7
0
def apple_bobbing():
    np = NeoPage(path)
    if np.contains('Give it a shot!'):
        np.get(path, bobbing=1)
        message = np.search("<div id='bob_middle'>(.*?)</div>")[1].strip()
        message = strip_tags(message)
        print(message)
    elif np.contains('blind underneath this hat'):
        print('Already apple bobbed today.')
    else:
        print("Couldn't find apple bobbing.")
Beispiel #8
0
def stats():
    np = NeoPage()
    np.get(path)
    tbl = np.search(r'<table align=center cellpadding=3 cellspacing=0 border=0>(.*?)</table>')[0]
    rows = util.table_to_tuples(tbl)

    freqs = {i: 0 for i in range(1, 31)}
    for date, prize, winners, share, matches_needed, numbers in rows[1:]:
        nums = map(int, numbers.split(','))
        for n in nums:
            freqs[n] += 1

    for n, freq in sorted(freqs.items()):
        print(f'{n}, {freq}')
Beispiel #9
0
def ensure_np(amount):
    # Withdraws from the bank to get up at least [amount] NP.
    np = NeoPage()
    np.get('/bank.phtml')
    if np.contains('Collect Interest ('):
        bank_interest.bank_interest()
    nps = np.search(
        r'''<a id='npanchor' href="/inventory.phtml">(.*?)</a>''')[1]
    nps = int(nps.replace(',', ''))
    if nps >= amount: return
    need = amount - nps
    denom = 10**max(len(str(need)), len(str(amount)))
    need = (need // denom + 1) * denom
    np.post('/process_bank.phtml', 'type=withdraw', f'amount={need}')
    print(f'Withdrawing {need} NP')
Beispiel #10
0
def shapeshifter(timeout=10 * 60):
    np = NeoPage()
    np.get(path_index)
    starting_np = np.current_np()
    if np.contains('Continue Game!'):
        np.get(path_game)
    elif np.contains('Start Game!'):
        np.post(path_process, f'type=init')

    # Just in case we won but left it in the completed state
    if np.contains('You Won!') or np.contains('You Lost!'):
        np.post(path_process + '?type=init')

    tbl = np.search(r'''<table border=1 bordercolor='gray'>(.*?)</table>''')[1]
    imgs = re.findall(
        r'''<img src='(.*?)' border=0 name='i_'>(<br><b><small>GOAL</small></b></td>)?''',
        tbl)
    imgs = imgs[:-1]
    N = len(imgs)
    goal_idx = next(i for i, (_, goal) in enumerate(imgs) if goal)
    to_idx = {img: i for i, (img, _) in enumerate(imgs)}

    tbl = np.search(
        r'''<table align=center cellpadding=0 cellspacing=0 border=0>(.*?)</table>'''
    )[1]
    goal_grid = []
    for row in re.findall(r'''<tr>(.*?)</tr>''', tbl, flags=re.DOTALL):
        imgs = re.findall(r'''<img src='(.*?)' .*?>''', row)
        goal_row = [to_idx[img] for img in imgs]
        goal_grid.append(goal_row)

    tbl = np.search(
        r'''<center><b><big>ACTIVE SHAPE</big></b><p>(.*?)</table>\n''')[1]
    shape_grids = []
    for shape_info in re.findall(r'''<table.*?>(.*?)</table>''', tbl):
        grid = []
        for row_info in re.findall(r'''<tr>(.*?)</tr>''', shape_info):
            tiles = re.findall(r'''<td.*?>(.*?)</td>''', row_info)
            grid.append(
                [int('square.gif' in tile_info) for tile_info in tiles])
        shape_grids.append(grid)

    # Compute kvho difficulty.
    R = len(goal_grid)
    C = len(goal_grid[0])
    min_shifts_needed = R * C * (N - 1) - sum(map(sum, goal_grid))
    shifts_available = sum(
        sum(sum(rows) for rows in grid) for grid in shape_grids)
    num_overshifts = shifts_available - min_shifts_needed
    num_flips = num_overshifts // N
    print(f'Puzzle permits {num_flips} flips ({num_overshifts} overshifts)')

    kvho_input = make_kvho_input(goal_grid, shape_grids, goal_idx)

    print(f'Waiting for kvho.')
    start_time = datetime.now()

    positions = []
    try:
        proc = subprocess.run(['c/ss'],
                              input=kvho_input,
                              encoding='utf-8',
                              capture_output=True,
                              timeout=timeout)
        for line in proc.stdout.splitlines():
            if 'x' in line and '=' in line:
                x = list(
                    map(
                        int,
                        line.replace('x', ' ').replace('=',
                                                       ' ').strip().split()))
                for c, r, _ in zip(x[::3], x[1::3], x[2::3]):
                    positions.append((r, c))
        print(f'Solution found in {datetime.now() - start_time}: {positions}')
    except subprocess.TimeoutExpired:
        print(
            f'Solution not found in time. Throwing this puzzle to get a new one.'
        )
        for _ in shape_grids:
            positions.append((0, 0))

    for i, (shape, (r, c)) in enumerate(zip(shape_grids, positions)):
        print(f'\rPlacing piece {i+1}/{len(positions)}', end='')
        np.set_referer_path(path_game)
        np.get(path_process, 'type=action', f'posx={c}', f'posy={r}')
        time.sleep(0.5)
    print()

    if np.contains('You Won!'):
        np.set_referer_path(path_game)
        np.post(path_process + '?type=init')
        ending_np = np.current_np()
        print(f'Done level, earned {ending_np - starting_np} NP')
        return 1
    elif np.contains('reached your max neopoints'):
        print('Done for today.')
        return 2
    else:
        print('Did not solve level??')
        return 0
Beispiel #11
0
def update_prices(item_name, laxness=1):
    char_groups = 'an0 bo1 cp2 dq3 er4 fs5 gt6 hu7 iv8 jw9 kx_ ly mz'.split()
    c2g = dict(sum(([(c, i) for c in cs] for i, cs in enumerate(char_groups)), []))
    markets = defaultdict(dict)
    ub_count = 0
    search_count = 0

    np = NeoPage('/market.phtml?type=wizard')
    opts = []
    opts.append('type=process_wizard')
    opts.append('feedset=0')
    opts.append(f'shopwizard={item_name}')
    opts.append('table=shop')
    opts.append('criteria=exact')
    opts.append('min_price=0')
    opts.append('max_price=999999')

    # Repeatedly search the shop wizard, collecting all results seen.
    while not markets or any(len(market_data) < len(char_groups) - laxness for market_data in markets.values()):
        print(f'\r({sum(len(md) for md in markets.values())}/{len(markets) * len(char_groups)}) ', end='')
        np.post('/market.phtml', *opts)
        tbl = np.search(r'<table width="600".*?>(.*?)</table>')
        if not tbl:
            if np.contains('Whoa there, too many'):
                print('Shop wizard banned.')
                raise ShopWizardBannedException
            ub_count += 1
            if ub_count >= 5: break
            continue
        tbl = tbl[1]
        rows = lib.table_to_tuples(tbl, raw=True)[1:]
        search_count += 1
        if search_count >= 50: break
        market_data = []
        obj_info_id = None
        for owner, item, stock, price in rows:
            result = re.search(r'<a href="(.*?)"><b>(.*?)</b></a>', owner)
            link = result[1]
            owner = result[2]
            result = re.search(r'/browseshop.phtml\?owner=(.*?)&buy_obj_info_id=(.*?)&buy_cost_neopoints=(\d+)', link)
            obj_info_id = int(result[2])
            price = lib.amt(lib.strip_tags(price))
            stock = lib.amt(stock)
            market_data.append((price, stock, link))
        g = c2g[lib.strip_tags(rows[0][0])[0]]
        markets[obj_info_id][g] = market_data

    level2_by_item = {}
    # Consolidate results for each item into a quote.

    if markets:
        for obj_info_id, market_data in markets.items():
            # TODO: Check suspiciously cheap prices to see if owners are frozen.
            level2 = sorted(sum(market_data.values(), []))
            level2_by_item[obj_info_id] = level2
            # The price of an item for our purposes is the price of the 2nd
            # cheapest item in the market.
            cur_amt = 0
            cur_price = UNBUYABLE_PRICE
            for price, stock, link in level2:
                cur_price = price
                cur_amt += stock
                if cur_amt >= 2:
                    break
            print(f'The price of {item_name} (id {obj_info_id}) is {cur_price} NP.')
            cur_amt = 0
            for price, stock, link in level2:
                cur_amt += stock
                if cur_amt >= 30:
                    break
            c = conn.cursor()
            c.execute('''
            SELECT name FROM items WHERE obj_info_id=?
            ''', (obj_info_id,))
            result = c.fetchone()
            if not result or result[0][0] != item_name:
                for market_data in level2:
                    # Visit the shop and populate a bunch of fields
                    np.get(market_data[2])
                    res = np.search(r'''<A href=".*?" onClick=".*?"><img src="http://images.neopets.com/items/(.*?)" .*? title="(.*?)" border="1"></a> <br> <b>(.*?)</b>''')
                    if not res:
                        print(f'{market_data[2]} is froze?')
                        continue
                    image = res[1]
                    desc = res[2]
                    name = res[3]
                    c.execute('''
                    INSERT INTO items (name, image, desc, obj_info_id, last_updated)
                    VALUES (?, ?, ?, ?, datetime('now'))
                    ON CONFLICT (name, image)
                    DO UPDATE SET desc=?, obj_info_id=?, last_updated=datetime('now')
                    ''', (name, image, desc, obj_info_id, desc, obj_info_id))
                    print(f'The object id of {name} is {obj_info_id}')
                    break
                else:
                    print('Unable to find legit seller for {obj_info_id}. Will not store it in itemdb.')
                    continue
            c.execute('''
            UPDATE items SET price=?, price_last_updated=datetime('now') WHERE obj_info_id=?
            ''', (cur_price, obj_info_id))
    else:
        print(f'It seems {item_name} is unbuyable.')
        # Item could not be found; assume it's unbuyable.
        # We use a cheap price estimate of 1,000,001 NP.
        # TODO: Items inserted in this way will have a wonky image property.
        c = conn.cursor()
        c.execute('''
        INSERT INTO items (name, image, last_updated, price, price_last_updated)
        VALUES (?, NULL, datetime('now'), 1000001, datetime('now'))
        ON CONFLICT (name, image)
        DO UPDATE SET last_updated=datetime('now'), price=1000001, price_last_updated=datetime('now')
        ''', (item_name,))

    conn.commit()
    return level2_by_item
Beispiel #12
0
def shapeshifter(timeout=10*60):
    np = NeoPage()
    np.get(path_index)
    starting_np = np.current_np()
    if np.contains('Continue Game!'):
        np.get(path_game)
    elif np.contains('Start Game!'):
        np.post(path_process, f'type=init')

    # Just in case we won but left it in the completed state
    if np.contains('You Won!') or np.contains('You Lost!'):
        np.post(path_process + '?type=init')

    tbl = np.search(r'''<table border=1 bordercolor='gray'>(.*?)</table>''')[1]
    imgs = re.findall(r'''<img src='(.*?)' border=0 name='i_'>(<br><b><small>GOAL</small></b></td>)?''', tbl)
    imgs = imgs[:-1]
    N = len(imgs)
    goal_idx = next(i for i, (_, goal) in enumerate(imgs) if goal)
    to_idx = {img: i for i, (img, _) in enumerate(imgs)}

    tbl = np.search(r'''<table align=center cellpadding=0 cellspacing=0 border=0>(.*?)</table>''')[1]
    goal_grid = []
    for row in re.findall(r'''<tr>(.*?)</tr>''', tbl, flags=re.DOTALL):
        imgs = re.findall(r'''<img src='(.*?)' .*?>''', row)
        goal_row = [to_idx[img] for img in imgs]
        goal_grid.append(goal_row)

    tbl = np.search(r'''<center><b><big>ACTIVE SHAPE</big></b><p>(.*?)</table>\n''')[1]
    shape_grids = []
    for shape_info in re.findall(r'''<table.*?>(.*?)</table>''', tbl):
        grid = []
        for row_info in re.findall(r'''<tr>(.*?)</tr>''', shape_info):
            tiles = re.findall(r'''<td.*?>(.*?)</td>''', row_info)
            grid.append([int('square.gif' in tile_info) for tile_info in tiles])
        shape_grids.append(grid)

    # Compute kvho difficulty.
    R = len(goal_grid)
    C = len(goal_grid[0])
    min_shifts_needed = R * C * (N - 1) - sum(map(sum, goal_grid))
    shifts_available = sum(sum(sum(rows) for rows in grid) for grid in shape_grids)
    num_overshifts = shifts_available - min_shifts_needed
    num_flips = num_overshifts // N
    print(f'Puzzle permits {num_flips} flips ({num_overshifts} overshifts)')

    kvho_input = make_kvho_input(goal_grid, shape_grids, goal_idx)

    print(f'Waiting for kvho.')
    start_time = datetime.now()

    positions = []
    try:
        proc = subprocess.run(['c/ss'], input=kvho_input, encoding='utf-8', capture_output=True, timeout=timeout)
        for line in proc.stdout.splitlines():
            if 'x' in line and '=' in line:
                x = list(map(int, line.replace('x', ' ').replace('=', ' ').strip().split()))
                for c, r, _ in zip(x[::3], x[1::3], x[2::3]):
                    positions.append((r, c))
        print(f'Solution found in {datetime.now() - start_time}: {positions}')
    except subprocess.TimeoutExpired:
        print(f'Solution not found in time. Throwing this puzzle to get a new one.')
        for _ in shape_grids:
            positions.append((0, 0))

    for i, (shape, (r, c)) in enumerate(zip(shape_grids, positions)):
        print(f'\rPlacing piece {i+1}/{len(positions)}', end='')
        np.set_referer_path(path_game)
        np.get(path_process, 'type=action', f'posx={c}', f'posy={r}')
        time.sleep(0.5)
    print()

    if np.contains('You Won!'):
        np.set_referer_path(path_game)
        np.post(path_process + '?type=init')
        ending_np = np.current_np()
        print(f'Done level, earned {ending_np - starting_np} NP')
        return 1
    elif np.contains('reached your max neopoints'):
        print('Done for today.')
        return 2
    else:
        print('Did not solve level??')
        return 0
Beispiel #13
0
def battledome(forever=False):
    np = NeoPage(path)
    np.get(path_arena)
    neopoints_left = True
    prizes_left = True
    while prizes_left or forever:
        battle_id = None
        if np.contains('battleid:'):
            print('Battledome: Already in fight.')
            battle_id = np.search(r"battleid:'(\d+)',")[1]
        else:
            print('Battledome: Starting fight.')
            np.post(path_start_fight, 'type=2', f'pet={PET_NAME}',
                    f'npcId={npc_id}', f'toughness={toughness}')
            battle = json.loads(np.content)
            if not battle['success']:
                print('Battledome: Error.')
                return
            battle_id = battle['battle']['id']
        time.sleep(2)
        np.set_referer_path(path)
        np.get(path_arena)
        ts = int(time.time() * 1000)
        for step in itertools.count():
            intro = 1 if step == 0 else 0
            np.set_referer_path(path_arena)
            np.post(path_arena_ajax, f'battleid={battle_id}', f'step={step}',
                    f'intro={intro}', 'status=1')
            resp = json.loads(np.content)
            abils = resp['p1']['abils']
            chosen_abil = ''
            for abil in ['21', '2', '14', '17', '1']:
                if abil in abils and not abils[abil]['hasCooldown']:
                    chosen_abil = abil
                    break
            items = re.findall(
                r'<li><img.*?id="(\d+)".*?title="(.*?)".*?/></li>',
                resp['p1']['items'])
            item1id = items[0][0]
            item2id = items[1][0]
            opts = []
            opts.append(f'p1s=')
            opts.append(f'eq1={item1id}')
            opts.append(f'eq2={item2id}')
            opts.append(f'p1a={chosen_abil}')
            opts.append(f'chat=')
            opts.append('action=attack')
            opts.append(f'ts={ts}')
            opts.append(f'battleid={battle_id}')
            opts.append(f'step={step}')
            opts.append(f'intro=0')
            opts.append('status=1')
            print(
                f'Battledome: Attacking with {items[0][1]} and {items[1][1]}')
            np.set_referer_path(path_arena)
            np.post(path_arena_ajax, *opts)
            resp = json.loads(np.content)
            if resp['battle']['prizes'] or 'prize_messages' in resp['battle']:
                prizes = ', '.join(
                    x['name'] for x in resp['battle']['prizes']) or "nothing"
                print(f'Battledome: Won {prizes}')
                if 'prize_messages' in resp['battle']:
                    prize_messages = resp['battle']['prize_messages']
                    if any('reached the item limit' in m
                           for m in prize_messages):
                        prizes_left = False
                    if any('reached the NP limit' in m
                           for m in prize_messages):
                        neopoints_left = False
                    print(f'Battledome: {prize_messages}')
                break
            # TODO: Detect defeat.
            elif step > 5:
                print(
                    f'Battledome: Took more than 5 steps. Something went wrong.'
                )
Beispiel #14
0
def food_club():
    np = NeoPage()
    np.get(path, 'type=bet')
    if np.contains('All betting gates are now closed!'):
        print(f"Dangit, we're late.")
        return
    maxbet = util.amt(
        re.search(r'You can only place up to <b>(.*?)</b> NeoPoints per bet',
                  np.content)[1])
    print(f'Max bet is {maxbet}')

    # We compute the current round number because it seems there's no way to
    # find it explicitly unless you actually make a bet.
    day = int(
        re.search(
            r'Next match: <b>.*?</b> the <b>(\d+).*?</b> at <b>02:00 PM NST</b>',
            np.content)[1])
    date = neotime.now_nst().replace(day=day, hour=14, minute=0, second=0)
    epoch = datetime(2018, 10, 7, 14, 0, 0)
    from_epoch = round((date - epoch) / timedelta(days=1))
    cur_round = 7093 + from_epoch
    print(f'Food Club round {cur_round}')

    dl_fc_history(cur_round - 1, cur_round)
    table = stats()
    rnd = get_rounds(table[table['round'] == cur_round])[0]

    for i, arena in enumerate(arena_names):
        participants = ', '.join(pirate_names[p.pirate]
                                 for p in rnd.pirates[i])
        print(f'{arena} Arena: {participants}')

    bets = maxter_strategy(rnd, 1000000 / maxbet)
    total_bet_amt = 0
    total_exp_win = 0
    TER = 0
    for exp_win, spend, bet in bets:
        if exp_win < 1.0:
            print('Not making -EV bet: {bet}')
            continue
        bet_amt = round(spend * maxbet)
        print(f'Bet {bet_amt} NP on {bet}. EV: {exp_win * bet_amt:.1f} NP')
        opts = []
        total_odds = 1
        for i, (ps, b) in enumerate(zip(rnd.pirates, bet)):
            if b == None:
                continue
            ps[b].pirate
            opts.append(f'winner{i+1}={ps[b].pirate}')
            opts.append(f'matches[]={i+1}')
            total_odds *= ps[b].closing_odds
        opts.append(f'bet_amount={bet_amt}')
        opts.append(f'total_odds={total_odds}')
        opts.append(f'winnings={bet_amt * total_odds}')
        opts.append('type=bet')
        np.post('/pirates/process_foodclub.phtml', *opts)
        total_bet_amt += bet_amt
        total_exp_win += bet_amt * exp_win
        TER += exp_win

    print(
        f'Made {total_bet_amt} NP of bets. Expect to win {total_exp_win:.1f} NP. (TER {TER:.2f}; ROI {total_exp_win / total_bet_amt:.3f})'
    )

    np.get('/pirates/foodclub.phtml', 'type=collect')
    if np.contains("<input type='submit' value='Collect Your Winnings!'>"):
        tbl = np.search(r"<table border='0' .*?>(.*?)</table>")[1]
        tuples = util.table_to_tuples(tbl)
        earliest_rnd = int(tuples[2][0])
        total_winnings = tuples[-1][-1]
        print(
            f'Note: We have {total_winnings} winnings dating from round {earliest_rnd}.'
        )
        if cur_round - earliest_rnd >= 7:
            print(f'Should collect winnings before they vanish!')
Beispiel #15
0
def food_club():
    np = NeoPage()
    np.get(path, 'type=bet')
    if np.contains('All betting gates are now closed!'):
        print(f"Dangit, we're late.")
        return
    maxbet = util.amt(re.search(r'You can only place up to <b>(.*?)</b> NeoPoints per bet', np.content)[1])
    print(f'Max bet is {maxbet}')

    # We compute the current round number because it seems there's no way to
    # find it explicitly unless you actually make a bet.
    day = int(re.search(r'Next match: <b>.*?</b> the <b>(\d+).*?</b> at <b>02:00 PM NST</b>', np.content)[1])
    date = neotime.now_nst().replace(day=day, hour=14, minute=0, second=0)
    epoch = datetime(2018, 10, 7, 14, 0, 0)
    from_epoch = round((date - epoch) / timedelta(days=1))
    cur_round = 7093 + from_epoch
    print(f'Food Club round {cur_round}')

    dl_fc_history(cur_round - 1, cur_round)
    table = stats()
    rnd = get_rounds(table[table['round'] == cur_round])[0]

    for i, arena in enumerate(arena_names):
        participants = ', '.join(pirate_names[p.pirate] for p in rnd.pirates[i])
        print(f'{arena} Arena: {participants}')

    bets = maxter_strategy(rnd, 1000000 / maxbet)
    total_bet_amt = 0
    total_exp_win = 0
    TER = 0
    for exp_win, spend, bet in bets:
        if exp_win < 1.0:
            print('Not making -EV bet: {bet}')
            continue
        bet_amt = round(spend * maxbet)
        print(f'Bet {bet_amt} NP on {bet}. EV: {exp_win * bet_amt:.1f} NP')
        opts = []
        total_odds = 1
        for i, (ps, b) in enumerate(zip(rnd.pirates, bet)):
            if b == None:
                continue
            ps[b].pirate
            opts.append(f'winner{i+1}={ps[b].pirate}')
            opts.append(f'matches[]={i+1}')
            total_odds *= ps[b].closing_odds
        opts.append(f'bet_amount={bet_amt}')
        opts.append(f'total_odds={total_odds}')
        opts.append(f'winnings={bet_amt * total_odds}')
        opts.append('type=bet')
        np.post('/pirates/process_foodclub.phtml', *opts)
        total_bet_amt += bet_amt
        total_exp_win += bet_amt * exp_win
        TER += exp_win

    print(f'Made {total_bet_amt} NP of bets. Expect to win {total_exp_win:.1f} NP. (TER {TER:.2f}; ROI {total_exp_win / total_bet_amt:.3f})')

    np.get('/pirates/foodclub.phtml', 'type=collect')
    if np.contains("<input type='submit' value='Collect Your Winnings!'>"):
        tbl = np.search(r"<table border='0' .*?>(.*?)</table>")[1]
        tuples = util.table_to_tuples(tbl)
        earliest_rnd = int(tuples[2][0])
        total_winnings = tuples[-1][-1]
        print(f'Note: We have {total_winnings} winnings dating from round {earliest_rnd}.')
        if cur_round - earliest_rnd >= 7:
            print(f'Should collect winnings before they vanish!')