2013-08-24 62 views
1

下面是一个'大酒杯模拟器'程序的代码示例,给出了一些百分比,也绘制了它们。我包括了一切构建的三个主要部分,您可以看到数据结构。我相信其余的代码与我的问题无关。脚本运行良好。除非我把范围设置得太高(有多少鞋子需要处理,多少次)。运行大约一小时后(RAM以3MB /秒的速度吃掉),这个过程就会被杀死。我试图用pympler和内置的sys模块进行测试,但我无法找到任何原因来实际消耗内存。任何帮助/建议,将不胜感激。python脚本被OOM杀手扼杀

import datetime 
from random import choice 
import matplotlib 
matplotlib.use('Agg') 
import matplotlib.pyplot as plt 
import numpy as np 
from matplotlib.backends.backend_pdf import PdfPages 

class Rules(object): 

    """a dictionary of dictionaries describing the drawing rules in certain situations""" 

    def __init__(self): 
     self.rules = { 2:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"H", "10":"D", "11":"D", "12":"H", "13":"S", "14":"S", "15":"S", "16":"S", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"H", "A7":"S", "A8":"S", "A9":"S", "A10":"S","D2":"SP", "D3":"SP", "D4":"H", "D5":"D","D6":"SP", "D7":"SP", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         3:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"D", "10":"D", "11":"D", "12":"H", "13":"S", "14":"S", "15":"S", "16":"S", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"D", "A7":"D", "A8":"S", "A9":"S", "A10":"S","D2":"SP", "D3":"SP", "D4":"H", "D5":"D","D6":"SP", "D7":"SP", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         4:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"D", "10":"D", "11":"D", "12":"S", "13":"S", "14":"S", "15":"S", "16":"S", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"D", "A5":"D", "A6":"D", "A7":"D", "A8":"S", "A9":"S", "A10":"S","D2":"SP", "D3":"SP", "D4":"H", "D5":"D","D6":"SP", "D7":"SP", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         5:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"D", "10":"D", "11":"D", "12":"S", "13":"S", "14":"S", "15":"S", "16":"S", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"D", "A3":"D", "A4":"D", "A5":"D", "A6":"D", "A7":"D", "A8":"S", "A9":"S", "A10":"S","D2":"SP", "D3":"SP", "D4":"SP", "D5":"D","D6":"SP", "D7":"SP", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         6:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"D", "10":"D", "11":"D", "12":"S", "13":"S", "14":"S", "15":"S", "16":"S", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"D", "A3":"D", "A4":"D", "A5":"D", "A6":"D", "A7":"D", "A8":"S", "A9":"S", "A10":"S","D2":"SP", "D3":"SP", "D4":"SP", "D5":"D","D6":"SP", "D7":"SP", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         7:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"H", "10":"D", "11":"D", "12":"H", "13":"H", "14":"H", "15":"H", "16":"H", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"H", "A7":"S", "A8":"S", "A9":"S", "A10":"S","D2":"SP", "D3":"SP", "D4":"H", "D5":"D","D6":"H", "D7":"SP", "D8":"SP", "D9":"S", "D10":"S", "D11":"SP" }, 
         8:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"H", "10":"D", "11":"D", "12":"H", "13":"H", "14":"H", "15":"H", "16":"H", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"H", "A7":"S", "A8":"S", "A9":"S", "A10":"S","D2":"H", "D3":"H", "D4":"H", "D5":"D","D6":"H", "D7":"H", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         9:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"H", "10":"D", "11":"D", "12":"H", "13":"H", "14":"H", "15":"H", "16":"H", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"H", "A7":"H", "A8":"S", "A9":"S", "A10":"S","D2":"H", "D3":"H", "D4":"H", "D5":"D","D6":"H", "D7":"H", "D8":"SP", "D9":"SP", "D10":"S", "D11":"SP" }, 
         10:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"H", "10":"H", "11":"H", "12":"H", "13":"H", "14":"H", "15":"H", "16":"H", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"H", "A7":"H", "A8":"S", "A9":"S", "A10":"S","D2":"H", "D3":"H", "D4":"H", "D5":"H","D6":"H", "D7":"H", "D8":"H", "D9":"S", "D10":"S", "D11":"SP" }, 
         11:{"5":"H", "6":"H", "7":"H", "8":"H", "9":"H", "10":"H", "11":"H", "12":"H", "13":"H", "14":"H", "15":"H", "16":"H", "17":"S", "18":"S", "19":"S", "20":"S", "21":"S", "A2":"H", "A3":"H", "A4":"H", "A5":"H", "A6":"H", "A7":"H", "A8":"S", "A9":"S", "A10":"S","D2":"H", "D3":"H", "D4":"H", "D5":"H","D6":"H", "D7":"H", "D8":"H", "D9":"S", "D10":"S", "D11":"H" } 

         } 


    def lookup(self, bank_val, player_str): #!!!! bank_val > int , player_str > str 
     return self.rules[bank_val][player_str] 


class Deck(object): 

    """4*13 cards""" 

    def __init__(self): 
     self.cards = [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11] 
     self.onedeck = [] 
     for decks in range(0, 4): 
      self.onedeck[len(self.onedeck):]=self.cards 


class Shoe(Deck): 

    """this shoe contains 6 decks (312 cards) 
     --todo: set number of decks as a parameter""" 

    def __init__(self): 

     deck = Deck() 
     self.handcounter = 0 
     self.decks=[] 
     for i in range (0, 6): 
      self.decks[len(self.decks):]=deck.onedeck 


class Money(object): 

    """acts as our wallet,doesnt change after we start a new shoe. works with a single parameter,the start amount of money""" 

    def __init__(self, money): 
     self.money = money 
     self.moneylist = [] 
     self.handlist = [] 

    def display(self): 
     return self.money 

    def lose(self, m, shoe): 
     self.money = self.money - m 
     self.moneylist.append(self.money) 
     self.handlist.append(shoe.handcounter) 

    def win(self, m, shoe): 
     self.money = self.money + m 
     self.moneylist.append(self.money) 
     self.handlist.append(shoe.handcounter) 

    def zero(self): 
     self.money = 0 


def money_eval(dic_player, dic_bank, shoe): 

    """decides who wins,and how much in every situation, once the hand finished""" 

    bank_val=sum(dic_bank["bank"]) 
    #current = shoe.money 
    for key, value in dic_player.iteritems(): 
     for i in range(len(value)): 
      shoe.handcounter += 1 
      if value[i].count("D")<1: 
       multi =1 
      else: 
       value[i].pop() 
       multi = 2 
      if len(value[i])==2 and sum(value[i])==21: 
       multi=1.5 
      if multi==1 and sum(value[i])>21: 
       money.lose(multi, shoe) 
       #print "lost ", multi," ", sum(value[i]), " vs ", bank_val 
      elif multi==1 and sum(value[i])<=21 and bank_val > 21: 
       money.win(multi, shoe) 
       #print "won ", multi," ", sum(value[i]), " vs ", bank_val 
      elif multi==1 and sum(value[i])<=21 and bank_val <= 21 and sum(value[i])>bank_val: 
       money.win(multi, shoe) 
       #print "won ", multi," ", sum(value[i]), " vs ", bank_val 
      elif multi==1 and len(dic_bank["bank"])>2 and sum(value[i])<=21 and bank_val <= 21 and sum(value[i])<bank_val: 
       money.lose(multi, shoe) 
       #print "lost ", multi," ", sum(value[i]), " vs ", bank_val 
      elif multi==1.5 and len(dic_bank["bank"])>2: 
       money.win(multi, shoe) 
       #print "BJ won ", multi," ", sum(value[i]), " vs ", bank_val 
      elif multi==1 and len(dic_bank["bank"])==2 and bank_val==21: 
       money.lose(multi, shoe) 
       #print "lost against BJ", multi," ", sum(value[i]), " vs ", bank_val 
      else: 
       pass 
       #print "draw!" 
    return 

def lngth(shoe): 

    """how many cards are still in the current shoe""" 

    return len(shoe.decks) 

def deal_next_card(shoe, list): 

    """the random process where a card is selected from the remaining ones,and put in a certain list""" 

    card=choice(shoe.decks) 
    list.append(card) 
    shoe.decks.remove(card) 

def construct_player_str(list): 

    """translates the players hand into readable categories for Rules.lookup""" 

    if list[0]==list[1]: 
     return "D"+str(list[0]) 
    elif list[0]==11 or list[1]==11: 
     return "A"+str(list[0]+list[1]-11) 
    else: 
     return str(list[0]+list[1]) 

def deal_a_hand(shoe, playerboxes=1): 

    """the process of dealing a single hand""" 

    dic_player = { "hand" + str(i) : [] for i in range(int(playerboxes)) } 
    dic_bank = { "bank" : []} 
    for key in dic_player.keys():              #deal first cards 
     tmp_list1=[] 
     deal_next_card(shoe,tmp_list1) 
     dic_player[key] =[tmp_list1] 
    deal_next_card(shoe, dic_bank["bank"])         #deal to bank 
    for key in dic_player.keys():              #deal second cards 
     deal_next_card(shoe,dic_player[key][0]) 

    for key in dic_player.keys(): 
     review_player_options(dic_player, key, shoe,dic_bank)         #all splitting and doubling first 
    draw_bank_cards(dic_bank, shoe) 
    money_eval(dic_player, dic_bank, shoe) 
    shoe.handcounter=shoe.handcounter+1 
    return 

def split(dic_player,key, shoe, dic_bank): 
    for tosplit in dic_player[key]: 
     rule = rules.lookup(dic_bank["bank"][0], construct_player_str(tosplit)) 
     if tosplit[0]==tosplit[1] and rule == "SP": 
      tmp0=[tosplit[0]] 
      tmp1=[tosplit[1]] 
      deal_next_card(shoe, tmp0) 
      deal_next_card(shoe, tmp1) 
      #print "splitting:", tmp0, tmp1 
      dic_player[key].remove(tosplit) 
      dic_player[key].append(tmp0) 
      dic_player[key].append(tmp1) 
    return 1 

def has_splittable(dic_player, key, dic_bank): 
    for hands in dic_player[key]: 
     if hands[0]==hands[1] and rules.lookup(dic_bank["bank"][0], construct_player_str(hands)) == "SP": 
      return 1 
    return 0 

def has_doubleable(dic_player, key, dic_bank): 
    for hands in dic_player[key]: 
     if hands.count("D")<1 and rules.lookup(dic_bank["bank"][0], construct_player_str(hands)) == "D": 
      return 1 
    return 0 

def double(dic_player, key, shoe, dic_bank): 
    for todbl in dic_player[key]: 
     if len(todbl)<3 and rules.lookup(dic_bank["bank"][0], construct_player_str(todbl)) == "D": 
      deal_next_card(shoe, todbl) 
      todbl.append("D") 
    return 

def draw_player_cards(dic_player, key, shoe): 

    """once all the splitting and doubling done,draws all the remaining cards to the unfinished hands of the player boxes,not forgetting the optional techniqe to count an Ace as 1. 0 at the end of the list is signalling the Bust.""" 

    for hands in dic_player[key]: 
     if hands.count("D")<1: 
      while sum(hands)<17 and hands.count(0)<1: 
       deal_next_card(shoe, hands) 
       if sum(hands)>21: 
        if hands.count(11)>0: 
         hands.remove(11) 
         hands.append(1) 
        else: 
         hands.append(0) 

def draw_bank_cards(dic_bank, shoe): 

    """similar to draw_player_cards""" 

    while sum(dic_bank["bank"])<17 and dic_bank["bank"].count(0)<1: 
     deal_next_card(shoe, dic_bank["bank"]) 
     if sum(dic_bank["bank"])>21: 
      if dic_bank["bank"].count(11)>0: 
       dic_bank["bank"].remove(11) 
       dic_bank["bank"].append(1) 
      else: 
       dic_bank["bank"].append(0) 

def review_player_options(dic_player, key, shoe, dic_bank): 

    """first all the splits,next the doubles,and the rest""" 

    while has_splittable(dic_player, key, dic_bank): 
     split(dic_player, key, shoe, dic_bank) 
    while has_doubleable(dic_player, key, dic_bank): 
     #print "D D D", dic_player[key] 
     double(dic_player, key, shoe, dic_bank) 
    draw_player_cards(dic_player, key, shoe) 


def compute(): 
    sum_money = [] 
    sum_handcounter = [] 
    Shoes= [ Shoe() for i in range(1000) ]      # how many shoes 
    for i in Shoes: 
     if (Shoes.index(i)%100==0): 
      print Shoes.index(i) 
     while lngth(i)>52: 
      deal_a_hand(i, 2) 
      sum_money.append(money.display()) 
      sum_handcounter.append(len(sum_money)) 
    percents.append(-1*sum_money[-1]/sum_handcounter[-1]*100) 
    money.zero() 


percents = [] 
money = Money(0) 
rules = Rules() 
times=range(1000) 
for i in times: 
    print '<<starting the '+str(i+1)+'th set of shoes of '+str(len(times)-1)+'>>' 
    compute() 
print percents 
dt = str(datetime.datetime.now()) 
datedfilename = 'bjresults_'+dt+'.pdf' 
pdf = PdfPages(datedfilename) 
plot = plt.hist(percents, 3000) 
plt.savefig(pdf, format='pdf') 
plt.close() 
pdf.close() 
#plot.savefig('lol.png') 
#plt.show() 
+0

可能想在这里理清缩进...另外 - 你确定它工作正常吗?匆匆一瞥,我看不到'长度'来自哪里...... –

+0

@Jon Clements是的,很好。它只是我的代码的一部分。我相信如果我发布所有这些信息,可能会有警告/禁令/ postdel。有没有限制?我的文件是18.8KB – hip

+0

@JonClements也不担心缩进,这是我的第一篇文章,仍然在试验如何正确地做到这一点 – hip

回答

1

您的程序崩溃的原因是您根本没有考虑内存管理。如果你想尝试一个快速的解决方案,你不应该建立鞋业对象的列表,也许

for i in range(1000): 
    Shoe = Shoe() 
    #Do stuff 

OR

for i in (Shoe() for s in range(1000)): 
    #Do stuff 

..这将创造擦鞋对象的发生器,它们被称为,而不是预先分配所有的对象。

你的鞋和甲板对象的交互是可怕的和不必要的。你需要使用super()方法。当你完成一个对象(将你的统计信息附加到列表后),使用del()方法手动删除它;而不是依靠垃圾收集。

您的代码一览无余,因为您没有正确使用对象。仅仅因为Python可以在句法上处理你的程序并不意味着它“正确运行”。在处理缩放时(在这种情况下,不同套牌的迭代)需要更加谨慎,以便如何分配对象并创建内存。学习使用生成器(yield)或函数作为具有充当reduce()方法上的函数数组的内部方法的类,而不是使用如此多的类(对象)结构。

while ratio_generator(): 
    generators = [ 
     file_id_generator(), 
     types_generator(), 
     date_generator(), 
     names_generator(), 
     folder_generator(), 
     content_generator(), 
     id_chain_generator(), 
    ] 
    dynamics = [ 
     get_datetime, 
     get_code_status, 
    ] 
    statics = [ 
     self.today, 
     self.test_id, 
     self.author 
    ] 

    yield flatten([x for x in statics], [x() for x in dynamics], [next(x) for x in generators]) 

过滤器,地图,减少可能是你的新朋友,而不是使用这么多的静态查找。

祝你好运。

+0

嗨。抱歉没有回复这么久。我通过简单地从货币对象中删除'self.moneylist = [] self.handlist = []'(及其连接的方法)来解决问题。它经历了太多迭代,并极大地增加了列表的大小。到目前为止,我的计算已经运行了三天,我认为它正在解决,但@Blake VanderMerwe我已经完成了研究,我仍然不知道如何在这种情况下使用发生器。你能帮我一把吗? – hip

+0

你需要你和我坐在一起,因为我甚至不知道如何玩二十一点:)只有当你需要“很多”东西时,唯一突出的东西是,通常是一个糟糕的主意,预先分配对象(除非需要)。在上面的回答中,我列举了Shoe()作为例子。不要创建1000个Shoe对象的列表,最好调用一个每次调用它时都会产生一个Shoe对象的生成器;这样你每次都得到一个新对象,并且在内存后面没有放置1000-N个对象。 – blakev

+0

你也可以做一些改进,这会加快你的程序:比如使用'in'关键字而不是在列表中计算'11'的实例,删除那个实例,并附加数字1。你可以翻转那整个东西,这将加速该操作约5倍(即使它已经不那么慢)。 – blakev