课程设计记录(三)

1 版本回顾

1.1 Version1

黄金点游戏 Version1包括的功能有:

  • 采用简单控制台界面的方式实现玩家输入数字的基本功能
  • 游戏开始时,可以进行玩家人数和游戏局数的输入设置
  • 计算每轮游戏得分,以及统计游戏结束后的总得分
  • 对命令行输出进行格式化,使得输出尽量对齐、美观

游戏运行结果如下:


Version1运行结果.png

1.2 Version 2.0

在第一个版本的基础上,我们决定继续为该游戏增添其余的功能,由此迭代开发形成黄金点游戏 Version2.0。由于该黄金点游戏规则的本身已经得到了实现,下一步应该把视角转移到方便玩家用户的需求上,以及游戏管理者的存储记录需求上。对于这两个总体方面的需求,我们讨论交流之后,首先筛选出了在目前阶段较为重要的需求,其中包括增加以下的功能:

  • 游戏玩家的输入UI界面(包括游戏玩家的姓名输入、数字输入)
  • 数字输入时对外不可见,增加游戏的可玩性与公平性
  • 建立数据库,存储每轮游戏的玩家输入的数字、得分

游戏运行结果如下:


Version2.0主界面

Version2.0后台输出

Version2.0数据库存储

1.3 Version 2.9

1.2中的Version2.0版本虽然添加了用户输入姓名以及数字的界面,但是程序内部设定游戏轮数与玩家数,终端输出结果,实际上没有完全实现UI界面和终端后台的分离,真实应用中,没有用户愿意和终端后台打交道。

Version2.9在Version2.0的基础上新添功能:

  • 将游戏轮数与参与游戏的人数做成UI,接受用户的输入
  • UI输出每一轮游戏的结果以及整局游戏的结果,即各位玩家的得分;最后输出赢家
  • 每次用户输入后,清空输入姓名与数字的文本框;每次用户输入数字不合理时,仅清空输入数字的文本框,优化用户体验
玩家数目输入框

游戏局数输入框

每一轮游戏得分

最终游戏得分

游戏赢家(考虑分数相同情况)

本版本作为PyQt UI版黄金点的最终版本,其实还有很多继续改进的空间,但是我们开发计划最终产品不以Qt UI的形式呈现,所以将不再更新。其实从Version2.0到2.9,有很多一点点改动优化的地方,但是博客里为了简化语言并且便于表达,所以划分成立两个大的版本。

1.4 Version2.9新增功能代码

import sys
from utils import *
from PyQt5.QtWidgets import *
from window import Ui_MainWindow
from connect_mysql import *


class Window(Ui_MainWindow, QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        self.setupUi(self)

        player, _ = QInputDialog.getInt(self, "玩家数", "请输入玩家数目:", 3, 1, 99, 1)
        round, _ = QInputDialog.getInt(self, "局数", "请输入游戏局数:", 2, 1, 99, 1)

        self.GOLDEN = 0.618  # 黄金分割点常数
        self.PLAYER, self.ROUND = player, round  # 玩家数 & 游戏轮数
        self.ADD, self.MINUS = self.PLAYER, -2  # 赢家加分 & 输家减分
        self.cnt_player, self.cnt_round = 0, 0  # 玩家计数器 & 游戏计数器
        self.dir_number, self.dir_grade = {}, {}  # 数字字典 & 成绩字典
        self.dir_all_grade = {}
        self.flag = 0

    def clickOkButton(self):
        _name, _number = self.edit_name.text(), self.edit_number.text()
        print("-->用户输入:", "【姓名】:", _name, "【数字】:", _number)
        # 输入数合法
        if isSatisfiedNum(_number):
            self.cnt_player += 1
            self.dir_number[_name] = float(_number)

            # 一轮游戏结束
            if self.cnt_player == self.PLAYER:
                self.cnt_round += 1
                # 每局数字存入数据库
                InsertData('Number', self.dir_number)

                self.calGrades()
                # 每局分数存入数据库
                InsertData('Grade', self.dir_grade)
                print("--------------------第%2d局游戏结果--------------------" % self.cnt_round)
                result = ""
                for item in self.dir_grade.items():
                    print("【%s\t%3d分】" % (item[0], item[1]))
                    result += "%s\t%3d分\n" % (item[0], item[1])
                print("--------------------第%2d局游戏结果--------------------" % self.cnt_round)

                QMessageBox.information(self, "第%d局得分" % self.cnt_round, result, QMessageBox.Yes | QMessageBox.No)

                self.cnt_player = 0  # 初始化
                self.dir_number, self.dir_grade = {}, {}  # 初始化

            # 整局游戏结束
            if self.cnt_round == self.ROUND:
                print("\n========================最终游戏结果========================")
                result, MAX, winners = "", 0, set()
                for item in self.dir_all_grade.items():
                    print("【%s\t%3d分】" % (item[0], item[1]))
                    result += "%s\t%3d分\n" % (item[0], item[1])
                    if item[1] >= MAX:
                        _winner, MAX = item[0], item[1]
                for item in self.dir_all_grade.items():
                    if item[1] == MAX:
                        winners.add(item[0])
                print("========================最终游戏结果========================")

                QMessageBox.information(self, "得分", result, QMessageBox.Yes | QMessageBox.No)
                QMessageBox.information(self, "赢家", "赢家是:"+str(winners), QMessageBox.Yes | QMessageBox.No)

                self.close()

            self.edit_name.clear()
            self.edit_number.clear()

        # 输入数不合法
        else:
            QMessageBox.warning(self, "警告", "请输入0~100的有理数")
            self.edit_number.clear()

    def clickExitButton(self):
        self.close()

    def calGrades(self):
        _list_name, _list_number = [], []
        for item in self.dir_number.items():
            _list_name.append(item[0])
            _list_number.append(item[1])

        GP = sum(_list_number) / self.PLAYER * self.GOLDEN  # get golden point
        bias = np.abs(np.array(_list_number) - GP)
        _list_grade = np.zeros(self.PLAYER)
        _list_grade[bias == np.max(bias)] = -2
        _list_grade[bias == np.min(bias)] = self.PLAYER

        # 每局分数字典
        for i in range(self.PLAYER):
            self.dir_grade[_list_name[i]] = _list_grade[i]

        # 总分累计字典
        if self.flag == 0:  # dir_all_grade字典为空
            print("-->字典为空")
            for i in range(self.PLAYER):
                self.dir_all_grade[_list_name[i]] = _list_grade[i]
            self.flag = 1
        else:  # dir_all_grade字典非空
            print("-->字典非空")
            for i in range(self.PLAYER):
                self.dir_all_grade[_list_name[i]] += _list_grade[i]


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

游戏对局结果展示窗口用QMessage实现

QMessageBox.information(self, "第%d局得分" % self.cnt_round, result, QMessageBox.Yes | QMessageBox.No)
QMessageBox.information(self, "得分", result, QMessageBox.Yes | QMessageBox.No)
QMessageBox.information(self, "赢家", "赢家是:"+str(winners), QMessageBox.Yes | QMessageBox.No)

QmessageBox是一种通用的弹出式对话框,用于显示消息,允许用户通过单击不同的标准按钮对消息进行反馈,每个标准按钮有一个预定义的文本,角色和十六进制数。
QMessageBox类提供了许多常用的弹出式对话框,如提示。警告,错误,询问,关于,等会话框,这些不同类型的QMessageBox对话框只是显示的图标不同,其他的功能是一样的。
QMessage.information(QWdiget parent,title,text,buttons,defaultButton) 弹出消息对话框,各参数解释如下:

  • parent:指定的父窗口控件
  • title:对话框标题
  • text:对话框文本
  • buttons:多个标准按钮,默认为ok按钮
  • defaultButton:默认选中的标准按钮,默认选中第一个标准按钮

清空数据输入框用文本框对象的clear方法实现

self.edit_name.clear()
self.edit_number.clear()

游戏参数设定采用QInputDialog实现

player, _ = QInputDialog.getInt(self, "玩家数", "请输入玩家数目:", 3, 1, 99, 1)
round, _ = QInputDialog.getInt(self, "局数", "请输入游戏局数:", 2, 1, 99, 1)

PyQt的QInputDialog类继承自QDialog,提供了一种简单方面的对话框来获得用户的单个输入信息。QInputDialog控件是一个标准对话框,有一个文本框和两个按钮(ok和cancel)组成,当用户单击ok或enter键后,在父窗口可以收集通过QInputDialog控件输入的信息,QInputDialog控件是QDialog标准对话框的一部分,在QInpuTDialog控件中可以输入数字,字符串或列表中的选项,标签用于提示必要的信息它提供了4种数据类型的输入:

  1. 字符串型(方法QInputDialog.getText
  2. Int类型数据(方法QInputDialog.getInt
  3. double类型数据(方法QInputDialog.getDouble
  4. 下拉列表框的条目(方法QInputDialog.getItem

我们这里采用方法getInt,设定默认的玩家数和对局数是3,2,并且设定其最小值均为1,最大值均为99;同时,QInputDialog.getInt窗口提供了一个增加/减少按钮,设定该按钮对于整数的增加/减少步长为1。

2 新版设计:Version 3.0

2.1 设计思路

在新版本Version3中,我们打算使用微信小程序的形式实现黄金点小游戏。因为,我们考虑到了游戏本身不复杂,我们游戏的亮点能在哪?最后,我们团队成员一致认为本软件开发需要做到“快、准、狠”三点:

  • :游戏能够便捷的启动、结束,不让用户在等待中耗费激情。这就要求程序是B/S结构,即网页端的应用或者微信小程序、支付宝小程序等以WEB浏览器作为客户端最主要的应用软件。这种模式统一了客户端,将系统功能实现的核心部分集中到服务器上。
  • :准确获取用户的信息并存储游戏相关的数据。我们最开始还打算自己建立数据库,设置用户登录注册的界面,自行管理用户的账户密码等信息。然而,我们团队成员尚处于学习阶段,万一数据库管理不当丢失或者被黑客攻击,这不仅对我们游戏的运行造成影响,更重要的是暴露的用户的隐私。于是,后来我们想到借助腾讯微信大团队开发的稳定安全的平台,用户通过微信账号登录游戏,我们用过微信小程序API接口存取用户的个人信息与游戏信息,整个数据库虽然由我们设计,但是存储保护都是依托微信平台,安全、准确、放心。
  • :所谓“狠”,也许有点抽象,我们想的就是要让用户喜欢上我们的游戏,舍不得离开,甚至愿意花费一点点费用在游戏中。但是我们只是一个简简单单甚至很枯燥的黄金点小游戏,怎么才能努力做到这点呢?这让我想到了几年前在微信平台爆火的“打飞机”和“跳一跳”等无比简单的小游戏,这些游戏都是无比简单但是当时却特别火爆,为什么呢?很显然是微信的“好友效应”,即我的好友玩了,他们还邀请我挑战我,我为了比他们有更高的分数或者与他们互动,于是积极地进入了游戏,于是这样形成了一个游戏玩家的良性循环,大家都有越来越高的热情来玩。简单来说,某种意义上,游戏越简单,潜在玩家基数越大;再加上类似微信平台的好友效应,玩家数量就有可能远远超出预期。

2.2 初次需求分析要点

对于该微信小程序的开发,我们根据之前的设计思路,从一些受欢迎程度高的小程序入手,启发思路,大致形成了以下的新的需求。

  1. 微信小程序用户登录后发起游戏,然后可以设定开始的游戏局数和人数。
  2. 游戏普通玩家扫描二维码就可以进行登录自己的微信号,然后输入自己的数字进行比赛。
  3. 在玩家个人界面,可以查看个人曾经输入的数字,相当于一个历史记录的展示,以及每一局黄金点的曲线变动折线图,这样便可以看到本游戏中数学博弈原理的变化趋势。

2.3 程序实现流程

对于上面的简单需求分析,我们接着进行进一步的研究和继续细化,一步一步确定该微信小程序的设计流程,如下图所示。但要明确的是,这只是我们的总体需求方向,不可避免我们在后续的开发迭代过程中会遇到新的变更和新的需求,这些都会影响和改变我们目前的初步设计。


微信小程序实现流程

3 设计小结

在两周的设计过程中,实践上,我们主要进行了之前游戏开发版本的迭代开发,对于Qt实现的UI界面进行了改进和优化,但是由于该版本不适合新的用户需求和设计,经过严密的分析和思考之后,我们决定采用微信小程序为主要形式进行继续地迭代开发。
同时,我们也从理论上再次回顾结合了软件工程理论课程上的知识,其中包括需求工程、设计工程、各种UML图的表示与绘制等等,在这里仅展示出我们对于活动图这一概念的着重理解和实践体会。

活动图,用于表示系统中各种活动的次序,它的应用非常广泛,即可用来描述用例的工作流程,也可用来描述类中某个方法的操作行为。常用于表示业务流程,对系统功能建模,强调对象之间的控制流。活动图是由状态图变化而来的,活动图依据对象状态的变化来捕获动作。活动图中一个活动结束后将立即进入下一个活动,状态图中状态的变迁可能需要事件的触发。主要用于系统功能建模。

需求设计过程中,我们知道,类模型体现了系统的静态结构,用例模型则从用户的角度对系统的动态行为进行了宏观建模,并通过交互模型将对象与消息有机地结合在一起。但有些时候,我们还需要更好地表示行为的细节,这就可以借助于活动图和状态图来实现。

而另一方面,普通活动图虽然明确地说明了整个控制流的过程,但是却没有说明每个活动是由谁做的。对应到编程而言,就是没有明确地表示出每个活动是由什么类来负责的;对应到业务建模,就是没有明确地表示出机构中的哪一个部门负责实施什么操作。为了在简单活动图的基础上,有效地表示各个活动由谁负责的信息,还可以通过泳道图来实现。但这些图都各有其优势和发挥作用,下面是我们对于两者的比较理解。

  • 活动图VS传统流程图:程序流程图明确地指定了每个活动的先后顺序,而活动图仅描述了活动和必要的工作顺序,这是两者的根本区别。另外,流程图限于顺序进程,而活动图支持并发进程。
  • 活动图VS状态图:状态图注重于由事件驱动的系统的变化状态;活动图注重于从活动到活动的控制流。活动图是状态机的一种特殊情况,其中全部或大多数状态是活动状态,并且全部或大多数转换时通过源状态中活动的完成来触发的。活动图适应状态机的全部特征。活动图和状态图在对一个对象的生命周期建模时都是有用的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容