这是RotaBot一些功能的算法, 浏览之后欢迎向开发者和管理员提出意见
JOJO替身能力面板
playerInfo = getPlayerInfo()
destructivePower = 100 * ((playerInfo["__PlayTotalApp"] + playerInfo["__PlayTotalAp"] + 0.5 * playerInfo["__PlayTotalFc"]) / playerInfo["TotalPlayCount"])
precision = 100 * (1 - (sum([playerInfo["Combo"]["Status"]["Early"], playerInfo["Combo"]["Status"]["Late"]]) / sum([playerInfo["Combo"]["Status"]["Good"], playerInfo["Combo"]["Status"]["Miss"], playerInfo["Combo"]["Status"]["Perfect"], playerInfo["Combo"]["Status"]["PerfectPlus"]])))
endurance = 100 * ((((lambda s: int(s[0]) * 3600 + int(s[2]) * 60 + int(s[4]) * 1)(playerInfo["TotalPlayTime"].split(" ")) / playerInfo["TotalPlayCount"]) / 180) / 2.4)
growthPotential = 100 * (1 / (1 + numpy.exp(-1 * 0.008 * ((100 * (1 - (playerInfo["playerRating"] / PLAYER_RATING_MAX))) - 50))))
activity = 100 * ((lambda s: int(s[0]) / 1 + int(s[2]) / 60 + int(s[4]) / 3600)(playerInfo["TotalPlayTime"].split(" ")) / ((playerInfo["TotalPlayCount"]) / 6))
data = {
"destructivePower": destructivePower,
"precision": precision,
"endurance": endurance,
"growthPotential": growthPotential,
"activity": activity
}
运势
今日运势与塔罗牌的代码文件在同一个目录下, config.py
文件太大了 (就是太杂乱了)
每日运势
import random
import hashlib
from . import config
from datetime import datetime
from dataclasses import dataclass
from typing import Tuple, List, Dict
@dataclass
class LuckInfo:
name: str
description: str
color: int
advice: List[str]
stars: str
adjustments: Dict[str, Tuple[int, str]]
image: str
def getDailyFortune(addSeed) -> Tuple[int, LuckInfo]:
today = datetime.now().strftime("%Y-%m-%d")
seed_str = f"{today}-{addSeed}"
seed = int(hashlib.sha256(seed_str.encode()).hexdigest(), 16) // 10 ** 8
rng = random.Random(seed)
def getLuckLevel(fortunesNum: int) -> LuckInfo:
def getStarsRating(score: int, total_stars: int = 8) -> str:
filled = round(score / 100 * total_stars)
return "★" * filled + "☆" * (total_stars - filled)
adjustments = {
"事业": fortunesNum + rng.gauss(0, 5),
"桃花": fortunesNum + rng.gauss(1, 10),
"官运": fortunesNum + rng.gauss(0, 8),
"财运": fortunesNum + rng.gauss(0, 5)
}
for key in adjustments: adjustments[key] = max(0, min(100, adjustments[key]))
currentAvg = sum(adjustments.values()) / len(adjustments)
if currentAvg != 0:
scale_factor = fortunesNum / currentAvg
for key in adjustments: adjustments[key] = int(adjustments[key] * scale_factor)
for key in adjustments: adjustments[key] = max(0, min(100, adjustments[key]))
totalDiff = fortunesNum * len(adjustments) - sum(adjustments.values())
if totalDiff != 0:
adjustableItems = [key for key in adjustments if (totalDiff > 0 and adjustments[key] < 100) or (totalDiff < 0 and adjustments[key] > 0)]
if adjustableItems:
perItem = totalDiff / len(adjustableItems)
for key in adjustableItems: adjustments[key] = max(0, min(100, adjustments[key] + perItem))
for key, value in adjustments.items():
value = min(100, max(0, int(value)))
adjustments[key] = (value, getStarsRating(value))
def getLuck(fortunesNum):
levels = [
(100, "天选"), (95, "大吉"), (85, "中吉"), (75, "小吉"),
(50, "平"), (35, "小凶"), (10, "凶"), (0, "大凶")
]
for threshold, level in levels:
if fortunesNum >= threshold:
return level
return "大凶"
luck = getLuck(fortunesNum)
return LuckInfo(name=luck,
description=config.DAILY_LUCK_CONFIG[luck]["info"],
color=config.DAILY_LUCK_CONFIG[luck]["color"],
advice=rng.sample(config.DAILY_LUCK_CONFIG[luck]["advice"], 3),
stars=getStarsRating(fortunesNum),
adjustments=adjustments,
image=random.choice(config.DAILY_IMAGE_URLS()))
fortunesNum = rng.randint(1, 100)
return (fortunesNum, getLuckLevel(fortunesNum))
这个函数getDailyFortune
的参数addSeed
是根据用户定的
塔罗牌
import random
from . import config
from . import daily
def getTarotCard(addSeed):
fortunesNum, luckInfo = daily.getDailyFortune(addSeed)
luck = luckInfo.name
if luck == "天选":
cardBackProb = 0.003
elif luck == "大吉":
cardBackProb = 0.0015
else:
cardBackProb = 0.003 - 0.00002 * fortunesNum
cardBackProb = max(0.001, min(cardBackProb, 0.005))
uprightProb = config.TAROT_LUCK_UPRIGHT_WEIGHTS[luck]
reversedProb = 1 - uprightProb
majorBase = config.TAROT_CARDS_MAJOR_NUM / config.TAROT_CARDS_TOTAL_NUM
minorBase = config.TAROT_CARDS_MINOR_NUM / config.TAROT_CARDS_TOTAL_NUM
majorProb = majorBase + config.TAROT_LEVEL_COEFFICIENTS[luck]
minorprob = minorBase - config.TAROT_LEVEL_COEFFICIENTS[luck]
majorProb = max(0, min(majorProb, 1))
minorProb = max(0, min(minorprob, 1))
cardType = random.choices(["major", "minor", "back"], weights=[majorProb, minorProb, cardBackProb], k=1)[0]
if cardType == "back":
cardNames = list(config.TAROT_CARDS[cardType].keys())
cardName = random.choices(cardNames, k=1)[0]
return {
"name": cardName,
"image": f"{config.TAROT_IMAGE_DICT[cardType]}/{config.TAROT_CARDS[cardType][cardName]['image']}.png",
"description": config.TAROT_CARDS[cardType][cardName]["description"]
}
cardDirectionType = random.choices(["upright", "reversed"], weights=[uprightProb, reversedProb], k=1)[0]
cardNames = list(config.TAROT_CARDS[cardType].keys())
cardName = random.choices(cardNames, k=1)[0]
if cardType == "major":
cardMetaData = config.TAROT_CARDS[cardType][cardName]
return {
"name": f"{cardName} ({'正位' if cardDirectionType == 'upright' else '逆位'})",
"image": f"{config.TAROT_IMAGE_DICT[cardType]}/{cardMetaData['image']}-{cardDirectionType}.png",
"description": cardMetaData[cardDirectionType]
}
cardKeyNames = list(config.TAROT_CARDS[cardType][cardName]["hub"].keys())
cardKeyName = random.choices(cardKeyNames, k=1)[0]
cardMetaData = config.TAROT_CARDS[cardType][cardName]["hub"][cardKeyName]
cardMetaData["image"] = config.TAROT_CARDS[cardType][cardName]["image"]
cardKeyName = config.TAROT_MINOR_KEY_AND_NAME[cardKeyName]
return {
"name": f"{cardName} ({cardKeyName}) ({'正位' if cardDirectionType == 'upright' else '逆位'})",
"image": f"{config.TAROT_IMAGE_DICT[cardType]}/{cardMetaData['image']}-{cardDirectionType}.png",
"description": cardMetaData[cardDirectionType]
}
塔罗牌的抽取概率与今日运势相关
游戏
Rotaeno猜图片与Rotaeno猜音频的代码文件在同一个目录下, config
文件夹太大了 (就是太杂乱了)
Rotaeno猜图片
ImageGuessDifficultyLevel = config.rotaeno.ImageGuessDifficultyLevel
ImageGuessMethod = config.rotaeno.ImageGuessMethod
ImageGuessWinCoin = config.rotaeno.ImageGuessWinCoin
def randomSquareCrop(imagePath, cropSize, force=True):
image = Image.open(imagePath)
imageWidth, imageHeight = image.size
savePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.png"
if cropSize > min(imageWidth, imageHeight) and not force: raise ValueError(f"Crop size {cropSize} is larger than image dimensions {imageWidth}x{imageHeight} ({imagePath})")
if cropSize > min(imageWidth, imageHeight): cropSize = min(imageWidth, imageHeight)
x = random.randint(0, imageWidth - cropSize)
y = random.randint(0, imageHeight - cropSize)
croppedImage = image.crop((x, y, x + cropSize, y + cropSize))
croppedImage.save(savePath)
return savePath
def imageToGrayScale(imagePath):
grayImage = Image.open(imagePath).convert("L")
grayImagePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.png"
grayImage.save(grayImagePath)
return grayImagePath
def rotateImage(imagePath, angle):
image = Image.open(imagePath)
if image.mode == "L": fillcolor = 0
elif image.mode == "RGB": fillcolor = (0, 0, 0)
else: fillcolor = (0, 0, 0, 0)
rotatedImage = image.rotate(angle, expand=True, fillcolor=fillcolor)
rotatedImagePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.png"
rotatedImage.save(rotatedImagePath)
return rotatedImagePath
def invertImage(imagePath):
image = Image.open(imagePath)
alpha = None
if image.mode == 'RGBA':
alpha = image.split()[-1]
image = image.convert('RGB')
invertedImage = Image.eval(image, lambda x: 255 - x)
if alpha is not None:
invertedImage.putalpha(alpha)
invertedImagePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.png"
invertedImage.save(invertedImagePath)
return invertedImagePath
def getImageGuess(difficulty: ImageGuessDifficultyLevel) -> Tuple[str, str]:
if difficulty not in ImageGuessDifficultyLevel:
raise ValueError(f"Invalid difficulty level: {difficulty}")
useMethod = []
if difficulty == ImageGuessDifficultyLevel.BASIC:
cropSize = 768
useMethod.extend([ImageGuessMethod.RANDOM_CROP])
elif difficulty == ImageGuessDifficultyLevel.ADVANCED:
cropSize = 512
useMethod.extend([ImageGuessMethod.RANDOM_CROP])
elif difficulty == ImageGuessDifficultyLevel.EXPERT:
cropSize = 256
useMethod.extend([ImageGuessMethod.RANDOM_CROP])
elif difficulty == ImageGuessDifficultyLevel.MASTER:
cropSize = 256
useMethod.extend([ImageGuessMethod.RANDOM_CROP, ImageGuessMethod.GRAY_SCALE, ImageGuessMethod.ROTATE])
elif difficulty == ImageGuessDifficultyLevel.REMASTER:
cropSize = 128
useMethod.extend([ImageGuessMethod.RANDOM_CROP, ImageGuessMethod.GRAY_SCALE, ImageGuessMethod.ROTATE, ImageGuessMethod.INVERT])
else:
cropSize = 1024
useMethod.extend([ImageGuessMethod.RANDOM_CROP])
random.shuffle(useMethod)
songID = random.choice(config.rotaeno.SONG_IDS)
imagePath = f"{config.rotaeno.ASSETS_IMAGE_DIR}/{songID}.png"
for method in useMethod:
if method == ImageGuessMethod.RANDOM_CROP:
imagePath = randomSquareCrop(imagePath, cropSize)
elif method == ImageGuessMethod.GRAY_SCALE:
imagePath = imageToGrayScale(imagePath)
elif method == ImageGuessMethod.ROTATE:
imagePath = rotateImage(imagePath, random.randint(20, 340))
elif method == ImageGuessMethod.INVERT:
imagePath = invertImage(imagePath)
return (songID, imagePath, ImageGuessWinCoin[difficulty.name].value)
Rotaeno猜图片是调用getImageGuess
函数, 参数difficulty
控制难度
Rotaeno猜音频
HeardleDifficultyLevel = config.rotaeno.HeardleDifficultyLevel
HeardleMethod = config.rotaeno.HeardleMethod
HeardleWinCoin = config.rotaeno.HeardleWinCoin
def randomAudioCrop(inputPath, clipLengthSeconds, force=True):
audio = AudioSegment.from_file(inputPath).set_channels(1)
audioLengthMillisecond = len(audio)
clipLengthMillisecond = clipLengthSeconds * 1000
if clipLengthMillisecond > audioLengthMillisecond and not force: raise ValueError("The audio is too short to clip a segment of the specified length.")
if clipLengthMillisecond > audioLengthMillisecond: clipLengthMillisecond = audioLengthMillisecond
startMillisecond = random.randint(0, audioLengthMillisecond - clipLengthMillisecond)
endMillisecond = startMillisecond + clipLengthMillisecond
clip = audio[startMillisecond:endMillisecond]
savePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.wav"
clip.export(savePath, format="wav")
return savePath
def addNoiseToAudio(inputPath, noiseLevel):
audio = AudioSegment.from_file(inputPath).set_channels(1)
samples = numpy.array(audio.get_array_of_samples()).astype(numpy.float32)
noise = numpy.random.normal(0, 1, size=samples.shape)
maxAMP = numpy.max(numpy.abs(samples))
noise = noise * maxAMP * noiseLevel
noisySamples = samples + noise
noisySamples = numpy.clip(noisySamples, -32768, 32767).astype(numpy.int16)
noisyAudio = audio._spawn(noisySamples.tobytes())
savePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.wav"
noisyAudio.export(savePath, format="wav")
return savePath
def shuffleAudioSegments(inputPath, segmentDurationSeconds, force=False):
audio = AudioSegment.from_file(inputPath).set_channels(1)
segmentMillisecond = segmentDurationSeconds * 1000
totalSegments = int(len(audio) // segmentMillisecond)
print(f"音频总长: {len(audio)/1000:.2f}s, 分段数: {totalSegments}")
if totalSegments < 2 and not force: raise ValueError("The audio is too short to segment and shuffle")
segments = [audio[i * segmentMillisecond:(i + 1) * segmentMillisecond] for i in range(totalSegments)]
random.shuffle(segments)
shuffledAudio = sum(segments)
savePath = f"{config.rotaeno.TEMP_DIR}/{time.time()}.wav"
shuffledAudio.export(savePath, format="wav")
return savePath
def getHeardle(difficulty: HeardleDifficultyLevel) -> Tuple[str, str]:
if difficulty not in HeardleDifficultyLevel:
raise ValueError(f"Invalid difficulty level: {difficulty}")
useMethod = []
if difficulty == HeardleDifficultyLevel.BASIC:
clipLengthSeconds = 15
useMethod.extend([HeardleMethod.RANDOM_CROP])
elif difficulty == HeardleDifficultyLevel.ADVANCED:
clipLengthSeconds = 10
useMethod.extend([HeardleMethod.RANDOM_CROP])
elif difficulty == HeardleDifficultyLevel.EXPERT:
clipLengthSeconds = 5
noiseLevel = 0.1
useMethod.extend([HeardleMethod.RANDOM_CROP, HeardleMethod.NOISE])
elif difficulty == HeardleDifficultyLevel.MASTER:
clipLengthSeconds = 4
noiseLevel = 0.3
segmentDurationSeconds = 1
useMethod.extend([HeardleMethod.RANDOM_CROP, HeardleMethod.NOISE, HeardleMethod.SHUFFLE])
elif difficulty == HeardleDifficultyLevel.REMASTER:
clipLengthSeconds = 3
noiseLevel = 0.5
segmentDurationSeconds = 0.5
useMethod.extend([HeardleMethod.RANDOM_CROP, HeardleMethod.NOISE, HeardleMethod.SHUFFLE])
random.shuffle(useMethod)
songID = random.choice(config.rotaeno.SONG_IDS)
audioPath = f"{config.rotaeno.ASSETS_AUDIO_DIR}/{songID}.ogg"
for method in useMethod:
if method == HeardleMethod.RANDOM_CROP:
audioPath = randomAudioCrop(audioPath, clipLengthSeconds)
elif method == HeardleMethod.NOISE:
audioPath = addNoiseToAudio(audioPath, noiseLevel)
elif method == HeardleMethod.SHUFFLE:
audioPath = shuffleAudioSegments(audioPath, segmentDurationSeconds)
return (songID, audioPath, HeardleWinCoin[difficulty.name].value)
Rotaeno猜图片是调用getHeardle
函数, 参数difficulty
控制难度