2008年10月23日 星期四

發牌程式、doctest、unittest

記得之前曾經用Tcl寫過發牌程式,那時候大概的邏輯是
1. 產生52張牌,並依序放入一個list中
2. 產生0-51的亂數,當作index
3. 用index從list中取出牌
4. 從list中移除這張牌

其實也是沒寫幾行就搞定了。

但後來看到python的random模組提供了一個函式shuffle,這個函式的作用就是將list內的東西打亂,於是我又想到了發牌程式,其實這樣比較符合現實生活的「洗牌」與「發牌」行為。

邏輯是這樣的
1. 產生52張牌,並依序放入一個list中
2. 打亂52張牌的順序
3. 從第一張開始pop(取出並移除)出來


是不是很像人類在發牌呢?


但會寫這個主要是想要玩玩看doctest與unittest。

這兩樣在測試上都有其好用的地方,doctest非常適合一些簡易的測試,它可以讓註解等於測試,這樣很多習慣不良的開發者半強迫式的有了兩種習慣
1. 每一個function都會寫註解及範例
2. 每一個function都會寫測試程式

但畢竟是註解型測試,要忠實執行TDD(Test-driven development),可能還是得依靠unittest。

底下是 發牌程式 + doctest 與 unittest 兩段code

這是發牌程式 + doctest
dealer.py
# coding=utf-8

'''
只是一個簡單的發牌程式
'''


import doctest
import random

class dealer():
    
def __init__(self):
        
'''
        產生一副撲克牌
        '''

        
self.reset()
    
    
def reset(self):
        
'''
        重新產生一副撲克牌
        >>> d = dealer()
        >>> nothing = d.deal(1)
        >>> d.show().__len__() == 51
        True
        >>> d.reset()
        >>> d.show().__len__() == 52
        True
        '''

        
self.pokers = []
        
        
for pattern in ['spade', 'hearts', 'diamond', 'club']:
            
for number in range(1, 14):
                
self.pokers.append(pattern + str(number))
        
self.pokers.sort()
    
    
def shuffle(self):
        
'''
        洗剩下的撲克牌
        >>> d = dealer()
        >>> d.shuffle()
        >>> first = d.show()
        >>> first.__len__() == 52
        True
        >>> d.reset()
        >>> second = d.show()
        >>> second.__len__() == 52
        True
        >>> first == second
        False
        '''

        
random.shuffle(self.pokers)
    
    
def deal(self, number=1):
        
'''
        指定數目從牌庫中發出剩下的撲克牌
        >>> d = dealer()
        >>> nothing = d.deal(1)
        >>> d.show().__len__() == 51
        True
        >>> nothing = d.deal(51)
        >>> d.deal(1)
        'None'
        '''

        
        
# 如果指定的數目大於剩下的牌數,就把所有剩下的牌發出去
        
if self.pokers.__len__() < number:
            
number = self.pokers.__len__()
        
        
result = []
        
for i in range(number):
            
poker = self.pokers.pop()
            
result.append(poker)
        
        
if result.__len__() != 0:
            
result.sort()
            
return result
        
else:
            
return 'None'
    
    
def show(self):
        
'''
        把牌庫中剩下的撲克牌印出來
        '''

        
return self.pokers

if __name__ == '__main__':
    
doctest.testmod()



這是unittest
test_dealer.py
# coding=utf-8

'''
test for dealer.py
'''


import unittest
import dealer

class DealerTestCase(unittest.TestCase):
    
def setUp(self):
        
self.d = dealer.dealer()
        
self.original = list(self.d.show())
    
    
def testshuffle(self):
        
self.d.shuffle()
        
        
pokers = self.d.show()
        
self.assertNotEqual(self.original, pokers)
        
        
pokers.sort()
        
self.assertEqual(self.original, pokers)
    
    
def testreset(self):
        
self.d.reset()
        
        
pokers = self.d.show()
        
self.assertEqual(self.original, pokers)
    
    
def testdeal(self):
        
for i in range(52):
            
poker = self.d.deal()
            
self.assert_(poker[0] in self.original)
            
self.assert_(poker[0] not in self.d.show())
        
poker = self.d.deal()
        
self.assertEqual(poker, 'None')
    
    
def tearDown(self):
        
pass

if __name__ == '__main__':
    
suite = unittest.makeSuite(DealerTestCase)
    
unittest.TextTestRunner(verbosity=2).run(suite)


檔名記得跟我取的一樣喔!

將這兩個檔案都放在同一個資料夾,便可執行test_dealer.py!

沒有留言: