1. 程式人生 > >軟體工程部落格作業二 -- 結對程式設計(一)

軟體工程部落格作業二 -- 結對程式設計(一)

作業要求: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類圖。