第二次結對編程做業


1.網絡連接

本憨憨的連接 And 倉庫地址html

另外一個憨憨的連接 And 倉庫地址前端


2.分工

前端編寫與先後端交接:胡康java

後端算法測試與博文編寫:黃宇航python


3.PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 60 100
Estimate 估計這個任務須要多少時間 60 100
Development 開發 600 1000
Analysis 需求分析 (包括學習新技術) 600 600
Design Spec 生成設計文檔 30 40
Design Review 設計複審 20 20
Coding Standard 代碼規範 (爲目前的開發制定合適的規範) 20 30
Design 具體設計 50 60
Coding 具體編碼 1650 1800
Code Review 代碼複審 50 40
Test 測試(自我測試,修改代碼,提交修改) 100 120
Reporting 報告 100 100
Test Report 測試報告 30 30
Size Measurement 計算工做量 20 20
Postmortem & Process Improvement Plan 過後總結, 並提出過程改進計劃 50 60
合計 3440 4120

4.解題思路分析和設計實現說明

(1)網絡接口的使用

前端登陸註冊查看排行榜接口用JavaScript編寫git

//登陸
<script>
    var username=document.getElementById("i1");
    var password=document.getElementById("i2");
    function t1(){
    var data = JSON.stringify({
        "username": username.value,
        "password": password.value
    });
    if (username.value == "" ||password.value == "" )
    {
        alert("請輸入用戶名或密碼");
        return ;
    }
    var xhr = new XMLHttpRequest();
    xhr.addEventListener("readystatechange", function () {
    if (this.readyState === this.DONE) {
        console.log(this.responseText);
        var JsonObj = JSON.parse(this.responseText);
        if(JsonObj.status==0) 
        {
            localStorage.setItem("token",JsonObj.data.token);
            localStorage.setItem("id",JsonObj.data.user_id);
            alert("登陸成功");
            window.location.href = "game.html";
        }
        else if(JsonObj.status==1003)
            alert("教務處認證失敗");
        else
            alert("用戶名未註冊或密碼錯誤");
    }
    });
    xhr.open("POST","http://api.revth.com/auth/login");
    xhr.setRequestHeader("content-type", "application/json");
    xhr.send(data);
    }
</script>
//註冊
<script>
    var username=document.getElementById("i1");
    var password=document.getElementById("i2");
    function t1()
    {
        var data = JSON.stringify({
            "username": username.value,
            "password": password.value,
            "student_number": document.getElementById("ii3").value,
            "student_password" :document.getElementById("ii4").value
        });
        if (username.value == "" ||password.value == "" )
        {
            alert("請輸入用戶名或密碼");
            return ;
        }
        if(document.getElementById("ii3").value==""||document.getElementById("ii4").value=="")
        {
            alert("請輸入學號或教務處密碼");
            return ;
        }
        var xhr = new XMLHttpRequest();
        //xhr.withCredentials = true;
        xhr.addEventListener("readystatechange", function () {
        if (this.readyState === this.DONE) {
            console.log(this.responseText);
            var JsonObj = JSON.parse(this.responseText);
            if(JsonObj.status==0) alert("註冊成功,請登陸");
            else if(JsonObj.status==1001) alert("用戶名已被使用");
            else if(JsonObj.status==1002) alert("學號已綁定");
            else if(JsonObj.status==1003) alert("教務處認證失敗");
            else
                alert(JsonObj.status)
        }
        });
        xhr.open("POST", "http://api.revth.com/auth/register2");
        xhr.setRequestHeader("content-type", "application/json");
        xhr.send(data);
    }
</script>

//登陸驗證
function check(){
            $.ajax({
                url:'http://api.revth.com/auth/validate',
                type:'GET',
                headers:{
                    "Content-Type":'application/json',
                    "X-Auth-Token":localStorage.getItem('token')
                },
                dataType:'json',
                success:function(data){
                    if (data['data']['user_id']==null){
                        alert("登陸過時,請重試");
                        window.location.href='登入.html';
                    }
                    else{
                        document.getElementById("chupai").onclick=function () {
                            document.getElementById("fapai").style.display="block"
                            document.getElementById("chupai").style.display="none"
                        }        
                        if (localStorage.getItem('card')!=null)
                            showCard();
                        if (localStorage.getItem('gameid')!=null)
                            document.getElementById('gameid').innerText="房間:"+localStorage.getItem('gameid');
                    }
                },
                error:function(data){
                    alert("登陸過時,請重試");
                }
            });  
        }

//開始對局
function openGame(){
            $.ajax({
                url:'http://api.revth.com/game/open',
                type:'POST',
                dataType:'json',
                headers:{
                    'X-Auth-Token':localStorage.getItem('token')
                },
                success:function(data){
                    document.getElementById("chupai").style.display="block";
                    document.getElementById("fapai").style.display="none";
                    document.getElementById('gameid').innerText="房間:"+data['data']['id'];
                    localStorage.setItem("gameid",data['data']['id']);
                    localStorage.setItem("card",data['data']['card']);
                    showCard();
                },
                error:function(data){
                    alert("匹配異常,請重試");
                }
            });
        }

//排行榜
     function getRank(){
            $.ajax({
                url:'http://api.revth.com/rank',
                type:'GET',
                dataType:'json',
                success:function(data){
                    document.getElementById('paihangbang').style.display="";
                    for (var i=1;i<=5;++i){
                        var pid="pid"+i;
                        var s="s"+i;
                        var name="name"+i;
                        document.getElementById(pid).innerText=data[i-1]['player_id'];
                        document.getElementById(s).innerText=data[i-1]['score'];
                        document.getElementById(name).innerText=data[i-1]['name'];
                    }
                },
                error:function(data){
                    alert("請求出錯,請重試");
                }
            });
        }
//歷史戰績
 function query(){
            var username=document.getElementById('queryUser').value;
            $.ajax({
                url:'http://api.revth.com/history/id',
                type:'GET',
                dataType:'json',
                headers:{
                    'X-Auth-Token':localStorage.getItem('token')
                },
                success:function(data){
                    console.log(data);
                },
                error:function(data){
                    alert('查詢失敗,請重試');
                }
            });
        }

後端 開始遊戲接口使用了requests庫github

import requests
import cards_division

url = 'http://api.revth.com'
cards_list = []
max_list = []

'''開始遊戲並出牌'''
def newgameplay():
    global url
    global cards_list
    global max_list
    f = open('token.txt')
    token = f.readline()
    f.close
    headers = {"X-Auth-Token" : token}
    r = requests.post(url+'/game/open', headers=headers)
    print(r.text)
    data = r.json()
    status = data['status']
    game_id = data.get('data').get('id')
    cards_string = data.get('data').get('card')
    cards_list = cards_string.split(' ')
    max_list = cards_division.divide_cards(cards_list)
    front = ' '.join(max_list[10:13])
    middle = ' '.join(max_list[5:10])
    rear = ' '.join(max_list[0:5])
    payload = {
        "id": game_id,
        "card": [
            front,
            middle,
            rear
        ]
    }
    headers = {
        'content-type': "application/json",
        "X-Auth-Token": token
    }
    r2 = requests.post(url+'/game/submit', json=payload, headers=headers)
    print(r2.text)
    data = r2.json()
    status2 = data.get("status")
    for i in range(0, 13):
        if max_list[i][0] == '*':
            max_list[i] = '^'+max_list[i][1:]
    return max_list

(2)代碼組織與內部實現設計(類圖)


(3)說明算法的關鍵與關鍵實現部分流程圖

  • Step1:將牌’A‘轉變成值爲14的牌,並將獲得的13張牌升序排序ajax

    def change_and_sort_cards(list):
        for i in range(0, 13):
            if list[i][1]=='A':
                list[i]=list[i][0]+'14'
            elif list[i][1]=='J':
                list[i] = list[i][0]+'11'
            elif list[i][1]=='Q':
                list[i]=list[i][0]+'12'
            elif list[i][1]=='K':
                list[i]=list[i][0]+'13'
        list.sort(key=lambda x:int(x[1:]))
  • Step2:遍歷後墩算法

  • Step3:遍歷完後墩判斷是否繼續再遍歷中墩,json

  • Step4:剩下的排歸爲前墩

  • Step5:判斷各墩的牌的類型

  • Step6:判斷並選出權值最大的狀況


5.關鍵代碼及解釋

各類牌型的判斷

def same_flower(list):
    flag = True
    if len(list) == 3:
        return 0
    else:
        for i in range(1, 5):
            if list[0][0] != list[i][0]:
                flag = False
                break
        if flag:
            return TONGHUA + int(list[4][1:]) * P
        else:
            return 0

def shunzi(list):
    if len(list) == 3:
        return 0
    else:
        flag = True
        for i in range(1, 5):
            if int(list[i][1:]) - 1 != int(list[i - 1][1:]):
                flag = False
        if flag:
            return SHUNZI + int(list[4][1:]) * P
        else:
            return 0

def zhadan(list):
    flag = True
    if len(list) == 3:
        return 0
    else:
        if list[1][1:] != list[2][1:] or list[1][1:] != list[3][1:]:
            flag = False
        if list[1][1:] != list[0][1:] and list[4][1:] != list[3][1:]:
            flag = False
        if flag:
            return ZHADAN + int(list[2][1:]) * P
        else:
            return 0

def hulu(list):
    flag = True
    if len(list) == 3:
        return 0
    else:
        if list[0][1:] != list[1][1:] or list[3][1:] != list[4][1:]:
            flag = False
        if list[3][1:] != list[2][1:] and list[1][1:] != list[2][1:]:
            flag = False
        if flag:
            return HULU+int(list[2][1:])*P
        else:
            return 0
def santiao(list):
    flag = False
    if list[0][1:] == list[1][1:] and list[0][1:] == list[2][1:]:
        return SANTIAO+int(list[2][1:])*P
    elif len(list) == 5:
        for i in range(0, 3):
            if list[i][1:] == list[i+1][1:] and list[i][1:] == list[i+2][1:]:
                flag=True
                break
        if flag:
            return SANTIAO+int(list[2][1:])*P
        else:
            return 0
    else:
        return 0

def select_duizi(list):
    if len(list)==3:
        if list[0][1:] == list[1][1:] or list[2][1:] == list[1][1:]:
            return YIDUI+int(list[1][1:])*P
        else:
            return SANPAI+int(list[2][1:])*P
    elif len(list) == 5:
        tmp = []
        i = 0
        while i < 4:
            if list[i][1:] == list[i+1][1:]:
                tmp.append(i)
                i += 1
            i += 1
        if len(tmp) == 1:
            return YIDUI+int(list[tmp[0]][1:])*P
        elif len(tmp) == 2:
            if tmp[1]-tmp[0] == 2 and int(list[tmp[1]][1:])-int(list[tmp[0]][1:]) == 1:
                return LIANDUI+int(list[tmp[1]][1:])*P
            else:
                return ERDUI+int(list[tmp[1]][1:])*P
        elif len(tmp) == 0:
            return SANPAI+int(list[4][1:])*P

將牌分堆後找出權值最大的組合

def sort_cards(list):
    cards = [list[10:13], list[5:10], list[0:5]]
    scores = [0, 0, 0]
    for i in range(0, 3):
        if same_flower(cards[i]) == 0:
            scores[i] = zhadan(cards[i])
            if scores[i] != 0:
                continue
            scores[i] = hulu(cards[i])
            if scores[i] != 0:
                continue
            scores[i] = shunzi(cards[i])
            if scores[i] != 0:
                continue
            scores[i] = santiao(cards[i])
            if scores[i] != 0:
                continue
            scores[i] = select_duizi(cards[i])
        else:
            scores[i] = same_flower(cards[i])
            if shunzi(cards[i]) != 0:
                scores[i] += 3
    if scores[0] > scores[1] or scores[1] > scores[2]:
        return [0, 0, 0]
    else:
        return scores啊啊

6.性能改進與分析

先Profile一下代碼,結果如圖:

  • Call Graph

  • Statistics

由圖可知,消耗最大的是divide_cards2函數, 由於對於大多數狀況,後墩中墩不少是散牌或一對。因而增長了一個判斷函數,對於後墩狀況較好的(權值較大),再繼續遍歷中墩,這樣就大大提升了程序的效率。

7.單元測試

import cards_division
import cards_sorting
import openapi
import unittest

class test(unittest.TestCase):
    def test_cards_type(self):
        self.assertEqual(cards_sorting.select_duizi(['#2', '#2', '#2', '#2', '#2']), 4.25)
        self.assertEqual(cards_sorting.santiao(['#2', '#4', '#4', '#4', '#7']), 5.25)
    def test_divide(self):
        cards_division.divide_cards(['&2', '#3', '&4', '#K', '#4', '^2', '$2', '#5', '&6', '^4', '*$8', '&8', '#A'])
        self.assertEqual(cards_division.count, 72072)
    def test_openapi(self):
        self.assertEqual(openapi.sign_in('Cerberus', 'hyh990723'), 0)
        self.assertEqual(openapi.newgameplay(), 0)


if __name__ == '__main__':
    unittest.main()

測試函數:

  • 測試牌型判斷函數可否正確返回權值
  • 測試劃分函數是否正常遍歷全部狀況
  • 測試網絡接口可否正常鏈接,提交的數據是否正確

思路:
使用Python中的unittest模塊,對代碼中容易出錯但又沒法直接根據最終結果進行定位的模塊進行測試。


8.貼出Github代碼遷入記錄

啥也不知道寫了啥就往上丟啥吧


9.遇到的代碼模塊異常或結對困難及解決方法

  • 遇到困難:

    ​ 胡康不會python,我不會java,你們都痛恨C++,而後他說去自學python,後面由於忽然冒出來個接入網考試,時間大大縮水。

    ​ 兩個算法白癡,都想作前端,後來得知前端能夠用網頁寫,而後胡康恰好會就輪到我這個弟弟寫後端

    ​ 因爲沒有經驗,代碼規範未作好,在將兩人代碼組合時出現了許多問題

  • 嘗試

    ​ 嘗試用python的pygame寫前端,後來發現只要python的tkinter就能夠完成,再後來發現python的PyQt挺好用,最後放棄了用python寫前端。

    ​ 瘋狂用PS設計圖片以知足前端的要求

  • 是否解決

    ​ 最終解決了雙方語言不通的困難,前端用網頁寫,後端用python寫,最後用什麼ajax整到一塊兒,雖然還沒整好

  • 收貨

    ​ 感受前端更好玩一點,直男審美是時候露一手了

    ​ 更加深刻的學習了python···


10.評價個人隊友

黃宇航真的很優秀!不論是第一次結對做業仍是第二次結對做業,他都是挑大頭,頗有責任感,細心,審美也不錯,此次能和他作隊友真的是賺到!缺點是什麼?宇航沒有缺點!


11.學習進度條

第N周 新增代碼(行) 累計代碼(行) 本週學習耗時(小時) 累計學習耗時(小時) 重要成長
··· ··· ··· ··· ··· ···
6 431 431 15 23 經過學習瞭解了python列表一些更有用的使用方法,固然還有pygame和tkinter的白給學習時間
7 210 641 13 36 學習了一些JavaScript和H5的知識,緣由大概就是計算機圖形學要考試了,順便學學前端,由於對前端感興趣哈哈
8 116 757 7 43 嘗試與前端對接,完成全部代碼工做,學習了網絡接口的知識
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息