系列文章入口

《Python3编程实战Tetris机器人》

设计思路

将用户手动玩和AI主动玩游戏的历史记录下来,存入数据库,供前面进行剖析。为了不依赖某个特定的数据系统,设计了一个通用数据库操作接口,以不便在利用层面切换不同的数据库。

接口设计

class BaseDao(object):    def select(self, tablename, params={}, fields=None):     # 查问接口,参数:数据表名;查问参数(ORM规定融入字典中,请参看下一篇日志);返回数据字段        fields = [] if fields == None else fields            # Python的默认参数行为很是不同,会记录上一次调用的后果,有点象其它语言中的动态变量        return dbhelper.select(tablename, params, fields)    # 真正的查问实现,dbhelper是针对特定数据库的接口实现    def insert(self, tablename, params={}, fields=[]):       # 新增接口,CURD函数的参数模式统一,这种设计不便作rest微服务时,由http的不同拜访形式间接抉择后盾操作方法        if '_id_' in params and len(params) < 2 or '_id_' not in params and len(params) < 1:     # 要求提供_id_,约定_id_为所有表的主键            return {"code": 301, "err": "The params is error."}        return dbhelper.insert(tablename, params)    def update(self, tablename, params={}, fields=[]):        if '_id_' not in params or len(params) < 2:            return {"code": 301, "err": "The params is error."}        return dbhelper.update(tablename, params)    def delete(self, tablename, params={}, fields=[]):        if '_id_' not in params:            return {"code": 301, "err": "The params is error."}        return dbhelper.delete(tablename, params)    def querySql(self, sql, values = [], params = {}, fields = []):  # 手写查问接口        return dbhelper.querySql(sql, values, params, fields)    def execSql(self, sql, values = []):      # 手写非查问接口        return dbhelper.exec_sql(sql, values)    def insertBatch(self, tablename, elements : List):     # 批量写入接口        return dbhelper.insertBatch(tablename,elements)    def transGo(elements = [], isAsync = False):    # 事务接口,待实现        pass

具体实现(Sqlit3)

首先实现了对Sqlit3的操作接口。

间接面对Sqlit3的函数

def exec_sql(sql, values, opType = 0): # opType : 0 - 单条SQL语句; 1 - 批量操作语句;2 - 查问返回数据集;    try:    # 所有与数据操作都通过这个函数,须要用异样解决封装        flag = False    # 是否出错标识变量        error = {}        if not os.path.exists("./dist"):  # 存储地位目录存在判断            os.mkdir("dist")        conn = dbHandle.connect("./dist/log.db")  # 连贯数据库或新建        cur = conn.cursor()        if opType == 1:          # 批量操作            num = cur.executemany(sql, values)        else:                    # 单条语句            num = cur.execute(sql, values)        if opType == 2:          # 有后果集返回            result = cur.fetchall()        else:            conn.commit()        # print('Sql: ', sql, ' Values: ', values)    except Exception as err:     # 出错        flag = True        error = err        print('Error: ', err)    finally:        conn.close()            # 完结解决,并格式化返回后果        if flag:            return False, error, num if 'num' in dir() else 0    return True, result if 'result' in dir() else [], len(result) if opType == 2 else num.rowcount if 'num' in dir() else 0

查问函数

这里解说函数骨干,对于在字典中融入ORM的解析,请参看下篇日志。

def select(tablename, params={}, fields=None, sql = None, values = None):    where = ""    AndJoinStr = ' and '    reserveKeys = {}    for rk in ["sort", "search", "page", "size", "sum", "count", "group"]:        # 提取保留关键字    for k, v in params.items():        whereExtra = ""        if k == "ins":            # 保留关键字ins,lks,ors解决        else:            flag = False            if type(v) == "str":                # 不等查问操作解决            elif reserveKeys.get('search'):                # 准确查问与含糊查询处理            else:                whereExtra += k + " =? "                values.append(v)        where += whereExtra    # 排序、统计、分组和分页等操作解决    rs = exec_sql(sql, values, 2)    return {"code": 200, "rows": rs[1], "total": rs[2]}

插入函数

删除和更新与插入相似,这里解说插入函数

def insert(tablename, params={}):    sql = "insert into %s ( " % tablename     # 骨干    ks = params.keys()    vs = []    ps = ""    for al in ks:           # 解析参数        sql += al + ","     # 按插入语句拼接每一个参数        ps += "?,"          # python的sqlit3封装没法送入list对象来实现元组数据的写入,只能拆开        vs.append(params[al])    sql = sql[:-1] + ") values (" + ps[:-1] + ")"  # 去掉最初的逗号,并实现sql语句    rs = exec_sql(sql, vs)                         # 执行,vs参数中不能嵌套list,比起C++版本的实现,这里有些顺当    if rs[0]:                                      # 返回后果        return {"code": 200, "info": "create success.", "total": rs[2]}    else:        return {"code": 701, "error": rs[1].args[0], "total": rs[2]}

批量插入

事实证明,一条条的插入效率太低,AI运行时,数据写入跟不上节奏。

def insertBatch(tablename, elements : List):    if len(elements) == 0:    # 无输出元素,间接退出        return {"code": 201, "info": "There is no elements exist.", "total": 0}    elif len(elements) == 1:  # 只有一个元素,调用insert实现来实现        return insert(tablename, elements[0])    sql = "insert into %s ( " % tablename    isFirst = True      # 在循环的第一次,要解决操作字段    vs = []    ps = ""    for ele in elements:        if isFirst:            isFirst = False            ks = ele.keys()            for al in ks:   # 操作字段,只需解决一次                sql += al + ","                ps += "?,"  # 参数批占位符        items = []        for bl in ks:  # 按key的程序一一增加写入参数值,字典的拜访程序是不肯定的            items.append(ele[bl])        vs.append(items)    sql = sql[:-1] + ") values (" + ps[:-1] + ")"  # 最初的拼接    rs = exec_sql(sql, vs, 1)    # 执行    if rs[0]:                    # 返回后果        return {"code": 200, "info": "create success.", "total": rs[2]}    else:        return {"code": 701, "error": rs[1].args[0], "total": rs[2]}

内容预报

下一篇日志解说融入字典中的ORM规定设计及应用办法,有了这一套规定,无须要再写sql语句。欲后事如何,请继续关注,谢谢!

我的项目地址

https://gitee.com/zhoutk/ptetris或https://github.com/zhoutk/ptetris

运行办法

1. install python3, git2. git clone https://gitee.com/zhoutk/ptetris (or download and unzip source code)3. cd ptetris4. python3 tetrisThis project surpport windows, linux, macOson linux, you must install tkinter first, use this command:  sudo apt install python3-tk

相干我的项目

曾经实现了C++版,我的项目地址:

https://gitee.com/zhoutk/qtetris