游戏演示
说了这么多,让我们来先睹为快,让我来挑战一局接龙比赛吧:
<游戏展示>
既然是游戏,就得来个排名才有意思啊!
之前测试了几轮数据,这次我们使用一个Neo的新用户来进行游戏,随便接龙了几次,然后我编了一个“蝉鸣鸟叫”的成语结束这次演示。
可以看到游戏结束后退回到首页,并进行了挑战排序。还好没几个号,不然排不到前五都看不到效果了。
数据库信息
先来看看我们的数据库信息:

数据库表idiom
分为id,name,speak,meaning,example,hot 几个字段,hot是当时搜索的网站热词排行,跟咱们没有太大关系…主要是name和speak字段。
登陆排行
为了能增强可玩性,我们在每次开场前,允许用户随机输入一个名字。在挑战过程中,针对用户坚持的接龙次数进行排名。

创建用户排名表:
1CREATE TABLE rank (
2 name VARCHAR (50) NOT NULL,
3 round_num INT NOT NULL
4);
这里为什么不设置主键呢?主要是排名取前5,用户使用一个名字多次参战,如果犀利的话包揽前5看这也很帅气啊,清一色的都是我,想想都觉得自我满足感爆棚。
游戏界面
首先映入眼帘的是ROUND 1的接龙次数显示,有没有儿时拳皇对打的感觉…
为了帮助大家在玩游戏的同时能学习成语知识,也避免有些生僻字不认识,所以在界面中显示了成语、注音、解释和示例,当然示例不是每个成语都有,网站有啥我就展示啥呗…

成语判断
首先必须是四字的成语,用户输入非四字的成语会弹出警示栏,其次用户填写完成语后,会将成语在数据库中进行检索。
如果是成语则进行接龙后返回电脑的匹配结果,进行第二轮的接龙;
如果数据库中无此成语会弹出游戏结束的提示“挑战结束:用户输入的成语是自己编的吧!”,返回登陆页,并将用户的挑战结果入库rank表进行排行。
这里需要注意,成语接龙的收尾字可以不一样但音必须相同,包括声调!
拼音识别
数据库中的成语我们存在拼音了,但用户输入的是汉字,我们如何进行拼音转化呢?这里需要使用到python的一个模块pypinyin。
针对这个模块的使用,之前写过一篇文章Python为文档批量注音(生僻字歌词为例),喜欢的朋友可以去看看。用法很简单,但我们需要做到和数据库中相对应才行。
1from pypinyin import pinyin
2pinyin('唇枪舌剑')
3# output:
4[['chún'], ['qiāng'], ['shé'], ['jiàn']]
5# 此处为一个嵌套列表,我们需要转化为数据库中的格式
6' '.join(map(lambda x: x[0], pinyin('唇枪舌剑')))
7# output:
8'chún qiāng shé jiàn'
Jinjia2模板
大家看到不管是用户登录还是游戏界面,外框内容基本一致,基于这种场景使用Jinjia2的模板继承是个很不错的选择:
layout.html主要负责整体框架及相关css和js的引入工作,使用Bootstrap+Jquery分分钟搞定。
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta http-equiv="X-UA-Compatible" content="IE=edge">
6 <meta name="viewport" content="width=device-width, initial-scale=1 ,user-scalable=no">
7 <title>清风python</title>
8 <link rel="icon" href="{{ url_for('static',filename='favicon.ico') }}">
9 <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.min.css') }}">
10 <link rel="stylesheet" href="{{ url_for('static',filename='css/main.css') }}">
11 <script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
12</head>
13<body>
14
15<div class="container container-small">
16 <div class="content">
17 <div class="header">
18 成语接龙
19 </div>
20 <div class="block-info">
21 {% block contents %}
22 {% endblock %}
23 </div>
24 </div>
25 <div class="footer">
26 ©2019-欢迎关注我的公众号:<a href="https://www.jianshu.com/u/d23fd5012bed">清风Python</a>
27 </div>
28</div>
29
30</body>
31</html>
login.html涉及到挑战者排行和用户名提交与页面跳转
game.html主要负责成语接龙游戏的监控与AJAX数据的后台刷新。
Flask装饰器
首先,我们的游戏涉及到SQLite数据库的交互,所以在每次请求前,需要建立数据库,请求结束后需要释放连接。
此时我们需要使用到两个装饰器:
@app.before_request
@app.teardown_request
before_request见名知意就是在请求访问前调动该装饰器,那么为什么不用对应的@app.after_request呢?
因为即便用户代码在执行过程中,
出现任何错误,
也可以通过@app.teardown_request
最终释放数据库连接,
但@app.after_request可不行…
Flask整体代码如下:
1import sqlite3
2from flask import Flask, render_template, request, g, session, redirect, url_for, jsonify
3import random
4from pypinyin import pinyin
5
6app = Flask(__name__)
7DATABASE = 'static/db/database.db'
8app.secret_key = 'Breeze Python'
9
10
11def connect_db():
12 return sqlite3.connect(DATABASE)
13
14
15@app.before_request
16def before_request():
17 g.db = connect_db()
18
19
20@app.teardown_request
21def teardown_request(exception):
22 if hasattr(g, 'db'):
23 g.db.close()
24
25
26def query_db(query, args=(), one=True):
27 cur = g.db.execute(query, args)
28 rv = [dict((cur.description[idx][0], value)
29 for idx, value in enumerate(row)) for row in cur.fetchall()]
30 if not query.startswith('select'):
31 g.db.commit()
32 return (rv[0] if rv else None) if one else rv
33
34
35@app.route('/', methods=['GET', 'POST'])
36def index():
37 if request.method == 'POST':
38 user = request.form.get('name')
39 session['user'] = user
40 session['round'] = 1
41 return redirect(url_for('game'))
42 rank_list = query_db('select * from rank order by round_num desc limit 5',one=False)
43 print(rank_list)
44 return render_template('login.html', rank_list=rank_list)
45
46
47@app.route('/game')
48def game():
49 if not session.get('user'):
50 return redirect(url_for('index'))
51 id = random.randint(1, 30000)
52 idiom = query_db('select * from idiom where id = ?',
53 [id])
54 return render_template('game.html', user=session.get('user'), idiom=idiom)
55
56
57@app.route('/more/<user_idiom>')
58def more(user_idiom):
59 speak_list = pinyin(user_idiom)
60 print(speak_list[0][-1])
61 idiom_speak = ' '.join(map(lambda x: x[0], speak_list))
62 if query_db('select * from idiom where speak = ?',
63 [idiom_speak]):
64 new_idiom = query_db("select * from idiom where speak like ('%s%%')" % speak_list[-1][0])
65 session['round'] = session.get('round') + 1
66 print({'code': 200, 'round': session.get('round'), 'info': new_idiom})
67 return jsonify({'code': 200, 'round': session.get('round') + 1, 'info': new_idiom})
68 else:
69 query_db('replace into rank (name,round_num) values (?,?)',
70 [session.get('user'), session.get('round')])
71 return jsonify({'code': 404, 'error': "挑战结束:用户输入的成语是自己编的吧!", 'url': request.host_url})
手机搭建项目
搭建好的游戏网站,我们可以在局域网内共享, 但日常想进行练手,如果能使用手机运行我们的网站岂不是更爽?
之前写过一篇将安卓手机打造成你的python全栈开发利器,通过这种方式我们就能把python程序在手机端完美运行了,来试试看吧!
代码我已经上传到了我的git仓库,手机登陆Termux直接clone下载即可:
git clone
https://github.com/BreezePython/IdiomsGame.git
进入clone好的代码路径,之后输入pipenv install创建虚拟环境并下载依赖的模块
键入pipenv shell 进入虚拟环境
通过flask run运行我们的Flask app程序…


本文完整源码如下:
链接:https://pan.baidu.com/s/10GjjTCwO2OFR39bO7bvDtg
提取码:e65n
往期精彩
END
关注【程序IT圈】,更多的Python好文输出