首页 > 技术文章 > python之在线PK游戏(第六天)

wushank 2016-03-27 17:07 原文

 

 本节作业:

       熟练使用类和模块,写一个交互性强、有冲突的程序。

       

故本次写了一个文字回合制的PK游戏,系统主程序为根目录下的:game_menu.py

 

1. 系统功能模块:

第六天的作业:文字游戏程序是在python3.4环境下开发,在python2.7环境下大同小异,主要功能模块如下图:

 

2. 系统目录结构:

程序采用分层的方式编写,包括系统配置conf、数据库访问层database、业务逻辑层modules,业务处理主程序game_menu.py,主要分类如下图:

 

3.应用知识点:

a) 类的使用

b) 文件的读写操作

c) 系统模块、自定义模块的操作

d) 日志模块的使用

 

4.程序流程图如下:

 

5.程序代码如下

5.1 主程序game_menu.py:

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
游戏主程序文件
'''

import os,sys,random,time
from datetime import date,datetime
from conf import setting,menu
from modules import common,role,skill,report
from database import dbapi

###角色属性和技能显示函数###
def role_skill_show():
    '''
    当主菜单选择1时,进行角色的属性和技能查看
    :return: 没有返回值
    '''
    show_flag = False
    while not show_flag:

        ###输出角色默认的菜单,有战士,弓箭手,法师###
        print(menu.role_default_menu.format(today, common.numtochr(weekoftoday)))

        ###选择角色的编号、分别调用战士,弓箭手,法师三个角色类###
        role_choose = common.input_msg_choose("请选择查看角色[1-3]: ", ["1", "2", "3","b"]).strip()
        if role_choose == '1':
            roleadj = role.zhanshi(role_choose)
        elif role_choose == '2':
            roleadj = role.gongjianshou(role_choose)
        elif role_choose == '3':
            roleadj = role.fashi(role_choose)
        else:
            show_flag = True
            continue

        ###输出角色的说明介绍及初始技能###
        common.show_message('角色说明如下:', "NOTICE")
        roleadj.role_instruction()

        ###输出角色的三个攻击技能###
        common.show_message('【%s】技能如下:' % (roleadj.role), "NOTICE")
        skill.skill(role_choose).print_skill_list()

###角色选择###
def choose_role(vs_type,role_choose,role_list):
    """
    角色选择函数
    :param vs_type 指定是挑战方,还是应战方
    :param role_choose: 角色选择输入的字符
    :return:role_list 角色名字,角色,角色的血量,角色名字列表
    """

    role_info = {}
    ###判断输入值是否为b,如果是则退出###
    if role_choose == "b":
        sys.exit()
    else:
        ###角色名字列表赋值###
        name_list = role_list[role_choose]
        ###角色类的实例化###
        roleadj = role.role(role_choose)

        ###选择角色各自的名字
        vs_name = common.input_msg_choose("请选择%s名字%s : " % (vs_type,name_list), name_list).strip()

        ###角色初始技能是否开启###
        init_skill_info = (roleadj.init_skill,roleadj.init_chance_rate,roleadj.init_harm_rate)
        init_skill_flag = common.input_msg_choose("初始技能【%s : %s%% 机率减少伤害 %s%%】,是否开启(y/n)" % init_skill_info, ['y','n']).strip()
        ###调用role类中的初始技能开关函数###
        init_skill_result = roleadj.init_skill_choose(init_skill_flag)
        if init_skill_result:
            common.show_message('已正常开启初始技能【%s : %s%% 机率减少伤害 %s%%】' % init_skill_info, "INFORMATION")
        else:
            common.show_message('禁止启用初始技能【%s】' % roleadj.init_skill, "NOTICE")

        ###挑战和守擂双方在选择同一种角色时,强制选择不同的二个名字###
        name_list.remove(vs_name)

    ###输出角色的技能###
    common.show_message('%s【%s】技能如下:' % (vs_type,roleadj.role), "NOTICE")
    skill.skill(role_choose).print_skill_list()

    ###定义角色的信息字典,方便return###
    role_info = {
                  "name" : vs_name,
                  "role" : roleadj.role,
                  "life" : roleadj.life,
                  "init_skill_flag" : roleadj.init_skill_flag,
                  "init_skill" : roleadj.init_skill,
                  "init_chance_rate" : roleadj.init_chance_rate,
                  "init_harm_rate" : roleadj.init_harm_rate
    }

    return(role_info,name_list)

###挑战和守擂双方PK函数###
def role_vs(offensive_info,defensive_info):
    '''
    挑战和守擂双方PK函数
    :param offensive_info: 挑战方的信息字典
    :param defensive_info: 防守方的信息字典
    :return: 没有返回值
    '''

    ###获取challage的信息###
    offensive_choose = offensive_info['choose']
    offensive_name = offensive_info['name']
    offensive_role = offensive_info['role']
    offensive_life = offensive_info['life']
    offensive_init_skill_flag = offensive_info['init_skill_flag']
    offensive_init_skill = offensive_info['init_skill']
    offensive_init_chance_rate = offensive_info['init_chance_rate']
    offensive_init_harm_rate = offensive_info['init_harm_rate']


    ###获取response的信息###
    defensive_choose = defensive_info['choose']
    defensive_name = defensive_info['name']
    defensive_role = defensive_info['role']
    defensive_life = defensive_info['life']
    defensive_init_skill_flag = defensive_info['init_skill_flag']
    defensive_init_skill = defensive_info['init_skill']
    defensive_init_chance_rate = defensive_info['init_chance_rate']
    defensive_init_harm_rate = defensive_info['init_harm_rate']


    ###PK双方的技能实例化和获取技能编号列表###
    offensive_skill_obj = skill.skill(offensive_choose)
    offensive_skill_list = offensive_skill_obj.skill_id_list()
    defensive_skill_obj = skill.skill(defensive_choose)
    defensive_skill_list = defensive_skill_obj.skill_id_list()


    i = 1
    flag = False
    common.show_message('双方准备,对战开始',"INFO")

    ###自动对战的开关###
    auto_flag = common.input_msg_choose("是否自动对战(y/n):",['y','n']).strip()
    while not flag:
        ###VS对方菜单,显示对战双方的角色,名字和血量###
        print(menu.vs_menu.format(offensive_name,offensive_role,offensive_life,defensive_name,defensive_role,defensive_life))
        common.show_message('第{0}回合'.format(i),"NOTICE")

        ###如果挑战方血量大于0,挑战方开始选择技能###
        if offensive_life > 0:
            if auto_flag == "y":
                ###自动对战开启,在技能列表中随机生成一个数字###
                offensive_skill_id = str(random.randint(int(offensive_skill_list[0]),int(offensive_skill_list[-1])))
            else:
                ###手工对战时,输入技能编号###
                offensive_skill_id = common.input_msg_choose("请挑战方选择技能编号【%s]: " % offensive_skill_list, offensive_skill_list).strip()

            ###调用技能伤害函数,并计算双方的剩余血量###
            (offensive_life,defensive_life) = offensive_skill_obj.skill_harm(offensive_skill_id,offensive_info,defensive_info)
            offensive_info['life'] = offensive_life
            defensive_info['life'] = defensive_life
        else:
            common.show_message('挑战方【%s】已经挂了,应战方【%s】守擂成功' % (offensive_name,defensive_name),"ERROR")
            ###挑战方挂了,进行PK记录回写报表文件###
            report.record_input_file(offensive_role,offensive_name,defensive_role,defensive_name,True)
            ###挑战方挂了,进行PK记录回写日志文件###
            common.log_input_file(offensive_name,defensive_name,True)

            ###PK结束,退出本次循环###
            flag = True
            continue

        ###守擂方血量大于0,开始选择技能###
        if defensive_life > 0:
            if auto_flag == "y":
                ###自动对战开启,在技能列表中随机生成一个数字###
                defensive_skill_id = str(random.randint(int(defensive_skill_list[0]),int(defensive_skill_list[-1])))
            else:
                ###手工对战时,输入技能编号###
                defensive_skill_id = common.input_msg_choose("请应战方选择技能编号【%s]: " % defensive_skill_list, defensive_skill_list).strip()

            ###调用技能伤害函数,并计算双方的剩余血量###
            (defensive_life,offensive_life) = defensive_skill_obj.skill_harm(defensive_skill_id,defensive_info,offensive_info)
            defensive_info['life'] = defensive_life
            offensive_info['life'] = offensive_life
        else:
            common.show_message('挑战方【%s】成功获胜,应战方【%s】已经升天了' % (offensive_name,defensive_name),"ERROR")
            ###守擂方挂了,进行PK记录回写报表文件###
            report.record_input_file(offensive_role,offensive_name,defensive_role,defensive_name,False)
            ###挑战方挂了,进行PK记录回写日志文件###
            common.log_input_file(offensive_name,defensive_name,False)

            ###PK结束,退出本次循环###
            flag = True
            continue

        time.sleep(2)
        i += 1


###PK场菜单函数###
def battlefield(role_name_list):
    '''
    在进入PK场后,选择双方的角色###
    :param role_name_list: 角色名字列表
    :return: 没有返回值
    '''

    ###进入PK场,输出角色选择菜单###
    print(menu.role_default_menu.format(today, common.numtochr(weekoftoday)))

    ###初始化PK双方的角色信息字典###
    challenge_role_info = {}
    response_role_info = {}

    ###选择挑战方角色、名称及初始技能###
    challenge_choose = common.input_msg_choose("请选择挑战方角色[1-3]: ", ["1", "2", "3","b"]).strip()
    (challenge_role_info,role_name_list[challenge_choose]) = choose_role('挑战方',challenge_choose,role_name_list)
    challenge_role_info['choose'] = challenge_choose


    ###选择应战方角色、名称及初始技能###
    response_choose = common.input_msg_choose("请选择应战方角色[1-3]: ", ["1", "2", "3","b"]).strip()
    (response_role_info,role_name_list[response_choose]) = choose_role('应战方',response_choose,role_name_list)
    response_role_info['choose'] = response_choose
    response_role_info['life'] += 100


    ###调用role_vs,进行对战环节###
    role_vs(challenge_role_info,response_role_info)

###PK战绩排序函数###
def pk_result_sorted(sort_list,dates,value=0):
    '''
    对PK战绩进行不同的排序
    :param sort_list 角色名字列表
    :param dates 起始和结束日期
    :param value 指定排序的值
    :return 无返回值
    '''

    ###调用PK记录报表,获取所有角色名字的信息###
    all_user_pk_result = {}
    user_pk_list = report.print_vs_all_report(sort_list,dates,value)
    all_user_pk_result = {'startdate':dates['start'],
                          'enddate':dates['end'],
                          'vs_record':"\n".join(user_pk_list)
                         }
    ###输出所有角色名字的PK记录并进行排序###
    common.show_message(menu.all_vs_history.format(**all_user_pk_result),"NOTICE")


###主程序开始,显示主菜单###
if __name__ == "__main__":

    ###定义并赋值当前的日期和星期几的显示###
    today = datetime.now().strftime("%Y-%m-%d")
    weekoftoday = date.weekday(datetime.now())

    ### --------    开始主程序   ---------###
    flag = False
    while not flag:

        ###定义三类角色的名字列表,战士,弓箭手,法师各有二个用户###
        _role_list = { "1" : ['za','zb'],
                  "2" : ['ga','gb'],
                  "3" : ['fa','fb']
                }

        ###显示游戏的主菜单界面,分别有角色,PK场,战绩榜###
        print(menu.main_menu.format(today, common.numtochr(weekoftoday)))
        choose = common.input_msg_choose("选择功能编号[0-3]: ", ["0", "1", "2", "3"]).strip()

        ###0、退出游戏###
        if choose == "0":
            flag = True
            continue

        ###1、角色及技能介绍###
        if choose == "1":
            role_skill_show()                             ###调用角色信息显示函数###

        ###2、PK场###
        if choose == "2":
            battlefield(_role_list)                       ###调用PK场函数###

        ###3、战绩榜###
        if choose == "3":

            ###定义初始的用户列表和用户选择菜单###
            _name_list = ['za','zb','ga','gb','fa','fb']
            _name_add_list = ['za','zb','ga','gb','fa','fb','b']

            ###调用report函数的起始和结束日期输入函数###
            date_dict = report.get_date()
            ###调用PK记录的排序函数,默认是按用户排序的###
            pk_result_sorted(_name_list,date_dict)

            pk_flag = False
            while not pk_flag:

                ###输入指定排序的方式编号###
                sort_input = common.input_msg_choose("输入战绩排序编号[0-3]:", ["0","1","2","3"]).strip()
                ###选择0时,结束排序,进行用户查询环节###
                if sort_input != "0":
                    ###根据输入的排序编号进行具体排序###
                    pk_result_sorted(_name_list,date_dict,int(sort_input))
                    continue

                query_flag = False
                while not query_flag:
                    ###输入具体的角色名字,来查询具体的PK记录###
                    user_name = common.input_msg_choose("输入用户【%s】,返回(b): " % _name_list,_name_add_list).strip()
                    ###当输入b时,返回到主菜单###
                    if user_name == 'b':
                        pk_flag = True
                        query_flag = True
                        continue
                    else:
                        ###当选择具体角色名字时,生成并输出自己的PKu结果和具体记录###
                        user_pk_result = dict()
                        user_pk_result = report.print_vs_report(user_name,date_dict)
                        common.show_message(menu.role_vs_history.format(**user_pk_result),"NOTICE")
game_menu

 

5.2 配置文件包conf:

   5.2.1 参数配置文件setting.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
定义基本的变量
'''

import os,sys


###程序文件主目录###
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
###添加主目录到环境变量###
sys.path.append(BASE_DIR)

###定义角色和技能数据库信息###
DATABASE = dict(engineer="file", dbpath=os.path.join(BASE_DIR, "database"), tables={"role" : "role","skill" : "skill"})


###日志文件存放路径###
LOG_PATH = os.path.join(BASE_DIR, "log")

###角色初始生命值###
init_hp = 1000
setting

   5.2.2 界面显示模板文件menu.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯

该模块用来定义系统的菜单模板
'''

# 主程序中的菜单输出信息###

main_menu = '''
-------------------------------------------------------------------------
                            kevin在线游戏
                                            今天 {0}   星期{1}
-------------------------------------------------------------------------
       【1】角色介绍     【2】PK场      【3】战绩榜     【0】退出
-------------------------------------------------------------------------
'''

###角色菜单输出信息###

role_default_menu = '''
-------------------------------------------------------------------------
                               角色
                                            今天 {0}   星期{1}
-------------------------------------------------------------------------
          【1】战士         【2】弓箭手        【3】法师
返回(b)
-------------------------------------------------------------------------
'''

###对战菜单###

vs_menu = '''
-------------------------------------------------------------------------
         挑战方:{0}({1})                          应战方:{3}({4})
                                   VS
           血量:{2}                                 血量:{5}
-------------------------------------------------------------------------
'''

###所有用户战榜菜单###
all_vs_history = '''
------------------------------------------------------------------------------
                                 战绩榜

                                           VS时间:{startdate} 至 {enddate}
------------------------------------------------------------------------------
战绩:
   用户    挑战次数 挑战成功次数 守擂次数 守擂成功次数  PK次数  PK成功次数
{vs_record}
------------------------------------------------------------------------------
  【1】挑战成功次数   【2】守擂成功次数  【3】PK成功次数   【0】用户查询
------------------------------------------------------------------------------
'''

###具体用户的战榜和PK记录信息###
role_vs_history = '''
------------------------------------------------------------------------------
                                 战绩榜

用户:{user}                                VS时间:{startdate} 至 {enddate}
------------------------------------------------------------------------------
战绩:
挑战【{attack_sum}】次,成功【{attack_ok}】次 | 守擂【{defence_sum}】次,成功【{defence_ok}】次 | PK【{all_sum}】次,成功【{ok_sum}】次

PK战况:
------------------------------------------------------------------------------
     对战时间                             结果
{vs_record}
'''
menu

 

5.3 数据库包database:

   5.3.1 初始化数据模块db_init.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
定义角色和技能的信息,并初始化输出到数据库db文件
'''

import json,os,sys

###程序文件主目录######程序文件主目录###
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
###添加主目录到环境变量###
sys.path.append(BASE_DIR)

###导入如下三个模块###
from conf import setting
from modules import common
from database import dbapi

###角色列表###
role_list = {
    "1": {"role": "战士", "init_skill":"守护","init_skill_flag":1,"chance_rate" : 50 , "harm_rate" : 20},
    "2": {"role": "弓箭手", "init_skill":"闪避","init_skill_flag":1,"chance_rate" : 40 , "harm_rate" : 25},
    "3": {"role": "法师", "init_skill":"法盾","init_skill_flag":1,"chance_rate" : 30 , "harm_rate" : 40},
}

###角色的技能列表###
role_skill_list = {
    "1": {"role": "战士", "skill": (
        {"id": "11", "level": "初级技能", "name": "野蛮冲锋","init_harm": 100, "chance_rate" : 40 , "harm_rate" : 20},
        {"id": "12", "level": "中级技能", "name": "旋风斩", "init_harm": 150, "chance_rate" : 30 , "harm_rate" : 20},
        {"id": "13", "level": "高级技能", "name": "百刃斩" ,"init_harm": 200, "chance_rate" : 20 , "harm_rate" : 20},
    )},
    "2": {"role": "弓箭手", "skill": (
        {"id": "21", "level": "初级技能", "name": "点射","init_harm": 100, "chance_rate" : 40 , "harm_rate" : 20},
        {"id": "22", "level": "中级技能", "name": "连射", "init_harm": 150, "chance_rate" : 30 , "harm_rate" : 20},
        {"id": "23", "level": "高级技能", "name": "爆裂箭" ,"init_harm": 200, "chance_rate" : 20 , "harm_rate" : 10},
    )},
    "3": {"role": "法师", "skill": (
        {"id": "31", "level": "初级技能", "name": "火球术","init_harm": 100, "chance_rate" : 40 , "harm_rate" : 20},
        {"id": "32", "level": "中级技能", "name": "冰咆哮", "init_harm": 150, "chance_rate" : 30 , "harm_rate" : 20},
        {"id": "33", "level": "高级技能", "name": "地狱雷光" ,"init_harm": 200, "chance_rate" : 20 , "harm_rate" : 20},
    )}
}


'''
###初始化角色数据库文件 role.db###
def init_db_role():
    _db_file = os.path.join(setting.DATABASE['dbpath'], "role.db")
    with open(_db_file, "w+") as f:
        f.write(json.dumps(role_list))

###初始化角色技能数据库文件 skill.db###
def init_db_skill():
    _db_file = os.path.join(setting.DATABASE['dbpath'], "skill.db")
    with open(_db_file, "w+") as f:
        f.write(json.dumps(role_skill_list))

'''
###初始化角色数据库文件 role.db###
def init_db_role():
    _db_file = os.path.join(setting.DATABASE['dbpath'], "role.db")
    ###调用函数,将角色列表以json的格式写入文件###
    dbapi.write_db_json(role_list,_db_file)

###初始化角色技能数据库文件 skill.db###
def init_db_skill():
    _db_file = os.path.join(setting.DATABASE['dbpath'], "skill.db")
    ###调用函数,将角色技能列表以json的格式写入文件###
    dbapi.write_db_json(role_skill_list,_db_file)

###初始化技能数据表###
def init_database():
    tables = list(setting.DATABASE['tables'].values())            ###数据表名称列表###
    database = setting.DATABASE['dbpath']                         ###数据表存放路径###

    for _table in tables:
        ###数据库文件存在判断###
        if not os.path.exists(os.path.join(database, "{0}.db".format(_table))):
            print("Table {0}.db create successfull".format(_table))
        ###通过反射初始化数据表###
        if hasattr(sys.modules[__name__], "init_db_{0}".format(_table)):
           init_func = getattr(sys.modules[__name__], "init_db_{0}".format(_table))
           init_func()
        else:
           ###如果不存在,则输出错误日志###
           common.write_log("init table {0} failed,no function init_db_{0} found".format(_table),"error")


###主程序开始,调用初始化函数,生成数据库文件###
if __name__ == "__main__":
    init_database()
db_init

   5.3.2 数据访问层模块dbapi.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯

数据库访问层:
1 提供从数据文件、报表文件中读取数据的接口
2 将数据写入到数据文件的接口
'''

import os,sys,json
from conf import setting
from modules.common import write_log


###追加信息到指定文件###
def append_db_json(contant, filename):
    """
    将信息以 json 格式写入数据表文件(追加)
    :param contant: 要写入的 json 格式内容
    :param filename: 要写入的数据表文件名
    :return: 无返回
    """
    try:
        with open(filename, 'a+') as fa:
            fa.write(json.dumps(contant))
            fa.write("\n")
    except Exception as e:
        write_log(e,'critical')

###覆盖重写信息到指定文件###
def write_db_json(contant, filename):
    """
    将信息以 json 格式写入数据表文件(覆盖)
    :param contant: 写入的json数据内容
    :param filename: 要写入的文件名
    :return: 无返回结果
    """
    try:
        with open(filename, 'w+') as fw:
            fw.write(json.dumps(contant))
    except Exception as e:
        write_log(e,'critical')


###从指定数据库文件中读取数据###
def load_data_from_db(tabename):
    """
    从指定的数据表中获取所有数据,通过 json 方式将数据返回
    :param tabename: 数据文件名
    :return: 返回数据库文件信息
    """
    try:
        with open(tabename, 'r+') as f:
            return json.load(f)
    except Exception as e:
        write_log(e,'critical')

###从VS报表中读取指定用户的PK记录###
def load_vs_report(startdate, enddate,user=''):
    """
    查找报表记录中的指定用户的PK记录,将结果存入到列表中
    :param user:  角色用户名
    :param startdate: 开始日期
    :param enddate: 结束日期
    :return: 返回用户的PK记录
    """

    ###指定报表的文件名###
    _file = os.path.join(setting.LOG_PATH, "vs_report")
    result = list()

    try:
        with open(_file, "r") as f:
            for line in f:
                _record = json.loads(line)

                ###指定输入用户信息是否为空###
                if user:
                    ###判断用户是否存在###
                    if _record['attack_name'] == user or _record['defence_name'] == user:
                        ###通过输入的起始和结束日期对报表记录进行匹配,是则追加到返回列表中###
                        if _record["time"] >= startdate and _record["time"] <= enddate:
                            result.append(_record)
                ###输入用户为空,则追加所有用户记录到返回列表中###
                else:
                    if _record["time"] >= startdate and _record["time"] <= enddate:
                        result.append(_record)

        return result
    ###执行异常,则输出到错误日志文件###
    except Exception as e:
        write_log("dbapi > load_vs_report > {0}".format(e),"critical")
dbapi

   5.3.3 数据文件role.db

{"3": {"init_skill": "\u6cd5\u76fe", "harm_rate": 40, "chance_rate": 30, "init_skill_flag": 1, "role": "\u6cd5\u5e08"}, "1": {"init_skill": "\u5b88\u62a4", "harm_rate": 20, "chance_rate": 50, "init_skill_flag": 1, "role": "\u6218\u58eb"}, "2": {"init_skill": "\u95ea\u907f", "harm_rate": 25, "chance_rate": 40, "init_skill_flag": 1, "role": "\u5f13\u7bad\u624b"}}
role.db

   5.3.4 数据文件skill.db

{"3": {"skill": [{"level": "\u521d\u7ea7\u6280\u80fd", "chance_rate": 40, "id": "31", "name": "\u706b\u7403\u672f", "init_harm": 100, "harm_rate": 20}, {"level": "\u4e2d\u7ea7\u6280\u80fd", "chance_rate": 30, "id": "32", "name": "\u51b0\u5486\u54ee", "init_harm": 150, "harm_rate": 20}, {"level": "\u9ad8\u7ea7\u6280\u80fd", "chance_rate": 20, "id": "33", "name": "\u5730\u72f1\u96f7\u5149", "init_harm": 200, "harm_rate": 20}], "role": "\u6cd5\u5e08"}, "1": {"skill": [{"level": "\u521d\u7ea7\u6280\u80fd", "chance_rate": 40, "id": "11", "name": "\u91ce\u86ee\u51b2\u950b", "init_harm": 100, "harm_rate": 20}, {"level": "\u4e2d\u7ea7\u6280\u80fd", "chance_rate": 30, "id": "12", "name": "\u65cb\u98ce\u65a9", "init_harm": 150, "harm_rate": 20}, {"level": "\u9ad8\u7ea7\u6280\u80fd", "chance_rate": 20, "id": "13", "name": "\u767e\u5203\u65a9", "init_harm": 200, "harm_rate": 20}], "role": "\u6218\u58eb"}, "2": {"skill": [{"level": "\u521d\u7ea7\u6280\u80fd", "chance_rate": 40, "id": "21", "name": "\u70b9\u5c04", "init_harm": 100, "harm_rate": 20}, {"level": "\u4e2d\u7ea7\u6280\u80fd", "chance_rate": 30, "id": "22", "name": "\u8fde\u5c04", "init_harm": 150, "harm_rate": 20}, {"level": "\u9ad8\u7ea7\u6280\u80fd", "chance_rate": 20, "id": "23", "name": "\u7206\u88c2\u7bad", "init_harm": 200, "harm_rate": 10}], "role": "\u5f13\u7bad\u624b"}}
skill.db

 

5.4 业务处理包modules:

   5.4.1 公共函数模块common.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
'''

import os,sys,logging,random,re
from datetime import datetime,date
from conf import setting

###生成随机数,用于机率判断来是否触发技能###
def random_decide(random_num,cmp_value,cmp_mode='eq'):
    """
    生成一个指定范围值之内随机数,并对随机数进行判断
    :param random_num:用来定义随机数的范围值
    :param cmp_value:定义需要比对的值
    :param cmp_flag:如果是gt或lt,则进行大于或小于判断,如果是eq,则为等于判断,默认为eq
    :return:  返回True或False
    """

    ###生成指定范围的随机数
    ra = random.randrange(1,random_num)

    ###根据比较符,输出判断的字符串###
    if  cmp_mode == 'eq':
        express = "{0} == {1}"
    elif cmp_mode == 'gt':
        express = "{0} > {1}"
    elif cmp_mode == 'lt':
        express = "{0} < {1}"
    else:
        show_message('比较模式错误','ERROR')
        return False

    flag = eval(express.format(ra,cmp_value))

    ###根据结果,返回正确与否###
    if flag:
        return True
    return False


###根据等级分颜色显示###
def show_message(msg, msgtype):
    """
    对print函数进行封装,根据不同类型显示不同颜色
    :param msg:  显示的消息体
    :param msgtype:  消息类型
    :return: 返回格式化过的内容
    """

    if msgtype == "INFO":
        show_msg = "\033[1;34m{0}\033[0m\n".format(msg)
    elif msgtype == "INFORMATION":
        show_msg = "\033[1;32m{0}\033[0m\n".format(msg)
    elif msgtype == "NOTICE":
        show_msg = "\033[1;33m{0}\033[0m\n".format(msg)
    elif msgtype == "ERROR":
        show_msg = "\033[1;31m{0}\033[0m\n".format(msg)
    else:
        show_msg = "{0}\n".format(msg)
    print(show_msg)

###根据等级将异常信息输出到日志文件###
def write_log(content,levelname):
    """
    将程序执行过程上中的异常信息记录到日志文件
    :param content: 日志信息
    :param levelname:日志级别
    :return: 无返回,写入文件 game.log
    """

    ###指定日志文件的路径###
    _filename = os.path.join(setting.LOG_PATH, "game.log")

    ###自定义日志的格式和等级###
    logging.basicConfig(level=logging.INFO,
                encoding = "UTF-8",
                format='%(asctime)s %(levelname)s %(message)s',
                datefmt='%Y-%m-%d %H:%M:%S %p',
                filename=_filename,
                filemode='a+')
    if levelname == 'debug':
        logging.debug(content)
    elif levelname == 'info':
        logging.info(content)
    elif levelname == 'warning':
        logging.warning(content)
    elif levelname == 'error':
        logging.error(content)
    elif levelname == 'critical':
        logging.critical(content)
    else:
        show_message('输入错误',"ERROR")


###根据等级将异常信息输出到日志文件###
def write_file(content,levelname,file_name):
    """
    将程序执行过程上中的异常信息记录到指定日志文件
    :param content: 日志信息
    :param levelname:日志级别
    :return: 无返回,写入文件 game.log
    """

    ###自定义日志的格式和等级###
    logging.basicConfig(level=logging.INFO,
                encoding = "UTF-8",
                format='%(asctime)s %(levelname)s %(message)s',
                datefmt='%Y-%m-%d %H:%M:%S %p',
                filename = file_name,
                filemode='a+')
    if levelname == 'debug':
        logging.debug(content)
    elif levelname == 'info':
        logging.info(content)
    elif levelname == 'warning':
        logging.warning(content)
    elif levelname == 'error':
        logging.error(content)
    elif levelname == 'critical':
        logging.critical(content)
    else:
        show_message('输入错误',"ERROR")


###将PK记录单独的记录到一个VS日志中,并非report###
def log_input_file(attack_name,defence_name,flag=True):
    '''
    将PK记录单独的记录到一个VS日志
    :param attack_name: 日志中的挑战用户
    :param defence_name: 日志中的守擂用户
    :param flag: 对应挑战是否成功,输出的标志位
    :return: 无返回,直接写日志到相应文件
    '''
    if flag:
        info_str = '[{0}] is fail,[{1}] is success'
    else:
        info_str = '[{0}] is success,[{1}] is fail'
    log_info = info_str.format(attack_name,defence_name)
    vs_log = os.path.join(setting.LOG_PATH, "vs.log")
    write_file(log_info,'info',vs_log)


###将数字星期转换为中文数字###
def numtochr(num_of_weekday):
    """
    将数字星期转换为中文数字
    :param num_of_weekday: 星期几的数字字符( 0,1,2,3,4,5,6)
    :return: 中文 星期几
    """
    chrtuple = ('', '', '', '', '', '','')
    num = int(num_of_weekday)
    return chrtuple[num]


###获取汉字个数###
def get_chinese_num(uchar):
    i = 0
    for utext in uchar:
        if u'\u4e00' <= utext <= u'\u9fa5':
            i += 1
    return i


###中英文混合左对齐###
def myljust(str1, width, fillchar = None):
    '''
    中英文混合左对齐
    :param str1: 欲对齐字符串
    :param width: 宽度
    :param fillchar: 填充字符串
    :return: 新的经过左对齐处理的字符串对象
    '''
    if fillchar == None:
        fillchar = ' '

    ###将中文转化为gb2312进行计算字符长度,一般一个汉字占2个字节###
    length = len(str1.encode('gb2312'))
    fill_char_size = width - length if width >= length else 0
    return "%s%s" %(str1, fillchar * fill_char_size)

###中英文混合右对齐###
def myrjust(str1, width, fillchar = None):
    '''
    中英文混合右对齐
    :param str1: 欲对齐字符串
    :param width: 宽度
    :param fillchar: 填充字符串
    :return: 新的经过右对齐处理的字符串对象
    '''
    if fillchar == None:
        fillchar = ' '

    ###将中文转化为gb2312进行计算字符长度,一般一个汉字占2个字节###
    length = len(str1.encode('gb2312'))
    fill_char_size = width - length if width >= length else 0
    return "%s%s" %(fillchar * fill_char_size, str1)

###中英文混合居中###
def mycenter(str1, width, fillchar = None):
    '''
    中英文混合居中对齐
    :param str1: 欲对齐字符串
    :param width: 宽度
    :param fillchar: 填充字符串
    :return: 新的经过居中对齐处理的字符串对象
    '''
    if fillchar == None:
        fillchar = ' '

    ###将中文转化为gb2312进行计算字符长度,一般一个汉字占2个字节###
    length = len(str1.encode('gb2312'))

    fill_char_size = width - length if width >= length else 0
    if length%2 == 0:
        return "%s%s%s" %(fillchar * (fill_char_size //2), str1, fillchar* (fill_char_size // 2))
    else:
        return "%s%s%s" %(fillchar * (fill_char_size //2 + 1), str1, fillchar* (fill_char_size // 2))


###判断input输入的信息是否在指定列表中的公共检测函数###
def input_msg_choose(message, limit_value=list()):
    """
    判断input输入的信息是否为空的公共检测函数,为空继续输入,不为空返回输入的信息
    :param limit_value: 对输入的值有限制,必须为limit_value的值;ex:["admin","user"]
    :param message: input()函数的提示信息
    :return: 返回输入的信息
    """
    is_null_flag = True
    while is_null_flag:
        input_value = input(message).strip().lower()
        if not input_value:
            show_message("输入不能为空!", "ERROR")
            continue

        ###输出q,则退出判断循环###
        elif limit_value == 'q':
            is_null_flag = False
            continue
        elif len(limit_value) > 0:
            ###输入信息不在指定列表中###
            if input_value not in limit_value:
                show_message("输入的值不正确,请重新输入!", "ERROR")
                continue
            else:
                is_null_flag = False
        else:
            pass
    return input_value

###判断input输入的信息是否为空的公共检测函数###
def input_msg(message):
    """
    判断input输入的信息是否为空的公共检测函数,为空继续输入,不为空返回输入的信息
    :param message: input()函数的提示信息
    :return: 返回输入的信息
    """
    is_null_flag = True
    while is_null_flag:
        input_value = input(message).strip().lower()
        if not input_value:
            show_message("输入不能为空!", "ERROR")
            continue
        else:
             is_null_flag = False
    return input_value

###对输入的日期进行判断是否正确###
def input_date(msg, default_date):
    """
    对输入的日期进行判断是否正确 yyyy-mm-dd or yyyy-m-d
    :param msg:输入提示信息
    :param default_date: 默认日期
    :return:返回日期 str类型
    """
    check_flag = False
    while not check_flag:
        strdate = input(msg).strip()
        if not strdate:
            strdate = default_date

        try:
            date_list = strdate.split("-")
            ###对输入的日期以-进行分隔,并判断是否是日期数字###
            result = date(int(date_list[0]), int(date_list[1]), int(date_list[2]))
            check_flag = True
        except ValueError:
            show_message("输入日期不合法,请重新输入!", "ERROR")
            continue
    return result.strftime("%Y-%m-%d")


###对列表进行冒泡排序###
def list_sort_by(sort_list,num1,num2):
    '''
    根据name对列表进行排序
    :param sort_list:要排序的列表
    :param num1要排序的主列编号
    :param num1要排序的次列编号
    :return: 排序后的列表
    '''

    ###因输出的列表内容是以多个空格分隔的字符串,故要进行字符串匹配和分隔###
    r = re.compile('\s+')
    for i in range(len(sort_list)-1):
        for j in range(i+1,len(sort_list)):

            ###对字符串进行正则表达式分隔处理,并对相应的列进行大小比对,对列表进行冒泡操作###
            if r.split(sort_list[i])[num1] < r.split(sort_list[j])[num1]:
                tmp = sort_list[i]
                sort_list[i] = sort_list[j]
                sort_list[j] = tmp
            ###如果排序的主列值相同,则对次列进行大小判断,并对列表进行冒泡操作###
            elif r.split(sort_list[i])[num1] == r.split(sort_list[j])[num1]:
                if r.split(sort_list[i])[num2] < r.split(sort_list[j])[num2]:
                    tmp = sort_list[i]
                    sort_list[i] = sort_list[j]
                    sort_list[j] = tmp
            else:
                pass

    return(sort_list)
common

   5.4.2 报表模块report.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
主要对报表的读取、写入、修改等操作
'''


import calendar
import os
from datetime import datetime, timedelta
from datetime import date
from database import dbapi
from conf import menu,setting
from modules import common


###将角色PK的记录写回到指定的报表文件###
def record_input_file(attack_role,attack_name,defence_role,defence_name,flag=True):
    '''
    将角色PK的记录写回到指定的报表文件
    :param attack_role:  挑战方的角色
    :param attack_name:  挑战方的名字
    :param defence_role: 守擂方的角色
    :param defence_name: 守擂方的名字
    :param flag: 挑战是否成功的标志位
    :return: 没有返回值,直接回写报表文件
    '''

    result = False

    if flag:
        report_str = '【{0}:{1}】挑战失败,【{2}:{3}】守擂成功'

    else:
        report_str = '【{0}:{1}】挑战成功,【{2}:{3}】守擂失败'
        result = True

    report_info = report_str.format(attack_role,attack_name,defence_role,defence_name)

    ###报表要记录的内容格式###
    report_record = {"time": datetime.now().strftime("%Y-%m-%d %H:%M"),
                     "attack_role": attack_role,
                     "attack_name": attack_name,
                     "defence_role": defence_role,
                     "defence_name": defence_name,
                     "attack_result": result,
                     "detail": report_info
                    }

    vs_file = os.path.join(setting.LOG_PATH, "vs_report")
    dbapi.append_db_json(report_record,vs_file)


###日期输入及判断###
def get_date():
    """
    用户输入一个时间段,如果显示报表是要提供开始、结束日期,返回开始,结束时间
    :return: 字典格式,{"start":startdate, "end": enddate}
    """
    startdate = common.input_date("输入查询开始时间(yyyy-mm-dd)[default:2016-01-01]: ", "2016-01-01")
    enddate = common.input_date("输入查询结束时间(yyyy-mm-dd)[default: today]: ", datetime.now().strftime("%Y-%m-%d"))

    return {"start": startdate, "end": enddate}


###根据具体用户来获取PK的记录###
def print_vs_report(user,date_dict):
    """
    根据具体用户来获取PK的记录
    :param user: 角色用户的名字
    :param date_dict:起始和结束日期的字典
    :return: 返回用户的PK结果和记录
    """

    ###获取到起始和结束日期###
    startdate = date_dict["start"]
    enddate = date_dict["end"]

    ###定义PK的信息输出的列表和字典###
    msglist = list()
    msgdict = dict()

    ###调用报表加载函数来获取报表记录###
    _recordlist = dbapi.load_vs_report(startdate, enddate,user)

    ###初始化PK记录的初始变量###
    attack_sum = 0
    attack_ok = 0
    defence_sum = 0
    defence_ok = 0
    ok_sum = 0
    all_sum = 0

    ###对具体用户的PK记录,来统计挑战、守擂和PK的成功次数和总次数###
    for record in _recordlist:
        tmpmsg = "{time}          {detail}".format(time=record["time"],detail=record['detail'])
        msglist.append(tmpmsg)

        if user == record['attack_name']:
            if record['attack_result']:
                attack_ok += 1
            attack_sum += 1

        if user == record['defence_name']:
            if not record['attack_result']:
                defence_ok += 1
            defence_sum += 1

        all_sum += 1

    ###总的PK成功次数###
    ok_sum = attack_ok + defence_ok

    ###定义返回字典的具体信息###
    msgdict = { 'user':user,
                'startdate':startdate,
                'enddate':enddate,
                'attack_sum':attack_sum,
                'attack_ok':attack_ok,
                'defence_sum':defence_sum,
                'defence_ok':defence_ok,
                'all_sum':all_sum,
                'ok_sum':ok_sum,
                'vs_record':"\n".join(msglist)
             }
    return msgdict


###输出所有用户的PK报表信息###
def print_vs_all_report(user_list,dates,num=0):
    """
    输出所有用户的PK报表信息
    :param user_list: 角色用户名字列表
    :param dates 起始和结束的日期字典
    :param num 指定信息的排序编号
    :return:
    """

    ###定义PK的信息输出的列表和字典###
    msgdict = dict()
    msglist = list()


    ###对所有角色名字列表进行遍历并格式化输出信息###
    for user in user_list:
        record_dict = print_vs_report(user,dates)
        _user = common.mycenter(str(record_dict['user']),10)
        _attack_sum = common.mycenter(str(record_dict['attack_sum']),10)
        _attack_ok = common.mycenter(str(record_dict['attack_ok']),10)
        _defence_sum = common.mycenter(str(record_dict['defence_sum']),10)
        _defence_ok = common.mycenter(str(record_dict['defence_ok']),10)
        _all_sum = common.mycenter(str(record_dict['all_sum']),10)
        _ok_sum = common.mycenter(str(record_dict['ok_sum']),10)

        record_list = [_user,_attack_sum,_attack_ok,_defence_sum,_defence_ok,_all_sum,_ok_sum]
        record_msg = "{0}{1}{2}{3}{4}{5}{6}".format(*record_list)
        msglist.append(record_msg)

    ###根据输入的排序编号进行相应处理###
    if num == 0:
        sorted_msglist = msglist
    elif num == 1:
        sorted_msglist = common.list_sort_by(msglist,3,2)
    elif num == 2:
        sorted_msglist = common.list_sort_by(msglist,5,4)
    elif num == 3:
        sorted_msglist = common.list_sort_by(msglist,7,6)
    else:
        pass

    return(sorted_msglist)
report

   5.4.3 角色类模块role.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
定义角色的类和三种角色的子类
'''

import os
from conf import setting,menu
from modules import common
from database import dbapi


class role(object):
    '''
    定义角色的根类
    :param role_num 角色对应的数字编号
    :return 没有返回值
    '''

    ###指定角色的数据库文件####
    __database = "{0}.db".format(os.path.join(setting.DATABASE['dbpath'], setting.DATABASE["tables"]["role"]))

    def __init__(self,role_num):
        self.life = setting.init_hp                       ###角色的初始血量###

        ###定义角色初始化技能参数###
        self.role = ''
        self.init_skill = ''
        self.init_skill_flag = 0
        self.init_chance_rate = ''
        self.init_harm_rate = ''

        self.dict_role_list = {}

        self.role_num = role_num
        self.db_load()                                    ###调用执行角色数据库文件的读取和赋值###
        self.skill_init()                                 ###角色初始技能的赋值###

    ###调用执行角色数据库文件的读取,并赋值给dict_role_list###
    def db_load(self):
        self.dict_role_list = dbapi.load_data_from_db(self.__database)

    ###角色初始技能的赋值###
    def skill_init(self):
        self.dict_role = self.dict_role_list[self.role_num]
        self.role = self.dict_role["role"]
        self.init_skill = self.dict_role["init_skill"]
        self.init_chance_rate = self.dict_role["chance_rate"]
        self.init_harm_rate = self.dict_role["harm_rate"]

    ###根据输入的初始技能开关进行赋值###
    def init_skill_choose(self,flag):
        if flag == "y":
            self.init_skill_flag = self.dict_role["init_skill_flag"]
            return True
        else:
            return False


###定义战士角色的子类###
class zhanshi(role):
    '''
    定义战士角色的类,继承role类
    '''

    def __init__(self,role_num):
        super(zhanshi,self).__init__(role_num)

    ###战士子类的角色介绍函数###
    def role_instruction(self):
        self.mess = '''
               战士介绍:
                   出身于乡村,依靠出色的才能,光靠一把武器从事佣兵生活的少年。
               性格直率,充满正义感,像孩子一样,从来不会掩饰自己的感受。
               容易相信别人,虽然偶尔会因此受伤,但很快就会克服伤痛,重新站起来。
               是坚韧不拔的热血英雄。
                   战士具有强大的近战攻击力,一直活跃在战斗的最前方。
               强大的力量和充沛的体力让他可以摧毁敌人的一切防御。

               初始技能:
                   【%s】:%s%% 机率减少伤害 %s%%
        ''' % (self.init_skill,self.init_chance_rate,self.init_harm_rate)
        common.show_message(self.mess, "INFORMATION")

###定义弓箭手角色的子类###
class gongjianshou(role):
    '''
    定义弓箭手角色的类,继承role类
    '''
    def __init__(self,role_num):
        super(gongjianshou,self).__init__(role_num)

    ###弓箭手子类的角色介绍函数###
    def role_instruction(self):
        self.mess = '''
               弓箭手介绍:
                   虽然身在外地,但一直固守着自己特有的文化,是天生的弓箭手。
               原来是谨慎、善良的和平主义者,为了对抗破坏自然的龙而站出来战斗,是出色的战士。
               弓箭手擅长使用弓箭,在后方进行远距离攻击。
                   凭借快速的移动和多样的远程攻击,使敌人根本无法靠近自己。

               初始技能:
                   【%s】:%s%% 机率减少伤害 %s%%
        ''' % (self.init_skill,self.init_chance_rate,self.init_harm_rate)
        common.show_message(self.mess, "INFORMATION")

###定义法师角色的子类###
class fashi(role):
    '''
    定义法师角色的类,继承role类
    '''
    def __init__(self,role_num):
        super(fashi,self).__init__(role_num)

    ###法师子类的角色介绍函数###
    def role_instruction(self):
        self.mess = '''
               法师介绍:
                   拥有卓越的魔法才能,从小就离开了父母,加入魔法师团的天才少女。
               对自己的才能充满自信,并对未来充满希望,因此眼光很高。
               自尊心很强,甚至有点过于自信。对认可自己能力的人,表现出很强的依赖心。
               泼辣的语言,会让人对她的性格产生误解。其实她也有善良、温柔、可爱的一面。
                   魔法师拥有可以同时攻击多个敌人的强力魔法,但防御力较低。
               一旦被敌人包围,很容易陷入危险之中。

               初始技能:
                   【%s】:%s%% 机率减少伤害 %s%%
        ''' % (self.init_skill,self.init_chance_rate,self.init_harm_rate)
        common.show_message(self.mess, "INFORMATION")
role类

   5.4.4 技能类模块skill.py

#_*_ coding=utf-8 _*_

'''
Created on 2016年3月15日
@author: 王凯
定义角色的技能类
'''

import os
from conf import setting,menu
from modules import common
from database import dbapi

###技能的根类###
class skill(object):
    '''
    定义技能的类
    '''

    ###指定角色技能的数据库文件####
    __database = "{0}.db".format(os.path.join(setting.DATABASE['dbpath'],setting.DATABASE["tables"]["skill"]))

    def __init__(self,role_num):
        self.life = setting.init_hp                                ###角色的初始血量###

        ###定义技能值初始化参数###
        self.id = ''
        self.level = ''
        self.skill = ''
        self.init_harm = ''
        self.chance_rate = ''
        self.harm_rate = ''

        self.dict_skill_list = {}

        self.role_num = role_num
        self.db_load()                                           ###调用执行技能数据库文件的读取和赋值###
        self.dict_skill = self.dict_skill_list[self.role_num]
        self.role = self.dict_skill["role"]
        self.skill_list = self.dict_skill["skill"]

    ###调用执行技能数据库文件的读取和赋值###
    def db_load(self):
        self.dict_skill_list = dbapi.load_data_from_db(self.__database)

    ###输出角色技能列表###
    def print_skill_list(self):
        """
        将角色技能列表中的输出到屏幕
        :return: 输出到屏幕
        """

        print("|{0}|{1}|{2}|".format('技能编号'.center(8), '技能名称'.center(18),'技能描述'.center(40)))
        print('%s' % '-' * 85)

        for skill_info in self.skill_list:
            sk_name = '{0}:{1}'.format(skill_info['level'],skill_info['name'])
            sk_name_chinese_num = common.get_chinese_num(sk_name)
            sk_name_len_num = len(sk_name)
            sk_name_space_str = (20 - sk_name_len_num - sk_name_chinese_num) * " "

            sk_desc = '基础攻击:{0} {1}%机率产生暴击伤害,为基础伤害的{2}%'.format(skill_info['init_harm'],skill_info['chance_rate'],skill_info['harm_rate'])
            sk_desc_chinese_num = common.get_chinese_num(sk_name)
            sk_desc_len_num = len(sk_name)
            sk_desc_space_str = (70 - sk_name_len_num - sk_name_chinese_num) * " "

            print('| %-10s | %s |%s|' % (skill_info['id'], sk_name + sk_name_space_str,sk_desc + sk_desc_space_str))
        print('%s' % '-' * 85,"\n")


    ###获取技能的编号列表###
    def skill_id_list(self):
        """
        获取技能编号,并追加到技能列表,在对战中进行攻击,最后返回技能编号列表
        :return: 技能列表
        """
        _skill_list = []
        _skill_tuple = self.dict_skill["skill"]
        # 开始遍历,并生成技能编号列表
        for _skill in _skill_tuple:
            _skill_list.append(_skill['id'])
        return _skill_list


    ###角色PK中,技能的伤害计算###
    def skill_harm(self,skill_id,attack,defence):
        """
        根据输入的技能编号,在对战中进行攻击,输入相应信息,并进行伤害和血量的计算
        :param skill_id 技能的编号
        :param attack 挑战方的参数字典
        :param defence 守擂方的参数字典
        :return 返回对战双方的血量
        """

        ###技能元组的赋值###
        _skill_tuple = self.dict_skill["skill"]
        attack_harm = 0
        ###开始遍历,并判断赋值###
        for _skill in _skill_tuple:
            ###技能匹配成功,并进行参数赋值###
            if _skill['id'] == skill_id:
                skill_name = _skill['name']
                skill_init_harm = _skill['init_harm']
                skill_chance_rate = _skill['chance_rate']
                skill_harm_rate = _skill['harm_rate']

                ###调用随机数的生成和对比函数,来判断技能暴击属性是否触发###
                attack_skill_trigger = common.random_decide(100,skill_chance_rate,'lt')

                ###PK信息输出的变量初始化###
                mess_attack_init = '进攻方【{0}({1})】发动技能【{2}】,'.format(attack['name'],attack['role'],skill_name)
                mess_attack_trigger = '【{0}({1})】发动技能【{2}】并触发属性【暴击】,'.format(attack['name'],attack['role'],skill_name)
                mess_attack_harm = '造成伤害【{0}】,'
                mess_defence_init = '防守方【{0}({1})】'.format(defence['name'],defence['role'])
                mess_defence_trigger = '防守方【{0}({1})】触发初始技能【{2}】,'.format(defence['name'],defence['role'],defence['init_skill'])
                mess_defence_life = '剩余血量【{0}】'

                mess_attack_info = mess_attack_init
                mess_defence_info = mess_defence_init

                ###守擂方的初始技能是否开启###
                if defence['init_skill_flag'] == 1:
                    ###调用随机数的生成和对比函数,来判断防守方的初始属性是否触发###
                    defence_skill_trigger = common.random_decide(100,defence['init_chance_rate'],'lt')

                    ###挑战和防守双方的技能均触发成功,计算伤害值###
                    if defence_skill_trigger and attack_skill_trigger:
                        attack_harm = (skill_init_harm * (100 + skill_harm_rate) /100) * (100 - defence['init_harm_rate']) / 100

                        mess_attack_info = mess_attack_trigger
                        mess_defence_info = mess_defence_trigger
                    ###挑战方攻击技能暴击属性触发成功,计算伤害值###
                    elif attack_skill_trigger:
                        attack_harm = skill_init_harm * (100 + skill_harm_rate) /100

                        mess_attack_info = mess_attack_trigger
                    ###防守方的技能触发成功,计算伤害值###
                    elif defence_skill_trigger:
                        attack_harm = skill_init_harm * (100 - defence['init_harm_rate']) / 100

                        mess_defence_info = mess_defence_trigger
                    ###双方均未触发成功,计算伤害值###
                    else:
                        attack_harm = skill_init_harm

                else:
                    ###挑战方攻击技能暴击属性触发成功,计算伤害值###
                    if attack_skill_trigger:
                        attack_harm = skill_init_harm * (100 + skill_harm_rate) /100

                        mess_attack_info = mess_attack_trigger
                    ###双方均未触发成功,计算伤害值###
                    else:
                        attack_harm = skill_init_harm

                ###计算守擂方的剩余血量###
                defence['life'] -= attack_harm
                defence['life'] = int(defence['life'])
                message = mess_attack_info + mess_attack_harm.format(attack_harm) + mess_defence_info + mess_defence_life.format(defence['life'])
                common.show_message(message,"INFORMATION")

                ###返回双方的剩余血量###
                return(attack['life'],defence['life'])
skill类

 

5.5 日志报表目录log:

    5.5.1 错误日志game.log

2016-03-18 23:28:44 PM ERROR init table role failed,no function init_db_role found
2016-03-18 23:29:23 PM ERROR init table role failed,no function init_db_role found
2016-03-18 23:29:32 PM ERROR init table role failed,no function init_db_role found
2016-03-18 23:29:44 PM ERROR init table role failed,no function init_db_role found
2016-03-18 23:31:19 PM ERROR init table role failed,no function init_db_role found
2016-03-20 20:33:36 PM ERROR init table skill failed,no function init_db_skill found
2016-03-20 23:47:45 PM CRITICAL dbapi > load_vs_report > 'defence_name_name'
2016-03-20 23:52:19 PM CRITICAL dbapi > load_vs_report > 'defence_name_name'
2016-03-20 23:52:33 PM CRITICAL dbapi > load_vs_report > 'defence_name_name'
game.log

    5.5.2 对战日志vs.log

2016-03-20 22:40:48 PM INFO [za] is fail,[ga] is success
2016-03-20 22:42:07 PM INFO [fb] is fail,[fa] is success
2016-03-20 22:43:14 PM INFO [za] is success,[fb] is fail
2016-03-20 23:33:52 PM INFO [za] is fail,[zb] is success
2016-03-21 00:02:09 AM INFO [gb] is success,[za] is fail
2016-03-21 00:05:33 AM INFO [fb] is success,[fa] is fail
2016-03-22 22:47:32 PM INFO [zb] is success,[ga] is fail
2016-03-22 22:48:20 PM INFO [fa] is success,[fb] is fail
2016-03-26 12:22:40 PM INFO [za] is fail,[fb] is success
2016-03-26 20:47:11 PM INFO [gb] is success,[fa] is fail
2016-03-26 20:47:50 PM INFO [gb] is fail,[za] is success
2016-03-26 21:02:20 PM INFO [zb] is success,[fa] is fail
2016-03-26 21:02:59 PM INFO [zb] is success,[ga] is fail
2016-03-26 21:04:01 PM INFO [fb] is success,[ga] is fail
2016-03-26 22:35:46 PM INFO [gb] is fail,[fa] is success
2016-03-27 01:50:00 AM INFO [zb] is fail,[fa] is success
2016-03-27 01:51:07 AM INFO [ga] is success,[fa] is fail
2016-03-27 16:17:58 PM INFO [za] is success,[fa] is fail
vs.log

    5.5.3 对战报表vs_report

{"defence_name": "ga", "attack_result": false, "attack_name": "za", "time": "2016-03-20 22:40", "attack_role": "\u6218\u58eb", "defence_role": "\u5f13\u7bad\u624b", "detail": "\u3010\u6218\u58eb\uff1aza\u3011\u6311\u6218\u5931\u8d25,\u3010\u5f13\u7bad\u624b\uff1aga\u3011\u5b88\u64c2\u6210\u529f"}
{"defence_name": "fa", "attack_result": false, "attack_name": "fb", "time": "2016-03-20 22:42", "attack_role": "\u6cd5\u5e08", "defence_role": "\u6cd5\u5e08", "detail": "\u3010\u6cd5\u5e08\uff1afb\u3011\u6311\u6218\u5931\u8d25,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u6210\u529f"}
{"defence_name": "fb", "attack_result": true, "attack_name": "za", "time": "2016-03-20 22:43", "attack_role": "\u6218\u58eb", "defence_role": "\u6cd5\u5e08", "detail": "\u3010\u6218\u58eb\uff1aza\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afb\u3011\u5b88\u64c2\u5931\u8d25"}
{"defence_role": "\u6218\u58eb", "time": "2016-03-20 23:33", "attack_result": false, "attack_role": "\u6218\u58eb", "defence_name": "zb", "attack_name": "za", "detail": "\u3010\u6218\u58eb\uff1aza\u3011\u6311\u6218\u5931\u8d25,\u3010\u6218\u58eb\uff1azb\u3011\u5b88\u64c2\u6210\u529f"}
{"attack_result": true, "detail": "\u3010\u5f13\u7bad\u624b\uff1agb\u3011\u6311\u6218\u6210\u529f,\u3010\u6218\u58eb\uff1aza\u3011\u5b88\u64c2\u5931\u8d25", "attack_role": "\u5f13\u7bad\u624b", "attack_name": "gb", "defence_name": "za", "defence_role": "\u6218\u58eb", "time": "2016-03-21 00:02"}
{"defence_role": "\u6cd5\u5e08", "time": "2016-03-21 00:05", "attack_result": true, "attack_name": "fb", "attack_role": "\u6cd5\u5e08", "detail": "\u3010\u6cd5\u5e08\uff1afb\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u5931\u8d25", "defence_name": "fa"}
{"time": "2016-03-22 22:47", "detail": "\u3010\u6218\u58eb\uff1azb\u3011\u6311\u6218\u6210\u529f,\u3010\u5f13\u7bad\u624b\uff1aga\u3011\u5b88\u64c2\u5931\u8d25", "defence_name": "ga", "attack_result": true, "defence_role": "\u5f13\u7bad\u624b", "attack_role": "\u6218\u58eb", "attack_name": "zb"}
{"time": "2016-03-22 22:48", "detail": "\u3010\u6cd5\u5e08\uff1afa\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afb\u3011\u5b88\u64c2\u5931\u8d25", "defence_name": "fb", "attack_result": true, "defence_role": "\u6cd5\u5e08", "attack_role": "\u6cd5\u5e08", "attack_name": "fa"}
{"time": "2016-03-26 12:22", "defence_role": "\u6cd5\u5e08", "detail": "\u3010\u6218\u58eb\uff1aza\u3011\u6311\u6218\u5931\u8d25,\u3010\u6cd5\u5e08\uff1afb\u3011\u5b88\u64c2\u6210\u529f", "attack_name": "za", "attack_role": "\u6218\u58eb", "defence_name": "fb", "attack_result": false}
{"attack_role": "\u5f13\u7bad\u624b", "attack_name": "gb", "defence_name": "fa", "attack_result": true, "time": "2016-03-26 20:47", "defence_role": "\u6cd5\u5e08", "detail": "\u3010\u5f13\u7bad\u624b\uff1agb\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u5931\u8d25"}
{"attack_role": "\u5f13\u7bad\u624b", "attack_name": "gb", "defence_name": "za", "attack_result": false, "time": "2016-03-26 20:47", "defence_role": "\u6218\u58eb", "detail": "\u3010\u5f13\u7bad\u624b\uff1agb\u3011\u6311\u6218\u5931\u8d25,\u3010\u6218\u58eb\uff1aza\u3011\u5b88\u64c2\u6210\u529f"}
{"attack_name": "zb", "defence_name": "fa", "detail": "\u3010\u6218\u58eb\uff1azb\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u5931\u8d25", "attack_result": true, "attack_role": "\u6218\u58eb", "defence_role": "\u6cd5\u5e08", "time": "2016-03-26 21:02"}
{"attack_name": "zb", "defence_name": "ga", "detail": "\u3010\u6218\u58eb\uff1azb\u3011\u6311\u6218\u6210\u529f,\u3010\u5f13\u7bad\u624b\uff1aga\u3011\u5b88\u64c2\u5931\u8d25", "attack_result": true, "attack_role": "\u6218\u58eb", "defence_role": "\u5f13\u7bad\u624b", "time": "2016-03-26 21:02"}
{"attack_name": "fb", "defence_name": "ga", "detail": "\u3010\u6cd5\u5e08\uff1afb\u3011\u6311\u6218\u6210\u529f,\u3010\u5f13\u7bad\u624b\uff1aga\u3011\u5b88\u64c2\u5931\u8d25", "attack_result": true, "attack_role": "\u6cd5\u5e08", "defence_role": "\u5f13\u7bad\u624b", "time": "2016-03-26 21:04"}
{"attack_role": "\u5f13\u7bad\u624b", "defence_name": "fa", "attack_result": false, "detail": "\u3010\u5f13\u7bad\u624b\uff1agb\u3011\u6311\u6218\u5931\u8d25,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u6210\u529f", "defence_role": "\u6cd5\u5e08", "time": "2016-03-26 22:35", "attack_name": "gb"}
{"defence_role": "\u6cd5\u5e08", "attack_name": "zb", "attack_result": false, "detail": "\u3010\u6218\u58eb\uff1azb\u3011\u6311\u6218\u5931\u8d25,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u6210\u529f", "time": "2016-03-27 01:50", "defence_name": "fa", "attack_role": "\u6218\u58eb"}
{"defence_role": "\u6cd5\u5e08", "attack_name": "ga", "attack_result": true, "detail": "\u3010\u5f13\u7bad\u624b\uff1aga\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u5931\u8d25", "time": "2016-03-27 01:51", "defence_name": "fa", "attack_role": "\u5f13\u7bad\u624b"}
{"defence_role": "\u6cd5\u5e08", "attack_role": "\u6218\u58eb", "defence_name": "fa", "detail": "\u3010\u6218\u58eb\uff1aza\u3011\u6311\u6218\u6210\u529f,\u3010\u6cd5\u5e08\uff1afa\u3011\u5b88\u64c2\u5931\u8d25", "time": "2016-03-27 16:17", "attack_result": true, "attack_name": "za"}
vs_report

 

6.程序部分功能模块截图展示:

6.1 主菜单:

 

6.2 输入1,进入角色介绍菜单,并查看战士的介绍:

 

6.3 返回主菜单,输入2,进入PK场:

    说明:

           每个角色有二个用户,挑战和应战方不能同时选择同一种角色下的同一用户,例当挑战方选择了战士的za时,应战方如果选择战士角色,则只有zb可以选择。

    首先输入挑战方的角色、名字以及是否开启初始技能

 

    其次输入应战方的角色、名字以及是否开启初始技能

 

 

6.4对战开始,可以选择自动战斗,也可以手动战斗

    说明:

           自动战斗即在技能范围内随机生成一个数;而手动战斗是手工输入技能的编号

           初始技能和暴击属性的触发,也是根据随机生成数和机率进行比较,从而对伤害进行增加或减少。

           因挑战方先发起攻击,必然在PK有多出一招的优势,故为了平衡,在防守方的初始血量多加了100

           挑战方1000血量,应战方1100血量

       每一回合来计算对战双方的血量,如果血量小于0,则结束对战

 

 

6.5 返回主菜单,输入3,查看战绩榜:

      首先输入查询的开始和结束时间

      说明:正常输出PK战绩结果,默认是以角色用户来排序,可以根据其他三种方式进行排序,正常有主排序和次排序字段

      输入1,则以挑战成功数为主排序字段,以挑战次数为次排序字段

      输入2,则以守擂成功数为主排序字段,以守擂次数为次排序字段

 

      输入3,则以PK成功数为主排序字段,以PK总次数为次排序字段

 

6.6 输入0,进行具体用户查询环节,并可以查看具体的PK记录信息:

 

6.7 最后,返回主菜单,输入0,退出程序,结束:

 

推荐阅读