""" Cookie Clicker Simulator """ #http://www.codeskulptor.org/#user43_ULjUFtSgFkDefdl_12.py import simpleplot import math #import user43_G4PJLsAMW3DL8ai_22 as simpletest # Used to increase the timeout, if necessary import codeskulptor codeskulptor.set_timeout(20) import poc_clicker_provided as provided # Constants SIM_TIME = 10000000000.0 class ClickerState: """ Simple class to keep track of the game state. """ def __init__(self): self._cps = 1.0 self._cookies = 0.0 self._net_cookies = 0.0 self._time = 0.0 self._history = [(0.0, None, 0.0, 0.0)] def __str__(self): """ Return human readable state """ return str(self._cookies) + " cookies out of <" + str(self._net_cookies/(SIM_TIME*100000)) + "> million available at time " + str(self._time) def get_cookies(self): """ Return current number of cookies (not total number of cookies) Should return a float """ return self._cookies def get_net_cookies(self): """ Return total number of cookies Should return a float """ return self._net_cookies def get_cps(self): """ Get current CPS Should return a float """ return self._cps def get_time(self): """ Get current time Should return a float """ return self._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._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 self._cookies > cookies: return 0.0 else: return math.ceil(float(cookies-self._cookies)/self._cps) def wait(self, time): """ Wait for given amount of time and update state Should do nothing if time <= 0.0 """ if time <= 0: return else: self._cookies += time*self._cps self._net_cookies += time*self._cps self._time += time return def buy_item(self, item_name, cost, additional_cps): """ Buy an item and update state Should do nothing if you cannot afford the item """ if self._cookies < cost: return else: self._cookies -= cost self._cps += additional_cps self._history.append((self._time, item_name, cost, self._net_cookies)) return 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. """ #initialize locals local_build_info = build_info.clone() clicker = ClickerState() debug_log = [] debug = False #main logic while clicker.get_time() < duration: next_purchase = strategy(clicker.get_cookies(), clicker.get_cps(), clicker.get_history(), duration - clicker.get_time(), local_build_info) #check for break conditions #i.e. strategy doesn't return next purchase OR insufficient time/funds if not next_purchase: debug_log.append("Strategy declined purchasing item") break time_until_purchase = clicker.time_until(local_build_info.get_cost(next_purchase)) if time_until_purchase + clicker.get_time() > duration: debug_log.append(("Cannot afford another ", next_purchase)) break #buy next item clicker.wait(time_until_purchase) clicker.buy_item(next_purchase, local_build_info.get_cost(next_purchase), local_build_info.get_cps(next_purchase)) debug_log.append(("Buying ", next_purchase, " under strat ", strategy)) debug_log.append(clicker.get_history()) local_build_info.update_item(next_purchase) #use up remaining time time_remainder = duration - clicker.get_time() debug_log.append((time_remainder, " time left over")) clicker.wait(time_remainder) #buy items at end-time next_purchase = strategy(clicker.get_cookies(), clicker.get_cps(), clicker.get_history(), 0.0, local_build_info) debug_log.append(("Next purchase: ", next_purchase)) while next_purchase: if clicker.get_cookies() > local_build_info.get_cost(next_purchase): clicker.buy_item(next_purchase, local_build_info.get_cost(next_purchase), local_build_info.get_cps(next_purchase)) local_build_info.update_item(next_purchase) next_purchase = strategy(clicker.get_cookies(), clicker.get_cps(), clicker.get_history(), 0.0, local_build_info) else: break #debugging if debug: for statement in debug_log: print statement #end return clicker 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. """ cookies += cps*time_left items = build_info.build_items() costs_items = [] for item in items: costs_items.append((build_info.get_cost(item), item)) costs_items.sort() for cost_item in costs_items: if cookies >= cost_item[0]: return cost_item[1] return None def strategy_expensive(cookies, cps, history, time_left, build_info): """ Always buy the most expensive item you can afford in the time left. """ cookies += cps*time_left items = build_info.build_items() costs_items = [] for item in items: costs_items.append((build_info.get_cost(item), item)) costs_items.sort(reverse=True) for cost_item in costs_items: if cookies >= cost_item[0]: return cost_item[1] return None def strategy_best(cookies, cps, history, time_left, build_info): """ The best strategy that you are able to implement. Returns object you can afford with highest cps to cost ratio. """ cookies += cps*time_left items = build_info.build_items() costfactors_items = [] for item in items: costfactors_items.append((build_info.get_cps(item)/build_info.get_cost(item), item)) costfactors_items.sort(reverse=True) for costfactor_item in costfactors_items: if cookies >= build_info.get_cost(costfactor_item[1]): return costfactor_item[1] return None 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. """ #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_strategy("Cursor Broken", SIM_TIME, strategy_cursor_broken) return run() ##Simpletest Debugging #simpletest.run_suite_ClickerState(ClickerState) #simpletest.run_suite_cheap_simulation(simulate_clicker, strategy_cheap) #simpletest.run_suite_none_simulation(simulate_clicker, strategy_none)