mower-ng/mower/utils/operators.py
zhbaor ef74eb780a
Some checks failed
ci/woodpecker/push/check_format Pipeline failed
使用ruff格式化代码
2025-01-17 13:09:56 +08:00

833 lines
33 KiB
Python

"""
Copyright (c) 2024 zhbaor <zhbaor@zhaozuohong.vip>
This file is part of mower-ng (https://git.zhaozuohong.vip/mower-ng/mower-ng).
Mower-ng is free software: you may copy, redistribute and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, version 3 or later.
This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
This file incorporates work covered by the following copyright and
permission notice:
Copyright (c) 2023 Shawnsdaddy <wu2xx@dukes.jmu.edu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
import copy
from datetime import datetime, timedelta
from evalidate import Expr, base_eval_model
from mower.utils import config
from mower.utils.plan import PlanConfig
from ..data import agent_arrange_order, agent_list, base_room_list
from ..solvers.infra.record import save_action_to_sqlite_decorator
from ..utils.log import logger
class SkillUpgradeSupport:
support_class = None
level = 1
efficiency = 0
half_off = False
add_on = False
match = False
use_booster = True
name = ""
swap_name = ""
def __init__(self, name, skill_level, efficiency, match, swap_name="艾丽妮"):
self.name = name
self.level = skill_level
self.efficiency = efficiency
self.match = match
if self.level > 1:
self.half_off = True
self.swap_name = swap_name
class Operators:
config = None
operators = None
exhaust_agent = []
exhaust_group = []
groups = None
dorm = []
plan = None
global_plan = None
plan_condition = []
shadow_copy = {}
current_room_changed_callback = None
first_init = True
skill_upgrade_supports = []
@property
def party_time(self):
return config.party_time
def __init__(self, plan):
self.operators = {}
self.groups = {}
self.exhaust_agent = []
self.exhaust_group = []
self.dorm = []
self.workaholic_agent = []
self.free_blacklist = []
self.global_plan = plan
self.backup_plans = plan["backup_plans"]
# 切换默认排班
self.swap_plan([False] * (len(self.backup_plans)))
self.run_order_rooms = {}
self.clues = []
self.current_room_changed_callback = None
self.eval_model = base_eval_model.clone()
self.eval_model.nodes.extend(["Call", "Attribute"])
self.eval_model.attributes.extend(
[
"operators",
"party_time",
"is_working",
"is_resting",
"current_mood",
"current_room",
]
)
def __repr__(self):
return f"Operators(operators={self.operators})"
def calculate_switch_time(self, support: SkillUpgradeSupport):
hour = 0
half_off = support.half_off
level = support.level
match = support.match
efficiency = support.efficiency
same = support.name == support.swap_name
if level == 1:
half_off = False
# if left_minutes > 0 or left_hours > 0:
# hour = left_minutes / 60 + left_hours
# 基本5%
basic = 5
if support.add_on:
# 阿斯卡伦
basic += 5
if hour == 0:
hour = level * 8
if half_off:
hour = hour / 2
left = 0
if not same:
left = 5 * (100 + basic + (30 if match else 0)) / 100
left = hour - left
else:
left = hour
return left * 100 / (100 + efficiency + basic)
def swap_plan(self, condition, refresh=False):
self.plan = copy.deepcopy(self.global_plan["default_plan"].plan)
self.config: PlanConfig = copy.deepcopy(self.global_plan["default_plan"].config)
for index, success in enumerate(condition):
if success:
self.plan, self.config = self.merge_plan(index, self.config, self.plan)
self.plan_condition = condition
if refresh:
self.first_init = True
error = self.init_and_validate(True)
self.first_init = False
if error:
return error
def merge_plan(self, idx, ext_config, default_plan=None):
if default_plan is None:
default_plan = copy.deepcopy(self.global_plan["default_plan"].plan)
plan = copy.deepcopy(self.global_plan["backup_plans"][idx])
# 更新切换排班表
for key, value in plan.plan.items():
if key in default_plan:
for idx, operator in enumerate(value):
if operator.agent != "Current":
default_plan[key][idx] = operator
return default_plan, ext_config.merge_config(plan.config)
def generate_conditions(self, n):
if n == 1:
return [[True], [False]]
else:
prev_conditions = self.generate_conditions(n - 1)
conditions = []
for condition in prev_conditions:
conditions.append(condition + [True])
conditions.append(condition + [False])
return conditions
def init_and_validate(self, update=False):
self.groups = {}
self.exhaust_agent = []
self.exhaust_group = []
self.workaholic_agent = []
self.shadow_copy = copy.deepcopy(self.operators)
self.operators = {}
for room in self.plan.keys():
for idx, data in enumerate(self.plan[room]):
if data.agent not in list(agent_list.keys()) and data.agent != "Free":
return f"干员名输入错误: 房间->{room}, 干员->{data.agent}"
if data.agent in ["龙舌兰", "但书"]:
return f"高效组不可用龙舌兰,但书 房间->{room}, 干员->{data.agent}"
if data.agent == "菲亚梅塔" and idx == 1:
return f"菲亚梅塔不能安排在2号位置 房间->{room}, 干员->{data.agent}"
if data.agent == "菲亚梅塔" and not room.startswith("dorm"):
return "菲亚梅塔必须安排在宿舍"
if data.agent == "Free" and not room.startswith("dorm"):
return f"Free只能安排在宿舍 房间->{room}, 干员->{data.agent}"
if data.agent in self.operators and data.agent != "Free":
return f"高效组干员不可重复 房间->{room},{self.operators[data.agent].room}, 干员->{data.agent}"
self.add(
Operator(
data.agent,
room,
idx,
data.group,
data.replacement,
"high",
operator_type="high",
)
)
missing_replacements = []
for room in self.plan.keys():
if room.startswith("dorm") and len(self.plan[room]) != 5:
return f"宿舍 {room} 人数少于5人"
for idx, data in enumerate(self.plan[room]):
# 菲亚梅塔替换组做特例判断
if "龙舌兰" in data.replacement and "但书" in data.replacement:
return f"替换组不可同时安排龙舌兰和但书 房间->{room}, 干员->{data.agent}"
if "菲亚梅塔" in data.replacement:
return f"替换组不可安排菲亚梅塔 房间->{room}, 干员->{data.agent}"
r_count = len(data.replacement)
if "龙舌兰" in data.replacement or "但书" in data.replacement:
r_count -= 1
if r_count <= 0 and (
(data.agent != "Free" and (not room.startswith("dorm")))
or data.agent == "菲亚梅塔"
):
missing_replacements.append(data.agent)
for _replacement in data.replacement:
if (
_replacement not in list(agent_list.keys())
and data.agent != "Free"
):
return f"干员名输入错误: 房间->{room}, 干员->{_replacement}"
if data.agent != "菲亚梅塔":
# 普通替换
if (
_replacement in self.operators
and self.operators[_replacement].is_high()
):
return f"替换组不可用高效组干员: 房间->{room}, 干员->{_replacement}"
self.add(Operator(_replacement, ""))
else:
if _replacement not in self.operators:
return f"菲亚梅塔替换不在高效组列: 房间->{room}, 干员->{_replacement}"
if (
_replacement in self.operators
and not self.operators[_replacement].is_high()
):
return f"菲亚梅塔替换只能为高效组干员: 房间->{room}, 干员->{_replacement}"
# 判定替换缺失
if "菲亚梅塔" in missing_replacements:
return "菲亚梅塔替换缺失"
if len(missing_replacements):
return f"以下干员替换组缺失:{','.join(missing_replacements)}"
dorm_names = [k for k in self.plan.keys() if k.startswith("dorm")]
dorm_names.sort(key=lambda d: d, reverse=False)
added = []
# 竖向遍历出效率高到低
if not update:
for dorm in dorm_names:
free_found = False
for _idx, _dorm in enumerate(self.plan[dorm]):
if _dorm.agent == "Free" and _idx <= 1:
if "波登可" not in [_agent.agent for _agent in self.plan[dorm]]:
return "宿舍必须安排2个宿管"
if _dorm.agent != "Free" and free_found:
return "Free必须连续且安排在宿管后"
if (
_dorm.agent == "Free"
and not free_found
and (dorm + str(_idx)) not in added
and len(added) < self.config.max_resting_count
):
self.dorm.append(Dormitory((dorm, _idx)))
added.append(dorm + str(_idx))
free_found = True
continue
if not free_found:
return "宿舍必须安排至少一个Free"
# VIP休息位用完后横向遍历
for dorm in dorm_names:
for _idx, _dorm in enumerate(self.plan[dorm]):
if _dorm.agent == "Free" and (dorm + str(_idx)) not in added:
self.dorm.append(Dormitory((dorm, _idx)))
added.append(dorm + str(_idx))
else:
for key, value in self.shadow_copy.items():
if key not in self.operators:
self.add(Operator(key, ""))
if len(self.dorm) < self.config.max_resting_count:
return f"宿舍Free总数 {len(self.dorm)}小于最大分组数 {self.config.max_resting_count}"
# 跑单
for x, y in self.plan.items():
if not x.startswith("room"):
continue
if any(
("但书" in obj.replacement or "龙舌兰" in obj.replacement) for obj in y
):
self.run_order_rooms[x] = {}
# 判定分组排班可能性
current_high = self.config.max_resting_count
current_low = len(self.dorm) - self.config.max_resting_count
for key in self.groups:
high_count = 0
low_count = 0
_replacement = []
for name in self.groups[key]:
_candidate = next(
(
r
for r in self.operators[name].replacement
if r not in _replacement and r not in ["龙舌兰", "但书"]
),
None,
)
if _candidate is None:
return f"{key} 分组无法排班,替换组数量不够"
else:
_replacement.append(_candidate)
if self.operators[name].workaholic:
continue
if self.operators[name].resting_priority == "high":
high_count += 1
else:
low_count += 1
if high_count > current_high or low_count > current_low:
return f"{key} 分组无法排班,宿舍可用高优先{current_high},低优先{current_low}->分组需要高优先{high_count},低优先{low_count}"
# 设定令夕模式的心情阈值
self.init_mood_limit()
for name in self.workaholic_agent:
if name not in self.config.free_blacklist:
self.config.free_blacklist.append(name)
logger.info("宿舍黑名单:" + str(self.config.free_blacklist))
def set_mood_limit(self, name, upper_limit=24, lower_limit=0):
if name in self.operators:
self.operators[name].upper_limit = upper_limit
self.operators[name].lower_limit = lower_limit
logger.info(f"自动设置{name}心情下限为{lower_limit},上限为{upper_limit}")
def init_mood_limit(self):
# 设置心情阈值 for 夕,令,
if self.config.ling_xi == 1:
self.set_mood_limit("", upper_limit=12)
self.set_mood_limit("", lower_limit=12)
elif self.config.ling_xi == 2:
self.set_mood_limit("", upper_limit=12)
self.set_mood_limit("", lower_limit=12)
elif self.config.ling_xi == 0:
self.set_mood_limit("")
self.set_mood_limit("")
# 设置同组心情阈值
finished = []
for name in ["", ""]:
if (
name in self.operators
and self.operators[name].group != ""
and self.operators[name].group not in finished
):
for group_name in self.groups[self.operators[name].group]:
if group_name not in ["", ""]:
if self.config.ling_xi in [1, 2]:
self.set_mood_limit(group_name, lower_limit=12)
elif self.config.ling_xi == 0:
self.set_mood_limit(group_name, lower_limit=0)
finished.append(self.operators[name].group)
# 设置铅踝心情阈值
# 三种情况:
# 1. 铅踝不是主力:不管
# 2. 铅踝是红云组主力,设置心情上限 12、下限 8,效率 37%
# 3. 铅踝是普通主力:设置心情下限 20,效率 30%
TOTTER = "铅踝"
VERMEIL = "红云"
if TOTTER in self.operators and self.operators[TOTTER].operator_type == "high":
if (
VERMEIL in self.operators
and self.operators[VERMEIL].operator_type == "high"
and self.operators[VERMEIL].room == self.operators[TOTTER].room
):
self.set_mood_limit(TOTTER, upper_limit=12, lower_limit=8)
else:
self.set_mood_limit(TOTTER, upper_limit=24, lower_limit=20)
def evaluate_expression(self, expression):
try:
result = Expr(expression, self.eval_model).eval({"op_data": self})
return result
except Exception as e:
logger.exception(f"Error evaluating expression: {e}")
return None
def get_current_room(self, room, bypass=False, current_index=None):
room_data = {
v.current_index: v
for k, v in self.operators.items()
if v.current_room == room
}
res = [obj.agent for obj in self.plan[room]]
not_found = False
for idx, op in enumerate(res):
if idx in room_data:
res[idx] = room_data[idx].name
else:
res[idx] = ""
if current_index is not None and idx not in current_index:
continue
not_found = True
if not_found and not bypass:
return None
else:
return res
def predict_fia(self, operators, fia_mood, hours=240):
recover_hours = (24 - fia_mood) / 2
for agent in operators:
agent.mood -= agent.depletion_rate * recover_hours
if agent.mood < 0.0:
return False
if recover_hours >= hours or 0 < recover_hours < 1:
return True
operators.sort(
key=lambda x: (x.mood - x.lower_limit) / (x.upper_limit - x.lower_limit),
reverse=False,
)
fia_mood = operators[0].mood
operators[0].mood = 24
return self.predict_fia(operators, fia_mood, hours - recover_hours)
def reset_dorm_time(self):
for name in self.operators.keys():
agent = self.operators[name]
if agent.room.startswith("dorm"):
agent.time_stamp = None
@save_action_to_sqlite_decorator
def update_detail(self, name, mood, current_room, current_index, update_time=False):
agent = self.operators[name]
if update_time:
if agent.time_stamp is not None and agent.mood > mood:
agent.depletion_rate = (
(agent.mood - mood)
* 3600
/ ((datetime.now() - agent.time_stamp).total_seconds())
)
agent.time_stamp = datetime.now()
# 如果移出宿舍,则清除对应宿舍数据 且重新记录高效组心情(如果有备用班,则跳过高效组判定)
if (
agent.current_room.startswith("dorm")
and not current_room.startswith("dorm")
and (agent.is_high() or self.backup_plans)
):
self.refresh_dorm_time(
agent.current_room, agent.current_index, {"agent": ""}
)
if update_time:
self.time_stamp = datetime.now()
else:
self.time_stamp = None
agent.depletion_rate = 0
if (
self.get_dorm_by_name(name)[0] is not None
and not current_room.startswith("dorm")
and (agent.is_high() or self.backup_plans)
):
_dorm = self.get_dorm_by_name(name)[1]
_dorm.name = ""
_dorm.time = None
agent.current_room = current_room
agent.current_index = current_index
agent.mood = mood
# 如果是高效组且没有记录时间,则返还index
if agent.current_room.startswith("dorm") and (
agent.is_high() or self.backup_plans
):
for dorm in self.dorm:
if (
dorm.position[0] == current_room
and dorm.position[1] == current_index
and dorm.time is None
):
return current_index
if agent.name == "菲亚梅塔" and (
self.operators["菲亚梅塔"].time_stamp is None
or self.operators["菲亚梅塔"].time_stamp < datetime.now()
):
return current_index
def refresh_dorm_time(self, room, index, agent):
for idx, dorm in enumerate(self.dorm):
# Filter out resting priority low
# if idx >= self.config.max_resting_count:
# break
if dorm.position[0] == room and dorm.position[1] == index:
# 如果人为高效组,则记录时间
_name = agent["agent"]
if _name in self.operators.keys() and (
self.operators[_name].is_high() or self.config.free_room
):
dorm.name = _name
_agent = self.operators[_name]
# 如果干员有心情上限,则按比例修改休息时间
if _agent.mood != 24:
sec_remaining = (
(_agent.upper_limit - _agent.mood)
* ((agent["time"] - _agent.time_stamp).total_seconds())
/ (24 - _agent.mood)
)
dorm.time = _agent.time_stamp + timedelta(seconds=sec_remaining)
else:
dorm.time = agent["time"]
elif _name in list(agent_list.keys()):
dorm.name = _name
dorm.time = agent["time"]
break
def correct_dorm(self):
for idx, dorm in enumerate(self.dorm):
if dorm.name != "" and dorm.name in self.operators.keys():
op = self.operators[dorm.name]
if not (
dorm.position[0] == op.current_room
and dorm.position[1] == op.current_index
):
self.dorm[idx].name = ""
self.dorm[idx].time = None
else:
if (
self.dorm[idx].time is not None
and self.dorm[idx].time < datetime.now()
):
op.mood = op.upper_limit
op.time_stamp = self.dorm[idx].time
logger.debug(
f"检测到{op.name}心情恢复满,设置心情至{op.upper_limit}"
)
def get_train_support(self):
for name in self.operators.keys():
agent = self.operators[name]
if agent.current_room == "train" and agent.current_index == 0:
return agent.name
return None
def get_refresh_index(self, room, plan):
ret = []
if room.startswith("dorm") and self.config.free_room:
return [i for i, x in enumerate(self.plan[room]) if x == "Free"]
for idx, dorm in enumerate(self.dorm):
# Filter out resting priority low
if idx >= self.config.max_resting_count:
if not self.config.free_room:
break
if dorm.position[0] == room:
for i, _name in enumerate(plan):
if _name not in self.operators.keys():
self.add(Operator(_name, ""))
if not self.config.free_room:
if self.operators[_name].is_high() and not self.operators[
_name
].room.startswith("dorm"):
ret.append(i)
elif not self.operators[_name].room.startswith("dorm"):
ret.append(i)
break
return ret
def get_dorm_by_name(self, name):
for idx, dorm in enumerate(self.dorm):
if dorm.name == name:
return idx, dorm
return None, None
def add(self, operator):
if operator.name not in list(agent_list.keys()):
return
if self.config.is_resting_priority(operator.name):
operator.resting_priority = "low"
operator.exhaust_require = self.config.is_exhaust_require(operator.name)
operator.rest_in_full = self.config.is_rest_in_full(operator.name)
operator.workaholic = self.config.is_workaholic(operator.name)
operator.refresh_order_room = self.config.is_refresh_trading(operator.name)
if operator.name in agent_arrange_order:
operator.arrange_order = agent_arrange_order[operator.name]
# 复制基建数据
if operator.name in self.shadow_copy:
exist = self.shadow_copy[operator.name]
operator.mood = exist.mood
operator.time_stamp = exist.time_stamp
operator.depletion_rate = exist.depletion_rate
operator.current_room = exist.current_room
operator.current_index = exist.current_index
self.operators[operator.name] = operator
# 需要用尽心情干员逻辑
if (
operator.exhaust_require or operator.group in self.exhaust_group
) and operator.name not in self.exhaust_agent:
self.exhaust_agent.append(operator.name)
if operator.group != "":
self.exhaust_group.append(operator.group)
# 干员分组逻辑
if operator.group != "":
if operator.group not in self.groups.keys():
self.groups[operator.group] = [operator.name]
else:
self.groups[operator.group].append(operator.name)
if operator.workaholic and operator.name not in self.workaholic_agent:
self.workaholic_agent.append(operator.name)
def available_free(self, free_type="high"):
ret = 0
freeName = []
if free_type == "high":
idx = 0
for dorm in self.dorm:
if dorm.name == "" or (
dorm.name in self.operators.keys()
and not self.operators[dorm.name].is_high()
):
ret += 1
elif dorm.time is not None and dorm.time < datetime.now():
logger.info(f"检测到房间休息完毕,释放{dorm.name}宿舍位")
freeName.append(dorm.name)
ret += 1
if idx == self.config.max_resting_count - 1:
break
else:
idx += 1
else:
idx = self.config.max_resting_count
for i in range(idx, len(self.dorm)):
dorm = self.dorm[i]
# 释放满休息位
# TODO 高效组且低优先可以相互替换
if dorm.name == "" or (
dorm.name in self.operators.keys()
and not self.operators[dorm.name].is_high()
):
ret += 1
elif dorm.time is not None and dorm.time < datetime.now():
logger.info(f"检测到房间休息完毕,释放{dorm.name}宿舍位")
freeName.append(dorm.name)
ret += 1
if len(freeName) > 0:
for name in freeName:
if name in list(agent_list.keys()):
self.operators[name].mood = self.operators[name].upper_limit
self.operators[name].depletion_rate = 0
self.operators[name].time_stamp = datetime.now()
return ret
def assign_dorm(self, name):
is_high = self.operators[name].resting_priority == "high"
if is_high:
_room = next(
obj
for obj in self.dorm
if obj.name not in self.operators.keys()
or not self.operators[obj.name].is_high()
)
else:
_room = None
for i in range(self.config.max_resting_count, len(self.dorm)):
_name = self.dorm[i].name
if _name == "" or not self.operators[_name].is_high():
_room = self.dorm[i]
break
_room.name = name
return _room
def get_current_operator(self, room, index):
for key, value in self.operators.items():
if value.current_room == room and value.current_index == index:
return value
return None
def print(self):
ret = "{"
op = []
dorm = []
for k, v in self.operators.items():
op.append("'" + k + "': " + str(vars(v)))
ret += "'operators': {" + ",".join(op) + "},"
for v in self.dorm:
dorm.append(str(vars(v)))
ret += "'dorms': [" + ",".join(dorm) + "]}"
return ret
class Dormitory:
def __init__(self, position, name="", time=None):
self.position = position
self.name = name
self.time = time
def __repr__(self):
return (
f"Dormitory(position={self.position},name='{self.name}',time='{self.time}')"
)
class Operator:
time_stamp = None
depletion_rate = 0
workaholic = False
arrange_order = [2, "false"]
def __init__(
self,
name,
room,
index=-1,
group="",
replacement=[],
resting_priority="low",
current_room="",
exhaust_require=False,
mood=24,
upper_limit=24,
rest_in_full=False,
current_index=-1,
lower_limit=0,
operator_type="low",
depletion_rate=0,
time_stamp=None,
refresh_order_room=None,
):
if refresh_order_room is not None:
self.refresh_order_room = refresh_order_room
self.refresh_order_room = [False, []]
self.name = name
self.room = room
self.operator_type = operator_type
self.index = index
self.group = group
self.replacement = replacement
self.resting_priority = resting_priority
self._current_room = None
self.current_room = current_room
self.exhaust_require = exhaust_require
self.upper_limit = upper_limit
self.rest_in_full = rest_in_full
self.mood = mood
self.current_index = current_index
self.lower_limit = lower_limit
self.depletion_rate = depletion_rate
self.time_stamp = time_stamp
@property
def current_room(self):
return self._current_room
@current_room.setter
def current_room(self, value):
if self._current_room != value:
self._current_room = value
if Operators.current_room_changed_callback and self.refresh_order_room[0]:
Operators.current_room_changed_callback(self)
def is_high(self):
return self.operator_type == "high"
def is_resting(self):
return self.current_room.startswith("dorm")
def is_working(self):
return self.current_room in base_room_list and not self.is_resting()
def need_to_refresh(self, h=2, r=""):
# 是否需要读取心情
if (
self.time_stamp is None
or (
self.time_stamp is not None
and self.time_stamp + timedelta(hours=h) < datetime.now()
)
or (r.startswith("dorm") and not self.room.startswith("dorm"))
):
return True
def not_valid(self):
if self.room == "train":
return False
if self.operator_type == "high":
if self.workaholic:
return (
self.current_room != self.room or self.index != self.current_index
)
if not self.room.startswith("dorm") and self.current_room.startswith(
"dorm"
):
if self.mood == -1 or self.mood == 24:
return True
else:
return False
return (
self.need_to_refresh(2.5)
or self.current_room != self.room
or self.index != self.current_index
)
return False
def current_mood(self):
predict = self.mood
if self.time_stamp is not None:
predict = (
self.mood
- self.depletion_rate
* (datetime.now() - self.time_stamp).total_seconds()
/ 3600
)
if 0 <= predict <= 24:
return predict
else:
return self.mood
def __repr__(self):
return f"Operator(name='{self.name}', room='{self.room}', index={self.index}, group='{self.group}', replacement={self.replacement}, resting_priority='{self.resting_priority}', current_room='{self.current_room}',exhaust_require={self.exhaust_require},mood={self.mood}, upper_limit={self.upper_limit}, rest_in_full={self.rest_in_full}, current_index={self.current_index}, lower_limit={self.lower_limit}, operator_type='{self.operator_type}',depletion_rate={self.depletion_rate},time_stamp='{self.time_stamp}',refresh_order_room = {self.refresh_order_room})"