1. 程式人生 > >軟件工程博客作業二 -- 結對編程(一)

軟件工程博客作業二 -- 結對編程(一)

策略 後乘 類圖 計算 ack class trac number 需要

作業要求:https://edu.cnblogs.com/campus/ustc/InnovatingLeadersClass/homework/2231

項目源碼:https://github.com/jackroos/golden_number

黃金點遊戲簡介

N個同學(N通常大於10),每人寫一個0~100之間的有理數(不包括0或100),交給裁判,裁判算出所有數字的平均值,然後乘以0.618(所謂黃金分割常數),得到黃金點G。提交的數字最靠近G(取絕對值)的同學得到N分,離G最遠的同學得到-2分,其他同學得0分。

項目簡介

本次結對編程的項目是每個兩人小組編寫一個黃金點遊戲的bot,然後進行上下半場各400輪的黃金點比賽。

比賽規則

  • 假設有M個玩家, \(P_1, P_2, P_3, ..., P_M\)

  • 在 (0-100) 開區間內,所有玩家自由選擇兩個正有理數數字提交(可以相同或者不同)給服務器,假設\(N_{11}, N_{12},N_{21},N_{22}, ..., N_{M1},N_{M2}\)

  • \(M\cdot2\)個數字都提交後,服務器做如下計算:

    \[(N_{11}+N_{12}+N_{21}+N_{22}+…+N_{M1}+N_{M2})/(M\cdot2)\cdot0.618 = GN\]

  • 由此得到黃金點數字\(GN\)

  • 查看所有玩家提交的數字與\(GN\)的算術差的絕對值,值最小者得分,值最大者扣分。其它玩家不得分

  • 此回合結束,進行下一回合,多回合後,累計得分高者獲勝

計分規則

  • \(M\)個玩家比賽時,每輪離\(GN\)最近的玩家得\(M\)分,最遠的扣2分,其它玩家不得分

  • 如果一個玩家在一輪內提交兩個相同的數字並得分時,只計一次分

  • 多個玩家在一輪內同時離\(GN\)最近時,每個玩家都得\(M\)

接口設計與實現

項目包含的內容

  • 黃金點遊戲的bot
  • 為了測試我們的bot的表現,除了助教提供的復盤程序和以往比賽的數據可以用來測試外,我們小組還為此自己寫了一個簡單的黃金點遊戲服務器,然後通過寫一些簡單bot,和我們的bot一起進行模擬比賽。

源代碼文件結構

  • golden_number/
    • bots/
      • bot0.py
      • bot1.py, bot2.py, ..., bot6.py
    • env/
      • golden_number_server.py
    • run_golden_number_game.py
    • student/ (課程助教提供的復盤程序,這裏不做展開介紹)
    源代碼中主要包括三個源文件bot0.py, golden_number_server.py, run_golden_number_game.py:
  1. bot0.py 我們組的黃金點遊戲bot源代碼

  2. golden_number_server.py 一個可以多進程調用bots的黃金點遊戲server

  3. run_golden_number_game.py 示例程序,調用我們自己的黃金點遊戲server進行黃金點比賽

    註:bots文件夾中的bot1.py ~ bot6.py是一些簡單策略的bot,可以用來模擬比賽。

接口設計與實現

  • bot0.py源代碼接口及函數結構

    • 通過標準輸入得到歷史數據
    • 通過標準輸出輸出提交的兩個數,中間以‘\t‘分隔
    • bot核心思想:
      • 假設所有玩家的策略是與上一輪黃金點相關的,那麽可以基於歷史數據統計出所有玩家的每一輪提交的兩個數的平均值與上一輪黃金點的比例的概率分布,通過概率分布預測他們下一輪所提交的兩個數的平均值,進而可以求得自己應該提交的數。
      • 另外可能有些玩家基於“搗亂”策略,所以對於經常提交大數或小數的玩家,我們會計算他們提交大數或小數的概率以及大數/小數的均值,以此來預測他們下一輪的提交值。
    • bot0.py 中的核心函數:

      • get_stat
        • 功能:通過歷史數據,統計出上面所說的概率分布
      • pred
        • 功能:通過概率分布,求出別的玩家下一輪提交的數的平均值
      • bot
        • 通過歷史數據,調用get_stat和pred後,計算我們應該提交的數,並進行標準輸出
      • 其他:處理標準輸入數據的部分這裏不做展開介紹。
  • GoldenNumberServer 類接口

    • 構造函數:
      • 輸入:
        • players_bot_list, 即所有玩家的bot的源代碼路徑的列表
    • 成員函數:
      • run
        • 功能:從所有bot得到該輪提交的數字,並更新他們的得分
        • 實現:實現時利用了multiprocessing庫多進程獲取bot提交的數
      • reset
        • 功能:重置比賽數據,開始新的比賽
      • _get_number
        • 功能:調用bot指定的路徑的bot源代碼,得到該bot提交的數
      • _update_history_str
        • 功能:更新歷史數據的字符串(history_str_head, history_str_body)
    • 成員變量:
      • players_bot_list: 玩家的bot路徑列表
      • scores:玩家的得分情況
      • history_str_head:要提供給bot的標準輸入的第一行,包括歷史數據輪數以及每輪的數據的個數
      • history_str_body:要提供給bot的標準輸入中的主體部分,即歷史數據
      • pool: 多進程pool

Design by Contract(契約式設計)

  • 我覺得契約式設計的好處是可以在編程的時候就知道輸入數據的合法類型和範圍,這樣在編寫代碼的時候能夠減少很多邊界條件/異常情況的處理,而將註意力集中於主要算法的設計。他的缺點就是不適用於很多只在內部調用的模塊,內部調用的庫和函數,沒必要進行這樣的契約式設計,在內部調用的時候一般不會出現類型錯誤或者不合法值。

  • 我們項目中的契約主要就是bot主程序代碼讀取歷史數據和輸出提交的數都是通過標準輸入/輸出,然後輸入/輸出也符合提前約定好的格式,比如數與數之間用制表符分隔等。

代碼規範及異常處理

我們沒有顯式的去定義我們的代碼規範,只是根據Python語言中比較公認的一些規範進行編碼,然後盡量把每個獨立的部分單獨成函數,這樣便於測試和改進代碼。然後關於異常處理,由於假設輸入輸出都是標準輸入輸出,格式是預先定好的格式,所以我們的代碼也沒有進行什麽特別的異常處理。

結對編程

  • 結對過程:
    • 結對後,剛開始我們進行了討論,然後一開始我們的想法是利用強化學習的方法,然後通過設計一些對手,來訓練我們的智能體,但可能由於我們設計的對手太局限,這一想法在實驗時發現智能體選擇很簡單的策略(上一輪黃金點乘以某一固定常數)就能贏下對手,但這顯然並不能在真實中獲勝;後來,我們又進行了討論後,由於時間緊迫,我們決定換一種策略,基於統計對手的提交歷史數據的方法來預測對手的下一輪提交值,然後編寫了最後提交的bot。
  • 我們在結對編程的前半階段采取的是分工的方式,後半階段采取的是駕駛員和領航員的合作方式。

  • 結對編程的優點和缺點:

    • 優點:結對編程可以互相學習,互相借鑒對方的編程技巧;結對編程時能迫使編碼者更加集中註意力進行編程,提高代碼質量;結對編程的過程中代碼可以立即被復審,可以減少代碼中的bug。
    • 缺點:在結對編程時,會增加編程時的壓力,因為寫代碼時需要向對方展示自己的思路,這可能會使得有些人沒法自由發揮,在代碼選擇中偏向於選擇易於理解的代碼,而不是效率較高的代碼。
  • 我們各自的優缺點:

    • 我的優點:
      • 熟悉numpy等科學計算庫,能較快利用numpy的向量化操作實現函數
      • 熟悉封裝函數類
      • 善於對每個模塊進行測試
    • 我的缺點:
      • 不夠細心,有時會出現一些例如沒有進行深拷貝的錯誤
    • 同伴的優點:
      • 復審代碼時非常細致,經常在早期就能發現我編碼上的錯誤
      • 善於通過查找資料快速解決問題
      • 代碼較簡潔,可讀性強
    • 同伴的缺點:
      • 對python中numpy等的向量化操作不是很熟悉

    ?

其他收獲

  • 在這次黃金點遊戲的比賽中,我體會到的一點就是在剛開始做項目的時候,應該先完成一個初步版本,然後再通過實驗不斷改進,而不要所有事情都想一步到位,反而會什麽都沒做成。

附錄

  • 作業要求中關於7、8、9 界面模塊的方面,由於我們的結對編程項目側重於寫黃金點遊戲的bot,界面只有簡單的命令行輸出,所以沒有在博客中敘述界面模塊的內容。
  • 作業要求中的PSP表格由於我們當時並沒有進行這一步驟,所以無法在博客中給出。
  • 結對編程時,我們忘了進行拍照,所以沒有討論時的照片。
  • 關於UML圖,由於我們的代碼大部分都是調用numpy等庫,自己的代碼中主要是一個函數類和一個bot的主程序,所以沒有畫UML類圖。

軟件工程博客作業二 -- 結對編程(一)