第二次结对编程作业


1.网络链接

本憨憨的链接 And 仓库地址

另一个憨憨的链接 And 仓库地址


2.分工

前端编写与前后端交接:胡康

后端算法测试与博文编写:黄宇航


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编写

//登录
<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库

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张牌升序排序

    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:遍历完后墩判断是否继续再遍历中墩,

  • 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 尝试与前端对接,完成所有代码工作,学习了网络接口的知识
posted @ 2019-10-30 15:04  CerberusX  阅读(278)  评论(1编辑  收藏  举报