Principles of Computing (Part 1) -- Week 5 Mini-projects: Cookie Clicker Simulator

"""
Cookie Clicker Simulator
"""

import simpleplot
import math
import random

# Used to increase the timeout, if necessary
import codeskulptor
codeskulptor.set_timeout(20)

import poc_clicker_provided as provided

# Constants
SIM_TIME = 10000000000.0
#SIM_TIME = 1000

# Helper function
def max_idx_limit(data, limit='inf'):
    """
    return a list of index and value of 
    maximun values in data
    """
    maxvals = []
    for idx in range(len(data)):
        item = data[idx]
        if item > limit:
            continue
        elif len(maxvals) == 0:
            maxvals.append((idx, item))
        elif item == maxvals[0][1]:
            maxvals.append((idx, item))
        elif item > maxvals[0][1]:
            maxvals = [(idx, item)]
    return maxvals

def min_idx(data):
    """
    return a list of index and value of 
    minimun values in data
    """
    minvals = []
    for idx in range(len(data)):
        item = data[idx]
        if len(minvals) == 0:
            minvals.append((idx, item))
        elif item == minvals[0][1]:
            minvals.append((idx, item))
        elif item < minvals[0][1]:
            minvals = [(idx, item)]
    return minvals

class ClickerState:
    """
    Simple class to keep track of the game state.
    """
    
    def __init__(self):
        self._cookies_baked = 0.0
        self._cookies_in_bank = 0.0
        self._cookies_per_second = 1.0
        self._session_time = 0.0
        # history : (time, item, cost of item, total cookies)
        self._session_history = [(0.0, None, 0.0, 0.0)]
        self._time_until = 'inf'
        
        
    def __str__(self):
        """
        Return human readable state
        """
        #return str([self._session_time, self._cookies_in_bank, self._cookies_per_second, self._cookies_baked])
        return str("================================\n" +
                   "Game Time:          " + str(self._session_time) + "\n" +
                   "Cookies in Bank:    " + str(self._cookies_in_bank) + "\n" +
                   "Cookies per Second: " + str(self._cookies_per_second) + "\n" +
                   "Cookies Baked:      " + str(self._cookies_baked))
        
    def get_cookies(self):
        """
        Return current number of cookies 
        (not total number of cookies)
        
        Should return a float
        """
        return self._cookies_in_bank
    
    def get_cps(self):
        """
        Get current CPS

        Should return a float
        """
        return self._cookies_per_second
    
    def get_time(self):
        """
        Get current time

        Should return a float
        """
        return self._session_time
    
    def get_history(self):
        """
        Return history list

        History list should be a list of tuples of the form:
        (time, item, cost of item, total cookies)

        For example: [(0.0, None, 0.0, 0.0)]

        Should return a copy of any internal data structures,
        so that they will not be modified outside of the class.
        """
        return list(self._session_history)

    def time_until(self, cookies):
        """
        Return time until you have the given number of cookies
        (could be 0.0 if you already have enough cookies)

        Should return a float with no fractional part
        """
        if cookies <= self._cookies_in_bank:
            self._time_until = 0.0
        else:
            self._time_until = math.ceil((cookies - self._cookies_in_bank) / self._cookies_per_second)
        return self._time_until
    
    def wait(self, time):
        """
        Wait for given amount of time and update state

        Should do nothing if time <= 0.0
        """
        # increase the time, the current number of cookies, 
        # and the total number of cookies. 
        if time > 0:
            self._session_time += time
            self._cookies_in_bank += self._cookies_per_second * time
            self._cookies_baked += self._cookies_per_second * time
    
    def buy_item(self, item_name, cost, additional_cps):
        """
        Buy an item and update state

        Should do nothing if you cannot afford the item
        """
        # adjust the current number of cookies, the CPS, 
        # and add an entry into the history. 
        if self._cookies_in_bank >= cost:
            self._cookies_in_bank -= cost
            self._cookies_per_second += additional_cps
            self._buy_item_time = float(self._session_time)
            self._buy_item_cookies_baked = float(self._cookies_baked)
            self._session_history.append((self._buy_item_time, item_name, cost, self._buy_item_cookies_baked))
    
def simulate_clicker(build_info, duration, strategy):
    """
    Function to run a Cookie Clicker game for the given
    duration with the given strategy.  Returns a ClickerState
    object corresponding to the final state of the game.
    """
    clicker_state = ClickerState()
    while clicker_state.get_time() <= duration:
        
        # use the strategy to decide the item to purchase
        # break loop if no item to be purchased
        build_info_clone = build_info.clone()
        cookies = clicker_state.get_cookies()
        cps = clicker_state.get_cps()
        history = clicker_state.get_history()
        time_left = duration - clicker_state.get_time()
        
        item_name = strategy(cookies, cps, history, time_left, build_info_clone)
        if item_name == None:
            clicker_state.wait(time_left)
            break

        #print "item_name:", item_name
        # cost and cps for the item
        cost = build_info_clone.get_cost(item_name)
        additional_cps = build_info_clone.get_cps(item_name)
        
        # check weather there is enough cookies in bank to purchase the item
        if cookies >= cost:
            time_to_purchase = 0
        elif clicker_state.time_until(cost) > time_left:
            time_to_purchase = time_left
        else:
            time_to_purchase = clicker_state.time_until(cost)

        # update the status to time_to_purchase
        clicker_state.wait(time_to_purchase)
        clicker_state.buy_item(item_name, cost, additional_cps)
        # update the item cost
        build_info.update_item(item_name)
        
        if cookies < cost and clicker_state.time_until(cost) > time_left:
            break
        #print clicker_state
    return clicker_state


def strategy_cursor_broken(cookies, cps, history, time_left, build_info):
    """
    Always pick Cursor!

    Note that this simplistic (and broken) strategy does not properly
    check whether it can actually buy a Cursor in the time left.  Your
    simulate_clicker function must be able to deal with such broken
    strategies.  Further, your strategy functions must correctly check
    if you can buy the item in the time left and return None if you
    can't.
    """
    return "Cursor"

def strategy_none(cookies, cps, history, time_left, build_info):
    """
    Always return None

    This is a pointless strategy that will never buy anything, but
    that you can use to help debug your simulate_clicker function.
    """
    return None

def strategy_cheap(cookies, cps, history, time_left, build_info):
    """
    Always buy the cheapest item you can afford in the time left.
    """
    build_info_cheap = build_info.clone()
    item_cost_list = [build_info_cheap.get_cost(item) for item in build_info_cheap.build_items()]

    # get the cheapest cost and index
    index_cost_list = min_idx(item_cost_list)
    
    if index_cost_list[0][1] > cookies + cps * time_left:
        return None 
    else:
        index = random.choice(index_cost_list)[0]
        return build_info_cheap.build_items()[index]

def strategy_expensive(cookies, cps, history, time_left, build_info):
    """
    Always buy the most expensive item you can afford in the time left.
    """
    build_info_expensive = build_info.clone()
    item_cost_list = [build_info_expensive.get_cost(item) for item in build_info_expensive.build_items()]

    # get the affordable cost in the time left.
    afford_cost = cookies + cps * time_left
    
    # get the expensive cost you can afford in the time left
    index_cost_list = max_idx_limit(item_cost_list, afford_cost)
    
    if len(index_cost_list) == 0:
        return None 
    else:
        index = random.choice(index_cost_list)[0]
        return build_info_expensive.build_items()[index]

def strategy_best(cookies, cps, history, time_left, build_info):
    """
    The best strategy that you are able to implement.
    Best : ================================
    Game Time:          10000000000.0
    Cookies in Bank:    2.47909661207e+12
    Cookies per Second: 139876516.0
    Cookies Baked:      1.31434696714e+18
    """
    build_info_best = build_info.clone()

    # get the affordable cost in the time left.
    afford_cost = cookies + cps * time_left
    
    item_list = []
    qor_list = []
    prod_dict = {}
    # get the item list you can afford in the time left
    for item in build_info_best.build_items():
        if build_info_best.get_cost(item) <= afford_cost:
            item_list.append(item)
            item_cps  = build_info_best.get_cps(item)
            item_cost = build_info_best.get_cost(item)
            item_time = math.ceil((item_cost) / cps)
            item_qor  = item_cps / (item_cost * (cps + item_cps))
            item_prod = item_cps * math.ceil(time_left - item_time)
            item_k    = 2
            if time_left // item_time < 20:
                item_k **= (time_left // item_time)
            prod_dict[item] = item_prod * item_k
            qor_list.append(item_qor)
            #print item, item_cost, item_qor
    #print "------------------------------------"
    if len(item_list) == 0:
        return None
    else:
        index = random.choice(max_idx_limit(qor_list))[0]
        item_best = item_list[index]
    
    for item in prod_dict:
        if build_info_best.get_cost(item) < build_info_best.get_cost(item_best):
            if prod_dict[item] > prod_dict[item_best]:
                item_best = item
    return item_best
    
def run_strategy(strategy_name, time, strategy):
    """
    Run a simulation for the given time with one strategy.
    """
    state = simulate_clicker(provided.BuildInfo(), time, strategy)
    print strategy_name, ":", state
    # Plot total cookies over time

    # Uncomment out the lines below to see a plot of total cookies vs. time
    # Be sure to allow popups, if you do want to see it

    history = state.get_history()
    history = [(item[0], item[3]) for item in history]
    simpleplot.plot_lines(strategy_name, 1000, 400, 'Time', 'Total Cookies', [history], True)

def run():
    """
    Run the simulator.
    """    
    #run_strategy("Cursor", SIM_TIME, strategy_cursor_broken)

    # Add calls to run_strategy to run additional strategies
    #run_strategy("Cheap", SIM_TIME, strategy_cheap)
    #run_strategy("Expensive", SIM_TIME, strategy_expensive)
    run_strategy("Best", SIM_TIME, strategy_best)
    
run()
    

#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值