第二个mini project是实现游戏cookie clicker。这个游戏本身很无聊。
这次的Project完全没有算法难度,估计这次的Project的用意是让人体会将实际问题建模为类和翻译为代码。
1 数学知识准备
1.1 求和相关的数学公式(略)
log运算 https://class.coursera.org/principlescomputing-001/wiki/view?page=log_and_exp
数学表达式 https://class.coursera.org/principlescomputing-001/wiki/view?page=math_expressions
1.2 Log 和Plotting
通过Log和plotting来判断多项式的维度。
log(y)=log(axn)
log(y)=log(a)+nlog(x)
If the data points (xi,yi) lie on y=axn, then they must satisfy the equation
log(yi)=log(a)+nlog(xi)
where log(a) and n are constants. This observation suggests a strategy for determining whether the data points lie close to a polynomial function. Plot the data points (log(xi),log(yi)) and check whether these data points lie near a straight line. If they do, the original data points (xi,yi) lie near a polynomial function. Moreover, the degree of this polynomial is simply the slope of this line.
Many plotting packages support this kind of analysis by offering a log/log plotting option. Under this option, the axes are labelled with the various values of the x and y. However, these labels and the plotted data are positioned at the locationslog(x) and log(y). For example, plots with axes labeled 1,10,100,1000,... are usually log/log plots.
2 Higher-Order Functions
2.1 把函数作为另外一个函数的参数
def double(val): return 2 * val def square(val): return val ** 2 def twice(func, val): return func(func(val)); print twice(double,3) print twice(square,3)
def area(func, low, high, stepsize): total = 0; loc = low; while loc<high: total += func(loc) * stepsize; loc +=stepsize; return total def g(x): return x; def h(x): return x **2; print area(g, 0, 10, 0.01) print area(h, 0, 10, 0.01)
把函数作为map的参数
list1 = [1,3,6,8,9] list2 = [x*2 for x in list1] print list2 def double(val): return 2 * val list3 = map(double, list1) print list3
4 使用filter
把函数作为filter的参数
def even(val): if val % 2 == 0: return True else: return False list4 = filter(even, list3) print list4
4 mini-project
Note that simulate_clicker is a higher-order function: it takes a strategy function as an argument!
5 我的代码:
""" Cookie Clicker Simulator 最后提交测试的时候,有一个case中的一个数字有些小问题,得到98分,还没有找到问题原因。 """ import simpleplot import math # 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._total_game_cookies = 0.0 self._current_cookies= 0.0 self._current_time = 0.0 self._current_cps = 1.0 self._history_list = [(0.0, None, 0.0, 0.0)] def __str__(self): """ Return human readable state """ # res = "" # for each_item in self.history_list: # res += str(each_item) + "\n" # return res return "\nmy total cookies: "+str(self._total_game_cookies) + "\ncurrent cookies: " + str(self._current_cookies) + "\ncurrent time: " + str(self._current_time) + "\ncurrent CPS: " +str(self._current_cps) def get_cookies(self): """ Return current number of cookies (not total number of cookies) Should return a float """ return self._current_cookies def get_cps(self): """ Get current CPS Should return a float """ return self._current_cps def get_time(self): """ Get current time Should return a float """ return self._current_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) """ return self._history_list def time_until(self, cookies): """ Return time until you have the given number of cookies (could be 0 if you already have enough cookies) Should return a float with no fractional part """ if cookies <= self.get_cookies(): return 0.0 else: return float(math.ceil((cookies-self.get_cookies())/self.get_cps())) def wait(self, time): """ Wait for given amount of time and update state Should do nothing if time <= 0 """ if time<=0: return # 如何真正模拟How to simulate waiting for the given amout of time? self._current_time += time self._current_cookies += time * self._current_cps self._total_game_cookies += time * self._current_cps; 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._current_cookies < cost: return self._history_list.append((self._current_time, item_name, cost, self._total_game_cookies)) self._current_cookies -= cost; self._current_cps += additional_cps # print str(item_name) + "--" + str(self._current_time) + "--" + str(self._current_cookies) + "--" + str(self._current_cps) + "--" + str(self._total_game_cookies) 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 game. """ # Replace with your code build_info_clone = build_info.clone() new_clikcer_state = ClickerState() while new_clikcer_state.get_time()<duration: # print "A-1" if new_clikcer_state.get_time()>duration: #本句可省略? break suggested_item = strategy(new_clikcer_state.get_cookies(), new_clikcer_state.get_cps(),duration - new_clikcer_state.get_time(), build_info_clone) # print "A-2" if suggested_item == None: new_clikcer_state.wait(duration - new_clikcer_state.get_time()) # print "A-3" else: cookies_needed = build_info_clone.get_cost(suggested_item) cps_added = build_info_clone.get_cps(suggested_item) time_needed = new_clikcer_state.time_until(cookies_needed) # print "A-4" if time_needed + new_clikcer_state.get_time() > duration: new_clikcer_state.wait(duration - new_clikcer_state.get_time()) # print "A-5" else: new_clikcer_state.wait(time_needed) new_clikcer_state.buy_item(suggested_item, cookies_needed, cps_added) build_info_clone.update_item(suggested_item) # print "A-6" # print "A-7" if new_clikcer_state.get_time()==duration: suggested_item = strategy(new_clikcer_state.get_cookies(), new_clikcer_state.get_cps(),duration - new_clikcer_state.get_time(), build_info_clone) if suggested_item != None: cookies_needed = build_info_clone.get_cost(suggested_item) cps_added = build_info_clone.get_cps(suggested_item) if cookies_needed<=new_clikcer_state.get_cookies(): new_clikcer_state.buy_item(suggested_item, cookies_needed, cps_added) build_info_clone.update_item(suggested_item) return new_clikcer_state #return ClickerState() def strategy_cursor(cookies, cps, time_left, build_info): """ Always pick Cursor! Note that this simplistic strategy does not properly check whether it can actually buy a Cursor in the time left. Your strategy functions must do this and return None rather than an item you can't buy in the time left. """ return "Cursor" def strategy_none(cookies, cps, time_left, build_info): """ Always return None This is a pointless strategy that you can use to help debug your simulate_clicker function. """ return None def strategy_cheap(cookies, cps, time_left, build_info): """cheap Strategy""" pricelist = {} funding = cookies + cps * time_left for item in build_info.build_items(): if build_info.get_cost(item) <= funding: pricelist[build_info.get_cost(item)] = item if len(pricelist) > 0: return pricelist[min(pricelist)] else: return None def strategy_expensive(cookies, cps, time_left, build_info): """Expensive Strategy""" pricelist = {} funding = cookies + cps * time_left for item in build_info.build_items(): if build_info.get_cost(item) <= funding: pricelist[build_info.get_cost(item)] = item if len(pricelist) > 0: return pricelist[max(pricelist)] else: return None def strategy_best(cookies, cps, time_left, build_info): """Best Strategy""" pricelist = {} funding = cookies + cps * time_left for item in build_info.build_items(): if build_info.get_cost(item) <= funding: pricelist[build_info.get_cps(item)/build_info.get_cost(item)] = item if len(pricelist) > 0: return pricelist[max(pricelist)] elif len(pricelist) == 0: return None def run_strategy(strategy_name, time, strategy): """ Run a simulation with one strategy """ state = simulate_clicker(provided.BuildInfo({"Cursor":[15.0,50.0]},1.15), time, 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) # run_strategy("Cursor", 16, strategy_cursor) # 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()