課程設(shè)計(jì)記錄(三)

1 版本回顧

1.1 Version1

黃金點(diǎn)游戲 Version1包括的功能有:

  • 采用簡單控制臺界面的方式實(shí)現(xiàn)玩家輸入數(shù)字的基本功能
  • 游戲開始時,可以進(jìn)行玩家人數(shù)和游戲局?jǐn)?shù)的輸入設(shè)置
  • 計(jì)算每輪游戲得分,以及統(tǒng)計(jì)游戲結(jié)束后的總得分
  • 對命令行輸出進(jìn)行格式化,使得輸出盡量對齊、美觀

游戲運(yùn)行結(jié)果如下:


Version1運(yùn)行結(jié)果.png

1.2 Version 2.0

在第一個版本的基礎(chǔ)上,我們決定繼續(xù)為該游戲增添其余的功能,由此迭代開發(fā)形成黃金點(diǎn)游戲 Version2.0。由于該黃金點(diǎn)游戲規(guī)則的本身已經(jīng)得到了實(shí)現(xiàn),下一步應(yīng)該把視角轉(zhuǎn)移到方便玩家用戶的需求上,以及游戲管理者的存儲記錄需求上。對于這兩個總體方面的需求,我們討論交流之后,首先篩選出了在目前階段較為重要的需求,其中包括增加以下的功能:

  • 游戲玩家的輸入U(xiǎn)I界面(包括游戲玩家的姓名輸入、數(shù)字輸入)
  • 數(shù)字輸入時對外不可見,增加游戲的可玩性與公平性
  • 建立數(shù)據(jù)庫,存儲每輪游戲的玩家輸入的數(shù)字、得分

游戲運(yùn)行結(jié)果如下:


Version2.0主界面

Version2.0后臺輸出

Version2.0數(shù)據(jù)庫存儲

1.3 Version 2.9

1.2中的Version2.0版本雖然添加了用戶輸入姓名以及數(shù)字的界面,但是程序內(nèi)部設(shè)定游戲輪數(shù)與玩家數(shù),終端輸出結(jié)果,實(shí)際上沒有完全實(shí)現(xiàn)UI界面和終端后臺的分離,真實(shí)應(yīng)用中,沒有用戶愿意和終端后臺打交道。

Version2.9在Version2.0的基礎(chǔ)上新添功能:

  • 將游戲輪數(shù)與參與游戲的人數(shù)做成UI,接受用戶的輸入
  • UI輸出每一輪游戲的結(jié)果以及整局游戲的結(jié)果,即各位玩家的得分;最后輸出贏家
  • 每次用戶輸入后,清空輸入姓名與數(shù)字的文本框;每次用戶輸入數(shù)字不合理時,僅清空輸入數(shù)字的文本框,優(yōu)化用戶體驗(yàn)
玩家數(shù)目輸入框

游戲局?jǐn)?shù)輸入框

每一輪游戲得分

最終游戲得分

游戲贏家(考慮分?jǐn)?shù)相同情況)

本版本作為PyQt UI版黃金點(diǎn)的最終版本,其實(shí)還有很多繼續(xù)改進(jìn)的空間,但是我們開發(fā)計(jì)劃最終產(chǎn)品不以Qt UI的形式呈現(xiàn),所以將不再更新。其實(shí)從Version2.0到2.9,有很多一點(diǎn)點(diǎn)改動優(yōu)化的地方,但是博客里為了簡化語言并且便于表達(dá),所以劃分成立兩個大的版本。

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, "玩家數(shù)", "請輸入玩家數(shù)目:", 3, 1, 99, 1)
        round, _ = QInputDialog.getInt(self, "局?jǐn)?shù)", "請輸入游戲局?jǐn)?shù):", 2, 1, 99, 1)

        self.GOLDEN = 0.618  # 黃金分割點(diǎn)常數(shù)
        self.PLAYER, self.ROUND = player, round  # 玩家數(shù) & 游戲輪數(shù)
        self.ADD, self.MINUS = self.PLAYER, -2  # 贏家加分 & 輸家減分
        self.cnt_player, self.cnt_round = 0, 0  # 玩家計(jì)數(shù)器 & 游戲計(jì)數(shù)器
        self.dir_number, self.dir_grade = {}, {}  # 數(shù)字字典 & 成績字典
        self.dir_all_grade = {}
        self.flag = 0

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

            # 一輪游戲結(jié)束
            if self.cnt_player == self.PLAYER:
                self.cnt_round += 1
                # 每局?jǐn)?shù)字存入數(shù)據(jù)庫
                InsertData('Number', self.dir_number)

                self.calGrades()
                # 每局分?jǐn)?shù)存入數(shù)據(jù)庫
                InsertData('Grade', self.dir_grade)
                print("--------------------第%2d局游戲結(jié)果--------------------" % 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局游戲結(jié)果--------------------" % 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 = {}, {}  # 初始化

            # 整局游戲結(jié)束
            if self.cnt_round == self.ROUND:
                print("\n========================最終游戲結(jié)果========================")
                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("========================最終游戲結(jié)果========================")

                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()

        # 輸入數(shù)不合法
        else:
            QMessageBox.warning(self, "警告", "請輸入0~100的有理數(shù)")
            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

        # 每局分?jǐn)?shù)字典
        for i in range(self.PLAYER):
            self.dir_grade[_list_name[i]] = _list_grade[i]

        # 總分累計(jì)字典
        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_())

游戲?qū)纸Y(jié)果展示窗口用QMessage實(shí)現(xiàn)

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是一種通用的彈出式對話框,用于顯示消息,允許用戶通過單擊不同的標(biāo)準(zhǔn)按鈕對消息進(jìn)行反饋,每個標(biāo)準(zhǔn)按鈕有一個預(yù)定義的文本,角色和十六進(jìn)制數(shù)。
QMessageBox類提供了許多常用的彈出式對話框,如提示。警告,錯誤,詢問,關(guān)于,等會話框,這些不同類型的QMessageBox對話框只是顯示的圖標(biāo)不同,其他的功能是一樣的。
QMessage.information(QWdiget parent,title,text,buttons,defaultButton) 彈出消息對話框,各參數(shù)解釋如下:

  • parent:指定的父窗口控件
  • title:對話框標(biāo)題
  • text:對話框文本
  • buttons:多個標(biāo)準(zhǔn)按鈕,默認(rèn)為ok按鈕
  • defaultButton:默認(rèn)選中的標(biāo)準(zhǔn)按鈕,默認(rèn)選中第一個標(biāo)準(zhǔn)按鈕

清空數(shù)據(jù)輸入框用文本框對象的clear方法實(shí)現(xiàn)

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

游戲參數(shù)設(shè)定采用QInputDialog實(shí)現(xiàn)

player, _ = QInputDialog.getInt(self, "玩家數(shù)", "請輸入玩家數(shù)目:", 3, 1, 99, 1)
round, _ = QInputDialog.getInt(self, "局?jǐn)?shù)", "請輸入游戲局?jǐn)?shù):", 2, 1, 99, 1)

PyQt的QInputDialog類繼承自QDialog,提供了一種簡單方面的對話框來獲得用戶的單個輸入信息。QInputDialog控件是一個標(biāo)準(zhǔn)對話框,有一個文本框和兩個按鈕(ok和cancel)組成,當(dāng)用戶單擊ok或enter鍵后,在父窗口可以收集通過QInputDialog控件輸入的信息,QInputDialog控件是QDialog標(biāo)準(zhǔn)對話框的一部分,在QInpuTDialog控件中可以輸入數(shù)字,字符串或列表中的選項(xiàng),標(biāo)簽用于提示必要的信息它提供了4種數(shù)據(jù)類型的輸入:

  1. 字符串型(方法QInputDialog.getText
  2. Int類型數(shù)據(jù)(方法QInputDialog.getInt
  3. double類型數(shù)據(jù)(方法QInputDialog.getDouble
  4. 下拉列表框的條目(方法QInputDialog.getItem

我們這里采用方法getInt,設(shè)定默認(rèn)的玩家數(shù)和對局?jǐn)?shù)是3,2,并且設(shè)定其最小值均為1,最大值均為99;同時,QInputDialog.getInt窗口提供了一個增加/減少按鈕,設(shè)定該按鈕對于整數(shù)的增加/減少步長為1。

2 新版設(shè)計(jì):Version 3.0

2.1 設(shè)計(jì)思路

在新版本Version3中,我們打算使用微信小程序的形式實(shí)現(xiàn)黃金點(diǎn)小游戲。因?yàn)?,我們考慮到了游戲本身不復(fù)雜,我們游戲的亮點(diǎn)能在哪?最后,我們團(tuán)隊(duì)成員一致認(rèn)為本軟件開發(fā)需要做到“快、準(zhǔn)、狠”三點(diǎn):

  • :游戲能夠便捷的啟動、結(jié)束,不讓用戶在等待中耗費(fèi)激情。這就要求程序是B/S結(jié)構(gòu),即網(wǎng)頁端的應(yīng)用或者微信小程序、支付寶小程序等以WEB瀏覽器作為客戶端最主要的應(yīng)用軟件。這種模式統(tǒng)一了客戶端,將系統(tǒng)功能實(shí)現(xiàn)的核心部分集中到服務(wù)器上。
  • 準(zhǔn):準(zhǔn)確獲取用戶的信息并存儲游戲相關(guān)的數(shù)據(jù)。我們最開始還打算自己建立數(shù)據(jù)庫,設(shè)置用戶登錄注冊的界面,自行管理用戶的賬戶密碼等信息。然而,我們團(tuán)隊(duì)成員尚處于學(xué)習(xí)階段,萬一數(shù)據(jù)庫管理不當(dāng)丟失或者被黑客攻擊,這不僅對我們游戲的運(yùn)行造成影響,更重要的是暴露的用戶的隱私。于是,后來我們想到借助騰訊微信大團(tuán)隊(duì)開發(fā)的穩(wěn)定安全的平臺,用戶通過微信賬號登錄游戲,我們用過微信小程序API接口存取用戶的個人信息與游戲信息,整個數(shù)據(jù)庫雖然由我們設(shè)計(jì),但是存儲保護(hù)都是依托微信平臺,安全、準(zhǔn)確、放心。
  • :所謂“狠”,也許有點(diǎn)抽象,我們想的就是要讓用戶喜歡上我們的游戲,舍不得離開,甚至愿意花費(fèi)一點(diǎn)點(diǎn)費(fèi)用在游戲中。但是我們只是一個簡簡單單甚至很枯燥的黃金點(diǎn)小游戲,怎么才能努力做到這點(diǎn)呢?這讓我想到了幾年前在微信平臺爆火的“打飛機(jī)”和“跳一跳”等無比簡單的小游戲,這些游戲都是無比簡單但是當(dāng)時卻特別火爆,為什么呢?很顯然是微信的“好友效應(yīng)”,即我的好友玩了,他們還邀請我挑戰(zhàn)我,我為了比他們有更高的分?jǐn)?shù)或者與他們互動,于是積極地進(jìn)入了游戲,于是這樣形成了一個游戲玩家的良性循環(huán),大家都有越來越高的熱情來玩。簡單來說,某種意義上,游戲越簡單,潛在玩家基數(shù)越大;再加上類似微信平臺的好友效應(yīng),玩家數(shù)量就有可能遠(yuǎn)遠(yuǎn)超出預(yù)期。

2.2 初次需求分析要點(diǎn)

對于該微信小程序的開發(fā),我們根據(jù)之前的設(shè)計(jì)思路,從一些受歡迎程度高的小程序入手,啟發(fā)思路,大致形成了以下的新的需求。

  1. 微信小程序用戶登錄后發(fā)起游戲,然后可以設(shè)定開始的游戲局?jǐn)?shù)和人數(shù)。
  2. 游戲普通玩家掃描二維碼就可以進(jìn)行登錄自己的微信號,然后輸入自己的數(shù)字進(jìn)行比賽。
  3. 在玩家個人界面,可以查看個人曾經(jīng)輸入的數(shù)字,相當(dāng)于一個歷史記錄的展示,以及每一局黃金點(diǎn)的曲線變動折線圖,這樣便可以看到本游戲中數(shù)學(xué)博弈原理的變化趨勢。

2.3 程序?qū)崿F(xiàn)流程

對于上面的簡單需求分析,我們接著進(jìn)行進(jìn)一步的研究和繼續(xù)細(xì)化,一步一步確定該微信小程序的設(shè)計(jì)流程,如下圖所示。但要明確的是,這只是我們的總體需求方向,不可避免我們在后續(xù)的開發(fā)迭代過程中會遇到新的變更和新的需求,這些都會影響和改變我們目前的初步設(shè)計(jì)。


微信小程序?qū)崿F(xiàn)流程

3 設(shè)計(jì)小結(jié)

在兩周的設(shè)計(jì)過程中,實(shí)踐上,我們主要進(jìn)行了之前游戲開發(fā)版本的迭代開發(fā),對于Qt實(shí)現(xiàn)的UI界面進(jìn)行了改進(jìn)和優(yōu)化,但是由于該版本不適合新的用戶需求和設(shè)計(jì),經(jīng)過嚴(yán)密的分析和思考之后,我們決定采用微信小程序?yàn)橹饕问竭M(jìn)行繼續(xù)地迭代開發(fā)。
同時,我們也從理論上再次回顧結(jié)合了軟件工程理論課程上的知識,其中包括需求工程、設(shè)計(jì)工程、各種UML圖的表示與繪制等等,在這里僅展示出我們對于活動圖這一概念的著重理解和實(shí)踐體會。

活動圖,用于表示系統(tǒng)中各種活動的次序,它的應(yīng)用非常廣泛,即可用來描述用例的工作流程,也可用來描述類中某個方法的操作行為。常用于表示業(yè)務(wù)流程,對系統(tǒng)功能建模,強(qiáng)調(diào)對象之間的控制流?;顒訄D是由狀態(tài)圖變化而來的,活動圖依據(jù)對象狀態(tài)的變化來捕獲動作?;顒訄D中一個活動結(jié)束后將立即進(jìn)入下一個活動,狀態(tài)圖中狀態(tài)的變遷可能需要事件的觸發(fā)。主要用于系統(tǒng)功能建模。

需求設(shè)計(jì)過程中,我們知道,類模型體現(xiàn)了系統(tǒng)的靜態(tài)結(jié)構(gòu),用例模型則從用戶的角度對系統(tǒng)的動態(tài)行為進(jìn)行了宏觀建模,并通過交互模型將對象與消息有機(jī)地結(jié)合在一起。但有些時候,我們還需要更好地表示行為的細(xì)節(jié),這就可以借助于活動圖和狀態(tài)圖來實(shí)現(xiàn)。

而另一方面,普通活動圖雖然明確地說明了整個控制流的過程,但是卻沒有說明每個活動是由誰做的。對應(yīng)到編程而言,就是沒有明確地表示出每個活動是由什么類來負(fù)責(zé)的;對應(yīng)到業(yè)務(wù)建模,就是沒有明確地表示出機(jī)構(gòu)中的哪一個部門負(fù)責(zé)實(shí)施什么操作。為了在簡單活動圖的基礎(chǔ)上,有效地表示各個活動由誰負(fù)責(zé)的信息,還可以通過泳道圖來實(shí)現(xiàn)。但這些圖都各有其優(yōu)勢和發(fā)揮作用,下面是我們對于兩者的比較理解。

  • 活動圖VS傳統(tǒng)流程圖:程序流程圖明確地指定了每個活動的先后順序,而活動圖僅描述了活動和必要的工作順序,這是兩者的根本區(qū)別。另外,流程圖限于順序進(jìn)程,而活動圖支持并發(fā)進(jìn)程。
  • 活動圖VS狀態(tài)圖:狀態(tài)圖注重于由事件驅(qū)動的系統(tǒng)的變化狀態(tài);活動圖注重于從活動到活動的控制流?;顒訄D是狀態(tài)機(jī)的一種特殊情況,其中全部或大多數(shù)狀態(tài)是活動狀態(tài),并且全部或大多數(shù)轉(zhuǎn)換時通過源狀態(tài)中活動的完成來觸發(fā)的。活動圖適應(yīng)狀態(tài)機(jī)的全部特征?;顒訄D和狀態(tài)圖在對一個對象的生命周期建模時都是有用的。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容