# -*- coding=utf-8 -*-
"""
Created by lichen on 16/12/13.
技能在渔场中释放
"""

import time
import random
import math
import json

import freetime.util.log as ftlog
from poker.entity.biz import bireport
from poker.entity.dao import gamedata, daobase
from newfish.entity import config, util, weakdata
from newfish.entity.config import FISH_GAMEID
from newfish.entity.redis_keys import GameData, UserData, WeakData
from hall.entity import hallranking

class SkillBase(object):

    def __init__(self, table, player, skillId, skillState, skillStar, skillGrade, skillType=0):
        self.table = table
        self.player = player
        self.skillId = skillId
        self.skillState = skillState                        # 技能状态
        self.skillStar = skillStar                          # 技能星级
        self.skillGrade = skillGrade                        # 技能等级
        self.skillGrade = self.getRealEffectSkillGrade()    # 玩家在欢乐竞技和王者争霸中夺冠，则下次比赛中使用该技能降级
        self.skillType = skillType                          # 技能类型（0:主技能 1:辅助技能）
        self.skillMode = self.table.gameMode                # 0:经典 1:千炮
        self.position = {}                                  # [bulletId] = [fPosx, fPosy]
        self.initData()                                     # 初始化技能数据
        self.initState()                                    # 初始化技能状态

    def initLevel(self):
        """
        初始化等级属性数据
        """
        skillGradeConf = config.getSkillGradeConf(self.skillId, self.skillGrade, self.skillMode)
        self.cost = skillGradeConf.get("cost", 0)
        self.power = skillGradeConf.get("power", 0)
        self.HP = skillGradeConf.get("HP", 0)
        self.impale = skillGradeConf.get("impale", 0)
        self.clip = skillGradeConf.get("clip", 0)
        self.interval = skillGradeConf.get("interval", 0)
        self.duration = skillGradeConf.get("duration", 0)
        self.coolDown = skillGradeConf.get("coolDown", 0)
        self.range = skillGradeConf.get("range", 0)
        self.weaponId = skillGradeConf.get("weaponId", 0)
        self.double = skillGradeConf.get("double", [])
        self.isReturn = skillGradeConf.get("isReturn", 0)

    def initStar(self):
        """
        初始化星级属性数据
        """
        self.fatal = 0
        self.distance = False
        self.impaleCatch = False
        self.gunSkinMultiple = 1
        self.dayFreeMaxMulti = 0
        for skillStar in xrange(1, self.skillStar + 1):
            skillStarConf = config.getSkillStarConf(self.skillId, skillStar, self.skillMode)
            for abilityConf in skillStarConf["abilities"]:
                ability = abilityConf.get("ability", 0)
                value = abilityConf.get("value", 0)
                if ability == 1:                                # 减少冷却时间
                    self.coolDown -= value
                elif ability == 2:                              # 提高威力
                    self.power += self.power * value / 100
                elif ability == 3:                              # 增加技能效果时间
                    self.duration += value
                elif ability == 6:                              # 增加子弹数
                    self.clip += value
                elif ability == 7:                              # 增加机器人HP
                    self.HP += self.HP * value / 100
                elif ability == 8:                              # 提高对远距离鱼的威力
                    self.distance = True
                elif ability == 9:                              # 冰锥HP在65%以上时无法一击致命
                    self.fatal += value / 100
                elif ability == 10:                             # 被贯穿的鱼必被捕获
                    self.impaleCatch = True
                elif ability == 13:                             # 提升火蛇加农炮喷射距离
                    self.range += self.range * value / 100
                elif ability == 25:                             # 每天可在最高[var]倍下免费使用1次
                    self.dayFreeMaxMulti = value
        if self.table.typeName in config.NORMAL_ROOM_TYPE:      # 新手/普通/好友渔场-皮肤炮特性，技能双倍消耗/收益
            self.gunSkinMultiple = 2 if self.player.gunId in self.double else 1
            if self.table.typeName == config.FISH_GRAND_PRIX and self.player.isGrandPrixMode():  # 大奖赛冷却减半
                self.coolDown //= 2
                if self.skillType == 1:
                    self.cost = 0
        elif self.table.typeName in [config.FISH_TIME_MATCH, config.FISH_TIME_POINT_MATCH]: # 回馈赛-弹药消耗为0，冷却减半
            self.cost = 0
        elif self.table.typeName == config.FISH_FIGHT:          # 渔友竞技-冷却减半
            self.coolDown //= 2
        self.coolDown = max(self.coolDown, 1)

    def initCommonData(self):
        """
        初始化公共数据
        """
        self.fpMultiple = self.player.fpMultiple
        self.skillMultiple = 1
        # 格林机关枪除回馈赛外收益翻倍
        if self.skillId == 5110 and self.table.typeName != config.FISH_TIME_POINT_MATCH:
            self.skillMultiple = 2
        self.gunSkinMultiple *= self.skillMultiple
        self.gunX = util.getGunX(self.player.nowGunLevel, self.skillMode)
        self.powerRate = 1                                  # 技能威力倍率
        self.durationEndTime = 0                            # 技能效果结束时间
        self.skillFishes = []                               # 技能召唤的鱼(捕鱼机器人)
        self.originClip = self.clip                         # 技能原始总子弹数
        self.originEnergy = 0                               # 技能单发原始能量
        self.energy = 0                                     # 技能单发扣减能量
        self.totalPower = 0                                 # 技能总威力（无加成）
        self.originRealPower = 0                            # 技能总威力（加能量）
        self.deductionHP = 0                                # 技能扣减HP
        self.consumePower = 0                               # 技能曲线消耗能量
        self.onceCostCoin = self.getCost / float(self.originClip) * self.fpMultiple  # 技能单发价值金币数
        # 新手期间技能使用次数
        _key = UserData.newbieUseSkillTimes % (FISH_GAMEID, self.player.userId)
        self.newbieUseTimes = daobase.executeUserCmd(self.player.userId, "HGET", _key, str(self.skillId)) or 0

    def initData(self):
        """
        初始化技能数据
        """
        self.initLevel()
        self.initStar()
        self.initCommonData()

    def initState(self):
        """
        初始化技能状态
        """
        self.state = 0                                      # 0:未使用 1:装备中 2:使用中
        self.cdStartTime = int(time.time())                 # 技能冷却开始时的时间
        self.usingDayFree = False                           # 是否每日免费装备中

    def use(self, select):
        """
        选中取消技能
        """
        eventId = None
        orgState = self.state
        if select:
            if self.state == 0:
                if self.player.gchgTimer:
                    self.player.gchgTimer.cancel()
                self.state = 1
                self.clear()
                self.initData()
                if self.checkIfDayFree():
                    # 每日免费使用
                    self.doDayFreeUse()
                    self.usingDayFree = True
                else:
                    self.player.costClip(self.getCost, "BI_NFISH_USE_SKILL_%d" % self.skillId)
                self.player.addUsingSkill(self.skillId, self.skillType)
                eventId = "BI_NFISH_GE_SKILL_USE"
        else:
            if self.state == 1:
                if self.usingDayFree:
                    self.cancelDayFreeUse()
                else:
                    self.player.addClip(self.getCost, 0, self.skillId)
                self.state = 0
                self.player.removeUsingSkill(self.skillId, self.skillType)
                eventId = "BI_NFISH_GE_SKILL_CANCEL"
        self.table.broadcastSkillUse(self, select, self.player.userId, orgState)
        if config.LOCK_KINDID in self.player.skill_item:
            self.player.skill_item[config.LOCK_KINDID].pause_and_continue_time(config.LOCK_KINDID)
        self.player.gunEffectState(2)
        if eventId:
            bireport.reportGameEvent(eventId, self.player.userId, FISH_GAMEID, self.table.roomId,  self.table.tableId,
                                     int(self.skillId), self.player.level, 1 if self.usingDayFree else 0, 0, [], self.player.clientId)
            ftlog.info(
                "skill_release->use",
                "userId =", self.player.userId,
                "eventId =", eventId,
                "skillId =", self.skillId,
                "skillStar =", self.skillStar,
                "skillGrade =", self.skillGrade,
                "skillType =", self.skillType,
                "gunId =", self.player.gunId,
                "nowGunLevel =", self.player.nowGunLevel,
                "isDayFree =", self.usingDayFree
            )

    def end(self):
        """
        结束技能
        """
        if ftlog.is_debug():
            ftlog.debug("skill end, userId =", self.player.userId, "skillId =", self.skillId, "useTimes =", self.newbieUseTimes)
        if self.table.typeName == config.FISH_NEWBIE and not self.player.redState:
            _key = UserData.newbieUseSkillTimes % (FISH_GAMEID, self.player.userId)
            daobase.executeUserCmd(self.player.userId, "HINCRBY", _key, str(self.skillId), 1)
        self.initState()
        self.player.removeUsingSkill(self.skillId, self.skillType)
        self.table.broadcastSkillEnd(self)
        if config.LOCK_KINDID in self.player.skill_item:        # 锁定继承开始时间计时
            self.player.skill_item[config.LOCK_KINDID].pause_and_continue_time(config.LOCK_KINDID)
        self.player.syncSkillItemSlots()
        self.player.gunEffectState(2)
        if self.table.typeName in config.NORMAL_ROOM_TYPE:
            # 使用技能事件
            from newfish.game import TGFish
            from newfish.entity.event import UseSkillEvent
            chip = int(self.getCost * self.fpMultiple)
            event = UseSkillEvent(self.player.userId, FISH_GAMEID, self.table.roomId, self.table.tableId, self.skillId, self.fpMultiple, chip)
            TGFish.getEventBus().publishEvent(event)
            self.player.triggerUseSkillEvent(event)

    def clear(self):
        """
        清除技能数据(广播捕鱼机器人因主人离开房间而死亡)
        """
        self.player.delFire(wpId=self.weaponId)
        if self.skillFishes:
            fishes = []
            for fishId in self.skillFishes:
                if fishId in self.table.fishMap and self.table.fishMap[fishId]["alive"]:
                    for buffer in self.table.fishMap[fishId]["buffer"]:
                        skillId = buffer[0]
                        skillEndTime = buffer[1]
                        if skillId == self.skillId and skillEndTime >= time.time():
                            if self.table.fishMap[fishId]["alive"]:
                                self.table.refreshFishTypeCount(self.table.fishMap[fishId])
                            self.table.fishMap[fishId]["alive"] = False
                            fishes.append(fishId)
            if fishes:
                self.table.broadcastSkillEffect(self.player, time.time(), fishes, self.skillId)

    def getSkillFishesAlive(self):
        """
        获取捕鱼机器人是否存活
        """
        if self.skillFishes:
            for fishId in self.skillFishes:
                if fishId in self.table.fishMap and self.table.fishMap[fishId]["alive"]:
                    for buffer in self.table.fishMap[fishId]["buffer"]:
                        skillId = buffer[0]
                        skillEndTime = buffer[1]
                        if skillId == self.skillId and skillEndTime >= time.time():
                            return True
        return False

    def costClip(self, bulletId, fPosx=0, fPosy=0):
        """
        消耗技能子弹
        """
        if self.clip > 0:
            lastCoin = self.player.holdCoin
            for val in self.player.usingSkill:      # 当前技能使用中时，检测之前技能是否在使用中，如果是，结束之前技能
                skillId = val.get("skillId")
                skillType = val.get("skillType")
                if skillId != self.skillId:
                    skill = self.player.getSkill(skillId)
                    if skill.state == 2:
                        skill.end()
            self.state = 2
            self.clip -= 1
            self.position[bulletId] = [fPosx, fPosy]
            self.updateSkillData()
            self.player.reportBIFeatureData("BI_NFISH_GE_FT_FIRE", self.weaponId, self.onceCostCoin)

            # TODO 该段代码有问题（lichen）
            if lastCoin > self.table.runConfig.coinShortage > self.player.holdCoin:
                coinShortageCount = gamedata.getGameAttrJson(self.player.userId, FISH_GAMEID, GameData.coinShortageCount, {})
                coinShortageCount.setdefault(str(self.table.runConfig.fishPool), 0)
                coinShortageCount[str(self.table.runConfig.fishPool)] += 1
                gamedata.setGameAttr(self.player.userId, FISH_GAMEID, GameData.coinShortageCount, json.dumps(coinShortageCount))
            return True
        return False

    def returnClip(self):
        """
        返还技能打空子弹
        """
        pass

    def updateSkillData(self):
        """
        更新技能数据
        """
        self.updateSkillEnergy()
        skillGradeConf = config.getSkillGradeConf(self.skillId, self.skillGrade, self.skillMode)
        self.impale = skillGradeConf.get("impale", 0)
        powerAddtion = self.player.getPowerAddition(self.weaponId)
        self.totalPower = float(self.power * self.powerRate * powerAddtion)
        self.originRealPower = self.totalPower + self.energy
        self.deductionHP = self.totalPower
        if ftlog.is_debug():
            ftlog.debug(
                "updateSkillData->",
                "userId =", self.player.userId,
                "impale =", self.impale,
                "totalPower =", self.totalPower,
                "deductionHP =", self.deductionHP
            )

    def updateSkillEnergy(self):
        """
        更新技能能量（通过奖池转换而来）
        """
        bonus = 0
        if self.table.room.lotteryPool:
            bonus = self.table.room.lotteryPool.getSkillPoolCoin() // self.fpMultiple
        randomNum = random.uniform(0.5, 1.5)
        singleEnergy = math.ceil(self.power * self.powerRate * randomNum)
        self.originEnergy = min(self.player.energy, singleEnergy)
        self.energy = max(min(self.originEnergy, bonus), 0)
        self.player.energy -= self.energy
        if self.table.room.lotteryPool:
            self.table.room.lotteryPool.deductionSkillPoolCoin(int(self.energy * self.fpMultiple))
        if ftlog.is_debug():
            ftlog.debug(
                "updateSkillEnergy->",
                "userId =", self.player.userId,
                "bonus =", bonus,
                "randomNum =", randomNum,
                "singleEnergy =", singleEnergy,
                "originEnergy =", self.originEnergy,
                "energy =", self.energy,
                "player.energy =", self.player.energy
            )

    @property
    def getCost(self):
        """
        技能消耗子弹数
        """
        return self.cost * self.gunSkinMultiple / self.skillMultiple * self.gunX

    @property
    def cdTimeLeft(self):
        """
        技能剩余CD时间
        """
        timeLeft = self.cdStartTime + self.coolDown - int(time.time())
        return max(timeLeft, 0)

    def changeCDStartTime(self, coolDown=0):
        """
        改变技能CD的开始时间
        """
        cdAddition = self.player.getSkillCDAddition(self.weaponId)
        if coolDown:
            self.cdStartTime -= coolDown
        else:
            self.cdStartTime -= self.coolDown * cdAddition

    def catchFish(self, bulletId, wpId, fIds, extends):
        """
        技能捕获判定
        """
        raise NotImplementedError

    def normalCatchFish(self, bulletId, wpId, fIds, extends):
        """
        普通捕获算法
        """
        catch = []
        gain = []
        gainChip = 0
        exp = 0
        otherCatch = {}
        isCatch = False
        self.consumePower = 0
        fishes, fishesHPDict = self._sortedFishes(fIds, extends)
        totalPowerRate = self.totalPower / self.originRealPower  # 技能总威力（无加成）的占比
        realPower = self.originRealPower
        bufferCoinAdd = self.player.getCoinAddition(self.weaponId)
        curveProfitCoin = 0
        aloofOdds = self.player.dynamicOdds.getOdds(skill=self, aloofFish=True)
        nonAloofOdds = self.player.dynamicOdds.getOdds(skill=self, aloofFish=False)
        for fId, probbRadix in fishes:
            isOK, catchUserId = self.table.verifyFish(self.player.userId, fId, wpId)
            if not isOK:
                continue
            catchMap = {}
            catchMap["fId"] = fId
            catchMap["reason"] = 1
            fishInfo = self.table.fishMap[fId]
            probb, realPower = self.getSkillCatchProbb(fId, len(fIds), realPower, probbRadix, aloofOdds, nonAloofOdds)
            catchMap["HP"] = fishInfo["HP"]
            randInt = random.randint(1, 10000)
            if randInt <= probb:
                multiple = self.gunSkinMultiple
                # 欺诈只获得1倍收益.
                if catchUserId != self.player.userId:
                    multiple = 1
                fishGainChip, fishGain, fishExp = self.table.dealKillFishGain(fId, self.player, self.fpMultiple, multiple, bufferCoinAdd)
                catchMap["reason"] = 0
                if catchUserId == self.player.userId:
                    isCatch = True
                    catch.append(catchMap)
                    gainChip += fishGainChip
                    exp += fishExp
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain, catchMap, fishExp)
            else:
                if fId in fishesHPDict:
                    catch.append(catchMap)
                fishGainChip, fishGain = self.table.dealHitBossGain(self.totalPower, fId, self.player)
                if catchUserId == self.player.userId:
                    gainChip += fishGainChip
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain)
            # 技能曲线
            if self.table.typeName in config.DYNAMIC_ODDS_ROOM_TYPE:
                fishType = fishInfo["fishType"]
                fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
                if fishConf["type"] in config.NON_ALOOF_FISH_TYPE:
                    if catchMap["reason"] == 0:
                        curveProfitCoin += totalPowerRate * fishConf["value"] * self.gunSkinMultiple * self.fpMultiple
        self.consumePower = min(self.consumePower, self.originRealPower)
        curveLossCoin = self.onceCostCoin * self.consumePower / self.originRealPower
        if ftlog.is_debug():
            ftlog.debug(
                "normalCatchFish->",
                "userId =", self.player.userId,
                "catch =", catch,
                "gain =", gain,
                "gainChip =", gainChip,
                "onceCostCoin =", self.onceCostCoin,
                "consumePower =", self.consumePower,
                "originRealPower =", self.originRealPower,
                "totalPowerRate =", totalPowerRate,
                "curveLossCoin =", curveLossCoin,
                "curveProfitCoin =", curveProfitCoin
            )
        self.player.dynamicOdds.updateOdds(curveProfitCoin - curveLossCoin)
        if isCatch:
            self.player.addCombo()
        for userId, catchInfo in otherCatch.iteritems():
            otherPlayer = self.table.getPlayer(userId)
            if otherPlayer:
                self.table.dealCatch(bulletId, wpId, otherPlayer, catchInfo["catch"], catchInfo["gain"],
                                     catchInfo["gainChip"], catchInfo["exp"], self.fpMultiple, self.gunSkinMultiple, self.gunX)
        return catch, gain, gainChip, exp

    def _getFishProbbRadix(self, fishInfo, fishConf):
        # 新手任务期间概率特殊处理
        if self.player and util.isNewbieRoom(self.player.table.typeName):
            curTaskId = self.player.taskSystemUser.curTask.getTaskId()
            isTaskOver = self.player.taskSystemUser.curTask.isTaskOver()
            probbRadix = config.getCommonValueByKey("newbieFishProbb").get(str(fishConf["type"]), {}).get(str(curTaskId))
            if not isTaskOver and probbRadix:
                if fishConf["type"] == 2:
                    if self.player.bossAppearCount > 1:
                        return probbRadix
                else:
                    return probbRadix
        if fishInfo["HP"] > 0:
            return fishConf["probb2"]
        elif fishConf["type"] in config.RAINBOW_BONUS_FISH_TYPE:
            value = fishConf["score"]
            gunMultiple = config.getGunConf(self.player.gunId, self.player.clientId, self.player.gunLv, self.table.gameMode).get("multiple", 1)
            rainbowPoolCoin = 0
            if self.table.room.lotteryPool:
                rainbowPoolCoin = self.table.room.lotteryPool.getRainbowPoolCoin()
            if fishConf["type"] in config.TERROR_FISH_TYPE:
                value = config.getWeaponConf(fishConf["weaponId"], False, self.table.gameMode)["power"]
            if self.player.dynamicOdds.currRechargeBonus >= value * gunMultiple * self.fpMultiple:
                # 存在充值奖池
                if ftlog.is_debug():
                    ftlog.debug("_getFishProbbRadix->currRechargeBonus =", self.player.dynamicOdds.currRechargeBonus, fishConf)
                return fishConf["probb1"]
            elif (min(3 * self.originEnergy * self.fpMultiple, rainbowPoolCoin) >= value * gunMultiple * self.fpMultiple):
                # 存在能量及彩虹鱼奖池
                if ftlog.is_debug():
                    ftlog.debug("_getFishProbbRadix->energy =", self.originEnergy, rainbowPoolCoin, fishConf)
                return fishConf["probb1"]
            return fishConf["probb2"]
        else:
            return fishConf["probb1"]

    def _sortedFishes(self, fIds, extends=None):
        """
        排序所有鱼
        :param fIds: 鱼的Ids
        :param extends: 扩展
        """
        fishesDict = {}
        fishesHPDict = {}
        for fId in fIds:
            isOK = self.table.findFish(fId)
            if not isOK:
                continue
            fishInfo = self.table.fishMap[fId]
            originHP = fishInfo["HP"]
            fishType = fishInfo["fishType"]
            fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
            fatal = self.table.dealIceConeEffect(fId, fishConf)
            fishHP = int(originHP - self.deductionHP)
            fishHP = self.table.dealFishHP(fId, fishHP, fatal)
            fishesDict[fId] = self._getFishProbbRadix(fishInfo, fishConf)
            if originHP != fishHP:
                fishesHPDict[fId] = originHP
        fishes = sorted(fishesDict.iteritems(), key=lambda d: d[1])
        if ftlog.is_debug():
            ftlog.debug("_sortedFishes->", fishes, fishesHPDict, fIds)
        return fishes, fishesHPDict

    def getSkillCatchProbb(self, fId, fIdsCount, realPower, probbRadix, aloofOdds, nonAloofOdds):
        """
        技能捕获鱼的概率
        :param fId: 鱼的ID
        :param fIdsCount: 鱼的数量
        :param realPower: 真实威力
        :param probbRadix: 鱼被捕获的概率数
        :param aloofOdds: 高冷鱼概率
        :param nonAloofOdds: 非高冷鱼概率
        :return: 捕获概率、剩余能量
        """
        fishInfo = self.table.fishMap[fId]
        fishHP = fishInfo["HP"]
        ftType = fishInfo["type"]
        coefficient = self.table.getProbbCoefficient(self.player, fishInfo)
        odds = 0
        lossPower = 0
        # 概率参数为0时，无HP必定被捕获，有HP无法被捕获
        if probbRadix == 0:
            probb = 10000 if fishHP <= 0 else 0
        else:
            if realPower <= 0:
                probb = 0
            else:
                if ftType in config.NON_ALOOF_FISH_TYPE:
                    odds = nonAloofOdds
                else:
                    odds = aloofOdds
                if odds > 0:
                    lossPower = probbRadix / odds
                    if realPower >= lossPower:
                        probb = 10000
                    else:
                        probb = realPower / lossPower * 10000
                else:
                    lossPower = probbRadix * 2
                    probb = 0
                if ftType in config.NON_ALOOF_FISH_TYPE:
                    if realPower >= lossPower:
                        self.consumePower += lossPower
                    else:
                        self.consumePower += realPower
                realPower -= lossPower
        probb *= coefficient
        if ftType in config.RED_FISH_TYPE:
            probb *= self.player.catchRedFishProbbRatio
            if self.player.userId in config.getPublic("banRedFishList", []):
                probb *= 0.3
        if ftlog.is_debug():
            ftlog.debug(
                "getSkillCatchProbb->",
                "userId =", self.player.userId,
                "probb =", probb,
                "odds =", odds,
                "fId =", fId,
                "fIdsCount =", fIdsCount,
                "fishType =", fishInfo["fishType"],
                "fishHP =", fishHP,
                "realPower =", realPower,
                "probbRadix =", probbRadix,
                "aloofOdds =", aloofOdds,
                "nonAloofOdds =", nonAloofOdds,
                "lossPower =", lossPower,
                "consumePower =", self.consumePower,
                "coefficient =", coefficient
            )
        return probb, realPower

    def getRealEffectSkillGrade(self):
        """
        玩家在欢乐竞技和王者争霸中夺冠，则下次比赛中使用该技能降级
        """
        _skillGrade = self.skillGrade
        match_rank = None
        reductGrade = 0
        if self.table.runConfig.fishPool == 44102:
            reductGrade = 5
            match_rank = config.RANK_MATCH_HLJJ
        elif self.table.runConfig.fishPool == 44103:
            reductGrade = 8
            match_rank = config.RANK_MATCH_WZZB
        if match_rank and reductGrade:
            rankingList = hallranking.rankingSystem.getTopN(match_rank, 1, int(time.time()))
            if rankingList and len(rankingList.rankingUserList) > 0 and rankingList.rankingUserList[0].userId == self.player.userId:
                _skillGrade = max(1, _skillGrade - reductGrade)
        if ftlog.is_debug():
            ftlog.debug("getRealEffectSkillGrade, userId =", self.player.userId, "lastGrade =", self.skillGrade,
                    "curGrade =", _skillGrade, self.table.runConfig.fishPool, match_rank, reductGrade)
        return _skillGrade

    def checkIfDayFree(self):
        """检查技能是否免费"""
        if ftlog.is_debug():
            ftlog.debug("SkillBase.checkIfDayFree IN", "tableId=", self.table.tableId, "userId=", self.player.userId,
                        "dayFreeMaxMulti=", self.dayFreeMaxMulti, "gunX=", self.gunX)
        if self.table.typeName == config.FISH_TIME_MATCH or self.table.typeName == config.FISH_TIME_POINT_MATCH:
            return False
        self.gunX = util.getGunX(self.player.nowGunLevel, self.skillMode)
        if self.dayFreeMaxMulti >= self.gunX:
            data = weakdata.getDayFishData(self.player.userId, WeakData.skillFreeUseCount, {})
            if ftlog.is_debug():
                ftlog.debug("SkillBase.checkIfDayFree get data", "userId=", self.player.userId, "data=", data)
            if str(self.skillId) in data:
                # 目前固定每日免费1次，不走配置
                if data[str(self.skillId)] < 1:
                    return True
            else:
                return True
        return False

    def doDayFreeUse(self):
        """免费使用次数"""
        data = weakdata.getDayFishData(self.player.userId, WeakData.skillFreeUseCount, {})
        skillId = str(self.skillId)
        if skillId in data:
            data[skillId] += 1
        else:
            data[skillId] = 1
        weakdata.setDayFishData(self.player.userId, WeakData.skillFreeUseCount, data)
        return data[skillId]

    def cancelDayFreeUse(self):
        """取消免费使用的次数"""
        data = weakdata.getDayFishData(self.player.userId, WeakData.skillFreeUseCount, {})
        skillId = str(self.skillId)
        if skillId in data:
            data[skillId] -= 1
            weakdata.setDayFishData(self.player.userId, WeakData.skillFreeUseCount, data)
        return data.get(skillId, 0)

class SkillMissile(SkillBase):
    """
    合金飞弹(5101)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        """合金飞弹捕鱼"""
        useMissileCount = gamedata.getGameAttr(self.player.userId, FISH_GAMEID, GameData.useMissileCount)
        if not useMissileCount or useMissileCount < 5 and self.table.runConfig.fishPool == 44001:   # 普通渔场2倍爆炸威力增加5倍
            self.powerRate = 5
            useMissileCount = 1 if not useMissileCount else useMissileCount + 1
            gamedata.setGameAttr(self.player.userId, FISH_GAMEID, GameData.useMissileCount, useMissileCount)
        return self.normalCatchFish(bulletId, wpId, fIds, extends)

    def _sortedFishes(self, fIds, extends=None):
        """按照鱼的捕获概率排序"""
        fishesDict = {}
        fishesHPDict = {}
        for fId in fIds:
            isOK = self.table.findFish(fId)
            if not isOK:
                continue
            fishInfo = self.table.fishMap[fId]
            originHP = fishInfo["HP"]
            fishType = fishInfo["fishType"]
            fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
            fatal = self.table.dealIceConeEffect(fId, fishConf)         # 冰锥HP在65%以上时无法一击致命
            fishHP = int(originHP - self.deductionHP)
            fishHP = self.table.dealFishHP(fId, fishHP, fatal)
            fishesDict[fId] = self._getFishProbbRadix(fishInfo, fishConf)
            if originHP != fishHP:
                fishesHPDict[fId] = originHP
        # 网中的鱼数量大于5时从小到大排序;反之从大到小.
        fishes = sorted(fishesDict.iteritems(), key=lambda d: d[1], reverse=(len(fishesDict) <= 5))
        if ftlog.is_debug():
            ftlog.debug(self.skillId, ", _sortedFishes->", fishes, fishesHPDict, fIds)
        return fishes, fishesHPDict

class SkillMagic(SkillBase):
    """
    魔术炮(5102)
    """
    def initStar(self):
        super(SkillMagic, self).initStar()
        if self.table.typeName == config.FISH_TIME_MATCH:   # 回馈赛-召唤出的鱼永久无敌
            self.duration += 999
        elif self.table.typeName == config.FISH_TIME_POINT_MATCH:
            self.duration += 999

    def catchFish(self, bulletId, wpId, fIds, extends):
        catch, gain, gainChip, exp = [], [], 0, 0
        position = self.position.get(bulletId, [0, 0])
        buffer = None
        endTime = time.time() + self.duration
        fishType = 0
        if self.duration:
            buffer = [self.skillId, endTime, self.player.userId, self.skillStar, self.skillGrade, self.duration, 0]
            fishes = self.player.getTargetFishs()
            if fishes:
                fishType = random.choice(fishes)
        _skillGrade = self.skillGrade                       # self.getRealEffectSkillGrade()
        commonRoom = config.NORMAL_ROOM_TYPE
        if (self.table.typeName in commonRoom and not fishType) or \
           (self.table.typeName not in commonRoom):         # 普通场无任务时或者非普通场召唤倍率鱼
            callFishList = config.getCallMultipleFishConf(_skillGrade, self.table.typeName, self.table.runConfig.fishPool)
            totalWeight = sum([dict(fishes)["weight"] for fishes in callFishList if fishes])
            fishType = 0                                    # callFishList[0]["fishType"]
            if totalWeight > 0:
                weight = random.randint(0, totalWeight)
                for callFish in callFishList:
                    if callFish["weight"] == 0:
                        continue
                    if callFish and weight - callFish["weight"] <= 0:
                        fishType = callFish["fishType"]
                        break
                    weight -= callFish["weight"]
        if fishType > 0:
            groupName = "call_%d_lv%d" % (fishType, _skillGrade)
            self.table.insertFishGroup(groupName, position, buffer=buffer, gameResolution=self.player.gameResolution)
            if ftlog.is_debug():
                ftlog.debug("SkillMagic, insertFishGroup, userId =", self.player.userId, groupName, self.skillGrade, _skillGrade)
        else:
            if ftlog.is_debug():
                ftlog.debug("catchFish, not find fishType !", self.player.userId)
        return catch, gain, gainChip, exp

class SkillCannon(SkillBase):
    """
    火蛇加农炮(5103)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        catch = []
        gain = []
        gainChip = 0
        exp = 0
        otherCatch = {}
        isCatch = False
        bufferCoinAdd = self.player.getCoinAddition(self.weaponId)
        realPower = self.originRealPower
        aloofOdds = self.player.dynamicOdds.getOdds(skill=self, aloofFish=True)
        for fId in fIds:
            isOK, catchUserId = self.table.verifyFish(self.player.userId, fId)
            if not isOK or self.impale <= 0:
                break
            catchMap = {}
            catchMap["fId"] = fId
            catchMap["reason"] = 1
            fishInfo = self.table.fishMap[fId]
            fishType = fishInfo["fishType"]
            fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
            originHP = fishInfo["HP"]
            probbRadix = fishInfo["HP"] + fishConf["probb2"]
            if self.impale > probbRadix:  # 子弹能穿刺鱼
                self.impale -= probbRadix
                self.impale = max(self.impale, 0)
                randInt = random.randint(1, 10000)
                if randInt <= 9000 or self.impaleCatch:
                    catchMap["reason"] = 0
                if ftlog.is_debug():
                    ftlog.debug("SkillCannon->impale->",
                                "userId =", self.player.userId,
                                "randInt =", randInt,
                                "fId =", fId,
                                "fishType =", fishInfo["fishType"],
                                "probbRadix =", probbRadix,
                                "impale =", self.impale)
            else:  # 子弹无法穿刺鱼
                realPower += self.impale
                self.impale = 0
                fatal = self.table.dealIceConeEffect(fId, fishConf)
                self.deductionHP = self.totalPower
                fishHP = int(originHP - self.deductionHP)
                self.table.dealFishHP(fId, fishHP, fatal)
                probbRadix = self._getFishProbbRadix(fishInfo, fishConf)
                probb, realPower = self.getSkillCatchProbb(fId, len(fIds), realPower, probbRadix, aloofOdds, aloofOdds)
                randInt = random.randint(1, 10000)
                if randInt <= probb:
                    catchMap["reason"] = 0
            catchMap["HP"] = fishInfo["HP"]
            if catchMap["reason"] == 0:
                multiple = self.gunSkinMultiple
                # 欺诈只获得1倍收益.
                if catchUserId != self.player.userId:
                    multiple = 1
                fishGainChip, fishGain, fishExp = self.table.dealKillFishGain(fId, self.player, self.fpMultiple, multiple, bufferCoinAdd)
                if catchUserId == self.player.userId:
                    isCatch = True
                    catch.append(catchMap)
                    gainChip += fishGainChip
                    exp += fishExp
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain, catchMap, fishExp)
            else:
                if originHP != catchMap["HP"]:
                    catch.append(catchMap)
                fishGainChip, fishGain = self.table.dealHitBossGain(self.power + self.impale, fId, self.player)
                if catchUserId == self.player.userId:
                    gainChip += fishGainChip
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain)
        if ftlog.is_debug():
            ftlog.debug("catchFish->",
                        "userId =", self.player.userId,
                        "catch =", catch,
                        "gain =", gain,
                        "onceCostCoin =", self.onceCostCoin,
                        "originRealPower =", self.originRealPower)
        if isCatch:
            self.player.addCombo()
        for userId, catchInfo in otherCatch.iteritems():
            otherPlayer = self.table.getPlayer(userId)
            if otherPlayer:
                self.table.dealCatch(bulletId, wpId, otherPlayer, catchInfo["catch"], catchInfo["gain"],
                                     catchInfo["gainChip"], catchInfo["exp"], self.fpMultiple, self.gunSkinMultiple, self.gunX)
        return catch, gain, gainChip, exp

class SkillFrozen(SkillBase):
    """
    极冻炮(5104)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        catch = []
        gain = []
        gainChip = 0
        exp = 0
        position = self.position.get(bulletId, [0, 0])
        endTime = time.time() + self.duration
        invincibleEndTime = 0
        frozenBuffer = [self.skillId, endTime, self.player.userId, self.skillStar, self.skillGrade, self.duration, 0]
        self.table.insertFishGroup("call_piton_%d" % self.skillStar, position, self.HP, frozenBuffer,
                                   gameResolution=self.player.gameResolution)
        frozenFishes = []
        invincibleFishes = []
        addTimeGroups = []
        for fId in fIds:
            isOK = self.table.findFish(fId)
            if not isOK:
                continue
            ftType = self.table.fishMap[fId]["type"]
            if ftType in config.ICE_FISH_TYPE:    # 冰锥不会刷新冰冻时间
                continue
            isCoverInvincible = True
            buffers = self.table.fishMap[fId]["buffer"]
            # 计算能否冰冻
            isCoverFrozen, lastFrozenTime, frozenTime, _ = self.table.checkCoverFrozen(fId, self.duration, endTime)
            if isCoverFrozen:
                self.table.frozenFish(fId, frozenBuffer, lastFrozenTime, frozenTime, addTimeGroups)
                frozenFishes.append(fId)
            # 计算能否无敌
            for lastBuffer in buffers:
                if lastBuffer[0] == 5102 and lastBuffer[1] > time.time():
                    isCoverInvincible = False
                    break
            if isCoverInvincible and self.table.typeName not in config.NORMAL_ROOM_TYPE:  # 非普通场极冻炮附加无敌效果
                if ftType in [6, 7]:  # 冰锥、捕鱼机器人不会有无敌效果
                    continue
                invincibleDuration = round(self.duration / 3.0, 3)
                if self.player.gunId == 1165:
                    invincibleDuration += 2
                invincibleEndTime = time.time() + invincibleDuration
                invincibleBuffer = [5102, invincibleEndTime, self.player.userId, self.skillStar, self.skillGrade, invincibleDuration, 0, self.skillType]
                self.table.setFishBuffer(fId, invincibleBuffer)
                invincibleFishes.append(fId)
        if frozenFishes:    # 广播新处于冰冻状态的鱼
            self.table.broadcastSkillEffect(self.player, endTime, frozenFishes, self.skillId)
        if invincibleFishes:     # 广播新处于无敌状态的鱼
            self.table.broadcastSkillEffect(self.player, invincibleEndTime, invincibleFishes, 5102)
        return catch, gain, gainChip, exp

class SkillGrenade(SkillBase):
    """
    榴弹炮(5105)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        return self.normalCatchFish(bulletId, wpId, fIds, extends)

    def _sortedFishes(self, fIds, extends=None):
        fishesDict = {}
        fishesHPDict = {}
        for fId in fIds:
            isOK = self.table.findFish(fId)
            if not isOK:
                continue
            fishInfo = self.table.fishMap[fId]
            originHP = fishInfo["HP"]
            fishType = fishInfo["fishType"]
            fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
            fatal = self.table.dealIceConeEffect(fId, fishConf)
            fishHP = int(originHP - self.deductionHP)
            fishHP = self.table.dealFishHP(fId, fishHP, fatal)
            fishesDict[fId] = self._getFishProbbRadix(fishInfo, fishConf)
            if originHP != fishHP:
                fishesHPDict[fId] = originHP
        # 从大到小排序.
        fishes = sorted(fishesDict.iteritems(), key=lambda d: d[1], reverse=True)
        if ftlog.is_debug():
            ftlog.debug(self.skillId, ", _sortedFishes->", fishes, fishesHPDict, fIds)
        return fishes, fishesHPDict

    def returnClip(self):
        """
        打空返还技能子弹
        """
        if self.state == 2:
            if self.clip < self.originClip:
                self.clip += 1
                self.updateSkillData()

class SkillEnergy(SkillBase):
    """
    汇能弹(5106)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        catch = []
        gain = []
        gainChip = 0
        exp = 0
        otherCatch = {}
        isCatch = False
        bufferCoinAdd = self.player.getCoinAddition(self.weaponId)
        realPower = self.originRealPower
        aloofOdds = self.player.dynamicOdds.getOdds(skill=self, aloofFish=True)
        for fId in fIds:
            isOK, catchUserId = self.table.verifyFish(self.player.userId, fId)
            if not isOK or self.impale <= 0:
                break
            catchMap = {}
            catchMap["fId"] = fId
            catchMap["reason"] = 1
            fishInfo = self.table.fishMap[fId]
            fishType = fishInfo["fishType"]
            fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
            originHP = fishInfo["HP"]
            probbRadix = fishInfo["HP"] + fishConf["probb2"]
            if self.impale > probbRadix:    # 子弹能穿刺鱼
                self.impale -= probbRadix
                self.impale = max(self.impale, 0)
                randInt = random.randint(1, 10000)
                if randInt <= 9000 or self.impaleCatch:
                    catchMap["reason"] = 0
                if ftlog.is_debug():
                    ftlog.debug("SkillEnergy->impale->",
                                "userId =", self.player.userId,
                                "randInt =", randInt,
                                "fId =", fId,
                                "fishType =", fishInfo["fishType"],
                                "probbRadix =", probbRadix,
                                "impale =", self.impale)
            else:                           # 子弹无法穿刺鱼
                realPower += self.impale
                self.impale = 0
                fatal = self.table.dealIceConeEffect(fId, fishConf)
                self.deductionHP = self.totalPower
                fishHP = int(originHP - self.deductionHP)
                self.table.dealFishHP(fId, fishHP, fatal)
                probbRadix = self._getFishProbbRadix(fishInfo, fishConf)
                probb, realPower = self.getSkillCatchProbb(fId, len(fIds), realPower, probbRadix, aloofOdds, aloofOdds)
                randInt = random.randint(1, 10000)
                if randInt <= probb:
                    catchMap["reason"] = 0
            catchMap["HP"] = fishInfo["HP"]
            if catchMap["reason"] == 0:
                multiple = self.gunSkinMultiple
                # 欺诈只获得1倍收益.
                if catchUserId != self.player.userId:
                    multiple = 1
                fishGainChip, fishGain, fishExp = self.table.dealKillFishGain(fId, self.player, self.fpMultiple, multiple, bufferCoinAdd)
                if catchUserId == self.player.userId:
                    isCatch = True
                    catch.append(catchMap)
                    gainChip += fishGainChip
                    exp += fishExp
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain, catchMap, fishExp)
            else:
                if originHP != catchMap["HP"]:
                    catch.append(catchMap)
                fishGainChip, fishGain = self.table.dealHitBossGain(self.power + self.impale, fId, self.player)
                if catchUserId == self.player.userId:
                    gainChip += fishGainChip
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain)
        if ftlog.is_debug():
            ftlog.debug("catchFish->",
                        "userId =", self.player.userId,
                        "catch =", catch,
                        "gain =", gain,
                        "onceCostCoin =", self.onceCostCoin,
                        "originRealPower =", self.originRealPower)
        if isCatch:
            self.player.addCombo()
        for userId, catchInfo in otherCatch.iteritems():
            otherPlayer = self.table.getPlayer(userId)
            if otherPlayer:
                self.table.dealCatch(bulletId, wpId, otherPlayer, catchInfo["catch"], catchInfo["gain"],
                                     catchInfo["gainChip"], catchInfo["exp"], self.fpMultiple, self.gunSkinMultiple, self.gunX)
        return catch, gain, gainChip, exp

class SkillFraud(SkillBase):
    """
    欺诈水晶(5107)
    """
    def initStar(self):
        super(SkillFraud, self).initStar()
        if self.table.typeName in [config.FISH_TIME_MATCH, config.FISH_FIGHT]:  # [回馈赛, 渔友竞技]-欺诈水晶3倍时长
            self.duration *= 3
        elif self.table.typeName == config.FISH_TIME_POINT_MATCH:
            self.duration *= 3

    def catchFish(self, bulletId, wpId, fIds, extends):
        catch = []
        gain = []
        gainChip = 0
        exp = 0
        fishes = []
        endTime = time.time() + self.duration
        for fId in fIds:
            isOK, catchUserId = self.table.verifyFish(self.player.userId, fId)
            if not isOK:
                continue
            buffers = self.table.fishMap[fId]["buffer"]
            for lastBuffer in buffers:
                if lastBuffer[0] == 5102 and lastBuffer[1] > time.time():
                    isOK = False
                    break
            if not isOK:
                continue
            ftType = self.table.fishMap[fId]["type"]
            if ftType in config.ICE_FISH_TYPE + config.ROBOT_FISH_TYPE:  # 欺诈水晶对冰锥、机器人无效
                continue
            buffer = [self.skillId, endTime, self.player.userId, self.skillStar, self.skillGrade, self.duration, 0, self.skillType]
            self.table.setFishBuffer(fId, buffer)
            fishes.append(fId)
        if fishes:
            self.table.broadcastSkillEffect(self.player, endTime, fishes, self.skillId)
        return catch, gain, gainChip, exp

class SkillLaser(SkillBase):
    """
    激光炮(5108)
    """
    def costClip(self, bulletId, fPosx=0, fPosy=0):
        """
        消耗技能子弹
        """
        if self.clip > 0:
            for val in self.player.usingSkill:      # 当前技能使用中时，检测之前技能是否在使用中，如果是，结束之前技能
                skillId = val.get("skillId")
                skillType = val.get("skillType")
                if skillId != self.skillId:
                    skill = self.player.getSkill(skillId)
                    if skill.state == 2:
                        skill.end()
            self.state = 2
            self.clip -= 1
            self.position[bulletId] = [fPosx, fPosy]
            return True
        return False

    def catchFish(self, bulletId, wpId, fIds, extends):
        catch = []
        gain = []
        gainChip = 0
        exp = 0
        otherCatch = {}
        isCatch = False
        if not extends:
            return catch, gain, gainChip, exp
        self.consumePower = 0
        self.powerRate = self.getPowerRate(extends)
        self.updateSkillData()
        fishes, fishesHPDict = self._sortedFishes(fIds, extends)
        totalPowerRate = self.totalPower / self.originRealPower  # 技能总威力（无加成）的占比
        realPower = self.originRealPower
        bufferCoinAdd = self.player.getCoinAddition(self.weaponId)
        curveProfitCoin = 0
        aloofOdds = self.player.dynamicOdds.getOdds(skill=self, aloofFish=True)
        nonAloofOdds = self.player.dynamicOdds.getOdds(skill=self, aloofFish=False)
        for fId, probbRadix in fishes:
            isOK, catchUserId = self.table.verifyFish(self.player.userId, fId, wpId)
            if not isOK:
                continue
            catchMap = {}
            catchMap["fId"] = fId
            catchMap["reason"] = 1
            fishInfo = self.table.fishMap[fId]
            probb, realPower = self.getSkillCatchProbb(fId, len(fIds), realPower, probbRadix, aloofOdds, nonAloofOdds)
            catchMap["HP"] = fishInfo["HP"]
            randInt = random.randint(1, 10000)
            if randInt <= probb:
                multiple = self.gunSkinMultiple
                # 欺诈只获得1倍收益.
                if catchUserId != self.player.userId:
                    multiple = 1
                fishGainChip, fishGain, fishExp = self.table.dealKillFishGain(fId, self.player, self.fpMultiple, multiple, bufferCoinAdd)
                catchMap["reason"] = 0
                if catchUserId == self.player.userId:
                    isCatch = True
                    catch.append(catchMap)
                    gainChip += fishGainChip
                    exp += fishExp
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain, catchMap, fishExp)
            else:
                if fId in fishesHPDict:
                    catch.append(catchMap)
                count, fishGain, fishGainChip = extends[0], [], 0
                if count == 1:  # 只有第一次算捕获
                    fishGainChip, fishGain = self.table.dealHitBossGain(self.totalPower, fId, self.player)
                if catchUserId == self.player.userId:
                    gainChip += fishGainChip
                    if fishGain:
                        gain.extend(fishGain)
                else:
                    otherCatch = self.table.extendOtherCatchGain(fId, catchUserId, otherCatch, fishGainChip, fishGain)
            # 技能曲线
            if self.table.typeName in config.DYNAMIC_ODDS_ROOM_TYPE:
                fishType = fishInfo["fishType"]
                fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
                if fishConf["type"] in config.NON_ALOOF_FISH_TYPE:
                    if catchMap["reason"] == 0:
                        curveProfitCoin += totalPowerRate * fishConf["value"] * self.gunSkinMultiple * self.fpMultiple
        self.consumePower = min(self.consumePower, self.originRealPower)
        curveLossCoin = self.onceCostCoin * self.powerRate * self.consumePower / self.originRealPower
        if ftlog.is_debug():
            ftlog.debug("catchFish->",
                        "userId =", self.player.userId,
                        "catch =", catch,
                        "gain =", gain,
                        "onceCostCoin =", self.onceCostCoin * self.powerRate,
                        "consumePower =", self.consumePower,
                        "originRealPower =", self.originRealPower,
                        "totalPowerRate =", totalPowerRate,
                        "curveLossCoin =", curveLossCoin,
                        "curveProfitCoin =", curveProfitCoin)
        self.player.dynamicOdds.updateOdds(curveProfitCoin - curveLossCoin)
        if isCatch:
            self.player.addCombo()
        for userId, catchInfo in otherCatch.iteritems():
            otherPlayer = self.table.getPlayer(userId)
            if otherPlayer:
                self.table.dealCatch(bulletId, wpId, otherPlayer, catchInfo["catch"], catchInfo["gain"],
                                     catchInfo["gainChip"], catchInfo["exp"], self.fpMultiple, self.gunSkinMultiple, self.gunX)
        return catch, gain, gainChip, exp

    def _sortedFishes(self, fIds, extends=None):
        count = extends[0]
        fishesDict = {}
        fishesHPDict = {}
        for fId in fIds:
            isOK = self.table.findFish(fId)
            if not isOK:
                continue
            fishInfo = self.table.fishMap[fId]
            originHP = fishInfo["HP"]
            fishType = fishInfo["fishType"]
            fishConf = config.getFishConf(fishType, self.table.typeName, self.fpMultiple)
            fatal = self.table.dealIceConeEffect(fId, fishConf)
            fishHP = int(originHP - self.deductionHP)
            fishHP = self.table.dealFishHP(fId, fishHP, fatal)
            fishesDict[fId] = self._getFishProbbRadix(fishInfo, fishConf)
            if originHP != fishHP:
                fishesHPDict[fId] = originHP
        if count <= 2:
            fishes = sorted(fishesDict.iteritems(), key=lambda d: d[1])
        else:
            fishes = sorted(fishesDict.iteritems(), key=lambda d: d[1], reverse=True)
        if ftlog.is_debug():
            ftlog.debug("_sortedFishes->", fishes, fishesHPDict, fIds)
        return fishes, fishesHPDict

    def getPowerRate(self, extends):
        count = extends[0]
        if count == 1:
            powerRate = 0.2
        elif count == 2:
            powerRate = 0.3
        elif count == 3:
            powerRate = 0.5
        else:
            powerRate = 0.2
            ftlog.error("getPowerRate error", self.player.userId, extends)
        return powerRate

class SkillHunt(SkillBase):
    """
    猎鱼机甲(5109)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        catch, gain, gainChip, exp = [], [], 0, 0
        if wpId == 2209:        # 释放猎鱼机甲
            position = self.position.get(bulletId, [0, 0])
            endTime = time.time() + self.duration
            buffer = [self.skillId, endTime, self.player.userId, self.skillStar, self.skillGrade, self.duration, 0]
            group = self.table.insertFishGroup("call_robot", position, self.HP, buffer,
                                               gameResolution=self.player.gameResolution)
            self.durationEndTime = endTime
            self.onceCostCoin /= int(self.duration / self.interval)
            if group:
                self.skillFishes.append(group.startFishId)
        elif wpId == 2301:      # 猎鱼机甲开火
            if self.originRealPower and time.time() <= self.durationEndTime:
                return self.normalCatchFish(bulletId, wpId, fIds, extends)
        elif wpId == 2302:      # 猎鱼机甲爆炸
            if self.originRealPower:
                aliveTime = self.durationEndTime - time.time()
                aliveTime = aliveTime if 0 < aliveTime < self.duration else self.duration
                self.powerRate = (self.duration - aliveTime) * (1 / self.interval)
                powerAddtion = self.player.getPowerAddition(self.weaponId)
                self.totalPower = self.power * self.powerRate * powerAddtion + self.impale
                self.energy *= self.powerRate
                self.onceCostCoin *= self.powerRate
                self.originRealPower = self.totalPower + self.energy
                self.deductionHP = self.totalPower
                self.skillFishes = []
                if ftlog.is_debug():
                    ftlog.debug("SkillHunt->catchFish powerRate =", self.powerRate)
                return self.normalCatchFish(bulletId, wpId, fIds, extends)
        return catch, gain, gainChip, exp

class SkillGatling(SkillBase):
    """
    格林机关枪(5110)
    """
    def catchFish(self, bulletId, wpId, fIds, extends):
        return self.normalCatchFish(bulletId, wpId, fIds, extends)

    def returnClip(self):
        """
        打空返还技能子弹
        """
        if self.state == 2:
            if self.clip < self.originClip:
                self.clip += 1
                self.updateSkillData()

skillMap = {
    5101: SkillMissile,     # 合金飞弹
    5102: SkillMagic,       # 魔术炮
    5103: SkillCannon,      # 火蛇加农炮
    5104: SkillFrozen,      # 极冻炮
    5105: SkillGrenade,     # 榴弹炮
    5106: SkillEnergy,      # 汇能弹
    5107: SkillFraud,       # 欺诈水晶
    5108: SkillLaser,       # 激光炮
    5109: SkillHunt,        # 猎鱼机甲
    5110: SkillGatling      # 格林机关枪
}

# 技能ID-武器ID对应关系
skillWeaponMap = {
    5101: 2201,
    5102: 2202,
    5103: 2203,
    5104: 2204,
    5105: 2205,
    5106: 2206,
    5107: 2207,
    5108: 2208,
    5109: 2209,
    5110: 2210
}

# 武器ID-技能ID对应关系
weaponSkillMap = {
    2201: 5101,
    2202: 5102,
    2203: 5103,
    2204: 5104,
    2205: 5105,
    2206: 5106,
    2207: 5107,
    2208: 5108,
    2209: 5109,
    2210: 5110
}

def createSkill(table, player, skillId, skillState, skillStar, skillGrade, skillType):
    skillClass = skillMap.get(skillId)
    skill = None
    if skillClass:
        skill = skillClass(table, player, skillId, skillState, skillStar, skillGrade, skillType)
    return skill


from tkinter import *
import sqlite3
import tkinter.messagebox
import os.path
import sys
sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/' + '../..'))
from HospitalManagementDBMS import settings

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
db_path = os.path.join(BASE_DIR, "../database.db")
conn = sqlite3.connect(db_path)
c = conn.cursor()

ids = []

class Application:
    def __init__(self, master):
        self.master = master

        self.frame = Frame(master, width=1200, height=720, bg='lightgreen')
        self.frame.pack(side=LEFT)

        self.heading = Label(self.frame, text="Add Reasons", font=('arial 40 bold'), fg='black', bg='lightgreen')
        self.heading.place(x=0, y=0)

        self.name = Label(self.frame, text="Name", font=('arial 18 bold'), fg='black', bg='lightgreen')
        self.name.place(x=0, y=100)

        self.cost = Label(self.frame, text="Cost", font=('arial 18 bold'), fg='black', bg='lightgreen')
        self.cost.place(x=0, y=140)

        self.name_ent = Entry(self.frame, width=30)
        self.name_ent.place(x=250, y=100)

        self.cost_ent = Entry(self.frame, width=30)
        self.cost_ent.place(x=250, y=140)

        self.submit = Button(self.frame, text="Add", width=20, height=2, bg='steelblue', command=self.add_reason)
        self.submit.place(x=200, y=200)

    def add_reason(self):
        self.val1 = self.name_ent.get()
        self.val2 = self.cost_ent.get()

        if self.val1 == '' or self.val2 == '':
            tkinter.messagebox.showinfo("Warning", "Please Fill Up All Boxes")
        else:
            sql = "INSERT INTO 'reason' (name, cost) VALUES(?, ?)"
            c.execute(sql, (self.val1, self.val2))
            conn.commit()
            tkinter.messagebox.showinfo("Success", "Reason added")
            self.name_ent.delete(0,'end')
            self.cost_ent.delete(0,'end')

root = Tk()
b = Application(root)
root.geometry("1200x720+0+0")
root.title("New Reason")
root.resizable(False, False)
root.mainloop()


"""
-t - сканировать tcp
-u - сканировать udp
-p N1 N2, --ports N1 N2 - диапазон портов

Вывод:
В одной строке информация об одном открытом порте (через пробел):
TCP 80 HTTP
UDP 128
UDP 123 SNTP

Если протокол не определен - сообщить об ошибке

"""

import argparse  # для разбора аргументов
import socket
import threading
from traceback import print_exc
from typing import List, Tuple
from threading import Lock
from queue import Queue
from imaplib import IMAP4, IMAP4_SSL
import poplib
from smtplib import SMTP_SSL

import struct
import random

TIMEOUT = 3
THREADS = 5

def parse_args():
    parser = argparse.ArgumentParser(description='Yet another port scanner')
    parser.add_argument('-t', '--tcp', action='store_true', help='Scan TCP')
    parser.add_argument('-u', '--udp', action='store_true', help='Scan UDP')
    parser.add_argument(
        'host',
        type=str,
        default=['8.8.8.8'],
        help='Remote host'
        )
    parser.add_argument(
        '-p', '--ports',
        type=int,
        nargs=2,
        default=['1', '65535'],
        help='Define port range'
        )
    return parser.parse_args().__dict__

def validate_ports(port_range: list) -> Tuple[int, int]:
    """ Проверка валидности портов
        (номер первого порта меньше второго и оба номера
        расположены в диапазоне допустимых значений) """
    a, b = port_range
    valid = a < b and 1 < a < 65535 and 1 < b < 65535
    return (a, b) if valid else (None, None)

def scan(tcp: bool, udp: bool, host: str, ports: List[str]):
    port_start, port_end = validate_ports(ports)
    if port_start:
        scanner = Scanner(
            tcp, udp, host, (port_start, port_end), TIMEOUT, THREADS)
        scanner.run()

class Scanner:
    def __init__(self, tcp, udp, host: str, ports, timeout=1.0, t_count=10):
        self.tcp = tcp
        self.udp = udp
        self.host = host
        self.timeout = timeout
        self.threadcount = t_count
        self.port_range = range(ports[0], ports[1] + 1)
        self.ports_queue = Queue()
        self._lock = Lock()
        self._condition = threading.Condition(self._lock)
        self._ports_active = []
        # перечень проверяемых портов в текущий момент
        self._ports_being_checked = []
        self._next_port, self._last_port = ports

    def run(self):
        try:
            while True:
                self._condition.acquire()  # блокируем
                while len(self._ports_being_checked) >= self.threadcount:
                    # ждем если все потоки заняты работой
                    self._condition.wait()
                l_ports = len(self._ports_being_checked)
                slots_available = self.threadcount - l_ports
                self._condition.release()  # снимаем блокировку
                if self._next_port > self._last_port:
                    return
                for i in range(slots_available):  # запустить пачку потоков
                    self.start_another_thread()
        except AllThreadsStarted:
            print("All threads started ...")
        except Exception:
            print_exc()

    def start_another_thread(self):
        """ Запускаем проверку очередного порта в новом потоке
            (берем номер очередного порта, запускаем его в обработку,
            записываем номер порта в список обрабатываемых в текущий момент)"""
        port = self._next_port
        self._next_port += 1
        t = threading.Thread(target=self.check_port, args=(port,))
        with self._lock:
            self._ports_being_checked.append(port)
        t.start()

    def check_port(self, port):
        """ Отрабатывает проверку порта и по завершению убирает
            номер порта из списка обрабатываемых """
        try:
            self.check_port_(port)
        finally:
            self._condition.acquire()
            self._ports_being_checked.remove(port)
            self._condition.notifyAll()
            self._condition.release()

    def check_port_(self, port):
        if self.tcp:
            self.check_tcp(port)
        if self.udp:
            self.check_udp(port)

    def check_tcp(self, port):
        """ Если удалось подключиться значит порт доступен/открыт """
        # socket.AF_INET - Семейство сокетов IPv4, socket.SOCK_STREAM - TCP
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(self.timeout)
        try:
            resp = s.connect_ex((self.host, port))  # 0 если всё ОК
            if resp != 0:  # подключение не удалось
                return
            protocol = self.check_socket_protocol(s, port, 'tcp')
            with self._lock:
                self._ports_active.append(port)
                print("TCP: {} {}".format(port, protocol))
        except socket.timeout:
            return
        finally:
            s.close()

    def check_udp(self, port):
        """ Если удалось подключиться значит порт доступен/открыт """
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.settimeout(self.timeout)
        try:
            resp = s.connect_ex((self.host, port))
            if resp != 0:  # подключение не удалось
                return
            protocol = self.check_socket_protocol(s, port, 'udp')
            with self._lock:
                self._ports_active.append(port)
                print('UDP {} {}'.format(port, protocol))
        except socket.timeout:
            return
        finally:
            s.close()

    def check_socket_protocol(self, s, port, protocol):
        """ Определяем протокол порта """
        res = ""
        if self._is_dns(s, port):
            res = "DNS"
        elif protocol == 'tcp' and self._is_http(s):
            res = "HTTP"
        elif protocol == 'tcp' and self._is_smtp(s, port):
            res = "SMTP"
        elif protocol == 'udp' and self._is_ntp(s):
            res = "NTP"
        elif protocol == 'tcp' and self._is_pop3(port):
            res = "POP3"
        elif protocol == 'tcp' and self._is_imap_ssl(port):
            res = "IMAP_SSL"
        elif protocol == 'tcp' and self._is_imap_no_ssl(port):
            res = "IMAP"
        # если проверки ничего не показали
        # покажем стандартный протокол порта (если есть)
        else:
            try:
                res = "may be standart {}".format(
                    socket.getservbyport(port, protocol).upper())
            except Exception:
                res = "Unknown"

        return res

    def _is_dns(self, s, port):
        """ А может ДНС? """
        try:
            pkg = self._build_dns_packet()
            s.sendto(pkg, (self.host, port))
            response = s.recv(1024)
        except Exception:
            return False
        if len(response) > 0:  # TODO: check response
            return True
        return False

    def _build_dns_packet(self):
        url = 'www.google.com'
        randint = random.randint(0, 65535)
        packet = struct.pack(">H", randint)  # Query Ids (Just 1 for now)
        packet += struct.pack(">H", 0x0100)  # Flags
        packet += struct.pack(">H", 1)  # Questions
        packet += struct.pack(">H", 0)  # Answers
        packet += struct.pack(">H", 0)  # Authorities
        packet += struct.pack(">H", 0)  # Additional
        split_url = url.split(".")
        for part in split_url:
            packet += struct.pack("B", len(part))
            for s in part:
                packet += struct.pack('c', s.encode())
        packet += struct.pack("B", 0)  # End of String
        packet += struct.pack(">H", 1)  # Query Type
        packet += struct.pack(">H", 1)  # Query Class
        return packet

    def _is_http(self, s):
        """ А может HTTP """
        try:
            req = "GET / HTTP/1.1\r\n\r\n".encode()
            s.send(req)
            response = s.recv(2048).decode()
        except Exception:
            return False

        if 'HTTP' in response:
            return True
        return False

    def _is_smtp(self, s, port):
        """ А может SMTP """
        try:
            with SMTP_SSL(host=self.host, port=port) as smtp:
                smtp.noop()
        except Exception:
            return False
        return True

    def _is_ntp(self, s):
        """ А может NTP """
        packet = bytearray(48)
        packet[0] = 0b11100011
        s.send(packet)
        response = s.recv(2048)
        if len(response) > 0:
            return True
        else:
            return False

    def _is_pop3(self, port):
        """ pop3 ответы всегда начинаются с '+OK' """
        try:
            M = poplib.POP3(self.host)
            return True
        except Exception:
            return False

    def _is_imap_ssl(self, port):
        try:
            mailbox = IMAP4_SSL(self.host, port=port)
        except Exception:
            return False
        return True

    def _is_imap_no_ssl(self, port):
        try:
            mailbox = IMAP4(self.host, port=port)
        except Exception:
            return False
        return True

class AllThreadsStarted(Exception):
    pass

if __name__ == "__main__":
    try:
        args = parse_args()
        scan(**args)

    except KeyboardInterrupt:
        print('\nTerminated.')
        exit()

"""
Как это работает?
==================================================
Вызов - разбираются аргументы и передаются в scan()
  scan()  Вызывает проверку указанных портов validate_ports() и если всё ок -
  Создает экземпляр класса Scanner и запускает его метод scanner.run()

    run() - выполняется пока есть непроверенные порты.
    * "аккуратно", т.е. с блокировкой ресурсов на время их проверки, провеярет
        сколько потоков заняты работой, если занято меньше указанного в THREADS
        (self.threadcount), то вычисляет сколько конкретно еще он может
        запустить (slots_available) и запускает, вызывая start_another_thread()

      start_another_thread() -
      * запускает в обработку порт с очередным номером (port) в новый поток,
        методом self.check_port() ,
      * вычисляет следующий для проверки (self._next_port),
      * пополняет список проверяемых в текущий момент портов
        (self._ports_being_checked)

        check_port() -
        * вызывает непосредственно проверку порта (self.check_port_()),
        * по завершению проверки "аккуратно" убирает порт из списка
            обрабатываемых

          check_port_() -
          * если указаны tcp/udp делает соответствующие вызовы

            check_tcp() - проверяет порт хоста
            * настраивает сокет
            * пытается подключиться (s.connect_ex()), если удалось - половина
                успеха
                * пытается в обмен сообщениями методом check_socket_protocol()

            check_udp() - проверяет порт хоста
            * настраивает сокет
            * пытается подключиться (s.connect_ex()), если удалось - успех
                * пытается в обмен сообщениями - check_socket_protocol
                    почти всегда общение не удастся, так как принимающий
                    сервер вероятно просто
                     ===> фильтрует входящие пакеты <===
                * опять же "аккуратно" записывает порт в список активных
                    (self._ports_active)
                * "аккуратно" выводит сообщение на экран
"""


# -*- coding: utf-8 -*-
"""
Created on Thu Dec 10 13:35:19 2020

@author: ilail
"""

import pandas as pd
import re
import matplotlib.pyplot as plt
from os import path
import os

# %% Reads MPT or DTA to common data structure

class CyclicVoltammetry:
    def __init__(self, path):
        self.filepath = path  # path of input file
        self.settings = {}  # info from input header
        self.data = None  # contains Pandas dataframe of CV

        self._load_cv()  # read input data

    def _load_cv(self):
        # choose input parser based on file extension
        filename = os.path.basename(self.filepath)
        root = path.splitext(filename)[0]
        extension = path.splitext(filename)[1]

        if extension.lower() == ".dta":
            self.settings["format"] = "Gamry"
            # self.settings['folder'] = ''
            self.settings["filename"] = filename
            self.settings["file_rootname"] = root
            self.settings["extension"] = "dta"
            self._read_DTA()
        elif extension.lower() == ".mpt":
            self.settings["format"] = "Biologic"
            self.settings["filename"] = filename
            self.settings["extension"] = "mpt"
            self.settings["file_rootname"] = root
            self._read_MPT()
        elif extension.lower() == "":
            print("No file selected")
            raise ValueError
        else:
            # if extension is not recognized, raise error
            raise ValueError

    def _read_MPT(self):
        with open(self.filepath, "r", encoding="utf8", errors="ignore") as f:
            iterator = iter(f)  # iterator avoids checking settings after header

            for line in iterator:
                if "Nb header lines" in line:
                    skiprows = int(line.split()[4])
                elif "Ei (V)" in line:
                    v_init = line.replace(",", ".").split()[2]
                    self.settings["initial voltage"] = float(v_init)
                elif "E1 (V)" in line:
                    v_final = line.replace(",", ".").split()[2]
                    self.settings["final voltage"] = float(v_final)
                elif "mode	ox/red	error" in line:
                    break

            self.data = pd.read_csv(
                self.filepath, sep="\t", skiprows=skiprows - 1, decimal=",",
            )

            uniques = self.data["cycle number"].value_counts()  #
            self.settings["n_cycles"] = len(uniques)
            self.data.rename(
                columns={
                    "time/s": "T",
                    "Ewe/V": "Vf",
                    "<I>/mA": "Im",
                    "cycle number": "Cycle n",
                },
                inplace=True,
            )

            self.data = self.data[["Cycle n", "T", "Vf", "Im"]]
            self.data["Cycle n"] -= 1  # switch to 0 based cycle indexing
            self.data.set_index("Cycle n", inplace=True)

    def _read_DTA(self):
        # only consider data after label CURVE\d and ignore CURVEOCV
        is_it_curve = re.compile("CURVE\d")
        with open(self.filepath, "r", encoding="utf8", errors="ignore") as f:
            iterator = iter(f)  # iterator avoids checking settings after header

            # headers for self.settings dict
            row_idx = 0
            for line in iterator:
                row_idx += 1
                if "SCANRATE" in line:
                    scanrate = line.replace(",", ".").split("\t")[2]
                    self.settings["scan rate"] = float(scanrate)
                elif "VINIT" in line:
                    v_init = line.replace(",", ".").split("\t")[2]
                    self.settings["initial voltage"] = float(v_init)
                elif "VLIMIT1" in line:
                    vlimit_1 = line.replace(",", ".").split("\t")[2]
                    self.settings["vlimit 1"] = float(vlimit_1)
                elif "VLIMIT2" in line:
                    vlimit_2 = line.replace(",", ".").split("\t")[2]
                    self.settings["vlimit 2"] = float(vlimit_2)
                elif "INSTRUMENTVERSION" in line:
                    break

            # Find peak (either maximum or minimum) in voltage scan...
            # This is gamry nonsense :@
            if float(v_init) == float(vlimit_1):
                self.settings["final voltage"] = float(vlimit_2)
            else:
                self.settings["final voltage"] = float(vlimit_1)
            file_format = None
            header = None

            for line in iterator:
                row_idx += 1
                if "OCVCURVE" in line:
                    continue
                elif "CURVE" in line:
                    if "CURVE	TABLE" in line:
                        file_format = "Single table"
                        header = next(f)
                        header_units = next(f)
                        break
                    elif is_it_curve.match(line):
                        file_format = "Multiple tables"
                        next(f)
                        next(f)
                        break

            # DTA can come in two formats...
            useful_keys = ["Cycle n", "T", "Vf", "Im"]

            if file_format == "Single table":
                header = header.replace("Cycle", "Cycle n")
                header = header.split("\t")
                self.data = pd.read_csv(
                    self.filepath,
                    sep="\t",
                    skiprows=row_idx + 2,
                    names=header,
                    decimal=",",
                )
                self.data = self.data.drop(self.data.columns[0], axis=1)
                uniques = self.data["Cycle n"].value_counts()  #
                self.settings["n_cycles"] = len(uniques)

                self.data = self.data[useful_keys]
                self.data.set_index("Cycle n", inplace=True)
                return
            else:
                curves = []
                curveN = 0
                # read CV data, multiple tables
                for line in iterator:
                    if is_it_curve.match(line):
                        curveN += 1
                        next(f)
                        next(f)
                        continue
                    if curveN >= 0:
                        line_float = [
                            float(ele)
                            for ele in line.strip().replace(",", ".").split("\t")[0:7]
                        ]
                        curves.append([curveN] + line_float)

                self.settings["n_cycles"] = curveN + 1
                header = ["Cycle n", "Pt", "T", "Vf", "Im", "Vu", "Sig0", "Ach"]
                # just keep the relevant ones and use cycle num as vertical key
                self.data = pd.DataFrame(curves, columns=header)[useful_keys]
                self.data.set_index("Cycle n", inplace=True)

    def _plot(self, cycle):
        curr = self[cycle]["Im"]
        volt = self[cycle]["Vf"]
        plt.rcParams.update({"font.size": 12})
        plt.rc("legend", fontsize=9)
        plt.xlabel("V vs ref [V]")
        plt.ylabel("i [mA]")

        plt.plot(volt, curr)

    def __getitem__(self, key):
        """
        Return table filtered by value key.

        Parameters
        ----------
        key : int or str
            If int, key is interpreted as cycle number, otherwise as pd column

        Returns
        -------
        Pandas dataframe
            Returns pandas filtered by cycle number if key is int
            Returns pandas filtered by column otherwise

        """
        if key in self.data.columns:
            return self.data[key]
        elif isinstance(key, int):
            return self.data.loc[key]
        # else:
        #    return self.data.filter(like = key, axis = 0)

    def __repr__(self):
        """
        Return __repr__ of the data Pandas dataframe.

        Returns
        -------
        str
            Representation of data Pandas dataframe.

        """
        return self.data.__repr__()
        # return self.filepath

    def __str__(self):
        return self.filepath

    def __iter__(self):
        # return pd table filtered by cycle number as in __getitem__
        for cycle in range(self.settings["n_cycles"]):
            yield self.data.loc[cycle]

    def _current_dens(self, area):
        self.data["Idens"] = self.data["Im"] * 1000 / area


import game as imput
import Attempt3Functions as f
import Queue

SelectedGame = 3
numbers = [1,2,3,4,5,6,7,8,9]

game = imput.game[SelectedGame]
f.pprint(game)

for i in range(9):
    for j in range(9):
        if game[i][j] == 0:
            game[i][j] = numbers[:]
        else:
            game[i][j] = [game[i][j]]
f.pprint(game)

f.clear(game)


class Solution:
    def numRollsToTarget(self, n: int, k: int, target: int) -> int:
        MOD = 10**9+7
        @cache
        def solve(rolls,target):
            if rolls==0:
                if target==0:
                    return 1
                return 0

            ans = 0
            for curRoll in range(1,k+1):
                ans = (ans+solve(rolls-1,target-curRoll))%MOD
            return ans

        return solve(n,target)


"""
    Rolul acestei metode este de a determina cuvintele ce apar o singura data intr-un text

    Parametrii de intrare:

        text - sirul de cuvinte separate prin spatiu

    Paremetrii de iesire:

        result - lista de cuvinte ce apar o singura data in text
"""

def determina_cuvinte_unice(text):
    l = text.split()

    m = {}

    for i in range(0, len(l)):

        if l[i] not in m:

            m[l[i]] = 1

        else:

            m[l[i]] += 1

    result = []

    for key in m:

        if m[key] == 1:
            result.append(key)

    return result

# Rolul acestei metode este de a testa functia ce determina cuvintele ce apar o singura data intr-un text

def test_cuvinte_unice():
    assert determina_cuvinte_unice("ana are ana are mere rosii ana") == ["mere", "rosii"]
    assert determina_cuvinte_unice("ana are are ana") == []
    assert determina_cuvinte_unice("popa are mere mari") == ["popa", "are", "mere", "mari"]
    assert determina_cuvinte_unice("") == []
    assert determina_cuvinte_unice("ana Ana aNa") == ["ana", "Ana", "aNa"]

# Aceasta este functia main in care este citit sirul de cuvinte si in care sunt afisate cuvintele ce apar o singura data

def main():
    s = input("Introduceti sirul de cuvinte: ")

    print("Cuvintele ce apar o singura data in sirul nostru sunt " + str(determina_cuvinte_unice(s)))

test_cuvinte_unice()
main()


class prime(Exception):
    def __str__(self):
        return "magic method executed"
try:
    n=int(input("n: "))
    for i in range(2,n//2+1):
        if n%i==0:
            raise prime(n,"not prime")
except prime as msg:
    print(msg)


from torch.utils import data
from torch.utils.data import DataLoader
import torch
from librosa.util import find_files
import numpy as np
import os
from hparams import create_hparams

import pandas as pd

class MUSDB(data.Dataset):

    def __init__(self, file_path, out_channels_list):
        self.file_list = find_files(file_path, 'npz')
        self.out_channels_list = out_channels_list

    def __len__(self, ):
        return len(self.file_list)

    def __getitem__(self, idx):
        data = np.load(self.file_list[idx])
        spec = data['data']
        label = data['label']
        label = label[self.out_channels_list]

        return torch.from_numpy(spec).type(torch.FloatTensor), torch.from_numpy(label).type(torch.FloatTensor)

def get_loader(hparams):

    train_path = os.path.join(hparams.fft_path, 'train')
    eval_path = os.path.join(hparams.fft_path, 'valid')
    train = MUSDB(train_path, hparams.out_channels_list)
    valid = MUSDB(eval_path, hparams.out_channels_list)

    valid_loader = DataLoader(dataset=valid,
                              batch_size=hparams.batch_size,
                              shuffle=True,
                              num_workers=hparams.num_workers)

    train_loader = DataLoader(dataset=train,
                              batch_size=hparams.batch_size,
                              shuffle=True,
                              num_workers=hparams.num_workers)

    return train_loader, valid_loader


# -*- coding=utf-8 -*-
"""
Created by lichen on 2017/4/12.
"""

import time
import random

from freetime.util import log as ftlog
from freetime.core.timer import FTLoopTimer
from poker.entity.dao import gamedata
from hall.entity import hallvip
from newfish.entity import config, util
from newfish.entity.config import FISH_GAMEID
from newfish.entity.redis_keys import GameData
from newfish.entity.event import EnterTableEvent, LeaveTableEvent

class CouponFishGroup(object):
    """
    奖券鱼鱼群
    """
    def __init__(self, table):
        self.table = table
        self._lastUtilAppearTime = int(time.time())     # 上一次添加所有人可见红包奖券出现的事件
        self.checkTimer = FTLoopTimer(10, -1, self._checkCondition)     # 检查条件
        self.checkTimer.start()
        self._clearUserCouponFishData()                 # 清理用户奖券数据
        self._registerEvent()                           # 注册事件

    def clearTimer(self):
        """清理定时器"""
        if self.checkTimer:
            self.checkTimer.cancel()
            self.checkTimer = None
        for player in self.table.players:
            if player and player.userId:
                self._clearPlayerTimer(player.userId)   # 清理玩家定时器

    def _clearUserCouponFishData(self):
        """
        初始化个人可见奖券鱼相关数据
        """
        self._playerCouponTimerDict = {}                # 玩家奖券定时器
        for player in self.table.players:
            if player and player.userId:
                self._clearPlayerTimer(player.userId)

    def _clearPlayerTimer(self, userId):
        """
        清除与player相关的定时器和鱼阵
        """
        timer = self._playerCouponTimerDict.get(userId)
        if timer:
            timer.cancel()
            self._playerCouponTimerDict.pop(userId)

    def _checkCondition(self):
        """
        定时器条件检查
        """
        self._checkUtilConditionNew()

    def _checkUtilConditionNew(self):
        """
        检查定时的奖券鱼
        """
        couponFishConf = config.getCouponFishConf(self.table.runConfig.fishPool)
        if not couponFishConf:
            return
        interval = time.time() - self._lastUtilAppearTime
        couponRange = couponFishConf.get("couponRange")
        if interval >= couponFishConf["maxSecond"]:
            if ftlog.is_debug():
                ftlog.debug("_checkUtilConditionNew, interval =", interval, couponRange, self.table.tableId)
            randInt = random.randint(1, 10000)
            for fish in couponFishConf["fishes"]:
                probb = fish["probb"]
                if probb[0] <= randInt <= probb[1]:
                    fishType = fish["fishType"]
                    allCouponGroupIds = self.table.runConfig.allCouponGroupIds
                    groupId = random.choice(allCouponGroupIds[fishType])
                    self._addUtilCouponFishGroupNew(groupId, couponRange)
                    break

    def _addUtilCouponFishGroupNew(self, groupId, couponRange):
        """
        添加定时出生的奖券鱼
        """
        self._lastUtilAppearTime = time.time()
        if not couponRange:
            if ftlog.is_debug():
                ftlog.debug("_addUtilCouponFishGroupNew, groupId =", groupId, "tableId =", self.table.tableId)
            self.table.insertFishGroup(groupId)
            return
        sendUserList = []
        for player in self.table.players:
            if player and player.userId > 0:
                totalEntityAmount = gamedata.getGameAttr(player.userId, FISH_GAMEID, GameData.totalEntityAmount)
                totalEntityAmount = float(totalEntityAmount) if totalEntityAmount else 0
                if couponRange[0] <= totalEntityAmount < couponRange[1]:
                    sendUserList.append(player.userId)
        if ftlog.is_debug():
            ftlog.debug("_addUtilCouponFishGroupNew, groupId =", groupId, "couponRange =", couponRange,
                    "sendUserList =", sendUserList, "tableId =", self.table.tableId)
        if sendUserList:
            self.table.insertFishGroup(groupId, sendUserId=sendUserList)

    def _checkUserConditionNew(self, userId):
        """
        检查个人可见奖券鱼出现条件是否满足
        """
        userCouponFishConf = config.getUserCouponFishConf(self.table.runConfig.fishPool)
        groupId, interval, totalBullet = self._isAppearUserCouponFishNew(userId, userCouponFishConf)
        if groupId:
            timer = FTLoopTimer(interval, 0, self._addUserCouponFishGroupNew, groupId, userId, totalBullet)
            timer.start()
            self._playerCouponTimerDict[userId] = timer
            if ftlog.is_debug():
                ftlog.debug("_checkUserConditionNew->userId =", userId, "groupId =", groupId, "interval =", interval,
                        "totalBullet =", totalBullet, "tableId =", self.table.tableId)

    def _isAppearUserCouponFishNew(self, userId, userCouponFishConf):
        """
        个人奖券鱼是否出现
        """
        groupId = None
        interval = 0
        totalBullet = 0
        if not userCouponFishConf:
            return groupId, interval, totalBullet
        catchCountDict = gamedata.getGameAttrJson(userId, FISH_GAMEID, GameData.catchUserCouponFishCount, {})
        catchCount = catchCountDict.get(str(self.table.runConfig.fishPool), 0)
        rechargeAmount = hallvip.userVipSystem.getUserVip(userId).vipExp // 10
        totalEntityAmount = gamedata.getGameAttr(userId, FISH_GAMEID, GameData.totalEntityAmount)
        totalEntityAmount = float(totalEntityAmount) if totalEntityAmount else 0
        for conf in userCouponFishConf:
            if catchCount >= conf["limitCount"]:
                continue
            section = conf["sections"]
            if ftlog.is_debug():
                ftlog.debug("_isAppearUserCouponFishNew->tabId=", self.table.tableId, "userId =", userId,
                        "groupId =", groupId, "section =", section)
            # 充值金额大于等于指定值，获得金额小于指定值
            if len(section) == 3 and rechargeAmount >= section[0] and section[1] <= totalEntityAmount < section[2]:
                randInt = random.randint(1, 10000)
                for fish in conf["fishes"]:
                    probb = fish["probb"]
                    if probb[0] <= randInt <= probb[1]:
                        fishType = fish["fishType"]
                        allCouponGroupIds = self.table.runConfig.allCouponGroupIds
                        groupId = random.choice(allCouponGroupIds[fishType])
                        interval = random.randint(conf["minSecond"], conf["maxSecond"])
                        totalBullet = conf["totalBullet"]
                break
        if ftlog.is_debug():
            ftlog.debug("_isAppearUserCouponFishNew->tabId=", self.table.tableId, "userId =", userId, "groupId =", groupId,
                    "catchCountDict =", catchCountDict, "rechargeAmount =", rechargeAmount,
                    "totalEntityAmount =", totalEntityAmount, "interval =", interval, "totalBullet =", totalBullet)
        return groupId, interval, totalBullet

    def _addUserCouponFishGroupNew(self, groupId, userId, totalBullet):
        """
        添加个人可见的奖券鱼
        """
        player = self.table.getPlayer(userId)
        self._clearPlayerTimer(userId)
        if player:
            playerBullet = player.couponConsumeClip
            if playerBullet < totalBullet:
                timer = FTLoopTimer(10, 0, self._addUserCouponFishGroupNew, groupId, userId, totalBullet)
                timer.start()
                self._playerCouponTimerDict[userId] = timer
                if ftlog.is_debug():
                    ftlog.debug("_addUserCouponFishGroupNew, check later, tableId =", self.table.tableId, "userId =", userId,
                            "playerBullet =", playerBullet, "totalBullet =", totalBullet)
            else:
                player.resetCouponConsumeClip()
                if ftlog.is_debug():
                    ftlog.debug("_addUserCouponFishGroupNew, tabId =", self.table.tableId, "userId =", userId, "groupId =", groupId)
                self.table.insertFishGroup(groupId, userId=userId, sendUserId=userId)
                self._checkUserConditionNew(userId)

    def _dealEnterTable(self, event):
        """
        处理进入事件
        """
        if event.tableId == self.table.tableId and not event.reconnect:
            self._checkUserConditionNew(event.userId)

    def _dealLeaveTable(self, event):
        """
        处理离开事件
        """
        if event.tableId == self.table.tableId:
            self._clearPlayerTimer(event.userId)

    def _registerEvent(self):
        """
        注册监听事件
        """
        from newfish.game import TGFish
        TGFish.getEventBus().subscribe(EnterTableEvent, self._dealEnterTable)
        TGFish.getEventBus().subscribe(LeaveTableEvent, self._dealLeaveTable)


from mpl_toolkits import mplot3d
from mpl_toolkits.mplot3d import art3d as art
import numpy as np
import matplotlib.pyplot as plt

def fun(x1,x2):
    return 6 + 2*np.power(x1,2) + 2*np.power(x2,2)

#Question 1
fig = plt.figure()
axis = plt.axes(projection= '3d')
x1 = np.arange(-5,5,0.1)
x2 = np.arange(-5,5,0.1)
X, Y = np.meshgrid(x1,x2)
Z = fun(X,Y)
axis.contourf(X, Y, Z,200,cmap='CMRmap')
axis.set_xlabel('x1')
axis.set_ylabel('x2')
axis.set_zlabel('y1(x)')
plt.title('Plotting the function y1(x)')
plt.show()

#Question 2
fig1 = plt.figure()
axis = plt.axes(projection='3d')
y2=8
y2 = np.asarray(y2)
axis.plot3D(X,Y,y2,'gray')
plt.title('Plotting the function y2(x)')
plt.show()

#Question 3(1)
c = plt.Circle((0,0),1,color='r')
fig, ax = plt.subplots()
ax.set_xlim((-2,2))
ax.set_ylim((-2,2))
ax.add_patch(c)
plt.show()

#Viewing the 3-D model
fig2 = plt.figure()
axis = plt.axes(projection= '3d')
x1 = np.arange(-5,5,0.1)
x2 = np.arange(-5,5,0.1)
X, Y = np.meshgrid(x1,x2)
Z = fun(X,Y)
axis.set_xlabel('x1')
axis.set_ylabel('x2')
y2=8
y2 = np.asarray(y2)
plt.title('Decision Boundary in red seperating 2 classes')
axis.contourf(X, Y, Z,200,cmap='CMRmap')
axis.plot3D(X,Y,y2,'gray')
axis.set_zlim3d(0, 12)
axis.set_xlabel('x1')
axis.set_ylabel('x2')
c = plt.Circle((0,0),1,color='r')
axis.add_patch(c)
art.patch_2d_to_3d(c,z=8,zdir='z')
plt.show()


import random
import string
from words import words

def get_valid_word(words):
    word = random.choice(words)
    while '-' in word or ' ' in word:
        word = random.choice(words)
    return word

def hangman():

    word = get_valid_word(words).upper()
    word_letters = set(word)  #letters in the word
    alphabet = set(string.ascii_uppercase)
    used_letters = set()
    print(word)
    #getting user input

    lives = 5

    while len(word_letters) != 0 and lives > 0:

        #letters used
        #" ".join(['a', 'b', 'c']) --> 'a b c'
        print("You have used these letters: ", " ".join(used_letters))

        #what the current word is --> (G E - R G E)
        word_list = [letter if letter in used_letters else "-" for letter in word]
        print("Current word: ", " ".join(word_list)+"\n")

        user_letter = input("Make a guess: ")

        if user_letter in alphabet - used_letters:
            used_letters.add(user_letter)
            if user_letter in word_letters:
                word_letters.remove(user_letter)
            else:
                print("Letter {} is not in the word we are looking for ".format(user_letter))
                lives = lives-1
                print("\n \n You have {} remaining lives \n".format(lives))
        elif user_letter in used_letters:
            print ("You have already made that guuess. Try again")
        else:
            print("Invalid character. Try again")

    if lives == 0:
        print("You run out of lives!")
    else:
        print("You found it. The word was {}".format(word))

hangman()


import os

# 获取val_test/wav目录下的文件列表
wav_files = os.listdir('val_test/wav')
#print(wav_files)
# 获取val_test/video目录下的.avi文件列表
video_files = [filename for filename in os.listdir('val_test/txt') if filename.endswith('.txt')]
print(len(video_files))
# 找到匹配的文件名
matching_files = [filename for filename in wav_files if filename[:-4] + '.avi' in video_files]

#print(len(matching_files))
# 删除没有匹配的文件
for filename in video_files:
    print(filename[:-4] + '.wav')
    if filename[:-4] + '.wav' not in wav_files:
        print(filename)
        os.remove(os.path.join('val_test/txt', filename))

print("匹配的文件已保存，未匹配的文件已删除。")


class Solution(object):
    def subarraySum(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: int
        """
        sumDict = {0:1}
        n = len(nums)
        count = 0
        sum = 0

        for num in nums:
            sum+=num
            if sum-k in sumDict:
                count+= sumDict[sum-k]
            if sum in sumDict:
                sumDict[sum] += 1
            else:
                sumDict[sum] = 1
        return count

if __name__ == "__main__":
    objSol = Solution()
    nums = [1, 1, 1]
    k = 2
    print(objSol.subarraySum(nums, k))


import numpy as np

class extractor:
    def __init__(self, hist):
        self.hist = hist # Receive data
        self.lstentry = hist.GetEntries() # Set the total of entries

        self.distaopHb = [] # It stores the dist. AoP for HBase
        self.aveaopHb = [] # It stores Average AoP for HBase
        self.rmsaopHb = [] # It stores RMS AoP for HBase

        self.distaopCa = [] # It stores the dist. AoP for Calib
        self.aveaopCa = [] # It stores Average AoP for Calib
        self.rmsaopCa = [] # It stores RMS AoP for Calib

        self.totEvts = 0 # It stores total events per Station

        self.nfitokAoPHb = 0 # It stores the dist. Xi for HBase
        self.nfitokAoPCa = 0 # It stores the dist. Xi for HBase
        self.nfitokCh = 0 # It stores the dist. Xi for HBase

    # =========================
    # *** For AoP for HBase ***
    def getAoPDistHb(self):
        print("Doing AoP Distribution for HBase")
        tmpentry = 0
        for evt in range(0, self.lstentry):
            self.hist.GetEntry(evt)
            if tmpentry != self.hist.eventStat[1]:
                tmpentry = self.hist.eventStat[1]
                if self.hist.apHbase.GetBinContent(0) > 0:
                    self.distaopHb.append(self.hist.apHbase.GetBinContent(0))

    def getAoPAveRmsHb(self):
        print("Doing AoP Average and RMS for HBase")
        tmprms = 0
        if len(self.distaopHb) > 0:
            tmpmean = np.average(np.array(self.distaopHb))
            for bn in self.distaopHb:
                tmprms += (bn - tmpmean)*(bn - tmpmean)
            self.rmsaopHb.append( np.sqrt(tmprms/len(self.distaopHb)) )
            self.aveaopHb.append(tmpmean)
        else:
            self.rmsaopHb.append(0)
            self.aveaopHb.append(0)

    # =========================
    # *** For AoP for Calib ***
    def getAoPDistCa(self):
        print("Doing AoP Distribution for Calib")
        tmpentry = 0
        for evt in range(0, self.lstentry):
            self.hist.GetEntry(evt)
            if tmpentry != self.hist.eventStat[1]:
                tmpentry = self.hist.eventStat[1]
                if self.hist.apCalib.GetBinContent(0) > 0:
                    self.distaopCa.append(self.hist.apCalib.GetBinContent(0))

    def getAoPAveRmsCa(self):
        print("Doing AoP Average and RMS for Calib")
        tmprms = 0
        if len(self.distaopCa) > 0:
            tmpmean = np.average(np.array(self.distaopCa))
            tmprms = 0
            for bn in self.distaopCa:
                tmprms += (bn - tmpmean)*(bn - tmpmean)
            self.rmsaopCa.append( np.sqrt(tmprms/len(self.distaopCa)) )
            self.aveaopCa.append(tmpmean)
        else:
            self.rmsaopCa.append(0)
            self.aveaopCa.append(0)

    # ============================
    # *** For Total of Events ***
    def getTotEvents(self):
        self.totEvts = 0
        for evt in range(0, self.lstentry):
            self.hist.GetEntry(evt)
            self.totEvts = self.hist.eventStat.GetBinContent(1)

        return self.totEvts

    # ===================
    # *** For Fit-Oks ***

    # *** For HBase ***
    def getFitokHb(self):
        print("Doing Get Fits Ok AoP for HBase")
        tmpentry = 0
        self.nfitokAoPHb = 0
        for evt in range(0, self.lstentry):
            self.hist.GetEntry(evt)
            if tmpentry != self.hist.eventStat[1]:
                tmpentry = self.hist.eventStat[1]
                if self.hist.apHbase.GetBinContent(0) > 0:
                    self.nfitokAoPHb += 1
        return self.nfitokAoPHb

    # *** For Calib ***
    def getFitokCa(self):
        print("Doing Get Fits Ok AoP for Calib")
        tmpentry = 0
        self.nfitokAoPCa = 0
        for evt in range(0, self.lstentry):
            self.hist.GetEntry(evt)
            if tmpentry != self.hist.eventStat[1]:
                tmpentry = self.hist.eventStat[1]
                if self.hist.apCalib.GetBinContent(0) > 0:
                    self.nfitokAoPCa += 1
        return self.nfitokAoPCa