Flask開發成語接龍游戲,以後閒了手機玩玩自己寫的遊戲吧!#華爲雲·尋找黑馬程序員#

明天你好,我叫幹不倒

一直比較喜歡看《歡樂喜劇人》,其中有一期小沈龍的節目,名字叫明天你好,我叫幹不倒
父親的身體一直不好,因爲常年的酗酒抽菸,患腦梗好些年頭了,週末病情有些惡化,住院治療了。我是家中獨子,父母生病只能靠我來照看,但IT這行請假尤其長假簡直奢望,但母親一個人忙不過來,讓我有些不知如何是好。希望老爸的病情能有所好轉,早日康復吧。

英語單詞學習應用

週五發佈的文章Flask開發天氣查詢軟件,帶你掌握pipenv的使用與手機Termux下的部署發佈後,看到喜歡的人比較多。本來週末打算照着扇貝/極光單詞,寫一個英語單詞自測工具。但苦於單詞的分類和數據沒找到很細緻的文檔,所以這個內容往後延一下。
如果大家有英語單詞、音標、翻譯這種根據不同年級或等級分類的數據可以共享給我,開發完成大家一起學習背單詞。

成語接龍

6月高考的前一天,我發佈的一篇文章,決戰高考,幫你秒變成語之王,當時只是吧網站的成語爬下來保存到數據庫中,文末提到有機會了抽時間拿這些數據搞點事情,那麼今天就來搞事情吧。用3W+的成語數據庫,開發一款成語接龍的小遊戲。

接龍規則

成語接龍是中華民族傳統的文字遊戲。它不僅有着悠久的歷史和廣泛的社會基礎,同時還是體現我國文字、文化、文明的一個縮影,是老少皆宜的民間文化娛樂活動。
成語接龍規則多樣,大家一般熟知的是採用成語字頭與字尾相連不斷延伸的方法進行接龍;用四個字成語的最後一個字與下一句成語的第一個相同的字【音同就可以】,首尾相接不斷延伸,形成長龍。

實現分析
數據庫信息

先來看看我們的數據庫信息:
數據庫信息
數據庫表idiom分爲id,name,speak,meaning,example,hot 幾個字段,hot是當時搜索的網站熱詞排行,跟咱們沒有太大關係…主要是name和speak字段。

登陸排行

爲了能增強可玩性,我們在每次開場前,允許用戶隨機輸入一個名字。在挑戰過程中,針對用戶堅持的接龍次數進行排名。
挑戰者記錄
創建用戶排名表:

CREATE TABLE rank (
    name  VARCHAR (50) NOT NULL,
    round_num INT      NOT NULL
);

這裏爲什麼不設置主鍵呢?主要是排名取前5,用戶使用一個名字多次參戰,如果犀利的話包攬前5看這也很帥氣啊,清一色的都是我,想想都覺得自我滿足感爆棚。

遊戲界面

首先映入眼簾的是ROUND 1的接龍次數顯示,有沒有兒時拳皇對打的感覺…
爲了幫助大家在玩遊戲的同時能學習成語知識,也避免有些生僻字不認識,所以在界面中顯示了成語、注音、解釋和示例,當然示例不是每個成語都有,網站有啥我就展示啥唄…
遊戲界面

成語判斷

首先必須是四字的成語,用戶輸入非四字的成語會彈出警示欄,其次用戶填寫完成語後,會將成語在數據庫中進行檢索,如果是成語則進行接龍後返回電腦的匹配結果,進行第二輪的基隆,如果數據庫中無此成語會彈出遊戲結束的提示“挑戰結束:用戶輸入的成語是自己編的吧!”,返回登陸頁,並將用戶的挑戰結果入庫rank表進行排行。
這裏需要注意,成語接龍的收尾字可以不一樣但音必須相同,包括聲調哦!

拼音識別

數據庫中的成語我們存在拼音了,但用戶輸入的是漢字,我們如何進行拼音轉化呢?這裏需要使用到python的一個模塊pypinyin。針對這個模塊的使用,之前寫過一篇文章Python爲文檔批量注音(生僻字歌詞爲例),喜歡的朋友可以去看看。用法很簡單,但我們需要做到和數據庫中相對應纔行。

from pypinyin import pinyin
pinyin('脣槍舌劍')
# output:
[['chún'], ['qiāng'], ['shé'], ['jiàn']]
# 此處爲一個嵌套列表,我們需要轉化爲數據庫中的格式
'  '.join(map(lambda x: x[0], pinyin('脣槍舌劍')))
# output:
'chún  qiāng  shé  jiàn'
代碼編寫
Jinjia2模板

大家看到不管是用戶登錄還是遊戲界面,外框內容基本一致,基於這種場景使用Jinjia2的模板繼承是個很不錯的選擇:
layout.html主要負責大體框架及相關css和js的引入工作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1 ,user-scalable=no">
    <title>清風python</title>
    <link rel="icon" href="{{ url_for('static',filename='favicon.ico') }}">
    <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}">
    <link rel="stylesheet" href="{{ url_for('static',filename='css/main.css') }}">
    <script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
</head>
<body>

<div class="container container-small">
    <div class="content">
        <div class="header">
            成語接龍
        </div>
        <div class="block-info">
            {% block contents %}
            {% endblock %}
        </div>
    </div>
    <div class="footer">
        ©2019-歡迎關注我的公衆號:<a href="https://www.jianshu.com/u/d23fd5012bed">清風Python</a>
    </div>
</div>

</body>
</html>

login.html涉及到挑戰者排行和用戶名提交與頁面跳轉

{% extends "layout.html"%}

{% block contents %}
<form method="post">
    <div class="form-group has-success">
        <div class="input-group">
            <div class="input-group-addon">
                選手姓名:
            </div>
            <input id='name' name="name" class="form-control" required autofocus>

        </div>

    </div>
    <div class="form-group ">
        <button type="submit" class="form-control btn-primary" id="load">火前留名</button>
    </div>
</form>
<div class="form-group ">
    <span class="label label-info">如果戰績足夠出色,你的名字也將出現在下方!</span>
</div>
<div class="form-group table_show">

    <table class="table table-hover  table-bordered">
        <thead>
        <tr>
            <th>排名</th>
            <th>挑戰者</th>
            <th>對答次數</th>
        </tr>
        </thead>
        <tbody>
        {% if rank_list|length %}
            {% for rank in rank_list %}
            <tr>
                <th scope="row">{{ loop.index }}</th>
                <td>{{rank.name}}</td>
                <td>{{rank.round_num}}</td>
            </tr>
            {% endfor %}
        {% endif %}
        </tbody>
    </table>
</div>

{% endblock %}

game.html主要負責成語接龍游戲的監控與AJAX數據的後臺刷新。

{% extends "layout.html"%}

{% block contents %}
<div class="form-group has-success">
    <h3 id='round_num' class="round_num">ROUND 1</h3>
</div>

<div class="form-group rank">

    <table class="table table-hover  table-bordered table_show table-condensed">
        <tbody>
        <tr>
            <th>成語</th>
            <td id="idiom_name"> {{idiom.name}} ({{idiom.speak}})</td>
        </tr>
        <tr>
            <th>解釋</th>
            <td id="idiom_meaning">{{idiom.meaning}}</td>
        </tr>
        <tr>
            <th>示例</th>
            <td id="idiom_example"> {{idiom.example}}</td>
        </tr>
        </tbody>
    </table>
</div>

<div class="form-group has-success">
    <div class="input-group">
        <div class="input-group-addon" required autofocus>
            成語:
        </div>
        <input id='user_idiom' class="form-control">

    </div>

</div>
<div class="form-group ">
    <button class="form-control btn-primary" id="load">用戶:{{user}} 作答</button>
</div>
<script type="text/javascript">
    $(function () {
        $('#load').click(function () {
            let user_idiom = $('#user_idiom').val();
            if (user_idiom.length != 4) {
                alert("請填寫四字成語...");
            } else {
                $.ajax({
                    url: '/more/' + user_idiom,
                    type: 'get',
                    success: function (data) {
                        $('.result').html(data);
                        if (data['code'] == 200) {
                            $('#round_num').html('ROUND ' + data['round']);
                            $('#idiom_name').html(data['info']['name'] + ' (' + data['info']['speak'] + ')');
                            $('#idiom_meaning').html(data['info']['meaning']);
                            $('#example').html('ROUND ' + data['info']['example']);
                        } else {
                            alert(data['error']);
                            $(location).attr('href', data['url']);
                        }
                    }
                })
            }
        })
    })
</script>
{% endblock %}
Flask裝飾器

首先,我們的遊戲涉及到SQLite數據庫的交互,所以在每次請求前,需要建立數據庫,請求結束後需要釋放連接。此時我們需要使用到兩個裝飾器,@app.before_request和**@app.teardown_request**,before_request見名知意就是在請求訪問前調動該裝飾器,那麼爲什麼不用對應的**@app.after_request呢?因爲即便用戶代碼在執行過程中,出現任何錯誤,也可以通過@app.teardown_request裝飾器最終釋放數據庫連接,但@app.after_request**可不行…
Flask整體代碼如下

import sqlite3
from flask import Flask, render_template, request, g, session, redirect, url_for, jsonify
import random
from pypinyin import pinyin

app = Flask(__name__)
DATABASE = 'static/db/database.db'
app.secret_key = 'Breeze Python'


def connect_db():
    return sqlite3.connect(DATABASE)


@app.before_request
def before_request():
    g.db = connect_db()


@app.teardown_request
def teardown_request(exception):
    if hasattr(g, 'db'):
        g.db.close()


def query_db(query, args=(), one=True):
    cur = g.db.execute(query, args)
    rv = [dict((cur.description[idx][0], value)
               for idx, value in enumerate(row)) for row in cur.fetchall()]
    if not query.startswith('select'):
        g.db.commit()
    return (rv[0] if rv else None) if one else rv


@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        user = request.form.get('name')
        session['user'] = user
        session['round'] = 1
        return redirect(url_for('game'))
    rank_list = query_db('select * from rank order by round_num desc limit 5',one=False)
    print(rank_list)
    return render_template('login.html', rank_list=rank_list)


@app.route('/game')
def game():
    if not session.get('user'):
        return redirect(url_for('index'))
    id = random.randint(1, 30000)
    idiom = query_db('select * from idiom where id = ?',
                     [id])
    return render_template('game.html', user=session.get('user'), idiom=idiom)


@app.route('/more/<user_idiom>')
def more(user_idiom):
    speak_list = pinyin(user_idiom)
    print(speak_list[0][-1])
    idiom_speak = '  '.join(map(lambda x: x[0], speak_list))
    if query_db('select * from idiom where speak = ?',
                [idiom_speak]):
        new_idiom = query_db("select * from idiom where speak like ('%s%%')" % speak_list[-1][0])
        session['round'] = session.get('round') + 1
        print({'code': 200, 'round': session.get('round'), 'info': new_idiom})
        return jsonify({'code': 200, 'round': session.get('round') + 1, 'info': new_idiom})
    else:
        query_db('replace into rank (name,round_num) values (?,?)',
                 [session.get('user'), session.get('round')])
        return jsonify({'code': 404, 'error': "挑戰結束:用戶輸入的成語是自己編的吧!", 'url': request.host_url})
遊戲演示

說了這麼多,讓我們來開一局:
遊戲展示
我們使用一個Neo的新用戶來進行遊戲,隨便接龍了幾次,然後我編了一個**“蟬鳴鳥叫”**的成語結束這次演示,不然好好答不來個幾百輪的那裏能結束啊,哈哈…可以看到遊戲結束後退回到首頁,並進行了挑戰排序。還好沒幾個號,不然排不到前五都看不到效果了…

手機搭建項目

既然上一篇的天氣預報工程可以搭建到手機,那麼這篇成語接龍,也一樣來試試唄。

代碼clone

代碼我已經上傳到了我的git倉庫,手機登陸Termux直接clone下載即可:
git clone https://github.com/KingUranus/IdiomsGame.git

pipenv導入

進入clone好的代碼路徑,之後輸入pipenv install創建虛擬環境並下載依賴的模塊
鍵入pipenv shell 進入虛擬環境
通過flask run運行我們的Flask app程序…
image.png

image.png

image.png

The End

今天的Flask項目開發成語接龍游戲的文章,大家是否喜歡,如果覺得內容對你有所幫助,歡迎點擊文章右下角的“在看”。
項目代碼我都貼出來了,但是有些css和引用的js就沒往出貼。如果大家想搭建app玩玩,可以從git上下載代碼,或公衆號回覆成語接龍,自動獲取代碼的百度網盤地址。
不知道父親之後幾天的住院治療情況如何,如果比較麻煩可能會斷更了,希望大家體諒…
期待你關注我的公衆號**清風Python**,如果你覺得不錯,希望能動動手指轉發給你身邊的朋友們。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章