第二次结对编程作业

一、

(本作业博客的链接)

王镇隆 (博文链接)(Github项目地址)fork了珊珊的仓库
陈珊珊 (博文链接)(Github项目地址) 主仓库
吴珂雨 (博文链接)(Github项目地址) fork了珊珊的仓库

二、给出具体分工

陈珊珊:主要负责前端代码,写出主要框架,对算法进行性能分析,提供部分文档资料。
吴珂雨:主要负责前端代码,美化页面并且进行定位和交互处理,文档撰写。
王镇隆:主要负责后端代码,进行单元测试,以及接口的使用,提供部分文档资料。

  我们在分析和确定需求后,开始准备实现各自负责的部分,并学习需要使用到的技术。接着逐步完成各个功能,进行性能分析和单元测试,并编写博客。

三、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 75
· Estimate · 估计这个任务需要多少时间 60 75
Development 开发 15740 19620
· Analysis · 需求分析 (包括学习新技术) 7200 7920
· Design Spec · 生成设计文档 150 180
· Design Review · 设计复审 50 75
· Coding Standard · 代码规范 (为目前的开发制定或选择合适的规范) 120 150
· Design · 具体设计 480 600
· Coding · 具体编码 7200 10080
· Code Review · 代码复审 240 255
· Test · 测试(自我测试,修改代码,提交修改) 300 360
Reporting 报告 150 130
· Test Report · 测试报告 60 45
· Size Measurement · 计算工作量 40 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出改进计划 50 55
  · 合计 15950 19825

四、解题思路描述与设计实现说明

(1)网络接口的使用

  因为我们的前端是使用js写的,所以接口是js的类型,因为有样例所以比较简单。
  具体说明在以下代码中:

        var data = JSON.stringify({
		"id":id_1,
		"card":[qans1,qans2,qans3]
		});
	//data根据是否需要提交数据,如果不用则改为null
        var xhr = new XMLHttpRequest();

        xhr.addEventListener("readystatechange", function () {
        if (this.readyState === this.DONE) {
			console.log(this.responseText);
			var JsonObject = JSON.parse(this.responseText);
				//在这里加入根据返回的值进行的行为        
        }
        });

        xhr.open("POST", "https://api.shisanshui.rtxux.xyz/game/submit");//选择POST或者GET
        xhr.setRequestHeader("x-auth-token",token);	//token也是根据需要,决定这句要不要
        xhr.setRequestHeader("content-type", "application/json");//json也是根据需要决定这句要不要
        xhr.send(data);

(2)代码组织与内部实现设计(类图)

(3)说明算法的关键与关键实现部分流程图

算法关键:

Step.1将牌型处理成数组,进行排列组合,使得所有的牌型都计算出来。
Step.2在保留一个最大的得分值和得分牌型,之后遍历所有的组合,计算每一种牌型得分,如果这种牌型符合条件,且得分大于之前的最大得分,则更新最大的得分值和得分牌型。
Step.3便利完所有牌型之后,最大的得分牌型就是我们要出的牌型。

流程图:

五、关键代码解释

(1)判断出取十三张牌并分成三墩的所有可能情况:

用used数组来判断这十三张牌分别是属于哪一墩的,1表示属于为前墩,0表示属于中墩,2表示属于后墩。

def find2(pos, n):
    if pos == 13 and n == 0:
        score()
        return True
    if pos == 13:
        return False
    while used[pos] == 1:
        pos += 1
        if pos == 13:
            return False
    if n > 0:
        used[pos] = 2
        find2(pos + 1, n - 1)
    used[pos] = 0
    find2(pos + 1, n)
def find1(pos, n):
    global ans
    if pos == 13 and n == 0:
        find2(0, 5)
        return True
    if pos == 13:
        return False
    if n > 0:
        used[pos] = 1
        find1(pos + 1, n - 1)
    used[pos] = 0
    find1(pos + 1, n)

(2)获取一墩牌的分数的算法:

score1()用于判断前墩的牌型和获取前墩的分数:
用数组a[]表示牌上的数字
if a[0]==a[1]and a[1]==a[2]表示三张牌一样大
if a[0]==a[1] or a[1]==a[2]表示对子
剩下的情况就是乌龙

def score1(a, b):
    if a[0]==a[1]and a[1]==a[2]:
        return 3+(a[0]+a[1]+a[2])/10000
    if a[0]==a[1] or a[1]==a[2]:
        return 2+(a[0]+a[1]+a[2])/10000
    return 1+(a[0]+a[1]+a[2])/10000

score2()用于判断中墩和后墩的牌型和获取中墩和后墩的分数:
用数组a[]表示牌上的数字,数组b[]表示花色
用if语句和node标记判断是哪一种牌型,具体在以下代码中注释

def socre2(a,b):
    ex = 0
    for i in a:
        ex += i
    ex = ex / 10000
    node = 1
    for i in range(4)://判断是否为顺子,花色是否相等
        if a[i]+1 != a[i+1] or b[i]!=b[i+1]:
            node = 0
            break
    if node == 1://表示该墩牌型为同花
        return 15+ex
    node = 1
    for i in range(4)://判断是否为顺子
        if a[i]+1 != a[i+1]:
            node = 0
            break
    if node == 1://表示该墩牌型为同花顺
        return 13+ex
    node = 1
    for i in range(3)://判断是否有四张牌大小一致
        if a[i] != a[i+1]:
            node = 0
            break
    if node == 1://表示该墩牌型为炸弹
        return 10+ex
    node = 1
    for i in range(1,4)://判断是否有三张牌大小一致
        if a[i] != a[i + 1]:
            node = 0
            break
    if node == 1:
        return 10+ex
    if a[1]==a[2] and a[1] == a[0] and a[3]==a[4]://表示该墩牌型为葫芦
        return 8+ex
    if a[4] == a[3] and a[3] == a[2] and a[1] == a[0]://表示该墩牌型为葫芦
        return 8+ex
    node = 1
    for i in range(4):
        if b[i] != b[i + 1]://判断花色是否相等
            node = 0
            break
    if node == 1:
        return 6+ex
    if a[0]+1==a[1]and a[1]+1==a[2]and a[2]+1==a[3]and a[3]+1==a[4]://表示该墩牌型为顺子
        return 5+ex
    if (a[0]==a[1]and a[1]==a[2])or (a[2]==a[1]and a[2]==a[3]) or (a[2]==a[3]and a[3]==a[4])://表示该墩牌型为三条
        return 4+ex
    node =0
    for i in range(4):
        if a[i]==a[i+1]://表示该墩牌型为二对子
            node +=1
    if node !=0:
        return node+ex//表示该墩牌型为对子
    return 1+ex//表示该墩牌型为乌龙

六、性能分析与改进

(1)改进思路

改进前的思路:

  先进行判断,判断是否为特殊牌型,之后再枚举所有牌型,得出最大出牌方案。

存在问题:

  然后发现有些牌型比如三顺子之类的本来就是最大的牌型,在最开始进行一个特殊牌型的判断其实是没有必要的,会造更多的消耗。

改进后的思路:

  直接枚举所有牌型得出的方案就会是最大牌型,减少了消耗。

(2)性能分析图



(3)消耗最大的函数

由以上性能分析图可以得出消耗最大的函数为score()

def score():
    num1=0;num2=0;num0=0
    for i in range(13):
        if used[i]==0:
            num0+=1
        elif used[i]==1:
            num1+=1
        else:
            num2+=1
    global maxnc,qans2,qans1,qans3,ttt
    a = [];b = [];c=[];d=[];e=[];f=[]
    for i in range(13):
        if used[i] == 1:
            a.append(pai[i])
            b.append(paise[i])
    ans1 = score1(a,b)

    for i in range(13):
        if used[i] == 0:
            c.append (pai[i])
            d.append(paise[i])
    for i in range(4):
        for j in range(4-i):
            if c[j] > c[j+1]:
                c[j],c[j+1] = c[j+1],c[j]
                d[j],d[j+1] = d[j+1],d[j]
    ans2 = socre2(c,d)

    for i in range(13):
        if used[i] == 2:
            e.append(pai[i])
            f.append(paise[i])
    for i in range(4):
        for j in range(4-i):
            if e[j] > e[j+1]:
                e[j],e[j+1] = e[j+1],e[j]
                f[j],f[j+1] = f[j+1],f[j]

    ans3 = socre2(e,f)
    if ans1<=ans2 and ans2<=ans3:
        if ans1+ans2+ans3 > maxnc:
            maxnc = ans1+ans2+ans3
            qans1 = lol(3,a,b)
            qans2 = lol(5,c,d)
            qans3 = lol(5,e,f)

七、单元测试

(1)测试函数说明:

单元测试 测试项 被测试代码 功能
test_lol 分别测试3张牌和5张牌的情况 main.py 将牌型转换成字符串
test_score2 测试5张牌的情况 main.py 将牌型转换成分数

(2)单元测试代码:

进行了十组单元测试,单元测试代码如下:

import unittest
from run_main import *

class TestRun_main(unittest.TestCase): 
    def test_lol(self):
        self.assertEqual("&J*Q$K", lol(3, [11, 12, 13], [0,3,1])) 
        self.assertEqual("&10$A#A*A&A", lol(5, [10, 14, 14, 14, 14], [0, 1, 2, 3, 0]))
        self.assertEqual("&10&J&Q&K&A", lol(5, [10, 11, 12, 13, 14], [0, 0, 0, 0, 0]))
        self.assertEqual("&10$J&Q*K#A", lol(5, [10, 11, 12, 13, 14], [0, 1, 0, 3, 2]))
        self.assertEqual("&7$7&8*8#10", lol(5, [7, 7, 8, 8, 10], [0, 1, 0, 3, 2]))

    def test_score2(self):
        self.assertEqual(12.0025, socre2([3, 4, 5, 6, 7], [0, 0, 0, 0, 0]))
        self.assertEqual(10.0019, socre2([3, 3, 3, 3, 7], [0, 1, 2, 3, 0]))
        self.assertEqual(6.0027, socre2([2, 3, 4, 8, 10], [0, 0, 0, 0, 0]))
        self.assertEqual(5.002, socre2([2, 3, 4, 5, 6], [0, 2, 0, 2, 1]))
        self.assertEqual(4.0029, socre2([5, 5, 5, 6, 8], [0, 1, 3, 0, 2]))


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

(3)测试结果:

(4)构造测试数据的思路:

  test_lol部分:考虑到有3张牌的情况和5张牌的情况,5张牌的情况更多,所以5张牌的测试数据更多,3张牌的数据采用了乌龙,5张牌的数据采用了二对子、顺子、铁支、同花顺四种情况,这样乌龙和具有牌型的情况都能够被测试到。
  test_score2部分:函数本身是用于进行中墩和后墩的分数转换,因为5张牌的情况更多,所以用了这个函数,数据采用了同花顺、铁支、乌龙、顺子、三条五种情况,这样乌龙和具有牌型的情况都能够被测试到。

八、Github的代码签入记录

九、遇到的代码模块异常或结对困难及解决方法

(1)牌型处理

问题描述:

  在进行牌型处理的时候,由于十三张牌需要用字符串来表示,不知道要如何用代码去表示和处理扑克牌及其花色,如果这个问题不能得到解决,算法将完全不能得到推进。

做过哪些尝试:

首先想到用字符来表示,后面改成用二维数组表示某种花色、某种牌拥有,但效果都不够理想,都有一部分优点,但也有缺点。
  

是否解决:

  是,解决了。最后使用了最简单的方法,利用两个数组,第一个数组用来表示牌的数字,第二个数组用来表示牌的花色。

有何收获:

  遇上困难或者瓶颈的时候,要和队友多沟通,自己一个人一直想如何去解决难题,很容易钻牛角尖,这时候,队友以旁人的视角来看待问题,可能更容易想出好的解决方法。


(2)制作弹窗

问题描述:

  和队友商量之后决定把一些做好的页面比如排行榜、往期对战结果等页面改成弹窗,会达到更好的效果。但是发现事情没有想象中那么简单,由于我们设计的往期对战结果详情是要在往期对战结果记录页面点击战局ID按钮才能够打开,但是往期对战结果已经是一个弹窗了,也就是说要在一个弹窗里显示另外一个弹窗,因为这次作业才学了前端,对弹窗的制作不熟悉,不知道代码应该放在该按钮的前面还是后面。

做过哪些尝试:

  首先尝试将代码放在了div按钮的前面,结果发现不行,会改变原有弹窗的样式。接着尝试放在了div按钮的中间,但是显示出来的内容不是在弹窗中。

是否解决:

  是,解决了。最后选择放在了div按钮的后面。

有何收获:

  通过这次作业,学会了弹窗的使用,前端的很多知识仍需学习。


(3)弹窗重合

问题描述:

  在弹框中再制作一个弹框,但是发现会和原来的弹窗重合,所以想要设计点击弹窗中的一个按钮,跳出一个弹窗,并将原有的弹窗遮盖住。

做过哪些尝试:

  尝试调整弹窗背景的颜色,透明度,但是都没有成功。

是否解决:

  是,解决了。制作了一个遮罩,调整其中中一个变量z-index的大小,设置成9999的时候可以完全遮盖住原来的弹窗,如果是设置成-1,则成透明。

有何收获:

  学会了如何使用遮盖,使得页面的效果更好,仍需多多了解一些前端的属性,让自己能够将前端做得更好。


十、评价你的队友

总体评价:
  这是一次非常愉快的合作。
  首先在作业布置下来以后我们会开一次会进行分工,沟通使用什么技术去实现自己负责的一部分内容,在实现各部分功能的过程中我们也会在群里进行遇到的问题的反馈一起探讨。
  并且分工明确,大家各司其职,互相配合,互相帮助。
  最重要的是队友都非常的随和靠谱,从不会说“不”,都会尽力完成自己负责的部分,并且为队友提供解决困难的想法。
  DDL前和队友一起在活动室熬夜的感觉挺有趣的,切实体会到了第一次博客作业中提到的“一起熬夜,一起吐槽,一起说说笑笑,一起打代码”的感觉,学到和很多东西,也收获了美好的回忆。

两个人都差不多就一起写了哈哈哈        好吧其实是,不会写文案的卑微

陈珊珊,吴珂雨:

值得学习的地方:
  两个女生的审美果然不是我能比的,设计出UI的时候我都惊了,这要是让我做,我很可能就纯文字界面了,还好还好,看起来我们做的就高大上很多了,两个人文案写得的很好,博客内容我都不需要担心,完全放心交给他们,太省心了,打call,两人也非常认真,其实一开始的时候,都不认识她们,很担心和那种偷懒,什么都不做的人一起合作,甚至做好了什么都我一个人做的打算,但是没想到两个人非常认真,很高兴和两个队友组队。

需要改进的地方:
  在代码方面的学习要加强,有一些接口,什么的,应该是很简单的,不难学,希望以后能在这方面有所提高,可以做的事情就更多了。

PS:(最后一些想法吧)
   这次其实作业可以提早一些完成的,但是因为我国庆中间生病,浪费了两三天,导致我自己这边的算法的进度慢了,还有一开始因为对这些不了解,也不清楚到底怎么做才最好,于是在最开始想好一个方案之后,我们又进行了修改,其实到最后发现,我们做出来的东西,其实和最开始的已经变得不一样了。但是,最后做出来的,也比我最初预想的好多了,在真正完工的时候非常兴奋,通过自己和队友的努力,做出了一个在之前从来没有想过我能做出来的东西,成就感爆棚。

十一、学习进度条

第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 100 100 10 10 学习代码查重的相关知识
2 650 750 29 39 成功将代码查重解决,虽然还没最终批量化处理
3 150 900 20 59 接触python的gui
4 900 1800 10 69 这周只是在写算法核心其实么很大进步
5 400 2200 10 79 完成了出牌算法以及学习接口使用
6 1500 3700 20 99 边学边用JS,HTML,CSS相关知识,补充接口和写代码

十二、UI部分展示

(B站链接)

posted @ 2019-10-15 22:26  一无所知小白龙  阅读(433)  评论(3编辑  收藏  举报