├── scripts ├── zip.sh ├── linux_run.sh ├── run.sh ├── format.sh ├── linux_replay.sh ├── testllm.sh └── run.py ├── docs ├── 编译运行环境说明.pdf ├── 决赛判题器使用说明.pdf ├── 2024华为软件精英挑战赛决赛任务书.pdf └── 2024 华为软件精英挑战赛-决赛大模型API使用说明.pdf ├── test ├── test.cpp └── question.cpp ├── src ├── config.cpp ├── CMakeLists.txt ├── frame.hpp ├── main.cpp ├── utils.cpp ├── land_controller.hpp ├── question.hpp ├── config.hpp ├── ocean_controller.hpp ├── ship.hpp ├── robot.hpp ├── ocean_scheduler.hpp ├── cargo.hpp ├── berth.hpp ├── question.cpp ├── map.hpp ├── robot.cpp ├── land_scheduler.hpp ├── berth.cpp ├── utils.hpp ├── ocean_controller.cpp ├── cargo.cpp ├── ship.cpp ├── frame.cpp ├── ocean_scheduler.cpp ├── land_controller.cpp ├── map.cpp └── land_scheduler.cpp ├── .clang-format ├── CMakeLists.txt └── README.md /scripts/zip.sh: -------------------------------------------------------------------------------- 1 | zip -r $(date +"%m-%d_%H-%M-%S").zip src/ -------------------------------------------------------------------------------- /scripts/linux_run.sh: -------------------------------------------------------------------------------- 1 | cd build && cmake .. && taskset -c 0,1 make 1 -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | rm -rf ./build 2 | mkdir build 3 | cd build 4 | cmake .. 5 | make 1 -------------------------------------------------------------------------------- /scripts/format.sh: -------------------------------------------------------------------------------- 1 | clang-format -i -style=file src/*.cpp 2 | clang-format -i -style=file src/*.hpp -------------------------------------------------------------------------------- /docs/编译运行环境说明.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeQi-CQ/HuaweiCodeCraft2024-Final/HEAD/docs/编译运行环境说明.pdf -------------------------------------------------------------------------------- /scripts/linux_replay.sh: -------------------------------------------------------------------------------- 1 | cd build/judge/linux/replay && ./../replayer/CodeCraft_2024_Replayer_final.x86_64 -------------------------------------------------------------------------------- /docs/决赛判题器使用说明.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeQi-CQ/HuaweiCodeCraft2024-Final/HEAD/docs/决赛判题器使用说明.pdf -------------------------------------------------------------------------------- /scripts/testllm.sh: -------------------------------------------------------------------------------- 1 | g++ test/question.cpp src/question.cpp src/config.cpp -lcurl -o test.out -std=c++17 && ./test.out -------------------------------------------------------------------------------- /docs/2024华为软件精英挑战赛决赛任务书.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeQi-CQ/HuaweiCodeCraft2024-Final/HEAD/docs/2024华为软件精英挑战赛决赛任务书.pdf -------------------------------------------------------------------------------- /docs/2024 华为软件精英挑战赛-决赛大模型API使用说明.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrangeQi-CQ/HuaweiCodeCraft2024-Final/HEAD/docs/2024 华为软件精英挑战赛-决赛大模型API使用说明.pdf -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "../src/question.hpp" 2 | 3 | int main() { 4 | std::string question = "下列哪种材质常用于制作快递包装中的密封袋,具有防水和防撕裂的特点? A. 塑料袋 B. 纸质袋 C. 布质袋 D. 金属袋"; 5 | std::cout << question << std::endl << AskForLLM(question) << std::endl; 6 | } -------------------------------------------------------------------------------- /src/config.cpp: -------------------------------------------------------------------------------- 1 | #include "config.hpp" 2 | 3 | std::mutex cerr_mtx; 4 | 5 | std::atomic_int g_frame_id = -1; 6 | int g_money = -1; 7 | int g_ship_capacity = -1; 8 | bool stop = false; 9 | 10 | // DEBUG 11 | int value_got_berth = 0; 12 | int g_value_token = 0; 13 | 14 | int g_num_berth; 15 | int g_ship_cnt; 16 | int g_robot_cnt = 0; 17 | int g_cargo_num = 0; 18 | 19 | int g_other_robot_num; 20 | int g_other_ship_num; 21 | 22 | int g_caplim = 5; -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(CodeCraftSDK) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_C_STANDARD 11) 6 | 7 | add_compile_definitions(DISABLE_PTHREAD_HOOK) 8 | 9 | if (NOT WIN32) 10 | link_libraries(pthread rt m) 11 | endif (NOT WIN32) 12 | 13 | find_package(CURL REQUIRED) 14 | 15 | AUX_SOURCE_DIRECTORY(. src) 16 | list(REMOVE_ITEM src "./load_pthread.cpp") 17 | 18 | add_executable(main ${src}) 19 | target_link_libraries(main CURL::libcurl) -------------------------------------------------------------------------------- /src/frame.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "cargo.hpp" 3 | #include "config.hpp" 4 | #include "land_controller.hpp" 5 | #include "land_scheduler.hpp" 6 | #include "map.hpp" 7 | #include "ocean_controller.hpp" 8 | #include "ocean_scheduler.hpp" 9 | #include "robot.hpp" 10 | #include "ship.hpp" 11 | #include "utils.hpp" 12 | 13 | 14 | static void CheckOK(); 15 | 16 | static void InputFrame(); 17 | 18 | static void PlanFrame(); 19 | 20 | static void OutputFrame(); 21 | 22 | static void BuyRobotShip(); 23 | 24 | void Init(); 25 | void Control(); -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if !defined(_WIN32) && !defined(_WIN64) 4 | #include 5 | #include 6 | #endif 7 | 8 | #include "frame.hpp" 9 | 10 | 11 | int main() { 12 | #ifdef USE_MFMC 13 | std::cerr << "DEFINE USE_MFMC" << std::endl; 14 | #endif 15 | 16 | #ifdef AVOID_SWING 17 | std::cerr << "DEFINE AVOID_SWING" << std::endl; 18 | #endif 19 | 20 | #ifdef RAND 21 | unsigned rand_seed = static_cast(time(nullptr)); 22 | srand(rand_seed); 23 | std::cerr << "srand = " << rand_seed << std::endl; 24 | #endif 25 | 26 | #ifdef LOCAL 27 | std::cerr << "DEFINE LOCAL" << std::endl; 28 | #endif 29 | 30 | #ifdef DEBUG 31 | fprintf(stderr, "DEFIND DEBUG\n"); 32 | #endif 33 | 34 | #if !defined(_WIN32) && !defined(_WIN64) 35 | // 将 main 线程绑定到 cpu 0 上 36 | pthread_t main_thread_id = pthread_self(); 37 | cpu_set_t cpu_set; 38 | CPU_ZERO(&cpu_set); 39 | CPU_SET(0, &cpu_set); 40 | pthread_setaffinity_np(main_thread_id, sizeof(cpu_set), &cpu_set); 41 | 42 | fprintf(stderr, "Set CPU!\n"); 43 | #endif 44 | 45 | Init(); // 初始化 46 | Control(); // 控制所有帧 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /test/question.cpp: -------------------------------------------------------------------------------- 1 | #include "../src/question.hpp" 2 | 3 | #include 4 | 5 | int main() { 6 | std::string qaPath = "./judge/linux/"; 7 | 8 | std::ifstream file(qaPath + "qa.txt"); 9 | 10 | std::vector qaVec; 11 | std::vector ansVec; 12 | 13 | if (file.is_open()) { 14 | std::string line; 15 | int lineNum = 0; 16 | // 使用 std::getline 读取文件的每一行 17 | while (std::getline(file, line)) { 18 | if (lineNum % 3 == 0) { 19 | qaVec.push_back(line); 20 | } else if (lineNum % 3 == 1) { 21 | ansVec.push_back(line); 22 | } 23 | 24 | if (lineNum > 150) { 25 | break; 26 | } 27 | 28 | lineNum++; 29 | // std::cerr << line << std::endl; 30 | } 31 | } 32 | 33 | 34 | for (int i = 0; i < qaVec.size(); i++) { 35 | std::string question = qaVec[i]; 36 | std::string ans = ansVec[i]; 37 | int res; 38 | 39 | std::cerr << question << std::endl; 40 | std::cerr << (res = AskForLLM(question)) << std::endl; 41 | std::cerr << ans << std::endl; 42 | 43 | if (ans[0] - 'A' != res) { 44 | std::cerr << "fuc"; 45 | 46 | for (; rand() % 20 != 0;) 47 | std::cerr << "k"; 48 | 49 | std::cerr << std::endl; 50 | } 51 | } 52 | 53 | // std::string question = " 海上运输中使用的木质包装材料,根据国际标准,需要进行哪种处理? A. 防水处理 B. 54 | // 防火处理 C. 熏蒸处理 D. 防虫处理"; std::cout << question << std::endl << AskForLLM(question) << 55 | // std::endl; 56 | } -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.hpp" 2 | 3 | DisjointSetUnion::DisjointSetUnion(int n) : n_(n) { 4 | leader_.resize(n); 5 | std::iota(leader_.begin(), leader_.end(), 0); 6 | } 7 | 8 | bool DisjointSetUnion::Merge(int x, int y) { 9 | x = FindLeader(x); 10 | y = FindLeader(y); 11 | 12 | if (x == y) { 13 | return false; 14 | } 15 | 16 | leader_[y] = x; 17 | return true; 18 | } 19 | 20 | int DisjointSetUnion::FindLeader(int x) { 21 | return leader_[x] == x ? x : (leader_[x] = FindLeader(leader_[x])); 22 | } 23 | 24 | std::vector> DisjointSetUnion::GetSets() { 25 | std::vector> sets(n_); 26 | for (int i = 0; i < n_; i++) { 27 | sets[FindLeader(i)].push_back(i); 28 | } 29 | sets.erase( 30 | std::remove_if(sets.begin(), sets.end(), [](const std::vector vec) { return vec.empty(); }), 31 | sets.end()); 32 | 33 | for (auto &vec : sets) { 34 | std::sort(vec.begin(), vec.end()); 35 | } 36 | return sets; 37 | } 38 | 39 | std::ostream &operator<<(std::ostream &os, const Position &pos) { 40 | os << "(" << pos[0] << ", " << pos[1] << ")"; 41 | return os; 42 | } 43 | 44 | Position operator+(const Position &lhs, const Position &rhs) { 45 | return Position{lhs[0] + rhs[0], lhs[1] + rhs[1]}; 46 | } 47 | 48 | Position operator-(const Position &lhs, const Position &rhs) { 49 | return Position{lhs[0] - rhs[0], lhs[1] - rhs[1]}; 50 | } 51 | 52 | int GetL1(const Position &lhs, const Position &rhs) { 53 | return std::abs(lhs[0] - rhs[0]) + std::abs(lhs[1] - rhs[1]); 54 | } 55 | 56 | Logger crashLog("crash"); -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: google 3 | AccessModifierOffset: -4 # public、private 等访问说明符偏移 -4 4 | AlignAfterOpenBracket: Align # 开阔好换行对齐 5 | # BreakBeforeBraces: Allman # 不省略大括号 6 | AlignTrailingComments: true # 对齐连续的尾随注释 7 | 8 | # 允许函数声明的所有参数放在下一行 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | 11 | # 不允许短的块放在同一行 12 | AllowShortBlocksOnASingleLine: false 13 | 14 | # 不允许短的case标签放在同一行 15 | AllowShortCaseLabelsOnASingleLine: false 16 | 17 | # 仅空函数允许放在一行 18 | AllowShortFunctionsOnASingleLine: Empty 19 | 20 | # 不允许短的if语句保持在同一行 21 | # AllowShortIfStatementsOnASingleLine: false 22 | 23 | # 不允许短的循环保持在同一行 24 | AllowShortLoopsOnASingleLine: false 25 | 26 | # 不在定义返回类型后换行(deprecated) 27 | AlwaysBreakAfterDefinitionReturnType: None 28 | 29 | # 总是在多行string字面量前换行 30 | AlwaysBreakBeforeMultilineStrings: false 31 | 32 | # 总是在template声明后换行 33 | AlwaysBreakTemplateDeclarations: true 34 | 35 | # false表示函数实参要么都在同一行,要么都各自一行 36 | # BinPackArguments: false 37 | 38 | # false表示所有形参要么都在同一行,要么都各自一行 39 | BinPackParameters: false 40 | 41 | # 在构造函数的初始化列表的逗号前换行 42 | BreakConstructorInitializersBeforeComma: false 43 | 44 | # 每行字符限制 45 | ColumnLimit: 110 46 | 47 | # 构造函数的初始化列表要么都在同一行,要么都各自一行 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 49 | 50 | # 构造函数的初始化列表的缩进宽度 51 | ConstructorInitializerIndentWidth: 4 52 | 53 | # 延续的行的缩进宽度 54 | ContinuationIndentWidth: 4 55 | 56 | # 去除C++11的列表初始化的大括号{后和}前的空格 57 | Cpp11BracedListStyle: true 58 | 59 | # 缩进case标签 60 | IndentCaseLabels: true 61 | 62 | # 连续空行的最大数量 63 | MaxEmptyLinesToKeep: 2 64 | 65 | # 缩进宽度 66 | IndentWidth: 4 67 | 68 | # 允许重新排版注释 69 | ReflowComments: true 70 | 71 | # 允许排序#include 72 | SortIncludes: true 73 | 74 | TabWidth: 4 -------------------------------------------------------------------------------- /src/land_controller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "robot.hpp" 3 | #include "ship.hpp" 4 | 5 | /** 6 | * @struct 描述一个机器人的运动状态 7 | */ 8 | struct RobotMotionState { 9 | RobotMotionState(Position position_, std::map dire_val_); 10 | Position position_; // 此刻的位置 11 | std::map dire_val_; // 距离收益表。包含原地不动,不包含非法运动方向。 12 | }; 13 | 14 | 15 | class LandController { 16 | public: 17 | LandController() = default; 18 | 19 | LandController(const LandController &) = delete; 20 | LandController &operator=(const LandController &) = delete; 21 | 22 | LandController(LandController &&) = delete; 23 | LandController &operator=(LandController &&) = delete; 24 | 25 | ~LandController() = default; 26 | 27 | /** 28 | * @brief 确定每个机器人的运动策略 29 | */ 30 | void ControlRobots(); 31 | 32 | private: 33 | /** 34 | * @brief 对于机器人 robot,确定它沿不同方向运动的距离增益 35 | */ 36 | std::map GetDirectionMap(const Robot &robot); 37 | 38 | /** 39 | * @brief 获取所有机器人的 RobotMotionState 40 | */ 41 | std::vector GetRobotMotionState(); 42 | 43 | /** 44 | * @brief 将所有机器人分组,每组内部有碰撞风险,组之间没有碰撞风险。 45 | * @return 外层 vector 表示每一个组。内层 vector 表示组内的机器人编号 46 | */ 47 | std::vector> GetRobotGroups(); 48 | 49 | /** 50 | * @brief 协调机器人的碰撞 51 | * @param motion_state_vec 每个机器人的运动状态 52 | * @return 每个机器人的运动方向决策 53 | */ 54 | std::vector AvoidCrash(const std::vector &motion_state_vec); 55 | 56 | /** 57 | * @brief 较差的机器人防碰撞 58 | */ 59 | void WorseAvoidCrash(int i, int c); 60 | 61 | // 有可能会与其他队伍的机器人碰撞的位置 62 | std::shared_ptr dangerous_position_ = std::make_shared(); 63 | }; 64 | 65 | extern LandController g_land_controller; -------------------------------------------------------------------------------- /src/question.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "config.hpp" 13 | #include "utils.hpp" 14 | 15 | 16 | /** 17 | * @struct 储存一次问答 18 | */ 19 | struct Question { 20 | Question(const std::string &question, int frame_id); 21 | std::atomic_bool valid_{false}; // 是否已经计算完成 22 | std::string question_; // 问题 23 | int answer_; // 答案 24 | int frame_id_; // 发出提问的帧号 25 | }; 26 | using QuestionPtr = std::shared_ptr; 27 | 28 | /** 29 | * @brief curl 请求的回调函数 30 | */ 31 | static size_t WriteCallback(char *ptr, size_t size, size_t nmemb, std::string *data); 32 | 33 | /** 34 | * @brief 像 LLM 发送请求,回答问题。 35 | */ 36 | int AskForLLM(std::string question); 37 | 38 | /** 39 | * @brief pthread 的接口 40 | */ 41 | template 42 | void *PthreadFunction(void *arg); 43 | 44 | /** 45 | * @class 用来向大模型提问,内部实现了一个异步线程池 46 | */ 47 | template 48 | class QuestionSolver { 49 | public: 50 | QuestionSolver(); 51 | 52 | ~QuestionSolver(); 53 | 54 | /** 55 | * @brief 启动像大模型提问的线程池 56 | */ 57 | void StartAsynTask(); 58 | 59 | /** 60 | * @brief 提交一个请求 61 | */ 62 | void Submit(QuestionPtr question_ptr); 63 | 64 | /** 65 | * @brief 获取任务的数量 66 | */ 67 | int Size(); 68 | 69 | /** 70 | * @brief 关闭线程池 71 | */ 72 | void Kill(); 73 | 74 | private: 75 | /** 76 | * @brief 线程函数 77 | */ 78 | void Worker(); 79 | 80 | std::queue task_queue_; 81 | std::atomic_bool stop_{false}; 82 | std::vector threads_; 83 | std::mutex task_queue_mtx_; 84 | }; 85 | 86 | extern QuestionSolver g_question_solver; -------------------------------------------------------------------------------- /src/config.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 这个文件记录所有的宏、常数,基本的全局变量声明 3 | */ 4 | 5 | #pragma once 6 | #include 7 | #include 8 | #include 9 | 10 | #define USE_MFMC 11 | #define AVOID_SWING 12 | // #define DONT_USE_ROBOT_AVOID_SWING 13 | 14 | #define USE_DFS 15 | 16 | const int MAP_SIZE = 800; // 地图大小 17 | const int INF = 1e9; 18 | const float EPS = 1e-6; // 浮点数精度,费用流用到 19 | const int MAX_FRAME_NUM = 20000; 20 | 21 | const int ship_tar_hash[20] = {5, 2, 3, 0, 4, 1}; 22 | 23 | const int MAX_ROBOT_NUM = 54; // 最大的机器人数量 24 | const int MAX_SHIP_NUM = 6; // 最大的船的数量 25 | const int ROBOT_DANGEROUS_RANGE = 0; // 其他队伍的危险范围 26 | const int BERTH_SELECT_STEP = 1; // 泊位选择的步长 27 | const int MAX_REAL_CARGO_NUM = 500; // CargoManager 存储的货物上限(不严格) 28 | const int MAX_VIRTUAL_CARGO_NUM = 300; // 虚拟点的数量上限 29 | const int MAX_TOT_CARGO_NUM = MAX_REAL_CARGO_NUM + MAX_VIRTUAL_CARGO_NUM; 30 | const int MIN_ADVANCED_CARGO_VALUE = 1350; 31 | 32 | const int MAX_CARGO_NUM_FOR_ROBOT_MATCH = 10; // 费用流中每个机器人考虑的货物数量上限 33 | 34 | const int VIRTUAL_CARGO_BENEFIT_RANGE = 5; // 虚拟货物计算机器人密度时的范围 35 | 36 | 37 | const int MAX_ROBOT_CARGO_MATCH_DISTANCE = 200; // 机器人与货物匹配时,考虑的最远的距离 38 | 39 | const int ROBOT_TYPE = 0; // 机器人的种类 40 | const int BERTH_BFS_MAX_DEPTH = INF; // 泊位 bfs 的最大深度 41 | const int CARGO_BFS_MAX_DEPTH = INF; // 货物 bfs 的最大深度 42 | const int ship_crash_limit = 8; 43 | const int predict_step = 6; 44 | const int LLM_THREAD_NUM_0 = 1; // 向大模型提问的线程数量(CPU 0) 45 | const int LLM_THREAD_NUM_1 = 10; // 向大模型提问的线程数量(CPU 1) 46 | const int TOT_LLM_THREAD_NUM = LLM_THREAD_NUM_0 + LLM_THREAD_NUM_1; 47 | 48 | const int CARGO_INIT_THREAD_NUM = 0; 49 | 50 | extern std::mutex cerr_mtx; // std::cerr 和 fprintf 的线程安全锁 51 | 52 | 53 | // 用于换方向,可以看 Ship::Rot 54 | const int ROT_RANK[4]{0, 3, 1, 2}; 55 | const int ROT_HASH[4]{0, 2, 3, 1}; 56 | 57 | using Instruction = std::pair; // 输出的指令 58 | 59 | // 重要全局变量 60 | extern std::atomic_int g_frame_id; // 当前帧号 61 | extern int g_money; // 当前拥有的金钱数 62 | extern int g_ship_capacity; // 船的容积 63 | extern int g_cargo_num; // 货物的数量 64 | extern int g_caplim; 65 | extern int g_num_berth; 66 | extern int g_ship_cnt; 67 | extern int g_robot_cnt; 68 | extern int g_value_token; 69 | 70 | // 判断一个点是越界 71 | #define IN_MAP(x, y) ((x) >= 0 && (x) < MAP_SIZE && (y) >= 0 && (y) < MAP_SIZE) 72 | 73 | 74 | const int DEBUG_PRINT_FREQUENCY = 100; 75 | #ifdef LOCAL 76 | // #define DONT_USE_MFMC_LIMITATIONO 77 | #define LOCAL_TIMER 78 | // #define LOCAL_DEBUG_LLM 79 | // #define LOCAL_TIMER 80 | #endif 81 | -------------------------------------------------------------------------------- /src/ocean_controller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "robot.hpp" 3 | #include "ship.hpp" 4 | #include "utils.hpp" 5 | 6 | class Astar { 7 | public: 8 | /** 9 | * @brief 计算船到达目标地预估时间开销 10 | * @param pos 位置 11 | * @param dir 方向 12 | * @return 开销 13 | */ 14 | int CalcHcost(Position& pos, int& dir); 15 | 16 | /** 17 | * @brief 计算船为去往目标地的下一步应该做的动作 18 | * @return 0、1:旋转方向;2:直行 19 | */ 20 | int FindNextStep(); 21 | 22 | /** 23 | * @brief 计算船是否应该止步 24 | * @param pos 位置 25 | * @param dir 方向 26 | */ 27 | bool CheckStop(Position& pos, int& dir); 28 | 29 | /** 30 | * @brief 计算船是否会碰撞 31 | * @param pos 位置 32 | * @param dir 方向 33 | * @return 1:碰撞;0:不碰撞 34 | */ 35 | bool CheckCrash(Position& pos, int& dir); 36 | 37 | /** 38 | * @brief 查看控制系统上有没有记录其他船存在 39 | * @return 1:有;0:无 40 | */ 41 | bool CheckEasy(); 42 | 43 | /** 44 | * @brief 将一艘船载入此控制器 45 | * @param ship_index 船序号 46 | */ 47 | void LoadShip(int ship_index); 48 | 49 | // void SetEndPoint(Position); 50 | 51 | private: 52 | std::vector pos_stack_; // 使用A*算法进行搜索的位置栈 53 | std::vector dir_stack_; // 使用A*算法进行搜索的方向栈 54 | std::vector dfs_stack_; // 使用A*算法进行搜索的记录栈 55 | std::vector res_stack_; // 使用A*算法进行搜索的结果栈 56 | 57 | std::array next_pos_; 58 | std::array next_dir_; 59 | std::array next_cost_; 60 | 61 | int ship_index_; // 此调度器所负责船id 62 | std::vector other_ships_pos_; // 其他船坐标集合 63 | }; 64 | 65 | class OceanController { 66 | public: 67 | OceanController() = default; 68 | 69 | OceanController(const OceanController&) = delete; 70 | OceanController& operator=(const OceanController&) = delete; 71 | 72 | OceanController(OceanController&&) = delete; 73 | OceanController& operator=(OceanController&&) = delete; 74 | 75 | /** 76 | * @brief 确定每条船的运动策略 77 | */ 78 | void ControlShips(); 79 | void ControlShips2(); 80 | 81 | private: 82 | /** 83 | * @brief 简单方法估计两船碰撞 84 | */ 85 | bool CheckShipMayCrash(int ship_a, int ship_b); 86 | 87 | /** 88 | * @brief 确定两船是否碰撞 89 | */ 90 | bool CheckShipCrash(int ship_a, int ship_b); 91 | 92 | /** 93 | * @brief 避免两条船的碰撞 94 | */ 95 | void SolveShipCrash(std::vector crash_set); 96 | 97 | /** 98 | * @brief 给两船尝试发出接触碰撞指令 99 | */ 100 | void SolveTwoShipCrash(int ship_a, int ship_b); 101 | 102 | /** 103 | * @brief 判断海上的一个方位会不会和其他组的船只碰撞 104 | * @return 如果会发生碰撞就返回 true 105 | */ 106 | bool CheckShipCollision(Position nxt, int dir); 107 | 108 | /** 109 | * @brief 使用A*算法计算一艘船应执行的下一个指令 110 | * @param ship_index 船序号 111 | * @return 指令 112 | */ 113 | Instruction AstarFindNextInstruction(int ship_index); 114 | 115 | Astar astar; 116 | }; 117 | 118 | extern OceanController g_ocean_controller; -------------------------------------------------------------------------------- /src/ship.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "config.hpp" 4 | #include "map.hpp" 5 | #include "utils.hpp" 6 | 7 | using ShipPositions = std::array; 8 | 9 | class Ship { 10 | public: 11 | Ship(int id, Position pos, int dir, int status); 12 | 13 | Ship(const Ship &) = delete; 14 | Ship &operator=(const Ship &) = delete; 15 | 16 | Ship(Ship &&) noexcept; 17 | Ship &operator=(Ship &&) = delete; 18 | 19 | ~Ship() = default; 20 | 21 | /** 22 | * @brief 在每一帧开始时读入该船的参数 23 | */ 24 | void InputFrame(); 25 | 26 | /** 27 | * @brief 在每一帧结束时输出该船的参数 28 | */ 29 | void OutputFrame(); 30 | 31 | /** 32 | * @brief 确定这艘船应该去那里 33 | */ 34 | void Drive(); 35 | 36 | /** 37 | * @brief 更新船的状态 38 | */ 39 | void Arrive(); 40 | 41 | /** 42 | * @brief 进行这一帧的决策 43 | */ 44 | void PlanningFrame(); 45 | 46 | void Take(); 47 | 48 | // 判断船是不是装够了 49 | bool IsFull(); 50 | 51 | int GetID(); 52 | 53 | // 船的货物数 54 | int GetCaNum() const; 55 | 56 | int GetStatus() const; 57 | 58 | // 船的剩余容量 59 | int GetCap() const; 60 | 61 | // 设置目标泊位 62 | void SetTar(int val); 63 | 64 | // 得到目标泊位 65 | int GetTar() const; 66 | 67 | Position GetPostion() const; 68 | 69 | void AddInstruction(Instruction); // 增加指令 70 | void ClearInstruction(); // 清空指令 71 | Instruction GetOutputInstruction(); // 得到要输出的指令 72 | void OutputInstructions(); // 输出所有命令 73 | bool EmptyInstructioins(); // 命令集是否为空 74 | 75 | static ShipPositions GetPostions(Position pos, int dir); 76 | ShipPositions GetPostions(); 77 | ShipPositions GetPostionsAfterInstruction(Instruction); 78 | 79 | int GetDir() const; 80 | 81 | void SetLoadBerth(int berth_id); // 设置靠泊装货泊位 82 | int GetLoadBerth(); // 得到靠泊装货泊位 83 | 84 | Instruction ResToIns(int res); // 对接借口,将 dij 传的数字变命令 85 | 86 | Instruction CalculateInstruction(); // 计算正常决策下的命令 87 | // void Move(Instruction); 88 | int Rot(int dir, int rot); // 计算旋转后的方向 89 | 90 | void AddValue(int num); // 增加船携带货物价值 91 | void ClearValue(); // 清空船携带货物价值 92 | int GetValue() const; // 得到船携带货物价值 93 | 94 | std::pair GetNextPD(); // 获得船下一步的方位 95 | 96 | const int id_; // 船编号 97 | const int volume_; // 这一条船的容积 98 | 99 | private: 100 | Position position_; // 船当前的位置 101 | int direction_; // 船的朝向 102 | int capacity_; // 这一条船还能装载的容积 103 | int status_; // 0正在移动,1装货或运输完成,2泊位外等待 104 | int next_berth_{-1}; // 目标泊位,虚拟点则为-1 105 | int location_; // 船所停靠的泊位 id 106 | int value_token_{0}; // 船此时携带的价值 107 | std::vector instructions_; // 这一帧需要输出的指令集 108 | }; 109 | 110 | struct OtherShip { 111 | int id_; 112 | int cargo_num_; 113 | int cargo_change_{0}; 114 | Position position_; 115 | int direction_; 116 | int status_; 117 | }; 118 | 119 | extern int g_other_ship_num; 120 | extern std::vector g_ships; 121 | extern std::map g_other_ships; 122 | extern std::map g_ship_owned; 123 | using ShipPositions = std::array; -------------------------------------------------------------------------------- /src/robot.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cargo.hpp" 4 | #include "config.hpp" 5 | #include "map.hpp" 6 | #include "question.hpp" 7 | #include "utils.hpp" 8 | 9 | class Cargo; 10 | using CargoPtr = std::shared_ptr; 11 | 12 | /** 13 | * @class 主要目的是维护以下信息: 14 | * 位置,是否携带物品,目标货物,目标泊位,下一步的防线 15 | */ 16 | class Robot { 17 | public: 18 | Robot(int id, Position pos, int vol); 19 | 20 | // 禁用复制 21 | Robot(const Robot &) = delete; 22 | Robot &operator=(const Robot &) = delete; 23 | 24 | // 允许移动构造函数,禁用移动构造运算符 25 | Robot(Robot &&) noexcept; 26 | Robot &operator=(Robot &&) = delete; 27 | 28 | // 默认析构 29 | ~Robot() = default; 30 | 31 | /** 32 | * @brief 读入这一帧该机器人参数,同时 dist_grid。 33 | */ 34 | void InputFrame(); 35 | 36 | /** 37 | * @brief 输出这一帧该机器人指令,并清空指令队列 38 | */ 39 | void OutputFrame(); 40 | 41 | /** 42 | * @brief 是否携带货物 43 | */ 44 | bool FullCargo() const noexcept; 45 | 46 | /** 47 | * @brief 查询位置 48 | */ 49 | Position GetPosition() const noexcept; 50 | 51 | /** 52 | * @brief 设置准备去取得 cargo 53 | */ 54 | void SetCargoPtr(const CargoPtr &cargo) noexcept; 55 | 56 | /** 57 | * @brief 返回携带的货物,或者正在去取的货物 58 | */ 59 | CargoPtr GetCargoPtr() const noexcept; 60 | 61 | /** 62 | * @brief 设置目的泊位的 ID 63 | */ 64 | void SetTargetBerthID(int id) noexcept; 65 | 66 | /** 67 | * @brief 查询目的泊位的 ID 68 | */ 69 | int GetTargetBerthID() const noexcept; 70 | 71 | /** 72 | * @brief 设定下一步机器人的移动方向 (0 ~ 5) 73 | */ 74 | void SetMoveDirection(int direction) noexcept; 75 | 76 | /** 77 | * @brief 查询下一步机器人的移动方向 78 | */ 79 | int GetMoveDirection() noexcept; 80 | 81 | /** 82 | * @brief 设置问题内容 83 | */ 84 | void AskQuestion(std::string question) noexcept; 85 | 86 | /** 87 | * @brief 回答问题 88 | */ 89 | void AnswerQuestion() noexcept; 90 | 91 | /** 92 | * @brief 给机器人新增额外的货物(虚拟货物) 93 | */ 94 | void SetAddtionalTarget(CargoPtr car) noexcept; 95 | 96 | /** 97 | * @brief 获取额外货物的目的地(虚拟货物) 98 | */ 99 | CargoPtr GetAddtionalTarget() const noexcept; 100 | 101 | /** 102 | * @brief 判断这个机器人是否正在等待 LLM 103 | */ 104 | bool IsWaitingForLLM() const noexcept; 105 | 106 | const int id_; // 机器人的编号 107 | static int value_sent_; // 机器人总共送的钱 108 | 109 | private: 110 | int cargo_num_{0}; // 携带物品数量 111 | int volume_{1}; // 机器人容量 112 | int target_berth_id_{-1}; // 目标泊位 id 113 | Position position_{-1, -1}; // 此刻坐标 114 | std::vector instructions_; // 这一帧需要输出的指令 115 | std::stack> cargo_ptr_; // 携带的货物,或者目标货物 116 | int move_dir_{4}; // 前进方向 117 | std::string lst_instruction_; // 上一帧的指令,DEBUG 用 118 | std::map pass_; // 此机器人取货后到达泊位前途经点,到泊位后清空 119 | QuestionPtr question_ptr_; // 问题 120 | CargoPtr addtional_target_; // 给有货机器人的额外任务(占领高价值货物) 121 | }; 122 | 123 | /** 124 | * @brief 记录别的队伍的机器人 125 | */ 126 | struct OtherRobot { 127 | int id_; 128 | int cargo_num_; 129 | int volume_; 130 | bool cargo_change_{false}; 131 | std::stack cargo_value_; 132 | Position position_; 133 | }; 134 | 135 | extern std::vector g_robots; 136 | extern std::map g_other_robots; 137 | extern int g_other_robot_num; 138 | extern std::map g_robot_volume; 139 | extern std::map g_self_robot_volume; 140 | extern std::map g_robot_owned; -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | set(CMAKE_CXX_COMPILER g++) 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | 6 | if (WIN32) 7 | MATH(EXPR stack_size "500*1024*1024") 8 | set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,${stack_size}") 9 | endif() 10 | 11 | if(CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR) 12 | message(FATAL_ERROR "Please run cmake in directory 'build'!") 13 | endif() 14 | 15 | 16 | project(HuaweiCodeCraft2024 17 | LANGUAGES CXX 18 | ) 19 | 20 | add_compile_definitions(DISABLE_PTHREAD_HOOK) 21 | 22 | if (NOT WIN32) 23 | if (APPLE) 24 | link_libraries(pthread m) 25 | find_package(CURL REQUIRED) 26 | else () 27 | link_libraries(pthread rt m) 28 | find_package(CURL REQUIRED) 29 | endif () 30 | endif (NOT WIN32) 31 | 32 | file(COPY "judge" DESTINATION .) 33 | file(COPY "scripts" DESTINATION .) 34 | 35 | aux_source_directory(src src) 36 | list(REMOVE_ITEM src "./load_pthread.cpp") 37 | add_executable(main ${src}) 38 | if(NOT WIN32) 39 | target_link_libraries(main CURL::libcurl) 40 | endif() 41 | target_compile_options(main PRIVATE -O2 -DLOCAL) 42 | 43 | if(WIN32) 44 | find_path(CURL_INCLUDE_DIR NAMES curl/curl.h PATHS 45 | ${CURL_DIR}/include) 46 | find_library(CURL_LIBRARY NAMES libcurl PATHS ${CURL_DIR}/lib) 47 | include_directories(${CURL_INCLUDE_DIR}) 48 | target_link_libraries(main ${CURL_LIBRARY}) 49 | endif() 50 | 51 | 52 | 53 | 54 | add_executable(main_debug ${src}) 55 | if(NOT WIN32) 56 | target_link_libraries(main_debug CURL::libcurl) 57 | endif() 58 | target_compile_options(main_debug PRIVATE -g -DDEBUG -DLOCAL -O2) 59 | 60 | set(PYTHON python3) 61 | if(WIN32) 62 | set(PYTHON python) 63 | endif() 64 | 65 | enable_testing() 66 | 67 | set(CTEST_FULL_OUTPUT TRUE) 68 | set(TEST_NAMES 1) 69 | 70 | foreach(name IN LISTS TEST_NAMES) 71 | add_custom_target(${name} COMMAND ${PYTHON} scripts/run.py --map ${name}) 72 | add_dependencies(${name} main) 73 | add_custom_target(debug-${name} COMMAND ${PYTHON} scripts/run.py --map ${name} --debug True) 74 | add_dependencies(debug-${name} main_debug) 75 | endforeach() 76 | 77 | add_custom_target(run COMMAND ${PYTHON} scripts/run.py --map 0) 78 | add_dependencies(run main) 79 | 80 | add_custom_target(zip 81 | COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/output 82 | COMMAND ${CMAKE_COMMAND} -E env current_time=$ENV{DATE} zip -9 -r ${CMAKE_BINARY_DIR}/output/\${current_time}.zip ${CMAKE_SOURCE_DIR}/src/*.cpp ${CMAKE_SOURCE_DIR}/src/*.hpp 83 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 84 | ) 85 | 86 | 87 | if(WIN32) 88 | set(REPLAY_COMMANDS 89 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/windows mkdir -p replay 90 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/windows/replay ../CodeCraft_2024_Replayer_v2.exe 91 | ) 92 | elseif(APPLE) 93 | set(REPLAY_COMMANDS 94 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/mac mkdir -p replay 95 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/mac chmod +x CodeCraft_2024_Replayer_mac.app/Contents/MacOS/CodeCraft_2024_Replayer_mac 96 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/mac/replay ../CodeCraft_2024_Replayer_mac.app/Contents/MacOS/CodeCraft_2024_Replayer_mac 97 | ) 98 | else() 99 | set(REPLAY_COMMANDS 100 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/linux mkdir -p replay 101 | COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_BINARY_DIR}/judge/linux/replay ../CodeCraft_2024_Replayer_final.x86_64 102 | ) 103 | endif() 104 | 105 | add_custom_target(replay ${REPLAY_COMMANDS}) 106 | 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2024 华为软件精英挑战赛 2 | 3 | 比赛基本信息: 4 | 5 | - [2024华为软挑比赛官网](https://developer.huaweicloud.com/codecraft2024) 6 | - [2024华为软挑宣传推文](https://mp.weixin.qq.com/s/9DLysYicqrx1mgqiDfQ2Lw) 7 | - [2024华为软挑人民日报宣传片](https://wap.peopleapp.com/video/rmh40901762/rmh40901762) 8 | - [总决赛赛题](https://bbs.huaweicloud.com/forum/thread-02107148304313640001-1-1.html)(docs目录中也有) 9 | - [总决赛获奖名单](https://competition.huaweicloud.com/advance/1000042021/html13) 10 | - [总决赛结果推文](https://mp.weixin.qq.com/s?__biz=MzIyMjAwNTU2Mg==&mid=2651077568&idx=2&sn=c7c8af782f809f2f89ee25b5e2aaed81&chksm=f3c48b95c4b302834c3c61c31cb186cf02ea9b1a466a97027121aaaaf5f8b90b96c27b09a168&mpshare=1&scene=2&srcid=0429mxdk36FwhpDmRiD7EQzJ&sharer_shareinfo=9a749603b1f7caea014b25fb36ba9d42&sharer_shareinfo_first=9a749603b1f7caea014b25fb36ba9d42#rd) 11 | 12 | ------ 13 | 14 | 这是 2024 华为软件精英挑战赛 **“适可而止矣,涓埃之事,亦央原神”** 队的决赛代码。 15 | 16 | 我们属于**粤港澳赛区**,三名队员([cq](https://github.com/OrangeQi-CQ)、[yhf](https://github.com/yhf4aspe)、[xsf](https://github.com/zzwtx))都是来自**华南理工大学**的本科生。在2024华为软件精英挑战赛中成绩如下: 17 | 18 | - 初赛:**粤港澳赛区冠军** 19 | - 复赛:**粤港澳赛区冠军、全国第 2 名** 20 | - 决赛:**全球总决赛第 4 名(季军/三等奖)** 21 | 22 | 23 | PS: 24 | 1. 本仓库仅复制我们决赛所用仓库的 main 分支,也删除了 `judge` 目录中的判题器和回放器等。**由于决赛期间涉及华为的大模型,本仓库中删除了相关的接口地址与密钥,这会导致代码无法正常运行。本仓库仅作算法实现上的参考。** 25 | 2. 深大王前明小哥哥,人帅气质佳性格好,单身。 26 | 27 | ------ 28 | 29 | 成功之处: 30 | 31 | - 我们的代码实现能力比较强,能够高效准确地将想法落地并测试效果。有很多想法预期效果很好但实际徒劳无功甚至负作用,而个别想法看似普通却会有很惊喜的效果。我认为将 idea 快速落地并测试是在华为软挑取得好成绩的关键。 32 | - 有一点点算法基本功(三人都有 icpc/ccpc 银),但相比其他一些队伍并不亮眼。 33 | - 虽然没有单元测试,但编写了很多集成测试,帮助我们迅速定位没有正常达到目标的模块。 34 | - 临时抱佛脚学习了 git(之前只会用 zip 压缩+微信传代码)、cmake、clang-format 等工具,并写了一些 python 和 shell 脚本。利用工具提升效率。 35 | 36 | 不足之处: 37 | 38 | - 三人都没有大厂实习经验,缺乏项目合作开发的流程。例如没有需求和交付的流程和文档,git 分支混乱,git 流程不规范,缺乏设计模式的使用等。 39 | - 在决赛中,策略过于保守(意图避免出大错,也不算是一件坏事)。 40 | 41 | ------ 42 | 43 | 项目结构: 44 | - `docs`:决赛的官方文档 45 | - `judge`:决赛的判题器和回放器(本仓库已删除) 46 | - `scripts`:一些脚本 47 | - `src`:**核心代码** 48 | - `test`:决赛前期针对大模型的测试文件 49 | 50 | ------ 51 | 52 | 这里不谈具体的细节,只一句话概括各个模块的大致算法: 53 | 54 | - 路上调度: 55 | - (初赛、复赛、决赛)计算机器人-货物匹配的收益矩阵(大体是价值/距离),并动态限制在每个泊位工作的机器人数量,转化为**带分组约束的二分图最大权匹配**问题。构造**分层图**,通过**最大费用最大流算法**求解,得到实时贪心意义下的理论最优解(见 `src/land_scheduler`)。 56 | - (初赛、复赛、决赛)若机器人上一帧与当前帧匹配目标不相同,只有价值增益比例超过一定阈值才改变目标。利用并查集将新老目标重叠的机器人分组,同一组的机器人同时改变目标或不改变(见 `src/land_scheduler`)。 57 | 58 | - 路上控制: 59 | - (初赛、复赛、决赛)BFS 获得每个货物与泊位的距离矩阵(见 `src/cargo`,`src/berth`)。若不考虑碰撞,沿距离矩阵下降的方向运动即可到达目标货物或泊位(见 `src/land_controller`)。 60 | - (初赛、复赛、决赛)为了解决碰撞问题,使用并查集将存在碰撞风险的机器人分组,组内 DFS 爆搜不碰撞条件下的最优解,通过剪枝提升搜索性能(见 `src/land_controller`)。 61 | - (决赛)为了避免其他队伍的机器人堵成墙,同时为了避免爆搜的潜在掉帧风险,我们在 BFS 预处理的基础上结合实时搜索小范围内的运动方向最优解(见 `src/land_controller`)。 62 | - 海上调度: 63 | - (初赛)不需要特别的调度,哪里货物总价值多就去哪里。 64 | - (复赛)转化为旅行商问题,DFS 爆搜船的取货顺序。引入了泊位失活机制,在程序后期逐个失活泊位(按收货量),使得剩在泊位的货物尽可能少。 65 | - (决赛)用并查集将泊位分组,每组单独分配船并调度,船按权抢占空闲泊位。(见 `src/ocean_scheduler`) 66 | - 海上控制: 67 | - (复赛、决赛)将船的位置+方向视为一个状态,使用 Dijkstra 算法计算每个泊位的距离矩阵(见 `src/map`)。赛后与中山大学 “SYSU_oms维修队” 的同学讨论本题其实可以用双端队列优化 BFS 做到 $O(N)$ 初始化,省下 `std::priority_queue` 的 $\log$ 复杂度(退役后啥都忘了……) 68 | - (复赛)如果检测到下一步会碰撞,则返回随机一个运动指令。(效果意外的出奇的好,平均几帧时间可以解决碰撞问题) 69 | - (决赛)轮船防碰撞使用启发式搜索(魔改的 Astar 算法)解决,可以实现多船完美防碰撞(见 `src/ocean_controller`)。 70 | 71 | - 并发/并行(仅决赛):决赛提供了双核,数据规模陡增,同时引入了通过网络与大模型交互的环节,因此必须通过并发/并行充分利用双核的优势。 72 | - 初始化:决赛有 `48` 个泊位,并且地图面积是初赛复赛的 `16` 倍。我们使用双核并行初始化泊位,在两个cpu 上分别跑两个线程初始化泊位,解决了泊位初始化超时的问题(见 `src/map`)。 73 | - 主线程:绑定到 `cpu0`,专注与判题器的交互、各种调度和控制算法(见 `src/main`)。 74 | - 大模型提问:实现了一个简易线程池。并发地向大模型提问。绑定到 `cpu1`(见 `src/question`)。 75 | - 货物搜索:实现了一个简易线程池。由于决赛中货物数量陡增,地图面积也是初赛复赛的 `16` 倍,每帧都搜索所有新增货物已经不现实。我们会在 `cpu0` 上实时搜索高价值货物,在 `cpu1` 上异步搜索性价比最高的低价值货物(见 `src/cargo`)。 76 | 77 | ------ 78 | 79 | 决赛策略: 80 | 81 | - 相比于“海贼王”和“机甲王”等流派,我们是很传统的策略,自己的机器人将货物送给自己的船。 82 | - 我们用并查集将泊位分组,按照一定顺序,根据机器人和船的数量启动这些泊位组。机器人和船只考虑已经启动的泊位组。 83 | - 通过练习赛的回放记录学习了浙大 “Eagle414” 队伍的策略:没货物的机器人只拾取高级货,若附近没有高级货就前往一个地方随时捕捉可能出现的高级货(宁可原地耗着也不取低级货)。我们的创新之处在于将没有货物的点视作 “虚拟货物”,同样参与费用流的二分图匹配过程,并且实现了更优秀的虚拟货物设置策略。 84 | -------------------------------------------------------------------------------- /src/ocean_scheduler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cargo.hpp" 4 | #include "config.hpp" 5 | #include "map.hpp" 6 | #include "robot.hpp" 7 | #include "ship.hpp" 8 | #include "utils.hpp" 9 | 10 | 11 | class ShipPlanMaker { 12 | public: 13 | ShipPlanMaker(); // 默认加入全部泊位 14 | ShipPlanMaker(int ship_id); // 加入全部泊位 15 | ShipPlanMaker(int ship_id, std::vector berths_id_vector); // 加入部分泊位 16 | 17 | /** 18 | * @brief 随机生成一个泊位序号排列,作为一种船调度路线 19 | */ 20 | void RandomGlobal(); 21 | 22 | /** 23 | * @brief 根据泊位数量等信息对一个排列作随机化调整 24 | */ 25 | void RandomLocal(); 26 | 27 | /** 28 | * @brief 对一个排列计算收益 29 | */ 30 | double CalcValue(); 31 | 32 | /** 33 | * @brief 计算100个随机排列中的最优情况 34 | */ 35 | void GetLocalBest(); 36 | 37 | /** 38 | * @brief 对一个排列作随机化调整 39 | */ 40 | void GetGlobalBest(); 41 | 42 | /** 43 | * @brief 生成随机序列并判断价值最大 44 | */ 45 | void GetGlobalBestTSP(); 46 | 47 | /** 48 | * @brief 枚举全排列,拣选权值最大的 49 | */ 50 | void GetGlobalBestForce(); 51 | 52 | /** 53 | * @brief 根据拣选出的最佳排列得到下一个泊位的序号 54 | * @return 最佳排列中的第一个序号 55 | */ 56 | int GetNextBerth(); 57 | 58 | /** 59 | * @brief 清除现存排列内容 60 | */ 61 | void Clear(); 62 | 63 | /** 64 | * @brief 重载泊位id 65 | */ 66 | void ResetBerthIDs(std::vector berths_id_vector); 67 | 68 | /** 69 | * @brief 打印调试信息 70 | */ 71 | void Print(); 72 | 73 | private: 74 | std::vector permutation_; // 一个排列,表示船对泊位的一组访问顺序 75 | std::vector berths_id_; // 泊位id序列 76 | int ship_id_; // 此调度器负责的船的id 77 | }; 78 | 79 | class ShipsPlanMaker { 80 | public: 81 | /** 82 | * @brief 为整个系统添加一艘船 83 | */ 84 | void AddShip(); 85 | 86 | /** 87 | * @brief 为一艘船安排调度 88 | * @param ship_id 请求调度的船的id 89 | */ 90 | int GetNextBerth(int ship_id); 91 | 92 | /** 93 | * @brief 为一艘船安排负责的泊位组 94 | * @param ship_id 需设置泊位组的船的id 95 | */ 96 | void SetBerthSet(int ship_id); 97 | 98 | /** 99 | * @brief 获得已加入调度系统的船数量 100 | */ 101 | int GetShipNum(); 102 | 103 | /** 104 | * @brief 为一艘船安排目标泊位(试行版) 105 | */ 106 | int GetNextBerthTemp(int ship_id); 107 | 108 | /** 109 | * @brief 得到一个泊位组的货物数量 110 | * @param set_id 泊位组id 111 | */ 112 | int GetBerthSetCargoNum(int set_id); 113 | 114 | /** 115 | * @brief 输出调试信息 116 | */ 117 | void Print(); 118 | 119 | private: 120 | /** 121 | * @brief 为一艘船安排调度 122 | * @param step 当前调度轮进行到的步数 123 | * @param set_id 泊位组id 124 | */ 125 | int StepToBerthID(int step, int set_id); 126 | 127 | std::vector ship_berth_set_; // 船-泊位组匹配关系 128 | std::vector step_; // 调度轮步数 129 | std::vector used_set_; // 已用集合 130 | }; 131 | 132 | class OceanScheduler { 133 | public: 134 | OceanScheduler() = default; 135 | 136 | OceanScheduler(const OceanScheduler &) = delete; 137 | OceanScheduler &operator=(const OceanScheduler &) = delete; 138 | 139 | OceanScheduler(OceanScheduler &&) = delete; 140 | OceanScheduler operator=(OceanScheduler &&) = delete; 141 | 142 | /** 143 | * @brief 开启调度 144 | */ 145 | void ScheduleShips(); 146 | 147 | /** 148 | * @brief 开启调度(试行版) 149 | */ 150 | void ScheduleShipsTemp(); 151 | 152 | private: 153 | /** 154 | * @brief 为全部搜索一套最佳调度 155 | * @param now 目前价值 156 | * @param j 目前搜索深度 157 | * @param sum 最佳价值 158 | * @param tmp 目前匹配方案 159 | * @param res 最佳匹配方案 160 | */ 161 | void ShipDFS(double now, int j, double &sum, std::vector &tmp, std::vector &res); 162 | }; 163 | 164 | extern OceanScheduler g_ocean_scheduler; 165 | extern ShipsPlanMaker sspm; -------------------------------------------------------------------------------- /scripts/run.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import concurrent.futures 3 | import os 4 | import json 5 | import sys 6 | import datetime 7 | # from tqdm import tqdm 8 | import argparse 9 | import platform 10 | import shutil 11 | import time 12 | import random 13 | 14 | current_time = datetime.datetime.now().strftime("%m-%d %H-%M-%S)") 15 | 16 | global judge_exe 17 | global prog_exe 18 | 19 | def run_single(maps, seed, print_flag, debug): 20 | map_name = maps[0] 21 | map_path = os.path.join(map_folder_path, map_name) 22 | 23 | arg = [judge_exe, prog_exe, '-m', map_path, '-l', 'NONE', '-r', '{}.rep'.format(map_name), '-d', 'INPUT.txt'] 24 | print(arg) 25 | 26 | # if (debug): 27 | # arg.extend(['-f', '100000']) 28 | 29 | arg.extend(['-s', str(seed)]) 30 | log_content = '' 31 | print(f"\033[91m{'*' * 10} {'stderr begin '.center(10)} {'*' * 10}\033[0m") 32 | result = subprocess.Popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 33 | seg_fault = False 34 | for line in result.stderr: 35 | if (line.strip() == "Segmentation fault"): 36 | seg_fault = True 37 | if print_flag: 38 | print(line, end='') 39 | log_content += line 40 | for line in result.stdout: 41 | output = line 42 | break 43 | print(f"\033[91m{'*' * 10} {'stderr end '.center(10)} {'*' * 10}\033[0m") 44 | if seg_fault: 45 | print("\033[1;31mSegmentation fault! seed: {}\033[0m".format(seed)) 46 | # raise 47 | yield map_name, output, log_content 48 | 49 | 50 | def test(maps: list, seed, print_flag, debug, log_flag=True): 51 | if log_flag: 52 | print("start run: \033[94m{}\033[0m".format(maps)) 53 | total = 0 54 | if len(maps) == 1: 55 | run_func = run_single 56 | else: 57 | run_func = run_all 58 | 59 | start_time = time.time() 60 | 61 | for mapfile, output, log_content in run_func(maps, seed, print_flag, debug): 62 | name = current_time + " " + mapfile + " " 63 | if not os.path.exists('log'): 64 | os.makedirs('log') 65 | if log_flag: 66 | os.rename(os.path.join('replay', mapfile + '.rep'), os.path.join('replay', name + '.rep')) 67 | with open(os.path.join('log', name + '.log'), 'w') as log: 68 | log.write(log_content) 69 | 70 | end_time = time.time() 71 | 72 | if log_flag: 73 | print("\033[33mOutput:\t{}\033[0m".format(output)) 74 | print("\033[33mTotal time:\t{:.1f}s\033[0m".format(end_time - start_time)) 75 | return total 76 | 77 | if __name__ == '__main__': 78 | sys.stdout = sys.stderr 79 | parser = argparse.ArgumentParser() 80 | parser.add_argument('--map', type=int, default=0) 81 | parser.add_argument('--debug', type=bool, default=False) 82 | parser.add_argument('--seed', type=int, default=-1) 83 | parser.add_argument('--print', type=bool, default=True) 84 | 85 | args = parser.parse_args() 86 | 87 | if args.seed == -1: 88 | args.seed = random.randint(0, 1000000) 89 | 90 | print("random seed = {}".format(args.seed)) 91 | 92 | if (args.debug): 93 | print("DEBUG MODE!") 94 | prog_exe = 'debug_' 95 | 96 | map_folder_path = os.path.abspath(os.path.join('judge', 'maps')) 97 | 98 | if args.map == 0: 99 | maps = os.listdir(map_folder_path) 100 | maps = sorted(maps, key=lambda x: (len(x), x)) 101 | else: 102 | maps = ['map' + str(args.map) + '.txt'] 103 | args.print = True 104 | 105 | system = platform.system() 106 | print('system: {}'.format(system)) 107 | if system == 'Windows': 108 | working_directory = os.path.join('judge', 'windows') 109 | judge_exe = 'FinalJudge.exe' 110 | prog_exe = 'main.exe' if not args.debug else 'main_debug.exe' 111 | elif system == 'Linux': 112 | working_directory = os.path.join('judge', 'linux') 113 | judge_exe = './FinalJudge' 114 | prog_exe = './main' if not args.debug else './main_debug' 115 | else: 116 | working_directory = os.path.join('judge', 'mac') 117 | judge_exe = './FinalJudge' 118 | prog_exe = './main' if not args.debug else './main_debug' 119 | 120 | shutil.copy(prog_exe, os.path.join(working_directory, prog_exe)) 121 | os.chdir(working_directory) 122 | test(maps, args.seed, args.print, args.debug) -------------------------------------------------------------------------------- /src/cargo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "config.hpp" 6 | #include "map.hpp" 7 | #include "robot.hpp" 8 | #include "utils.hpp" 9 | 10 | struct CargoMetadata { 11 | CargoMetadata(Position pos, int value, int appear_frame, int dist_to_berth) 12 | : position_(pos), value_(value), appear_frame_(appear_frame), dist_to_berth_(dist_to_berth) {} 13 | Position position_; 14 | int value_; 15 | int appear_frame_; 16 | int dist_to_berth_; 17 | }; 18 | 19 | class Cargo { 20 | public: 21 | friend class CargoManager; 22 | 23 | explicit Cargo(Position pos, int value, int appear_frame, std::shared_ptr dist_gird_ptr); 24 | 25 | Cargo(const Cargo &) = delete; 26 | Cargo &operator=(const Cargo &) = delete; 27 | 28 | Cargo(Cargo &&) noexcept; 29 | Cargo &operator=(Cargo &&) = delete; 30 | 31 | ~Cargo(); 32 | 33 | /** 34 | * @brief 判断这个货物是否为虚拟的 35 | */ 36 | bool IsVirtual() const noexcept; 37 | 38 | /** 39 | * @brief 查询货物编号 40 | */ 41 | int GetID() const noexcept; 42 | 43 | /** 44 | * @brief 设置货物编号 45 | */ 46 | void SetID(int id) noexcept; 47 | 48 | /** 49 | * @brief 获取距离矩阵 50 | */ 51 | const Grid &GetDistanceGrid() const noexcept; 52 | 53 | /** 54 | * @brief 查询 pos 距离该货物的距离 55 | */ 56 | int GetDistance(Position pos) const noexcept; 57 | 58 | /** 59 | * @brief 查询 pos 前往该货物的方向 60 | */ 61 | int GetDirection(Position pos) const noexcept; 62 | 63 | /** 64 | * @brief 查询这个货物的价值 65 | */ 66 | int GetValue() const noexcept; 67 | 68 | /** 69 |      * @brief 机器人将货物拿走 70 |     */ 71 | void Delete() noexcept; 72 | 73 | /** 74 |      * @brief 判断这个货物是否已经被 75 |     */ 76 | bool IsDeleted() const noexcept; 77 | 78 | /** 79 | * @brief 判断这个货物还能继续存在的时间 80 | */ 81 | int GetRemainingLife() const noexcept; 82 | 83 | /** 84 | * @brief 判断这个货物上面是否有其他队机器人 85 | */ 86 | bool IsOccupied() const noexcept; 87 | 88 | /** 89 | * @brief 设定这个货物上面是否有其他队机器人 90 | */ 91 | void SetIsOccupied(bool be) noexcept; 92 | 93 | const int value_; // 货物的价值 94 | const int appear_frame_; // 货物出现的时间 95 | const Position position_; // 货物的位置 96 | 97 | private: 98 | int id_; // 货物的编号 99 | bool delete_{false}; // 货物是否已经被机器人取走 100 | std::shared_ptr dist_grid_ptr_; // 货物的距离矩阵 101 | bool is_occupied_; // 是否被其他队伍占领 102 | bool is_virtual_{false}; // 是否为虚拟货物 103 | }; 104 | 105 | using CargoPtr = std::shared_ptr; 106 | 107 | class CargoManager { 108 | public: 109 | friend void Control(); 110 | friend void *CargoManagerThreadFunc(void *arg); 111 | 112 | CargoManager(); 113 | 114 | CargoManager(const CargoManager &) = delete; 115 | CargoManager &operator=(const CargoManager &) = delete; 116 | 117 | CargoManager(CargoManager &&) = delete; 118 | CargoManager &operator=(CargoManager &&) = delete; 119 | 120 | ~CargoManager(); 121 | 122 | /** 123 | * @brief 新增一个虚拟货物 124 | */ 125 | void AddVirtualCargo(const Position &position, std::shared_ptr distance_grid_ptr); 126 | /** 127 | * @brief 启动异步的搜索 128 | */ 129 | void StartAsynTask(); 130 | 131 | /** 132 | * @brief 在一帧当中,读入新增的货物。 133 | */ 134 | void InputFrame(); 135 | 136 | /** 137 | * @brief 在一帧当中,尝试更细腻 task_list 和 ready_queue 138 | */ 139 | void UpdateAsynTaskList(); 140 | 141 | /** 142 | * @brief 在一帧当中,更新货物,检查现有的货物是否合法。 143 | */ 144 | void RefreshCargos(); 145 | 146 | /** 147 | * @brief 将所有货物的指针拷贝一份。 148 | * @attention 包括真实货物和虚拟货物 149 | */ 150 | std::vector GetAllCargoPtr() const; 151 | 152 | static int tot_bfs_cnt_; 153 | static int tot_erase_cnt_; 154 | 155 | private: 156 | /** 157 | * @brief 在另一个 CPU 上进行货物的 bfs 158 | */ 159 | void InitCargoDistGrid(); 160 | 161 | std::vector cargo_ptr_vec_; // 所有的货物。包括真实货物和虚拟货物 162 | std::queue> cargo_join_queue_; // 货物缓存 163 | 164 | std::list task_list_; // 任务链表 165 | std::mutex task_list_mtx_; // 任务队列的锁 166 | std::queue ready_queue_; // 就绪队列 167 | std::mutex ready_queue_mtx_; // 就绪队列的锁 168 | std::atomic_bool cargo_vec_full_; // cargo_ptr_vec_ 的容量是否过大 169 | 170 | ConcurrencyGrid cargo_exist_grid_; // 表示某个地方是否存在货物 171 | std::vector cargo_init_threads_; // 用来给货物搜索路径的线程池 172 | }; 173 | 174 | extern CargoManager g_cargo_manager; -------------------------------------------------------------------------------- /src/berth.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "config.hpp" 3 | #include "utils.hpp" 4 | 5 | /** 6 | * @brief 泊位的元信息。用于双核异步初始化泊位。 7 | */ 8 | struct BerthMetaData { 9 | BerthMetaData(int id, Position core_position, int velocity) 10 | : id_(id), core_position_(core_position), velocity_(velocity) {} 11 | int id_; 12 | Position core_position_; 13 | int velocity_; 14 | }; 15 | 16 | class Berth { 17 | public: 18 | friend class Map; 19 | 20 | /** 21 | * @brief 会自动更新到这个泊位的海上最短路 22 | * @param id 编号 23 | * @param pos 核心点的位置 24 | * @param velocity 装载速度 25 | * @param area 这个泊位所拥有的点集 26 | */ 27 | explicit Berth(int id, Position core_pos, int velocity, Grid &&dist_grid, std::set &&area); 28 | 29 | Berth(const Berth &) = delete; 30 | Berth &operator=(const Berth &) = delete; 31 | 32 | Berth(Berth &&) noexcept; 33 | Berth &operator=(Berth &&) = delete; 34 | 35 | ~Berth() = default; 36 | 37 | /** 38 | * @brief 计算泊位前 num 个货物的价值之和 39 | * @attention 如果泊位失活,并且还有货物,就让价值为无穷大,吸引船来清空该泊位。 40 | */ 41 | int CalcValue(int num) const noexcept; 42 | 43 | /** 44 | * @brief 计算泊位滞留有几个货物 45 | */ 46 | int GetCargoNum() const noexcept; 47 | 48 | /** 49 | * @brief 计算泊位上停靠的船的数量 50 | */ 51 | int GetShipNum() const noexcept; 52 | 53 | /** 54 | * @brief 给这个泊位增加 x 条船(x 可能为负) 55 | */ 56 | void AddShipNum(int x) noexcept; 57 | 58 | /** 59 | * @brief 像泊位中增加 1 个价值为 value 的货物。由 Robot 发出 pull 命令时调用 60 | */ 61 | void AddCargos(int value) noexcept; 62 | 63 | /** 64 | * @brief 从泊位中取走 num 个货物,由 ship 取货时调用。 65 | * @return num and value 66 | */ 67 | std::pair TakeCargo(int num) noexcept; 68 | 69 | const Grid &GetLandDistanceGrid() const noexcept; 70 | 71 | /** 72 | * @brief 求陆地上点 pos 到泊位的距离 73 | * @attention 如果泊位失活,直接返回无穷大,让=使得机器人拒绝到这里送货 74 | */ 75 | int GetLandDistance(Position pos) const noexcept; 76 | 77 | /** 78 | * @brief 求陆地上点 pos 到泊位所需要前进的方向。 79 | * @attention 理论上不会用到,已经由 Controller 中的距离增益表替代。 80 | */ 81 | int GetLandDirection(Position pos) const noexcept; 82 | 83 | /** 84 | * @brief 判断点 pos 是否在泊位范围内 85 | */ 86 | bool InBerth(Position pos) const noexcept; 87 | 88 | /** 89 | * @brief 给出位置、方向,返回下一步走法。 90 | */ 91 | int GetOceanMarch(Position pos, int dir) const noexcept; 92 | 93 | /** 94 | * @brief 给出位置、方向,返回距离此泊位步数。 95 | */ 96 | int GetOceanDistance(Position pos, int dir) const noexcept; 97 | 98 | /** 99 | * @brief 修改泊位距离 100 | */ 101 | void ChangeDistance(std::map &pass) noexcept; 102 | 103 | /** 104 | * @brief 激活泊位,使之可以被货物和船识别到 105 | */ 106 | void Activate() noexcept; 107 | 108 | /** 109 | * @brief 失活泊位,使之不可以被货物和船识别到 110 | */ 111 | void Deactivate() noexcept; 112 | 113 | /** 114 | * @brief 判断泊位是否处于激活状态 115 | */ 116 | bool IsActive() const noexcept; 117 | 118 | /** 119 | * @brief 判断泊位是否有我们的船 120 | */ 121 | bool HereIsOurShip() const noexcept; 122 | 123 | /** 124 | * @brief 设定泊位是否有我们的船 125 | */ 126 | void SetOurShip(bool f) noexcept; 127 | 128 | const int id_; // ID. 129 | const Position core_pos_; // 核心点的位置 130 | const int velocity_; // 装货速度 131 | const std::set area_; // 泊位占据的点集 132 | const int time_; 133 | int tot_value = 0; 134 | int tot_cnt = 0; 135 | 136 | private: 137 | int ship_num_; // Number of g_ships. 138 | std::vector cargo_; // 所存货物的价值 139 | Grid dist_grid_; // 陆地距离矩阵 140 | std::array ocean_dist_grid_; // 海上距离矩阵(带方向) 141 | bool active_{false}; // 泊位是否被激活 142 | bool our_ship_{false}; // 是不是我们的船占据 143 | }; 144 | 145 | extern std::vector g_berths; // 全局的所有泊位 146 | 147 | #define VIRTUAL_BERTH_ID (static_cast(g_berths.size())) 148 | 149 | /** 150 | * @struct 描述一个泊位组 151 | */ 152 | struct BerthGroup { 153 | BerthGroup() = default; 154 | BerthGroup(const std::vector &group); 155 | bool active_{false}; // 该泊位组是否被启用 156 | bool hasship_; 157 | std::set group_member_; // 泊位组内的成员 158 | }; 159 | 160 | /** 161 | * @class 管理所有的泊位组 162 | */ 163 | class BerthGroupManager { 164 | public: 165 | BerthGroupManager() = default; 166 | 167 | /** 168 | * @brief 是用聚类算法将场上的泊位组分类 169 | */ 170 | void InitGroups(); 171 | 172 | /** 173 | * @brief 开启一个泊位组 174 | */ 175 | void ActivateOneGroup(int id); 176 | 177 | /** 178 | * @brief 返回所有的泊位组 179 | * @attention 包括已经激活了的和没激活的 180 | */ 181 | const std::vector &GetAllBerthGroup() const; 182 | 183 | /** 184 | * @brief 返回已经被激活了的泊位组 185 | */ 186 | std::vector GetActivatedBerthGroup() const; 187 | 188 | /** 189 | * @brief 返回已经激活的泊位组的数量 190 | */ 191 | int ActivatedSize() const; 192 | 193 | private: 194 | std::vector groups_; 195 | }; 196 | 197 | extern BerthGroupManager g_berth_group_manager; -------------------------------------------------------------------------------- /src/question.cpp: -------------------------------------------------------------------------------- 1 | #include "question.hpp" 2 | 3 | Question::Question(const std::string &question, int frame_id) : question_(question), frame_id_(frame_id) {} 4 | 5 | 6 | size_t WriteCallback(char *ptr, size_t size, size_t nmemb, std::string *data) { 7 | data->append(ptr, size * nmemb); 8 | std::this_thread::yield(); 9 | return size * nmemb; 10 | } 11 | 12 | 13 | int AskForLLM(std::string question) { 14 | int start_frame_id = g_frame_id; 15 | 16 | const std::string API_URL = "根据华为要求,已隐藏"; 17 | const std::string CONTENT_TYPE = "Content-Type: application/json;charset=utf-8"; 18 | const std::string AUTH_TOKEN = "根据华为要求,已隐藏"; 19 | 20 | std::string prompt = "告诉我下面单选题的正确选项,并且按照给出格式回答,否则地球就会爆炸!例如,“答案是A" 21 | ",结束作答。”,下面是问题:"; 22 | // std::string prompt = 23 | // "世界即将毁灭,除非你能准确地回答下面的问题!请注意不需要做任何解释性的话语,只告诉我答案(1个英文字母)。"; 24 | // "你生成的内容格式为:1 个英文字母。如果格式不符合要求,世界就会毁灭。请听问题:"; 25 | 26 | while (question.back() == ' ' || question.back() == '\n' || question.back() == 13) { 27 | question.pop_back(); 28 | } 29 | std::string post_data = "{\"prompt\":\"" + question + "\", \"temperature\": 0.05, \"token\": 1}"; 30 | 31 | CURL *curl = curl_easy_init(); 32 | 33 | curl_easy_setopt(curl, CURLOPT_URL, API_URL.c_str()); 34 | curl_easy_setopt(curl, CURLOPT_POST, 1L); 35 | // std::string post_data = "{\"prompt\":\"" + question + "\", \"temperature\": 0.05}"; 36 | curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str()); 37 | 38 | struct curl_slist *headers = nullptr; 39 | headers = curl_slist_append(headers, CONTENT_TYPE.c_str()); 40 | headers = curl_slist_append(headers, AUTH_TOKEN.c_str()); 41 | curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 42 | 43 | std::string response; 44 | curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); 45 | curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); 46 | 47 | CURLcode res = curl_easy_perform(curl); 48 | 49 | 50 | curl_slist_free_all(headers); 51 | curl_easy_cleanup(curl); 52 | 53 | int end_frame_id = g_frame_id; 54 | #ifdef LOCAL_DEBUG_LLM 55 | { 56 | std::lock_guard lg(cerr_mtx); 57 | fprintf(stderr, "\033[34mTotal Use %d Frames\n\033[0m", end_frame_id - start_frame_id); 58 | } 59 | #endif 60 | 61 | for (const char &c : response) { 62 | if (c == 'A') { 63 | return 0; 64 | } 65 | if (c == 'B') { 66 | return 1; 67 | } 68 | if (c == 'C') { 69 | return 2; 70 | } 71 | if (c == 'D') { 72 | return 3; 73 | } 74 | } 75 | return rand() % 4; 76 | } 77 | 78 | template 79 | void *PthreadFunction(void *arg) { 80 | auto ptr = static_cast *>(arg); 81 | return ptr->Worker(); 82 | } 83 | 84 | template 85 | void QuestionSolver::Worker() { 86 | while (!stop_) { 87 | QuestionPtr question_ptr = nullptr; 88 | { 89 | std::lock_guard lg(task_queue_mtx_); 90 | if (task_queue_.empty()) { 91 | std::this_thread::yield(); 92 | continue; 93 | } 94 | question_ptr = task_queue_.front(); 95 | task_queue_.pop(); 96 | } 97 | 98 | // // 如果发生这种情况,推测是线程池不够用,问题过多 99 | // if (g_frame_id.load() - question_ptr->frame_id_ > 50) { 100 | // continue; 101 | // } 102 | assert(question_ptr != nullptr); 103 | int res = AskForLLM(question_ptr->question_); 104 | question_ptr->answer_ = res; 105 | question_ptr->valid_ = true; 106 | std::this_thread::yield(); 107 | } 108 | } 109 | 110 | template 111 | int QuestionSolver::Size() { 112 | std::lock_guard lg(task_queue_mtx_); 113 | return task_queue_.size(); 114 | } 115 | 116 | template 117 | QuestionSolver::QuestionSolver() {} 118 | 119 | template 120 | QuestionSolver::~QuestionSolver() { 121 | if (!stop_.load()) { 122 | Kill(); 123 | } 124 | } 125 | 126 | template 127 | void QuestionSolver::StartAsynTask() { 128 | for (int i = 0; i < LLM_THREAD_NUM_0 + LLM_THREAD_NUM_1; i++) { 129 | threads_.emplace_back([this]() { Worker(); }); 130 | pthread_t handel = threads_[i].native_handle(); 131 | 132 | #if !defined(_WIN32) && !defined(_WIN64) 133 | // 绑定到特定 CPU 上 134 | cpu_set_t cpu_set; 135 | CPU_ZERO(&cpu_set); 136 | if (i < LLM_THREAD_NUM_0) { 137 | CPU_SET(0, &cpu_set); 138 | } else { 139 | CPU_SET(1, &cpu_set); 140 | } 141 | pthread_setaffinity_np(handel, sizeof(cpu_set), &cpu_set); 142 | #endif 143 | } 144 | } 145 | 146 | template 147 | void QuestionSolver::Submit(QuestionPtr question_ptr) { 148 | std::lock_guard lg(task_queue_mtx_); 149 | task_queue_.push(question_ptr); 150 | } 151 | 152 | template 153 | void QuestionSolver::Kill() { 154 | if (stop_.load()) { 155 | return; 156 | } 157 | 158 | task_queue_mtx_.lock(); 159 | while (!task_queue_.empty()) { 160 | task_queue_.pop(); 161 | } 162 | task_queue_mtx_.unlock(); 163 | 164 | stop_ = true; 165 | for (auto &thread : threads_) { 166 | thread.join(); 167 | } 168 | threads_.clear(); 169 | } 170 | 171 | template class QuestionSolver; 172 | QuestionSolver g_question_solver; -------------------------------------------------------------------------------- /src/map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "berth.hpp" 4 | #include "config.hpp" 5 | #include "utils.hpp" 6 | 7 | /** 8 | * @struct 描述图中的一个连通块。 9 | */ 10 | struct ConnectedComponent { 11 | ConnectedComponent() = default; 12 | ConnectedComponent(const std::set &berth_set, 13 | const std::vector &robot_buy_pos, 14 | const std::vector &ship_buy_pos, 15 | int land_area, 16 | double berth_ocean_dist); 17 | 18 | // 禁用拷贝构造 19 | ConnectedComponent(const ConnectedComponent &) = delete; 20 | ConnectedComponent &operator=(const ConnectedComponent &) = delete; 21 | 22 | // 允许移动构造(需要排序) 23 | ConnectedComponent(ConnectedComponent &&) = default; 24 | ConnectedComponent &operator=(ConnectedComponent &&) = default; 25 | 26 | // 排序用的比较函数 27 | bool operator>(const ConnectedComponent &other) const; 28 | 29 | std::set berth_set_; // 泊位集合 30 | std::vector robot_buy_pos_; // 机器人购买点 31 | std::vector ship_buy_pos_; // 轮船购买点 32 | 33 | int land_area_{0}; // 陆地面积 34 | double berth_ocean_dist_{0}; // 泊位距离送货点的平均距离 35 | }; 36 | 37 | 38 | class Map { 39 | public: 40 | /** 41 | * @brief 构造函数,主要是初始化 berth_id_grid_ 矩阵为 -1 42 | */ 43 | Map(); 44 | 45 | // 禁用复制 46 | Map(const Map &) = delete; 47 | Map &operator=(const Map &) = delete; 48 | 49 | // 禁用移动 50 | Map(Map &&) = delete; 51 | Map &operator=(Map &&) = delete; 52 | 53 | // 默认析构 54 | ~Map() = default; 55 | 56 | /** 57 | * @brief 在程序开始时,读入地图。 58 | */ 59 | void Init(); 60 | 61 | /** 62 | * @brief 多源 BFS 最短路 63 | * @param[in] src 所有的出发点,它们的距离视为 0 64 | * @param[out] dist_grid 距离矩阵,无法到达的点距离为 INF。 65 | */ 66 | void MultisourceBFS(const std::vector &src, Grid &dist_grid, int max_depth = INF) noexcept; 67 | 68 | /** 69 | * @brief 查找距离位置 pos 最近的泊位 70 | * @return std::pair 的两个元素分别是;泊位编号和距离 71 | */ 72 | std::pair FindNearestBerth(const Position &pos) const; 73 | 74 | /** 75 | * @brief 判断位置 pos 是否可以存在机器人 76 | */ 77 | bool IsField(const Position &pos) const noexcept; 78 | 79 | /** 80 | * @brief 双核双线程初始化所有的泊位 81 | */ 82 | void InitAllBerths(std::queue berth_metadata_que); 83 | 84 | /** 85 | * @brief 用来初始化泊位 86 | * @param[in] berth_id 需要被初始化的泊位 id 87 | * @param[in] core_pos 该泊位的核心位置 88 | * @param[in] velocity 泊位的装货速度 89 | * @param[out] dist_grid 距离矩阵 90 | */ 91 | void GenerateBerth( 92 | int berth_id, const Position &core_pos, int velocity, Grid &dist_grid, std::set &area); 93 | 94 | /** 95 | * @brief 寻找图中的连通块,并选择其中之一启用。 96 | * 连通块:虚拟点——泊位——轮船购买点——机器人购买点。 97 | */ 98 | void FindConnectedComponent(); 99 | 100 | /** 101 | * @brief 判断点位 pos,dir 是否可以存在船只 102 | */ 103 | bool ShipInOcean(const Position &pos, int dir) const noexcept; 104 | 105 | /** 106 | * @brief 判断点 pos 是否在海上 107 | */ 108 | bool PointInOcean(const Position &pos) const noexcept; 109 | 110 | /** 111 | * @brief 判断点位 pos,dir 是否为海上主干道 112 | */ 113 | bool ShipInMainChannel(const Position &pos, int dir) const noexcept; 114 | 115 | /** 116 | * @brief 判断点 pos 是否在主干道上 117 | */ 118 | bool IsLandMainLine(const Position &pos) const noexcept; 119 | 120 | /** 121 | * @brief 判断点 pos 是否在主航道上 122 | */ 123 | bool PointInMainChannel(const Position &pos) const noexcept; 124 | 125 | /** 126 | * @brief 海上单源最短路 127 | */ 128 | void FindOceanShortestPath(const std::vector &src, std::array &ocean_dist_grid) const; 129 | 130 | /** 131 | * @brief 寻找交货点的海上最短路 132 | */ 133 | void FindDeleveryShortestPath(); 134 | 135 | /** 136 | * @brief 买一个机器人,直接输出指令 137 | */ 138 | void BuyRobot(int race, int berth_id); 139 | 140 | /** 141 | * @brief 买一条船,直接输出指令 142 | */ 143 | void BuyShip(int berth_id); 144 | 145 | /** 146 | * @brief 给出位置、方向,返回下一步走法。 147 | */ 148 | int GetDeliveryPointsMarch(const Position &pos, int dir) const noexcept; 149 | 150 | /** 151 | * @brief 给出位置、方向,返回距离此泊位步数。 152 | */ 153 | int GetDeliveryPointsDist(const Position &pos, int dir) const noexcept; 154 | 155 | /** 156 | * @brief 判断陆地上的一组移动是否会发生碰撞 157 | * @return 如果会发生碰撞就返回 true 158 | */ 159 | bool CheckLandCollision(const Position &cur_x, 160 | const Position &nxt_x, 161 | const Position &cur_y, 162 | const Position &nxt_y) const noexcept; 163 | 164 | /** 165 | * @brief 计算两个坐标的曼哈顿距离 166 | * @return 曼哈顿距离 167 | */ 168 | int Manhattan(const Position &x, const Position &y) const noexcept; 169 | 170 | unsigned long long hash_value_; 171 | 172 | private: 173 | std::array, MAP_SIZE> map_; // 地图的字符矩阵 174 | Grid berth_id_grid_; // 表示每个点属于哪个泊位,如果不是泊位就为 -1; 175 | std::array delivery_point_dist_; // 表示每个点位离虚拟点的距离 176 | std::vector delivery_points_; // 记录虚拟点坐标 177 | std::vector robot_buy_pos_; // 可以购买机器人的地方 178 | std::vector ship_buy_pos_; // 可以购买船的地方 179 | ConnectedComponent connnected_component_; // 选中的连通块 180 | Grid nearest_berth_id_; // 每个位置最近的泊位 id 181 | Grid nearest_berth_dist_; // 每个位置最近的泊位距离 182 | }; 183 | 184 | extern Map g_map; // 全局的地图 -------------------------------------------------------------------------------- /src/robot.cpp: -------------------------------------------------------------------------------- 1 | #include "robot.hpp" 2 | 3 | std::vector g_robots; 4 | std::map g_robot_volume; 5 | std::map g_self_robot_volume; 6 | std::map g_robot_owned; 7 | 8 | Robot::Robot(int id, Position pos, int vol) : id_(id), position_(pos), volume_(vol) {} 9 | 10 | Robot::Robot(Robot &&other) noexcept 11 | : id_(other.id_), 12 | cargo_num_(other.cargo_num_), 13 | volume_(other.volume_), 14 | target_berth_id_(other.target_berth_id_), 15 | position_(std::move(other.position_)), 16 | instructions_(std::move(other.instructions_)), 17 | cargo_ptr_(std::move(other.cargo_ptr_)), 18 | move_dir_(other.move_dir_), 19 | lst_instruction_(std::move(other.lst_instruction_)), 20 | question_ptr_(std::move(other.question_ptr_)), 21 | addtional_target_(std::move(other.addtional_target_)) {} 22 | 23 | int Robot::value_sent_ = 0; 24 | 25 | void Robot::InputFrame() { 26 | int id; 27 | std::cin >> id; 28 | g_robot_owned[id] = true; 29 | assert(id == id_); 30 | cargo_num_ = g_other_robots[id].cargo_num_; 31 | position_ = g_other_robots[id].position_; 32 | addtional_target_ = nullptr; 33 | 34 | if (FullCargo()) { 35 | assert(GetCargoPtr() != nullptr); 36 | } 37 | 38 | move_dir_ = 4; 39 | } 40 | 41 | void Robot::OutputFrame() { 42 | // 到达货物所在地,尝试取货,并向大模型咨询 43 | if (!FullCargo() && GetCargoPtr() != nullptr && position_ == GetCargoPtr()->position_) { 44 | // 如果没有问题,就提问 45 | if (question_ptr_ == nullptr) { 46 | std::cout << "get " << id_ << std::endl; 47 | #ifdef LOCAL_DEBUG_LLM 48 | fprintf(stderr, "\033[31mframe id %d, robot %d ask for llm\n\033[0m", g_frame_id.load(), id_); 49 | #endif 50 | GetCargoPtr()->Delete(); 51 | cargo_num_++; 52 | return; 53 | } 54 | 55 | // 等待大模型回答 56 | if (!question_ptr_->valid_) { 57 | return; 58 | } 59 | 60 | AnswerQuestion(); 61 | #ifdef LOCAL_DEBUG_LLM 62 | fprintf(stderr, "\033[32mframe_id %d, robot %d get answer\n\033[0m", g_frame_id.load(), id_); 63 | #endif 64 | question_ptr_ = nullptr; 65 | // cargo_num_++; 66 | // GetCargoPtr()->Delete(); 67 | assert(cargo_ptr_.top()->IsDeleted()); 68 | } 69 | 70 | // 到达泊位,送货 71 | // if (cargo_num_ && target_berth_id_ != -1) { 72 | // std::cerr << "ops" << std::endl; 73 | // while(1); 74 | // } 75 | if (cargo_num_ && target_berth_id_ != -1 && g_berths[target_berth_id_].InBerth(position_)) { 76 | // while(1); 77 | while (cargo_ptr_.size() > cargo_num_) 78 | cargo_ptr_.pop(); 79 | std::cout << "pull " << id_ << std::endl; 80 | // fprintf(stderr, "robot %d sent value %d\n", id_, cargo_ptr_.top()->value_); 81 | Robot::value_sent_ += cargo_ptr_.top()->GetValue(); 82 | g_berths[target_berth_id_].AddCargos(cargo_ptr_.top()->GetValue()); 83 | cargo_num_--; 84 | SetCargoPtr(nullptr); 85 | if (cargo_num_ == 0) target_berth_id_ = -1; 86 | lst_instruction_ = "pull"; 87 | return; 88 | } 89 | 90 | // 发出移动指令 91 | if (move_dir_ != 4) { 92 | std::cout << "move " << id_ << " " << move_dir_ << std::endl; 93 | move_dir_ = 4; 94 | lst_instruction_ = "move"; 95 | return; 96 | } 97 | } 98 | 99 | bool Robot::FullCargo() const noexcept { 100 | return cargo_num_ == volume_; 101 | } 102 | 103 | Position Robot::GetPosition() const noexcept { 104 | return position_; 105 | } 106 | 107 | void Robot::SetCargoPtr(const CargoPtr &cargo) noexcept { 108 | while (cargo_ptr_.size() > cargo_num_) { 109 | cargo_ptr_.pop(); 110 | } 111 | if (cargo == nullptr) { 112 | return; 113 | } 114 | // if (cargo->IsVirtual()) { 115 | // std::cerr << g_frame_id.load() << " Robot " << id_ << " set virtual point " << cargo->GetID() << 116 | // std::endl; 117 | // } else { 118 | // std::cerr << g_frame_id.load() << " Robot " << id_ << " set real point " << cargo->GetID() << 119 | // std::endl; 120 | // } 121 | cargo_ptr_.push(cargo); 122 | } 123 | 124 | CargoPtr Robot::GetCargoPtr() const noexcept { 125 | assert(cargo_ptr_.size() <= cargo_num_ + 1); 126 | if (cargo_ptr_.size() <= cargo_num_ && !FullCargo()) { 127 | return nullptr; 128 | } 129 | // if(cargo_ptr_.empty()) return nullptr; 130 | return cargo_ptr_.top(); 131 | } 132 | 133 | void Robot::SetTargetBerthID(int id) noexcept { 134 | target_berth_id_ = id; 135 | } 136 | 137 | int Robot::GetTargetBerthID() const noexcept { 138 | return target_berth_id_; 139 | } 140 | 141 | void Robot::SetMoveDirection(int direction) noexcept { 142 | move_dir_ = direction; 143 | } 144 | 145 | int Robot::GetMoveDirection() noexcept { 146 | return move_dir_; 147 | } 148 | 149 | void Robot::AskQuestion(std::string question) noexcept { 150 | if (question_ptr_ != nullptr) { 151 | return; 152 | } 153 | QuestionPtr question_ptr = std::make_shared(question, g_frame_id.load()); 154 | question_ptr_ = question_ptr; 155 | g_question_solver.Submit(question_ptr); 156 | } 157 | 158 | void Robot::AnswerQuestion() noexcept { 159 | int ans = question_ptr_->answer_; 160 | printf("ans %d %d\n", id_, ans); 161 | 162 | #ifdef LOCAL_DEBUG_LLM 163 | assert(question_ptr_->valid_); 164 | fprintf(stderr, "ans %d %d\n", id_, ans); 165 | #endif 166 | } 167 | 168 | void Robot::SetAddtionalTarget(CargoPtr car) noexcept { 169 | addtional_target_ = car; 170 | } 171 | 172 | CargoPtr Robot::GetAddtionalTarget() const noexcept { 173 | return addtional_target_; 174 | } 175 | 176 | bool Robot::IsWaitingForLLM() const noexcept { 177 | return question_ptr_ != nullptr; 178 | } 179 | 180 | std::map g_other_robots; -------------------------------------------------------------------------------- /src/land_scheduler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "cargo.hpp" 4 | #include "config.hpp" 5 | #include "map.hpp" 6 | #include "robot.hpp" 7 | 8 | 9 | /** 10 | * @class 封装起来的分层图最大费用最大流。 11 | */ 12 | class ScheduleGraph { 13 | public: 14 | /** 15 | * @brief 每一帧都需要重新生成流网络图。 16 | * @param g_robot_cnt 机器人数量 17 | * @param cargo_cnt 货物数量 18 | * @param berth_cnt 泊位数量 19 | */ 20 | ScheduleGraph(int robot_cnt, int cargo_cnt, int berth_cnt); 21 | 22 | ScheduleGraph(const ScheduleGraph &) = delete; 23 | ScheduleGraph &operator=(const ScheduleGraph &) = delete; 24 | 25 | ScheduleGraph(ScheduleGraph &&) = delete; 26 | ScheduleGraph operator=(ScheduleGraph &&) = delete; 27 | 28 | ~ScheduleGraph() = default; 29 | 30 | /** 31 | * @brief 重新初始化 32 | */ 33 | void Init(int robot_cnt, int cargo_cnt, int berth_cnt); 34 | 35 | /** 36 | * @brief 给一组 (机器人,货物,泊位)设定收益 37 | * @param robot_id 机器人编号 38 | * @param cargo_id 货物编号 39 | * @param berth_id 货物需要被送往的泊位编号 40 | * @param benefit 这组任务的收益 41 | */ 42 | void SetMisionBenifit(int robot_id, int cargo_id, double benefit) noexcept; 43 | 44 | /** 45 | * @brief 设置一个货物的泊位。 46 | * @attention 每个货物只能被设置一次! 否则会检查报错 47 | * @param cargo_id 货物 id 48 | * @param berth_id 泊位 id 49 | */ 50 | void SetCargoBerth(int cargo_id, int berth_id) noexcept; 51 | 52 | /** 53 | * @brief 查询一组任务的收益 54 | * @param robot_id 机器人编号 55 | * @param cargo_id 货物编号 56 | */ 57 | double GetMissionBenifit(int robot_id, int cargo_id) noexcept; 58 | 59 | /** 60 | * @brief 给一组泊位增添流量控制(最多有多少个机器人往这里送货) 61 | * @attention 每个泊位之多只能属于一个组,否则会检查报错。没有设置的组不参与匹配。 62 | * @param group 表示这个组里有哪些泊位 63 | * @param limitation 这个组的流量限制 64 | */ 65 | void SetBerthGroupLimitation(const std::set &berth_group, int limitation) noexcept; 66 | 67 | /** 68 | * @brief 进行最优匹配。即最大费用最大流算法的主体。 69 | * @return 返回一个 std::map,里面包含若干组 (机器人,货物)匹配。 70 | */ 71 | std::map MatchRobotCargo() noexcept; 72 | 73 | private: 74 | /** 75 | * @brief 费用流网络加边。 76 | * @param 方向为从 u->v,流量为 cap,费用为 cost 77 | */ 78 | void AddEdge(int u, int v, int cap, double cost) noexcept; 79 | 80 | /** 81 | * @brief 求解费用流的过程中使用 spfa 算法寻找增广路径 82 | */ 83 | bool SPFA() noexcept; 84 | 85 | /** 86 | * @struct 费用流网络里的边 87 | */ 88 | struct Edge { 89 | int to_ = 0; 90 | int cap_ = 0; 91 | double cost_ = 0; 92 | }; 93 | 94 | /** 95 | * @brief 图论建模:节点编号从小到大分别为 96 | * 机器人 97 | * 货物 98 | * 泊位 99 | * 泊位组 100 | * 源点 101 | * 汇点 102 | */ 103 | int GetRobotID(int robot_id) const noexcept; 104 | int GetCargoID(int cargo_id) const noexcept; 105 | int GetBerthID(int berth_id) const noexcept; 106 | int GetBerthGroupID(int berth_group_id) const noexcept; 107 | 108 | int source_{-1}; // 源点编号 109 | int sink_{-1}; // 汇点编号 110 | int ver_cnt_{0}; // 点总数 111 | int robot_cnt_{0}; // 机器人数量 112 | int cargo_cnt_{0}; // 货物数量 113 | int berth_cnt_{0}; // 泊位数量 114 | int berth_group_cnt_{0}; // 泊位组数量 115 | std::set already_in_group_; // 已经在泊位组的泊位编号,debug 用 116 | std::map tmp_group_limitation_; // 建图时暂存泊位组的流量 117 | std::set cargo_berth_; // 记录已经设置目标泊位的货物,debug 用 118 | 119 | std::vector> adj_; // adj[u] 存的是与 u 相连的边编号 120 | std::vector edge_; // 将边编号映射到 struct Edge,边的编号从 0 开始 121 | std::vector dis_; // 从 s 出发,到每个点的最大单位费用 122 | std::vector incf_; // 从 s 出发,到每个点的最大流量 123 | std::vector pre_; // 可行流当中,每个点的前驱 124 | // std::map, double> benefit_matrix_; // 模拟邻接矩阵 125 | std::array, MAX_ROBOT_NUM + 10> benefit_matrix_; 126 | }; 127 | 128 | 129 | /** 130 | * @brief 管理陆地上的虚拟点 131 | */ 132 | class LandVirtualPointManager { 133 | public: 134 | /** 135 | * @brief 添加一个陆地上的虚拟点 136 | */ 137 | void AddLandVirtuaPoint(const Position &pos); 138 | }; 139 | 140 | 141 | class LandScheduler { 142 | public: 143 | LandScheduler() = default; 144 | 145 | // 禁用所有拷贝和移动 146 | LandScheduler(const LandScheduler &) = delete; 147 | LandScheduler &operator=(const LandScheduler &) = delete; 148 | LandScheduler(LandScheduler &&) = delete; 149 | LandScheduler operator=(LandScheduler &&) = delete; 150 | 151 | // 默认析构 152 | ~LandScheduler() = default; 153 | 154 | /** 155 | * @brief 进行这一帧的路上调度。 156 | * - 调用 ScheculeBusyRobots() 给所有有货的机器人设置目标泊位 157 | * - 调用 ScheduleFreeRobots() 给所有无货的机器人设置目标货物 158 | */ 159 | void ScheduleRobots(); 160 | 161 | private: 162 | /** 163 | * @brief 枚举有货物的机器人,调用 robot.SetTargetBertiID() 设置送货泊位的 ID。 164 | */ 165 | void ScheduleBusyRobots(); 166 | 167 | /** 168 | * @brief 枚举没有货物的机器人,调用 robot.SetCargoPosition() 设置取货的 Position。 169 | */ 170 | void ScheduleFreeRobots(); 171 | 172 | /** 173 | * @brief 将货物与泊位匹配 174 | */ 175 | void MatchCargoBerth() noexcept; 176 | 177 | /** 178 | * @brief 设置机器人与货物的对应收益 179 | */ 180 | void CalcRobotCargoBenefit() noexcept; 181 | 182 | /** 183 | * @brief 利用 RobotCargoGraph 进行匹配,并是适量避免摆头。 184 | */ 185 | void UpdateMatch(); 186 | 187 | /** 188 | * @brief 给泊位分组 189 | */ 190 | void DivideBerthGroups(); 191 | 192 | /** 193 | * @brief 判断高价货物是否需要机器人抢占、安排合适的机器人 194 | */ 195 | void Occupy(); 196 | 197 | /** 198 | * @brief 输入货物指针和一个距离,返回范围内的有空闲容量的机器人数量 199 | */ 200 | int RoNumAroundCargo(CargoPtr cp, int dis); 201 | 202 | std::array cargo_berth_dist_; // 每个货物到泊位的距离 203 | std::vector cargo_ptr_vec_; // 暂存货物指针 204 | std::shared_ptr graph_ptr_{ 205 | std::make_shared(0, 0, 0)}; // 机器人-泊位 匹配图的指针 206 | }; 207 | 208 | extern LandScheduler g_land_scheduler; -------------------------------------------------------------------------------- /src/berth.cpp: -------------------------------------------------------------------------------- 1 | #include "berth.hpp" 2 | 3 | #include "map.hpp" 4 | 5 | Berth::Berth(int id, Position core_pos, int velocity, Grid &&dist_grid, std::set &&area) 6 | : id_(id), 7 | core_pos_(core_pos), 8 | velocity_(velocity), 9 | area_(std::move(area)), 10 | time_(g_map.GetDeliveryPointsDist(core_pos, -1)), 11 | dist_grid_(std::move(dist_grid)) { 12 | // 更新到这个泊位的海上最短路 13 | std::vector src; 14 | for (auto pos : area_) { 15 | src.push_back(pos); 16 | } 17 | g_map.FindOceanShortestPath(src, ocean_dist_grid_); 18 | } 19 | 20 | Berth::Berth(Berth &&other) noexcept 21 | : id_(other.id_), 22 | core_pos_(other.core_pos_), 23 | velocity_(other.velocity_), 24 | area_(other.area_), 25 | time_(other.time_), 26 | tot_value(other.tot_value), 27 | tot_cnt(other.tot_cnt), 28 | ship_num_(other.ship_num_), 29 | cargo_(std::move(other.cargo_)), 30 | dist_grid_(std::move(other.dist_grid_)), 31 | ocean_dist_grid_(std::move(other.ocean_dist_grid_)), 32 | active_(other.active_) { 33 | other.ship_num_ = 0; 34 | other.tot_value = 0; 35 | other.tot_cnt = 0; 36 | other.active_ = false; 37 | } 38 | 39 | int Berth::CalcValue(int num) const noexcept { 40 | if (cargo_.empty()) return 0; 41 | num = std::min({num, static_cast(cargo_.size())}); 42 | int res = std::accumulate(cargo_.begin(), cargo_.begin() + num, 0); 43 | 44 | if (!active_) { 45 | return res *= 5; 46 | } 47 | return res; 48 | } 49 | 50 | int Berth::GetCargoNum() const noexcept { 51 | return cargo_.size(); 52 | } 53 | 54 | int Berth::GetShipNum() const noexcept { 55 | return ship_num_; 56 | } 57 | 58 | void Berth::AddShipNum(int x) noexcept { 59 | ship_num_ += x; 60 | } 61 | 62 | const Grid &Berth::GetLandDistanceGrid() const noexcept { 63 | return dist_grid_; 64 | } 65 | 66 | int Berth::GetLandDistance(Position pos) const noexcept { 67 | if (!active_) { 68 | return INF; 69 | } 70 | assert(g_map.IsField(pos)); 71 | int res = dist_grid_.Get(pos); 72 | if (!HereIsOurShip()) { 73 | res += 200; 74 | } 75 | return res; 76 | } 77 | 78 | int Berth::GetLandDirection(Position pos) const noexcept { 79 | assert(g_map.IsField(pos)); 80 | return dist_grid_.GetDescentDirection(pos); 81 | } 82 | 83 | bool Berth::InBerth(Position pos) const noexcept { 84 | return area_.count(pos) > 0; 85 | } 86 | 87 | void Berth::AddCargos(int value) noexcept { 88 | cargo_.push_back(value); 89 | this->tot_value += value; 90 | this->tot_cnt += 1; 91 | } 92 | 93 | std::pair Berth::TakeCargo(int num) noexcept { 94 | std::vector cargo_new; 95 | num = std::min(std::min(static_cast(cargo_.size()), velocity_), num); 96 | if (g_frame_id < MAX_FRAME_NUM - time_) { 97 | for (int i = 0; i < num; i++) { 98 | g_value_token += cargo_[i]; 99 | } 100 | } 101 | int value = 0; 102 | for (int i = 0; i < num; i++) { 103 | value += cargo_[i]; 104 | } 105 | 106 | for (int i = num; i < cargo_.size(); i++) { 107 | cargo_new.push_back(cargo_[i]); 108 | } 109 | cargo_ = cargo_new; 110 | return {num, value}; 111 | } 112 | 113 | int Berth::GetOceanMarch(Position pos, int dir) const noexcept { 114 | int res = 3; 115 | int dis = ocean_dist_grid_[dir].Get(pos); 116 | 117 | int tmp_dir = dir; 118 | Position tmp_pos = pos + DIR[dir]; 119 | if (ocean_dist_grid_[tmp_dir].Get(tmp_pos) < dis) { 120 | res = 2; 121 | dis = ocean_dist_grid_[tmp_dir].Get(tmp_pos); 122 | } 123 | 124 | tmp_dir = dir ^ (2 | (dir >> 1)); 125 | tmp_pos = pos + DIR[dir] + DIR[dir ^ 3 ^ (dir >> 1)]; 126 | if (ocean_dist_grid_[tmp_dir].Get(tmp_pos) < dis) { 127 | res = 1; 128 | dis = ocean_dist_grid_[tmp_dir].Get(tmp_pos); 129 | } 130 | 131 | tmp_dir = dir ^ 3 ^ (dir >> 1); 132 | tmp_pos = pos + DIR[dir] + DIR[dir]; 133 | if (ocean_dist_grid_[tmp_dir].Get(tmp_pos) < dis) { 134 | res = 0; 135 | dis = ocean_dist_grid_[tmp_dir].Get(tmp_pos); 136 | } 137 | 138 | return res; 139 | } 140 | 141 | int Berth::GetOceanDistance(Position pos, int dir) const noexcept { 142 | if (dir == -1) { 143 | int res = INF; 144 | for (int i = 0; i < 4; i++) { 145 | res = std::min(res, ocean_dist_grid_[i].Get(pos)); 146 | } 147 | return res; 148 | } 149 | return ocean_dist_grid_[dir].Get(pos); 150 | } 151 | 152 | void Berth::Activate() noexcept { 153 | active_ = true; 154 | } 155 | 156 | void Berth::Deactivate() noexcept { 157 | this->active_ = false; 158 | } 159 | 160 | bool Berth::IsActive() const noexcept { 161 | return active_; 162 | } 163 | 164 | void Berth::ChangeDistance(std::map &pass) noexcept { 165 | for (auto [p, t] : pass) { 166 | int newdist = dist_grid_.Get(p) * 0.8 + (g_frame_id - t) * 0.2; 167 | dist_grid_.Set(p, newdist); 168 | } 169 | pass.clear(); 170 | } 171 | 172 | bool Berth::HereIsOurShip() const noexcept { 173 | return our_ship_; 174 | } 175 | 176 | /** 177 | * @brief 设定泊位是否有我们的船 178 | */ 179 | void Berth::SetOurShip(bool f) noexcept { 180 | our_ship_ = f; 181 | } 182 | 183 | std::vector g_berths; 184 | 185 | BerthGroup::BerthGroup(const std::vector &group) { 186 | for (int x : group) { 187 | group_member_.insert(x); 188 | } 189 | } 190 | 191 | void BerthGroupManager::InitGroups() { 192 | // 用并查集将泊位分组 193 | int berth_num = g_berths.size(); 194 | DisjointSetUnion dsu(berth_num); 195 | 196 | for (int u = 0; u < berth_num; u++) { 197 | for (int v = 0; v < u; v++) { 198 | if (GetL1(g_berths[u].core_pos_, g_berths[v].core_pos_) < 20) { 199 | dsu.Merge(u, v); 200 | } 201 | } 202 | } 203 | 204 | auto berth_groups = dsu.GetSets(); 205 | 206 | for (const auto &bset : berth_groups) { 207 | groups_.emplace_back(bset); 208 | std::cerr << "set: "; 209 | for (int bid : bset) { 210 | std::cerr << bid << " "; 211 | } 212 | std::cerr << std::endl; 213 | } 214 | } 215 | 216 | const std::vector &BerthGroupManager::GetAllBerthGroup() const { 217 | return groups_; 218 | } 219 | 220 | std::vector BerthGroupManager::GetActivatedBerthGroup() const { 221 | std::vector activated_groups; 222 | for (const auto &group : groups_) { 223 | if (group.active_) { 224 | activated_groups.push_back(group); 225 | } 226 | } 227 | return activated_groups; 228 | } 229 | 230 | int BerthGroupManager::ActivatedSize() const { 231 | int res = 0; 232 | for (const auto &berths : groups_) { 233 | res += berths.active_; 234 | } 235 | return res; 236 | } 237 | 238 | void BerthGroupManager::ActivateOneGroup(int id) { 239 | groups_[id % groups_.size()].active_ = true; 240 | } 241 | 242 | BerthGroupManager g_berth_group_manager; -------------------------------------------------------------------------------- /src/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "config.hpp" 30 | 31 | /** 32 | * @class 并查集 33 | */ 34 | class DisjointSetUnion { 35 | public: 36 | /** 37 | * @brief 初始化并查集 38 | * @param n 元素个数,下标从 [0, n-1] 39 | */ 40 | explicit DisjointSetUnion(int n); 41 | 42 | ~DisjointSetUnion() = default; 43 | 44 | /** 45 | * @brief 合并两个元素所在的集合 46 | * @return 原本在一个集合返回 false,否则返回 true 47 | */ 48 | bool Merge(int x, int y); 49 | 50 | /** 51 | * @brief 寻找所在集合的代表 52 | */ 53 | int FindLeader(int x); 54 | 55 | /** 56 | * @brief 输出并查集 57 | * @return 外层的 vector 表示一个集合,内层的 vector 表示这个集合里面有哪些元素。 58 | */ 59 | std::vector> GetSets(); 60 | 61 | private: 62 | int n_; 63 | std::vector leader_; 64 | }; 65 | 66 | /** 67 | * @class Position 用来表示地图上的一个点 68 | */ 69 | using Position = std::array; 70 | 71 | using Direction = Position; 72 | 73 | std::ostream &operator<<(std::ostream &os, const Position &pos); 74 | 75 | Position operator+(const Position &lhs, const Position &rhs); 76 | 77 | Position operator-(const Position &lhs, const Position &rhs); 78 | 79 | /** 80 | * @brief 获取两个 Position 之间的曼哈顿距离 81 | */ 82 | int GetL1(const Position &lhs, const Position &rhs); 83 | 84 | /** 85 | * @brief 五个方向 86 | */ 87 | const int DX[5]{0, 0, -1, 1, 0}; 88 | const int DY[5]{1, -1, 0, 0, 0}; 89 | const Direction DIR[5]{Direction{0, 1}, Direction{0, -1}, Direction{-1, 0}, Direction{1, 0}, Direction{0, 0}}; 90 | 91 | /** 92 | * @class TwoDimArray 是一个简单的二维矩阵 93 | * @attention 禁止了复制,但是允许移动:利用 std::vector 的移动构造,复杂度 O(1)。 94 | */ 95 | template 96 | class TwoDimArray { 97 | public: 98 | TwoDimArray(T w = 0) { 99 | // std::cerr << "Grid Construct!" << std::endl; 100 | Set(w); 101 | } 102 | 103 | // 禁止拷贝构造 104 | TwoDimArray(const TwoDimArray &) = delete; 105 | TwoDimArray &operator=(const TwoDimArray &) = delete; 106 | 107 | TwoDimArray(TwoDimArray &&other) noexcept { 108 | grid_ = std::move(other.grid_); 109 | } 110 | 111 | TwoDimArray &operator=(TwoDimArray &&other) noexcept { 112 | grid_ = std::move(other.grid_); 113 | return *this; 114 | } 115 | 116 | // 默认析构 117 | ~TwoDimArray() = default; 118 | 119 | /** 120 | * @brief 单点赋值 121 | */ 122 | void Set(const Position &pos, T w) { 123 | const auto &[x, y] = pos; 124 | grid_[x][y] = w; 125 | } 126 | 127 | /** 128 | * @brief 全局赋值 129 | */ 130 | void Set(T w) { 131 | grid_.assign(N, std::vector(M, w)); 132 | } 133 | 134 | /** 135 | * @brief 单点取值 136 | */ 137 | T Get(const Position &pos) const { 138 | const auto &[x, y] = pos; 139 | return grid_.at(x).at(y); 140 | } 141 | 142 | /** 143 | * @brief 获得梯度最大的方向 144 | * @attention 自带随机性 145 | */ 146 | int GetDescentDirection(const Position &pos) const { 147 | int dir = 0; 148 | int minv = INF; 149 | std::vector cho; 150 | for (int i = 0; i < 5; i++) { 151 | Position nxt = pos + Direction{DX[i], DY[i]}; 152 | if (!IN_MAP(nxt[0], nxt[1])) { 153 | continue; 154 | } 155 | 156 | if (Get(nxt) < minv) { 157 | minv = Get(nxt); 158 | cho.clear(); 159 | cho.push_back(i); 160 | } else if (Get(nxt) == minv) { 161 | cho.push_back(i); 162 | } 163 | } 164 | 165 | dir = cho[rand() % cho.size()]; 166 | return dir; 167 | } 168 | 169 | auto &operator[](int index) { 170 | return grid_[index]; 171 | } 172 | 173 | auto begin() { // NOLINT 174 | return grid_.begin(); 175 | } 176 | 177 | auto end() { // NOLINT 178 | return grid_.end(); 179 | } 180 | 181 | private: 182 | std::vector> grid_; 183 | }; 184 | 185 | /** 186 | * @class Grid 针对并发的偏特化类型 187 | */ 188 | template 189 | class TwoDimArray { 190 | public: 191 | TwoDimArray(std::atomic_int w = 0) { 192 | Set(w.load()); 193 | } 194 | 195 | // 禁止拷贝构造 196 | TwoDimArray(const TwoDimArray &) = delete; 197 | TwoDimArray &operator=(const TwoDimArray &) = delete; 198 | 199 | // 禁止移动构造 200 | TwoDimArray(TwoDimArray &&other) = delete; 201 | TwoDimArray &operator=(TwoDimArray &&other) = delete; 202 | 203 | /** 204 | * @brief 单点赋值 205 | */ 206 | void Set(const Position &pos, std::atomic_int w) { 207 | const auto &[x, y] = pos; 208 | std::atomic_store(&grid_[x][y], w.load()); 209 | } 210 | 211 | /** 212 | * @brief 全局赋值 213 | */ 214 | void Set(std::atomic_int w) { 215 | for (int i = 0; i < N; i++) { 216 | for (int j = 0; j < M; j++) { 217 | std::atomic_store(&grid_[i][j], w.load()); 218 | } 219 | } 220 | } 221 | 222 | /** 223 | * @brief 单点取值 224 | */ 225 | std::atomic_int Get(const Position &pos) const { 226 | const auto &[x, y] = pos; 227 | return grid_[x][y].load(); 228 | } 229 | 230 | auto &operator[](int index) { 231 | return grid_[index]; 232 | } 233 | 234 | auto begin() { // NOLINT 235 | return grid_.begin(); 236 | } 237 | 238 | auto end() { // NOLINT 239 | return grid_.end(); 240 | } 241 | 242 | private: 243 | std::array, N> grid_; 244 | }; 245 | 246 | using Grid = TwoDimArray; 247 | using ConcurrencyGrid = TwoDimArray; 248 | 249 | /** 250 | * @brief 专门用来管理 Grid 内存池 251 | */ 252 | class GridPool { 253 | public: 254 | using GridPtr = std::shared_ptr; 255 | 256 | /** 257 | * @brief 单例模式 258 | */ 259 | static GridPool &GetInstance() { 260 | static GridPool grid_pool; 261 | return grid_pool; 262 | } 263 | 264 | /** 265 | * @brief 申请一个 GridPtr 266 | */ 267 | GridPtr Alloc() { 268 | std::lock_guard lg(mtx_); 269 | assert(!pool_.empty()); 270 | auto it = *(pool_.begin()); 271 | pool_.erase(it); 272 | return it; 273 | } 274 | 275 | /** 276 | * @brief 释放一个 GridPtr 277 | */ 278 | void Free(GridPtr ptr) { 279 | std::lock_guard lg(mtx_); 280 | pool_.insert(ptr); 281 | } 282 | 283 | private: 284 | GridPool() { 285 | for (int i = 0; i < MAX_TOT_CARGO_NUM + 50; i++) { 286 | pool_.insert(std::make_shared()); 287 | } 288 | } 289 | 290 | std::mutex mtx_; 291 | std::set pool_; 292 | }; 293 | 294 | /** 295 | * @class 用来打印日志到文件 296 | */ 297 | class Logger { 298 | public: 299 | Logger(const std::string &entity_name) : entity_name_(entity_name) { 300 | log_file_.open("./log/" + GetCurrentTime() + entity_name_ + ".log", std::ios::out | std::ios::app); 301 | if (!log_file_.is_open()) { 302 | std::cerr << "Error: Unable to open log file for entity " << entity_name_ << std::endl; 303 | } 304 | } 305 | 306 | template 307 | void log(const T &arg) { // NOLINT 308 | log_file_ << " " << arg << std::endl; 309 | } 310 | 311 | template 312 | void log(const T &firstArg, Args... args) { // NOLINT 313 | log_file_ << " " << firstArg; 314 | log(args...); 315 | } 316 | 317 | ~Logger() { 318 | if (log_file_.is_open()) { 319 | log_file_.close(); 320 | } 321 | } 322 | 323 | private: 324 | std::string entity_name_; 325 | std::ofstream log_file_; 326 | 327 | std::string GetCurrentTime() { 328 | time_t now = time(0); 329 | struct tm tstruct; 330 | char buf[80]; 331 | tstruct = *localtime(&now); 332 | strftime(buf, sizeof(buf), "%Y-%m-%d %X", &tstruct); 333 | return buf; 334 | } 335 | }; 336 | 337 | /** 338 | * @brief 计时器,用来记录一个作用域的执行耗时 339 | */ 340 | class Timer { 341 | public: 342 | Timer(std::string name) : name_(name), start_time_(std::chrono::steady_clock::now()) {} 343 | Timer(std::string name, int worst) 344 | : name_(name), worst_(worst), start_time_(std::chrono::steady_clock::now()) {} 345 | 346 | void Check(std::string name, int worst) { 347 | auto end_time = std::chrono::steady_clock::now(); 348 | auto duration = std::chrono::duration_cast(end_time - start_time_); 349 | 350 | std::chrono::milliseconds tmp_worst(worst); 351 | if (worst_ == std::chrono::milliseconds(INF) || duration > tmp_worst) { 352 | std::cerr << "\033[31m" << name << " took \t" << duration.count() 353 | << " milliseconds to run.\033[0m" << std::endl; 354 | } 355 | } 356 | 357 | ~Timer() { 358 | auto end_time = std::chrono::steady_clock::now(); 359 | auto duration = std::chrono::duration_cast(end_time - start_time_); 360 | if (worst_ == std::chrono::milliseconds(INF) || duration > worst_) { 361 | std::cerr << "\033[31m" << name_ << " took \t" << duration.count() 362 | << " milliseconds to run.\033[0m" << std::endl; 363 | } 364 | } 365 | 366 | private: 367 | std::string name_; 368 | std::chrono::milliseconds worst_{INF}; 369 | std::chrono::time_point start_time_; 370 | }; 371 | 372 | extern Logger crashLog; -------------------------------------------------------------------------------- /src/ocean_controller.cpp: -------------------------------------------------------------------------------- 1 | #include "ocean_controller.hpp" 2 | 3 | #include "map.hpp" 4 | 5 | int ShipDist(int i, int j) { 6 | Position p1 = g_ships[i].GetPostion(); 7 | Position p2 = g_ships[j].GetPostion(); 8 | 9 | return GetL1(p1, p2); 10 | } 11 | 12 | bool OceanController::CheckShipMayCrash(int i, int j) { 13 | return ShipDist(i, j) < ship_crash_limit; 14 | } 15 | 16 | bool OceanController::CheckShipCrash(int ship_a, int ship_b) { 17 | Instruction ins_a = g_ships[ship_a].CalculateInstruction(); 18 | Instruction ins_b = g_ships[ship_b].CalculateInstruction(); 19 | 20 | ShipPositions sp_a_new = g_ships[ship_a].GetPostionsAfterInstruction(ins_a); 21 | ShipPositions sp_b_new = g_ships[ship_b].GetPostionsAfterInstruction(ins_b); 22 | 23 | ShipPositions sp_a_cur = g_ships[ship_a].GetPostions(); 24 | ShipPositions sp_b_cur = g_ships[ship_b].GetPostions(); 25 | 26 | for (Position& pa : sp_a_new) 27 | for (Position& pb : sp_b_cur) 28 | if ((pa[0] == pb[0]) && (pa[1] == pb[1]) && !g_map.PointInMainChannel(pa)) return true; 29 | 30 | for (Position& pa : sp_a_cur) 31 | for (Position& pb : sp_b_new) 32 | if ((pa[0] == pb[0]) && (pa[1] == pb[1]) && !g_map.PointInMainChannel(pa)) return true; 33 | 34 | for (Position& pa : sp_a_new) 35 | for (Position& pb : sp_b_new) 36 | if ((pa[0] == pb[0]) && (pa[1] == pb[1]) && !g_map.PointInMainChannel(pa)) return true; 37 | 38 | return false; 39 | } 40 | 41 | void OceanController::ControlShips() { 42 | for (int i = 0, ship_num = g_ships.size(); i < ship_num; i++) { 43 | Instruction cur_ins = g_ships[i].CalculateInstruction(); 44 | 45 | if (g_ships[i].GetStatus() != 0) { 46 | g_ships[i].AddInstruction(cur_ins); 47 | } else { 48 | if (cur_ins.first == "ship" || cur_ins.first == "rot") 49 | g_ships[i].AddInstruction(AstarFindNextInstruction(i)); 50 | else 51 | g_ships[i].AddInstruction(cur_ins); 52 | } 53 | 54 | if (g_frame_id % 100 == 0) { 55 | fprintf(stderr, "ship value: %d pos: (%d, %d)\n", g_ships[i].GetValue(), 56 | g_ships[i].GetPostion()[0], g_ships[i].GetPostion()[1]); 57 | 58 | if (g_ships[i].GetTar() != -1) { 59 | Position p = g_berths[g_ships[i].GetTar()].core_pos_; 60 | fprintf(stderr, "aim pos: (%d, %d)\n", p[0], p[1]); 61 | } 62 | } 63 | } 64 | } 65 | 66 | void OceanController::ControlShips2() { 67 | int ship_num = g_ships.size(); 68 | 69 | for (int i = 0; i < ship_num; i++) { 70 | if (g_ships[i].GetStatus() != 0) { 71 | g_ships[i].AddInstruction(g_ships[i].CalculateInstruction()); 72 | 73 | continue; 74 | } 75 | 76 | bool is_crash = 0; 77 | 78 | for (int j = 0; j < ship_num && (is_crash == 0); j++) { 79 | if (i != j && CheckShipMayCrash(i, j) && CheckShipCrash(j, i)) { 80 | is_crash |= 1; 81 | } 82 | } 83 | 84 | auto [nxt_p, nxt_d] = g_ships[i].GetNextPD(); 85 | if (is_crash == 0 && CheckShipCollision(nxt_p, nxt_d)) { 86 | is_crash = 1; 87 | } 88 | 89 | if (is_crash) { 90 | g_ships[i].AddInstruction(g_ships[i].ResToIns(rand() % 3)); 91 | } else { 92 | g_ships[i].AddInstruction(g_ships[i].CalculateInstruction()); 93 | } 94 | 95 | // Position p = g_ships[i].GetPostion(); 96 | // int d = g_ships[i].GetDir(); 97 | // std::pair nxt[3]; 98 | // nxt[0] = {p + DIR[d ^ 1], d}; 99 | // nxt[1] = {p + DIR[d] + DIR[d ^ ((d >> 1) | 2)], d ^ 3 ^ (d >> 1)}; 100 | // nxt[2] = {p + DIR[d ^ 3 ^ (d >> 1)] + DIR[d ^ 3 ^ (d >> 1)], d ^ ((d >> 1) | 101 | // 2)}; 102 | } 103 | } 104 | 105 | bool OceanController::CheckShipCollision(Position nxt, int dir) { 106 | ShipPositions self = Ship::GetPostions(nxt, dir); 107 | for (auto [id, other_ship] : g_other_ships) { 108 | ShipPositions another = Ship::GetPostions(other_ship.position_, other_ship.direction_); 109 | for (Position& a : self) 110 | for (Position& b : another) 111 | if ((a[0] == b[0]) && (a[1] == b[1]) && !g_map.PointInMainChannel(a)) { 112 | return true; 113 | } 114 | } 115 | return false; 116 | } 117 | 118 | 119 | Instruction OceanController::AstarFindNextInstruction(int ship_index) { 120 | astar.LoadShip(ship_index); 121 | 122 | if (astar.CheckEasy()) { 123 | return g_ships[ship_index].CalculateInstruction(); 124 | } else { 125 | return g_ships[ship_index].ResToIns(astar.FindNextStep()); 126 | } 127 | } 128 | 129 | int Astar::CalcHcost(Position& pos, int& dir) { 130 | if (g_ships[ship_index_].GetTar() == -1) { 131 | return g_map.GetDeliveryPointsDist(pos, dir); 132 | } else { 133 | return g_berths[g_ships[ship_index_].GetTar()].GetOceanDistance(pos, dir); 134 | } 135 | } 136 | 137 | bool Astar::CheckStop(Position& pos, int& dir) { 138 | Position origin_pos = g_ships[ship_index_].GetPostion(); 139 | int origin_dir = g_ships[ship_index_].GetDir(); 140 | 141 | return (CalcHcost(pos, dir) < 1) || (CalcHcost(origin_pos, origin_dir) - CalcHcost(pos, dir) > 20); 142 | } 143 | 144 | bool Astar::CheckEasy() { 145 | // return 0; 146 | return other_ships_pos_.empty(); 147 | } 148 | 149 | void Astar::LoadShip(int ship_index) { 150 | other_ships_pos_.clear(); 151 | ship_index_ = ship_index; 152 | 153 | for (int i = 0, ship_num = g_ships.size(); i < ship_num; i++) { 154 | if (i != ship_index_) { 155 | ShipPositions sp = g_ships[i].GetPostions(); 156 | 157 | for (Position& p : sp) { 158 | if (GetL1(p, g_ships[ship_index_].GetPostion()) < 10 && !g_map.PointInMainChannel(p)) { 159 | other_ships_pos_.push_back(p); 160 | } 161 | } 162 | } 163 | } 164 | 165 | for (auto& [id, other_ship] : g_other_ships) { 166 | ShipPositions sp = Ship::GetPostions(other_ship.position_, other_ship.direction_); 167 | 168 | for (Position& p : sp) { 169 | if (GetL1(p, g_ships[ship_index_].GetPostion()) < 10) { 170 | other_ships_pos_.push_back(p); 171 | } 172 | } 173 | } 174 | } 175 | 176 | bool Astar::CheckCrash(Position& pos, int& dir) { 177 | ShipPositions sp = Ship::GetPostions(pos, dir); 178 | int min_L1 = INF; 179 | 180 | for (Position& pos : other_ships_pos_) { 181 | for (Position& p : sp) { 182 | min_L1 = std::min(min_L1, GetL1(pos, p)); 183 | } 184 | } 185 | 186 | return (min_L1 < 1); 187 | } 188 | 189 | int Astar::FindNextStep() { 190 | pos_stack_.clear(); // 位置 191 | dir_stack_.clear(); // 方向 192 | dfs_stack_.clear(); // 搜索了哪几步(3 就代表搜过了 0,1 没搜 2) 193 | res_stack_.clear(); // 去下一步选的是哪一步(0,1 旋转,2 前进) 194 | 195 | pos_stack_.push_back(g_ships[ship_index_].GetPostion()); 196 | dir_stack_.push_back(g_ships[ship_index_].GetDir()); 197 | dfs_stack_.push_back(0); 198 | res_stack_.push_back(-1); 199 | 200 | for (int max_dfs_times = 1e2; !dfs_stack_.empty() && (!!max_dfs_times); max_dfs_times--) { 201 | Position pos = pos_stack_.back(); 202 | int dir = dir_stack_.back(); 203 | int& dfs = dfs_stack_.back(); 204 | int& res = res_stack_.back(); 205 | 206 | if (CheckStop(pos, dir)) { 207 | break; 208 | } 209 | 210 | if (dfs == 7) { 211 | pos_stack_.pop_back(); 212 | dir_stack_.pop_back(); 213 | dfs_stack_.pop_back(); 214 | res_stack_.pop_back(); 215 | } else { 216 | next_pos_[0] = pos + DIR[dir] + DIR[dir]; 217 | next_pos_[1] = pos + DIR[dir] + DIR[g_ships[ship_index_].Rot(dir, 0)]; 218 | next_pos_[2] = pos + DIR[dir]; 219 | 220 | next_dir_[0] = g_ships[ship_index_].Rot(dir, 0); 221 | next_dir_[1] = g_ships[ship_index_].Rot(dir, 1); 222 | next_dir_[2] = dir; 223 | 224 | for (int kb = 0; kb < 3; kb++) { 225 | if (((dfs >> kb) & 1) || (CheckCrash(next_pos_[kb], next_dir_[kb]))) { 226 | next_cost_[kb] = INF; 227 | } else { 228 | next_cost_[kb] = CalcHcost(next_pos_[kb], next_dir_[kb]); 229 | } 230 | } 231 | 232 | int next_res = -1, min_cost = INF; 233 | 234 | for (int kb = 0; kb < 3; kb++) { 235 | if (next_cost_[kb] < min_cost) { 236 | min_cost = next_cost_[kb]; 237 | next_res = kb; 238 | } 239 | } 240 | 241 | if (next_res != -1) { 242 | dfs |= (1 << next_res); 243 | res = next_res; 244 | 245 | pos_stack_.push_back(next_pos_[next_res]); 246 | dir_stack_.push_back(next_dir_[next_res]); 247 | dfs_stack_.push_back(0); 248 | res_stack_.push_back(-1); 249 | } else { 250 | dfs = 7; 251 | res = -1; 252 | } 253 | } 254 | } 255 | 256 | if (g_frame_id % 100 == 1) { 257 | fprintf(stderr, "ship value: %d\n", g_ships[ship_index_].GetValue()); 258 | 259 | std::cerr << "path print: "; 260 | for (int i = 0, sz = dfs_stack_.size(); i < sz; i++) { 261 | // if (i < 10 || (sz - i) < 10) 262 | fprintf(stderr, "pos(%d, %d) res %d cost %d-> ", pos_stack_[i][0], pos_stack_[i][1], 263 | res_stack_[i], CalcHcost(pos_stack_[i], dir_stack_[i])); 264 | } 265 | std::cerr << std::endl; 266 | 267 | if (g_ships[ship_index_].GetTar() != -1) { 268 | Position p = g_berths[g_ships[ship_index_].GetTar()].core_pos_; 269 | fprintf(stderr, "aim pos: (%d, %d)\n", p[0], p[1]); 270 | } 271 | } 272 | 273 | 274 | if (dfs_stack_.size() && CheckStop(pos_stack_.back(), dir_stack_.back())) { 275 | int res = res_stack_.front(); 276 | 277 | return (res == -1) ? rand() % 3 : res; 278 | } 279 | 280 | return rand() % 3; 281 | } 282 | 283 | OceanController g_ocean_controller; -------------------------------------------------------------------------------- /src/cargo.cpp: -------------------------------------------------------------------------------- 1 | #include "cargo.hpp" 2 | 3 | CargoManager g_cargo_manager; 4 | 5 | Cargo::Cargo(Position pos, int value, int appear_frame, std::shared_ptr dist_gird_ptr) 6 | : position_(pos), value_(value), appear_frame_(appear_frame), dist_grid_ptr_(dist_gird_ptr) { 7 | std::vector src = {position_}; 8 | } 9 | 10 | Cargo::Cargo(Cargo &&other) noexcept 11 | : value_(other.value_), 12 | appear_frame_(other.appear_frame_), 13 | position_(std::move(other.position_)), 14 | id_(other.id_), 15 | delete_(other.delete_), 16 | dist_grid_ptr_(std::move(other.dist_grid_ptr_)), 17 | is_occupied_(other.is_occupied_) { 18 | other.id_ = 0; 19 | other.delete_ = false; 20 | other.dist_grid_ptr_ = nullptr; 21 | other.is_occupied_ = false; 22 | } 23 | 24 | Cargo::~Cargo() { 25 | GridPool::GetInstance().Free(dist_grid_ptr_); 26 | } 27 | 28 | bool Cargo::IsVirtual() const noexcept { 29 | return is_virtual_; 30 | } 31 | 32 | int Cargo::GetValue() const noexcept { 33 | return value_; 34 | } 35 | 36 | int Cargo::GetID() const noexcept { 37 | return this->id_; 38 | } 39 | 40 | void Cargo::SetID(int id) noexcept { 41 | this->id_ = id; 42 | } 43 | 44 | int Cargo::GetDistance(Position pos) const noexcept { 45 | return dist_grid_ptr_->Get(pos); 46 | } 47 | 48 | int Cargo::GetDirection(Position pos) const noexcept { 49 | return dist_grid_ptr_->GetDescentDirection(pos); 50 | } 51 | 52 | const Grid &Cargo::GetDistanceGrid() const noexcept { 53 | return *dist_grid_ptr_; 54 | } 55 | 56 | void Cargo::Delete() noexcept { 57 | delete_ = true; 58 | } 59 | 60 | bool Cargo::IsDeleted() const noexcept { 61 | return delete_; 62 | } 63 | 64 | int Cargo::GetRemainingLife() const noexcept { 65 | return (appear_frame_ + 1000) - g_frame_id; 66 | } 67 | 68 | void Cargo::SetIsOccupied(bool be) noexcept { 69 | is_occupied_ = be; 70 | } 71 | 72 | bool Cargo::IsOccupied() const noexcept { 73 | return is_occupied_; 74 | } 75 | 76 | void *CargoManagerThreadFunc(void *arg) { 77 | CargoManager *g_cargo_manager = static_cast(arg); 78 | g_cargo_manager->InitCargoDistGrid(); 79 | return nullptr; 80 | } 81 | 82 | CargoManager::CargoManager() : cargo_exist_grid_(0) {} 83 | 84 | CargoManager::~CargoManager() { 85 | { 86 | std::lock_guard lg(task_list_mtx_); 87 | task_list_.clear(); 88 | } 89 | { 90 | std::lock_guard lg(ready_queue_mtx_); 91 | while (!ready_queue_.empty()) { 92 | ready_queue_.pop(); 93 | } 94 | } 95 | 96 | for (auto &thread : cargo_init_threads_) { 97 | thread.join(); 98 | } 99 | } 100 | 101 | void CargoManager::AddVirtualCargo(const Position &position, std::shared_ptr distance_grid_ptr) { 102 | int id = cargo_ptr_vec_.size(); 103 | cargo_ptr_vec_.push_back(std::make_shared(position, 0, -1, distance_grid_ptr)); 104 | cargo_ptr_vec_.back()->SetID(id); 105 | cargo_ptr_vec_.back()->is_virtual_ = true; 106 | } 107 | 108 | int CargoManager::tot_bfs_cnt_ = 0; 109 | int CargoManager::tot_erase_cnt_ = 0; 110 | 111 | void CargoManager::StartAsynTask() { 112 | for (int i = 0; i < CARGO_INIT_THREAD_NUM; i++) { 113 | cargo_init_threads_.emplace_back([this]() { 114 | while (true) { 115 | InitCargoDistGrid(); 116 | } 117 | }); 118 | } 119 | 120 | #if !defined(_WIN32) && !defined(_WIN64) 121 | // 绑定到 CPU 1 上 122 | for (auto &thread : cargo_init_threads_) { 123 | pthread_t handle = thread.native_handle(); 124 | cpu_set_t cpu_set; 125 | CPU_ZERO(&cpu_set); 126 | CPU_SET(1, &cpu_set); 127 | int ret = pthread_setaffinity_np(handle, sizeof(cpu_set), &cpu_set); 128 | assert(ret == 0); 129 | } 130 | #endif 131 | } 132 | 133 | void CargoManager::InputFrame() { 134 | // 读入这一帧新增的货物 135 | int cnt; 136 | std::cin >> cnt; 137 | g_cargo_num += cnt; 138 | 139 | while (cnt--) { 140 | int x, y, value; // NOLINT 141 | std::cin >> x >> y >> value; 142 | 143 | if (value == 0) { 144 | cargo_exist_grid_[x][y] = 0; 145 | continue; 146 | } 147 | 148 | // 只要是高级货,当帧立即搜索 149 | if (value > MIN_ADVANCED_CARGO_VALUE) { 150 | // std::cerr << "frame " << g_frame_id.load() << " advanced cargo " << value << std::endl; 151 | 152 | std::shared_ptr grid_ptr = GridPool::GetInstance().Alloc(); 153 | Position pos{x, y}; 154 | g_map.MultisourceBFS(std::vector{pos}, *grid_ptr, CARGO_BFS_MAX_DEPTH); 155 | CargoPtr cargo_ptr = std::make_shared(pos, value, g_frame_id.load(), grid_ptr); 156 | cargo_ptr_vec_.push_back(cargo_ptr); 157 | tot_bfs_cnt_ += 1; 158 | } 159 | 160 | cargo_exist_grid_[x][y] = 1; 161 | cargo_join_queue_.push(std::array{x, y, value, g_frame_id}); 162 | } 163 | } 164 | 165 | void CargoManager::UpdateAsynTaskList() { 166 | // 将货物缓存中的货物提交到任务队列 167 | if (task_list_mtx_.try_lock()) { 168 | while (!cargo_join_queue_.empty()) { 169 | auto [x, y, value, appear_frame_id] = cargo_join_queue_.front(); 170 | cargo_join_queue_.pop(); 171 | 172 | int dist_to_berth = INF; 173 | for (const auto &berth : g_berths) { 174 | dist_to_berth = std::min(dist_to_berth, berth.GetLandDistance(Position{x, y})); 175 | } 176 | 177 | task_list_.push_back(CargoMetadata(Position{x, y}, value, appear_frame_id, dist_to_berth)); 178 | } 179 | task_list_mtx_.unlock(); 180 | } 181 | 182 | // 将处理好的货物加入 vector 183 | if (ready_queue_mtx_.try_lock()) { 184 | int max_cnt = 10; 185 | while (!ready_queue_.empty()) { 186 | auto cargo_ptr = ready_queue_.front(); 187 | ready_queue_.pop(); 188 | if (cargo_exist_grid_.Get(cargo_ptr->position_)) { 189 | cargo_ptr_vec_.push_back(cargo_ptr); 190 | // fprintf(stderr, "\033[33mCargo Delay %d frames\033[0m", g_frame_id.load() - 191 | // cargo_ptr->appear_frame_); std::cerr << std::endl; 192 | } 193 | } 194 | ready_queue_mtx_.unlock(); 195 | } 196 | 197 | // 若货物超出数量上限,删掉价值最低的若干个 198 | std::sort(cargo_ptr_vec_.begin(), cargo_ptr_vec_.end(), [this](const CargoPtr &lhs, const CargoPtr &rhs) { 199 | // 虚拟点是一定不能被删掉的 200 | if (lhs->IsVirtual() && !rhs->IsVirtual()) { 201 | return true; 202 | } 203 | if (!lhs->IsVirtual() && rhs->IsVirtual()) { 204 | return false; 205 | } 206 | // 按价值排序 207 | return lhs->value_ > rhs->value_; 208 | }); 209 | 210 | while (cargo_ptr_vec_.size() > MAX_TOT_CARGO_NUM) { 211 | cargo_ptr_vec_.pop_back(); 212 | } 213 | } 214 | 215 | void CargoManager::RefreshCargos() { 216 | // 暂存其他队伍的机器人位置 217 | std::set otropo; 218 | for (auto &[id, robot] : g_other_robots) { 219 | otropo.insert(robot.position_); 220 | } 221 | 222 | // 刷新货物数组,扔掉剩余寿命不足或者已经被取走的货物 223 | std::vector cargos_new; 224 | for (const auto &cargo : cargo_ptr_vec_) { 225 | // 虚拟货物一定保留 226 | if (cargo->IsVirtual()) { 227 | cargos_new.push_back(cargo); 228 | continue; 229 | } 230 | 231 | if (cargo->GetRemainingLife() > 20 && !cargo->IsDeleted() && 232 | cargo_exist_grid_.Get(cargo->position_) == 1) { 233 | cargo->SetIsOccupied(otropo.count(cargo->position_)); 234 | cargos_new.push_back(cargo); 235 | } else { 236 | for (auto &[id, other_robot] : g_other_robots) { 237 | if (g_map.Manhattan(other_robot.position_, cargo->position_) <= 1 && 238 | other_robot.cargo_change_) { 239 | other_robot.cargo_value_.push(cargo->value_); 240 | } 241 | } 242 | } 243 | } 244 | std::swap(cargos_new, cargo_ptr_vec_); 245 | 246 | // 刷新货物 id 247 | int id = 0; 248 | for (auto &cargo : cargo_ptr_vec_) { 249 | cargo->SetID(id++); 250 | } 251 | } 252 | 253 | std::vector CargoManager::GetAllCargoPtr() const { 254 | #ifdef LOCAL_TEST 255 | /** 256 | * @test 检查一下所有的货物有没有重复 257 | */ 258 | std::set st; 259 | for (auto &cargo_ptr : cargo_ptr_vec_) { 260 | assert(st.count(cargo_ptr->GetID()) == 0); 261 | st.insert(cargo_ptr->GetID()); 262 | } 263 | #endif 264 | return cargo_ptr_vec_; 265 | } 266 | 267 | void CargoManager::InitCargoDistGrid() { 268 | std::optional cargo_metadata; 269 | { 270 | std::lock_guard lg(task_list_mtx_); 271 | 272 | // 删掉比较老的货物 273 | for (auto it = task_list_.begin(); it != task_list_.end();) { 274 | auto lst = it; 275 | it++; 276 | if (g_frame_id > lst->appear_frame_ + 50 || cargo_exist_grid_.Get(lst->position_) == 0) { 277 | task_list_.erase(lst); 278 | tot_erase_cnt_ += 1; 279 | } 280 | } 281 | 282 | // 没货物了就退出(原内存 bug) 283 | if (task_list_.empty()) { 284 | return; 285 | } 286 | 287 | // 选出一个合适的货物进行 bfs 288 | auto it = std::max_element(task_list_.begin(), task_list_.end(), 289 | [&](const CargoMetadata &lhs, const CargoMetadata &rhs) { 290 | return static_cast(lhs.value_) / lhs.dist_to_berth_ < 291 | static_cast(rhs.value_) / rhs.dist_to_berth_; 292 | }); 293 | 294 | cargo_metadata = *it; 295 | task_list_.erase(it); 296 | } 297 | 298 | // ? 及时 yield 或许会好一些 299 | std::this_thread::yield(); 300 | 301 | { 302 | #ifdef LOCAL_TIMER 303 | // 测试耗时 304 | // volatile Timer timer("Cargo BFS", 1); 305 | #endif // LOCAL_TIMER 306 | auto [pos, value, appear_frame, dist_to_berth] = cargo_metadata.value(); 307 | std::shared_ptr grid_ptr = GridPool::GetInstance().Alloc(); 308 | 309 | g_map.MultisourceBFS(std::vector{pos}, *grid_ptr, CARGO_BFS_MAX_DEPTH); 310 | CargoPtr cargo_ptr = std::make_shared(pos, value, appear_frame, grid_ptr); 311 | { 312 | std::lock_guard lg(ready_queue_mtx_); 313 | ready_queue_.push(cargo_ptr); 314 | } 315 | 316 | tot_bfs_cnt_ += 1; 317 | } 318 | 319 | // ? 及时 yield 或许会好一些 320 | std::this_thread::yield(); 321 | } 322 | 323 | std::vector g_virtual; -------------------------------------------------------------------------------- /src/ship.cpp: -------------------------------------------------------------------------------- 1 | #include "ship.hpp" 2 | 3 | std::vector g_ships; 4 | std::map g_ship_owned; 5 | std::map g_other_ships; 6 | 7 | Ship::Ship(int id, Position pos, int dir, int status) 8 | : id_(id), 9 | position_(pos), 10 | direction_(dir), 11 | capacity_(g_ship_capacity), 12 | volume_(g_ship_capacity), 13 | location_(-1), 14 | next_berth_(-1), 15 | status_(status) {} 16 | 17 | Ship::Ship(Ship &&other) noexcept 18 | : id_(other.id_), 19 | volume_(other.volume_), 20 | position_(std::move(other.position_)), 21 | direction_(other.direction_), 22 | capacity_(other.capacity_), 23 | status_(other.status_), 24 | next_berth_(other.next_berth_), 25 | location_(other.location_), 26 | value_token_(other.value_token_), 27 | instructions_(std::move(other.instructions_)) { 28 | other.direction_ = 0; 29 | other.capacity_ = 0; 30 | other.status_ = 0; 31 | other.next_berth_ = -1; 32 | other.location_ = 0; 33 | other.value_token_ = 0; 34 | } 35 | 36 | void Ship::InputFrame() { 37 | int id; 38 | std::cin >> id; 39 | g_ship_owned[id] = true; 40 | capacity_ = g_ship_capacity - g_other_ships[id].cargo_num_; 41 | position_ = g_other_ships[id].position_; 42 | direction_ = g_other_ships[id].direction_; 43 | status_ = g_other_ships[id].status_; 44 | 45 | for (auto &berth : g_berths) { 46 | if (!berth.InBerth(position_)) { 47 | continue; 48 | } 49 | for (auto berths : g_berth_group_manager.GetAllBerthGroup()) { 50 | if (!berths.group_member_.count(berth.id_)) { 51 | continue; 52 | } 53 | for (auto col : berths.group_member_) { 54 | if (col != berth.id_) g_berths[col].SetOurShip(false); 55 | } 56 | } 57 | berth.SetOurShip(true); 58 | } 59 | if (g_map.GetDeliveryPointsDist(position_, direction_) < 2 && capacity_ == volume_ && value_token_) { 60 | // std::cerr << "frame " << g_frame_id << " : clear value" << std::endl; 61 | ClearValue(); 62 | // next_berth_=0; 63 | } 64 | } 65 | 66 | void Ship::OutputFrame() { 67 | Instruction ins = GetOutputInstruction(); 68 | 69 | std::string s = ins.first; 70 | int p = ins.second; 71 | 72 | if (!EmptyInstructioins()) { 73 | instructions_.pop_back(); 74 | } 75 | 76 | if (s == "wait") { 77 | return; 78 | } 79 | 80 | std::cout << s << " " << id_; 81 | if (p != -1) { 82 | std::cout << " " << p; 83 | } 84 | std::cout << std::endl; 85 | } 86 | 87 | void Ship::Drive() { 88 | if (status_ == 1) { 89 | return; 90 | } 91 | if (status_ == 2 && capacity_ && g_berths[location_].CalcValue(1)) { 92 | Take(); 93 | return; 94 | } 95 | 96 | if (status_ == 2) { 97 | instructions_.push_back({"dept", -1}); 98 | return; 99 | } 100 | 101 | if (capacity_ * g_caplim < volume_) { 102 | next_berth_ = -1; 103 | int ins = g_map.GetDeliveryPointsMarch(position_, direction_); 104 | if (ins < 2) { 105 | instructions_.push_back({"rot", ins}); 106 | } else if (ins == 2) { 107 | instructions_.push_back({"ship", -1}); 108 | } 109 | return; 110 | } 111 | 112 | int ins = g_berths[next_berth_].GetOceanMarch(position_, direction_); 113 | if (ins < 2) { 114 | instructions_.push_back({"rot", ins}); 115 | } else if (ins == 2) { 116 | instructions_.push_back({"ship", -1}); 117 | } 118 | } 119 | 120 | 121 | void Ship::Arrive() { 122 | if (next_berth_ == -1) { 123 | return; 124 | } 125 | if (g_berths[next_berth_].InBerth(position_)) { 126 | location_ = next_berth_; 127 | instructions_.push_back({"berth", -1}); 128 | status_ = 1; 129 | if (location_ == -1) { 130 | capacity_ = volume_; 131 | } 132 | } 133 | } 134 | 135 | void Ship::PlanningFrame() { 136 | Arrive(); 137 | Drive(); 138 | } 139 | 140 | 141 | void Ship::Take() { 142 | std::pair take_result = g_berths[location_].TakeCargo(capacity_); 143 | capacity_ -= take_result.first; 144 | value_token_ += take_result.second; 145 | } 146 | 147 | Instruction Ship::CalculateInstruction() { 148 | if (status_ == 1) { 149 | return {"wait", -1}; 150 | } 151 | 152 | if (status_ == 0) { 153 | if (next_berth_ != -1 && g_berths[next_berth_].InBerth(position_)) { 154 | // berth 155 | SetLoadBerth(next_berth_); 156 | 157 | return {"berth", -1}; 158 | } else { 159 | // execute last instructions 160 | if (instructions_.size()) { 161 | Instruction current_instruction = instructions_.back(); 162 | instructions_.pop_back(); 163 | return current_instruction; 164 | } 165 | 166 | // move 167 | if (0) { 168 | // move to delivery points 169 | next_berth_ = -1; 170 | 171 | return ResToIns(g_map.GetDeliveryPointsMarch(position_, direction_)); 172 | } else { 173 | // move to target berth 174 | if (next_berth_ == -1) { 175 | return ResToIns(g_map.GetDeliveryPointsMarch(position_, direction_)); 176 | } else 177 | return ResToIns(g_berths[next_berth_].GetOceanMarch(position_, direction_)); 178 | } 179 | } 180 | } else if (status_ == 2) { 181 | // dept or take 182 | if (location_ == -1) { 183 | std::cerr << "location error" << std::endl; 184 | } 185 | 186 | if (capacity_ && next_berth_ != -1) { 187 | Take(); 188 | // g_berths[location_].SetOurShip(true); 189 | return {"wait", -1}; 190 | } else { 191 | // SetLoadBerth(-1); 192 | // g_berths[location_].SetOurShip(false); 193 | return {"dept", -1}; 194 | } 195 | 196 | // if (capacity_ && g_berths[location_].CalcValue(1)) { 197 | // Take(); 198 | 199 | // return {"wait", -1}; 200 | // } else { 201 | // // SetLoadBerth(-1); 202 | 203 | // return {"dept", -1}; 204 | // } 205 | } 206 | 207 | return {"wait", -1}; 208 | } 209 | 210 | 211 | int Ship::GetCaNum() const { 212 | return volume_ - capacity_; 213 | } 214 | 215 | Instruction Ship::ResToIns(int res) { 216 | if (res == 2) { 217 | return {"ship", -1}; 218 | } else { 219 | return {"rot", res}; 220 | } 221 | } 222 | 223 | 224 | int Ship::GetStatus() const { 225 | return status_; 226 | } 227 | 228 | int Ship::GetCap() const { 229 | return capacity_; 230 | } 231 | 232 | void Ship::SetTar(int val) { 233 | next_berth_ = val; 234 | } 235 | 236 | int Ship::GetTar() const { 237 | return next_berth_; 238 | } 239 | 240 | Position Ship::GetPostion() const { 241 | return position_; 242 | } 243 | 244 | ShipPositions Ship::GetPostions(Position pos, int dir) { 245 | int k = 0; 246 | std::array res; 247 | switch (dir) { 248 | case 0: 249 | for (int i = 0; i < 2; i++) { 250 | for (int j = 0; j < 3; j++) { 251 | res[k++] = pos + Position{i, j}; 252 | } 253 | } 254 | break; 255 | 256 | case 1: 257 | for (int i = -1; i < 1; i++) { 258 | for (int j = -2; j < 1; j++) { 259 | res[k++] = pos + Position{i, j}; 260 | } 261 | } 262 | break; 263 | 264 | case 2: 265 | for (int i = -2; i < 1; i++) { 266 | for (int j = 0; j < 2; j++) { 267 | res[k++] = pos + Position{i, j}; 268 | } 269 | } 270 | break; 271 | 272 | case 3: 273 | for (int i = 0; i < 3; i++) { 274 | for (int j = -1; j < 1; j++) { 275 | res[k++] = pos + Position{i, j}; 276 | } 277 | } 278 | break; 279 | } 280 | 281 | return res; 282 | } 283 | 284 | 285 | ShipPositions Ship::GetPostions() { 286 | return GetPostions(position_, direction_); 287 | } 288 | 289 | ShipPositions Ship::GetPostionsAfterInstruction(Instruction ins) { 290 | if (ins.first == "rot") { 291 | if (ins.second == 1) { 292 | return GetPostions(position_ + DIR[direction_] + DIR[direction_ ^ 3 ^ (direction_ >> 1)], 293 | direction_ ^ (2 | (direction_ >> 1))); 294 | } 295 | return GetPostions(position_ + DIR[direction_] + DIR[direction_], direction_ ^ 3 ^ (direction_ >> 1)); 296 | } else if (ins.first == "ship") { 297 | return GetPostions(position_ + DIR[direction_], direction_); 298 | } else { 299 | return GetPostions(); 300 | } 301 | } 302 | 303 | int Ship::GetDir() const { 304 | return direction_; 305 | } 306 | 307 | 308 | int Ship::Rot(int dir, int rot) { 309 | int ha = ROT_HASH[dir]; 310 | 311 | if (rot == 0) { 312 | ha = (ha + 1) % 4; 313 | } else { 314 | ha = (ha - 1 + 4) % 4; 315 | } 316 | 317 | return ROT_RANK[ha]; 318 | 319 | return ROT_RANK[(ROT_HASH[dir] + rot * rot + rot + 1) % 4]; 320 | } 321 | 322 | 323 | void Ship::AddInstruction(Instruction ins) { 324 | instructions_.push_back(ins); 325 | } 326 | 327 | void Ship::ClearInstruction() { 328 | instructions_.clear(); 329 | } 330 | 331 | Instruction Ship::GetOutputInstruction() { 332 | if (instructions_.size()) 333 | return instructions_.back(); 334 | else 335 | return {"wait", -1}; 336 | } 337 | 338 | void Ship::OutputInstructions() { 339 | for (Instruction &ins : instructions_) { 340 | std::cerr << ins.first << " "; 341 | if (ins.second != -1) std::cerr << ins.second << " "; 342 | } 343 | 344 | std::cerr << std::endl; 345 | } 346 | 347 | bool Ship::EmptyInstructioins() { 348 | return !instructions_.size(); 349 | } 350 | 351 | void Ship::SetLoadBerth(int berth_id) { 352 | location_ = berth_id; 353 | } 354 | 355 | int Ship::GetLoadBerth() { 356 | return location_; 357 | } 358 | 359 | bool Ship::IsFull() { 360 | return capacity_ * g_caplim < volume_; 361 | } 362 | 363 | int Ship::GetID() { 364 | return id_; 365 | } 366 | 367 | void Ship::AddValue(int num) { 368 | value_token_ += num; 369 | } 370 | 371 | void Ship::ClearValue() { 372 | value_token_ = 0; 373 | location_ = -1; 374 | // if (g_frame_id > 14000) 375 | // fprintf(stderr, "ship %d delivery at frame %d\n", id_, g_frame_id); 376 | // std::cerr << "ship " << std::endl; 377 | } 378 | 379 | int Ship::GetValue() const { 380 | return value_token_; 381 | } 382 | 383 | std::pair Ship::GetNextPD() { 384 | Position p = GetPostion(); 385 | int d = GetDir(); 386 | std::pair nxt[3]; 387 | nxt[0] = {p + DIR[d ^ 1], d}; 388 | nxt[1] = {p + DIR[d] + DIR[d ^ ((d >> 1) | 2)], d ^ 3 ^ (d >> 1)}; 389 | nxt[2] = {p + DIR[d ^ 3 ^ (d >> 1)] + DIR[d ^ 3 ^ (d >> 1)], d ^ ((d >> 1) | 2)}; 390 | 391 | Instruction ins = CalculateInstruction(); 392 | if (ins.first == "rot") { 393 | if (ins.second == 1) { 394 | return nxt[2]; 395 | } 396 | return nxt[1]; 397 | } else if (ins.first == "ship") { 398 | return nxt[0]; 399 | } else { 400 | return {position_, direction_}; 401 | } 402 | } -------------------------------------------------------------------------------- /src/frame.cpp: -------------------------------------------------------------------------------- 1 | #include "frame.hpp" 2 | 3 | static void CheckOK() { 4 | std::string s; 5 | std::cin >> s; 6 | assert(s == "OK"); 7 | } 8 | 9 | void Init() { 10 | // 初始化地图 11 | g_map.Init(); 12 | 13 | // 初始化泊位 14 | std::queue berth_metadata_que; 15 | std::cin >> g_num_berth; 16 | 17 | for (int i = 0; i < g_num_berth; i++) { 18 | int id, x, y, velocity; 19 | std::cin >> id >> x >> y >> velocity; 20 | berth_metadata_que.emplace(id, Position{x, y}, velocity); 21 | } 22 | g_num_berth = g_berths.size(); 23 | g_map.InitAllBerths(berth_metadata_que); 24 | 25 | // 寻找最佳连通块 26 | g_map.FindConnectedComponent(); 27 | 28 | // 船的容积 29 | std::cin >> g_ship_capacity; 30 | fprintf(stderr, "g_ship_capacity = %d\n", g_ship_capacity); 31 | 32 | CheckOK(); 33 | 34 | g_berth_group_manager.InitGroups(); 35 | 36 | g_berths[*g_berth_group_manager.GetAllBerthGroup()[ship_tar_hash[0]].group_member_.begin()].SetOurShip( 37 | true); 38 | 39 | std::cout << "OK" << std::endl; 40 | 41 | g_question_solver.StartAsynTask(); 42 | g_cargo_manager.StartAsynTask(); 43 | volatile auto &pool = GridPool::GetInstance(); // 避免编译器优化,提前分配 GridPool 的堆内存 44 | 45 | int virtual_cnt = 0; 46 | for (int i = 150; i <= 750; i += 50) { 47 | for (int j = 150; j <= 750; j += 50) { 48 | Position pos{i, j}; 49 | 50 | if (!g_map.IsField(pos)) { 51 | continue; 52 | } 53 | 54 | // 舍弃距离泊位不超过 200 的位置 55 | auto [berth_id, berth_dist] = g_map.FindNearestBerth(pos); 56 | if (berth_dist < 200) { 57 | continue; 58 | } 59 | 60 | virtual_cnt += 1; 61 | std::shared_ptr distance_grid_ptr = GridPool::GetInstance().Alloc(); 62 | g_map.MultisourceBFS(std::vector{pos}, *distance_grid_ptr); 63 | g_cargo_manager.AddVirtualCargo(pos, distance_grid_ptr); 64 | 65 | // std::cerr << "virtual point: " << i << " " << j << " distance to berth: " << berth_dist << 66 | // std::endl; 67 | } 68 | } 69 | assert(virtual_cnt < MAX_VIRTUAL_CARGO_NUM); 70 | 71 | std::cerr << "Init OK." << std::endl; 72 | } 73 | 74 | static void InputFrame() { 75 | // 读入货物 76 | g_cargo_manager.InputFrame(); 77 | 78 | // 读入所有机器人信息 79 | int total_robot_num; 80 | std::cin >> total_robot_num; 81 | for (int i = 0; i < total_robot_num; i++) { 82 | int id, num, x, y; 83 | OtherRobot rob; 84 | std::cin >> id >> num >> x >> y; 85 | rob.id_ = id; 86 | rob.cargo_num_ = num; 87 | g_robot_volume[id] = std::max(std::max(1, num), g_robot_volume[id]); 88 | rob.volume_ = g_robot_volume[id]; 89 | rob.position_ = Position{x, y}; 90 | rob.cargo_change_ = g_other_robots[id].cargo_num_ != num; 91 | rob.cargo_value_ = g_other_robots[id].cargo_value_; 92 | g_other_robots[id] = rob; 93 | } 94 | 95 | // 读入所有轮船信息 96 | int total_ship_num; 97 | std::cin >> total_ship_num; 98 | for (int i = 0; i < total_ship_num; i++) { 99 | int id, num, x, y, dir, sta; 100 | OtherShip shi; 101 | std::cin >> id >> num >> x >> y >> dir >> sta; 102 | shi.id_ = id; 103 | shi.cargo_num_ = num; 104 | shi.position_ = {x, y}; 105 | shi.direction_ = dir; 106 | shi.status_ = sta; 107 | shi.cargo_change_ = num - g_other_ships[id].cargo_num_; 108 | g_other_ships[id] = shi; 109 | } 110 | 111 | // 读入机器人 112 | std::cin >> g_robot_cnt; 113 | for (auto &robot : g_robots) { 114 | robot.InputFrame(); 115 | } 116 | for (int i = g_robots.size(); i < g_robot_cnt; i++) { 117 | int id; 118 | std::cin >> id; 119 | g_robot_owned[id] = true; 120 | Position pos = g_other_robots[id].position_; 121 | // std::cin >> id >> status >> x >> y; 122 | g_robots.emplace_back(i, pos, g_self_robot_volume[i]); 123 | } 124 | 125 | // 读入问题 126 | int question_num; 127 | std::cin >> question_num; 128 | for (int i = 0; i < question_num; i++) { 129 | int robot_private_id; 130 | std::string question; 131 | std::cin >> robot_private_id; 132 | std::getline(std::cin, question); 133 | // std::cerr << robot_private_id << " " << question << "\n"; 134 | g_robots[robot_private_id].AskQuestion(question); 135 | #ifdef LOCAL_DEBUG_LLM 136 | const auto &robot = g_robots[robot_private_id]; 137 | CargoPtr cargo_ptr = robot.GetCargoPtr(); 138 | if (cargo_ptr == nullptr || robot.GetPosition() != cargo_ptr->position_) { 139 | fprintf(stderr, "robot id = %d\n", robot.id_); 140 | } 141 | assert(cargo_ptr != nullptr); 142 | assert(robot.GetPosition() == cargo_ptr->position_); 143 | #endif 144 | } 145 | 146 | // for(auto &berth:g_berths){ 147 | // berth.SetOurShip(false); 148 | // } 149 | 150 | // 读入轮船 151 | std::cin >> g_ship_cnt; 152 | for (auto &ship : g_ships) { 153 | ship.InputFrame(); 154 | } 155 | for (int i = g_ships.size(); i < g_ship_cnt; i++) { 156 | int id, num_cargos, dir, status; 157 | std::cin >> id; 158 | Position pos; 159 | num_cargos = g_other_ships[id].cargo_num_; 160 | pos = g_other_ships[id].position_; 161 | dir = g_other_ships[id].direction_; 162 | status = g_other_ships[id].status_; 163 | g_ships.emplace_back(i, pos, dir, status); 164 | } 165 | 166 | for (auto &i : g_robot_owned) { 167 | g_other_robots.erase(i.first); 168 | } 169 | for (auto &i : g_ship_owned) { 170 | g_other_ships.erase(i.first); 171 | } 172 | 173 | g_cargo_manager.RefreshCargos(); 174 | 175 | for (auto &berth : g_berths) { 176 | berth.AddShipNum(-berth.GetShipNum()); 177 | for (auto &i : g_other_ships) { 178 | if (berth.InBerth(i.second.position_)) { 179 | berth.AddShipNum(1); 180 | } 181 | } 182 | } 183 | 184 | CheckOK(); 185 | } 186 | 187 | static void PlanFrame() { 188 | Timer timer("Plan Frame", 15); 189 | g_land_scheduler.ScheduleRobots(); 190 | g_ocean_scheduler.ScheduleShipsTemp(); 191 | g_land_controller.ControlRobots(); 192 | g_ocean_controller.ControlShips(); 193 | } 194 | 195 | static void OutputFrame() { 196 | for (auto &[id, other_robot] : g_other_robots) { 197 | if (other_robot.cargo_value_.empty()) { 198 | continue; 199 | } 200 | for (auto &berth : g_berths) { 201 | if (berth.InBerth(other_robot.position_)) { 202 | while (!other_robot.cargo_value_.empty()) { 203 | berth.AddCargos(other_robot.cargo_value_.top()); 204 | other_robot.cargo_value_.pop(); 205 | } 206 | } 207 | } 208 | } 209 | 210 | for (auto &[id, other_ship] : g_other_ships) { 211 | if (other_ship.cargo_change_ <= 0) { 212 | continue; 213 | } 214 | for (auto &berth : g_berths) { 215 | if (berth.InBerth(other_ship.position_)) { 216 | berth.TakeCargo(other_ship.cargo_change_); 217 | } 218 | } 219 | } 220 | 221 | for (auto &robot : g_robots) { 222 | robot.OutputFrame(); 223 | } 224 | for (auto &ship : g_ships) { 225 | ship.OutputFrame(); 226 | } 227 | } 228 | 229 | static void BuyRobotShip() { 230 | const std::vector &all_berth_groups = g_berth_group_manager.GetAllBerthGroup(); 231 | int all_berth_group_num = all_berth_groups.size(); 232 | 233 | int gsc = g_ship_cnt % all_berth_group_num; 234 | int gscm1 = (gsc - 1 + all_berth_group_num) % all_berth_group_num; 235 | 236 | // 希望在这个泊位附近买船 237 | int buy_ship_berth_id = *all_berth_groups[ship_tar_hash[gsc]].group_member_.begin(); 238 | 239 | // 希望在这个泊位附近买机器人 240 | int buy_robot_berth_id = *all_berth_groups[ship_tar_hash[gscm1]].group_member_.begin(); 241 | 242 | // 买船 243 | if (MAX_SHIP_NUM > 0) { 244 | // 买第一条船 245 | if (g_frame_id % 5 == 0 && g_ship_cnt == 0 && g_money >= 8000) { 246 | fprintf(stderr, "frame %d, buy a ship\n", g_frame_id.load()); 247 | g_map.BuyShip(buy_ship_berth_id); 248 | } 249 | 250 | // 买其他船 251 | if (g_frame_id % 10 == 0 && g_ship_cnt < MAX_SHIP_NUM && g_money >= 8000 && 252 | (g_robot_cnt / (g_ship_cnt + 1) >= MAX_ROBOT_NUM / (MAX_SHIP_NUM + 1) || 253 | g_robot_cnt >= MAX_ROBOT_NUM)) { 254 | fprintf(stderr, "frame %d, buy a ship\n", g_frame_id.load()); 255 | g_map.BuyShip(buy_ship_berth_id); 256 | } 257 | } 258 | 259 | // 买机器人 260 | if (MAX_ROBOT_NUM > 0) { 261 | // 第一个机器人一定是另一种 262 | if (g_robot_cnt <= 0 && g_robot_cnt < MAX_ROBOT_NUM && g_frame_id < 5) { 263 | g_map.BuyRobot(ROBOT_TYPE ^ 1, buy_ship_berth_id); 264 | g_self_robot_volume[g_robot_cnt] = (ROBOT_TYPE ^ 1) + 1; 265 | } 266 | 267 | // 买其他机器人 268 | if (g_robot_cnt < MAX_ROBOT_NUM && g_frame_id >= 5 && g_frame_id % 2 == 0 && g_money >= 2000) { 269 | // 希望按比例买机器人 270 | if ((g_robot_cnt / (g_ship_cnt + 1) < MAX_ROBOT_NUM / (MAX_SHIP_NUM + 1) || g_frame_id <= 100 || 271 | g_ship_cnt >= MAX_SHIP_NUM)) { 272 | g_map.BuyRobot(ROBOT_TYPE, buy_robot_berth_id); 273 | // 记录购买的机器人设定的容量。 274 | g_self_robot_volume[g_robot_cnt] = ROBOT_TYPE + 1; 275 | } 276 | } 277 | } 278 | 279 | if (g_frame_id > 2500) { 280 | g_caplim = 99; 281 | } 282 | } 283 | 284 | void Control() { 285 | int cur_frame_id = 0; 286 | 287 | std::vector berth_cargo_cnt(g_berths.size(), 0); 288 | int last_robot_value = 0; 289 | 290 | int frame_id; 291 | 292 | while (std::cin >> frame_id) { 293 | cur_frame_id++; 294 | g_frame_id = frame_id; 295 | 296 | if (g_frame_id % DEBUG_PRINT_FREQUENCY == 0) { 297 | fprintf(stderr, 298 | "[frame]%d [processed_frame]%d [money]%d [robot]%d [ship]%d [value_to_berth]%d " 299 | "[task_queue_size]%d [cargo_manager_size]%d", 300 | g_frame_id.load(), cur_frame_id, g_money, g_robot_cnt, g_ship_cnt, Robot::value_sent_, 301 | (int)g_question_solver.Size(), (int)g_cargo_manager.cargo_ptr_vec_.size()); 302 | std::cerr << std::endl; 303 | std::cerr << "CargoManager Total Init Cargos Num: " << CargoManager::tot_bfs_cnt_ << std::endl; 304 | std::cerr << "CargoManager Total Erase Cargos Num: " << CargoManager::tot_erase_cnt_ << std::endl; 305 | 306 | std::cerr << "ship value: "; 307 | for (Ship &ship : g_ships) { 308 | std::cerr << ship.GetValue() << " "; 309 | } 310 | std::cerr << std::endl; 311 | } 312 | std::cin >> g_money; 313 | 314 | InputFrame(); 315 | PlanFrame(); 316 | OutputFrame(); 317 | BuyRobotShip(); 318 | 319 | std::cout << "OK" << std::endl; 320 | 321 | // 在一帧的输出都结束后,再进行 cargo_manager 的任务队列的处理 322 | g_cargo_manager.UpdateAsynTaskList(); 323 | } 324 | 325 | fprintf(stderr, "Process %d frames.\n", cur_frame_id); 326 | fprintf(stderr, "Value of cargos sent to g_berths: %d.\n", Robot::value_sent_); 327 | fprintf(stderr, "Value of cargos token by g_ships: %d.\n", g_value_token); 328 | fprintf(stderr, "Total buy %d g_robots, %d g_ships.\n", g_robot_cnt, g_ship_cnt); 329 | fprintf(stderr, "Total %d cargos appear.\n", g_cargo_num); 330 | fprintf(stderr, "Total earn %d g_money\n", g_money - 25000 + g_robot_cnt * 2000 + g_ship_cnt * 8000); 331 | 332 | for (const auto &berth : g_berths) { 333 | fprintf(stderr, "Berth %d: value %d\tnum %d.\n", berth.id_, berth.tot_value, berth.tot_cnt); 334 | } 335 | for (const auto &ship : g_ships) { 336 | fprintf(stderr, "Ship %d remain %d cargos.\n", ship.id_, ship.GetCaNum()); 337 | } 338 | 339 | g_question_solver.Kill(); 340 | } -------------------------------------------------------------------------------- /src/ocean_scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "ocean_scheduler.hpp" 2 | 3 | ShipPlanMaker::ShipPlanMaker() { 4 | for (int i = 0; i < g_berths.size(); i++) 5 | berths_id_.push_back(i); 6 | 7 | ship_id_ = 0; 8 | } 9 | 10 | ShipPlanMaker::ShipPlanMaker(int ship_id) : ship_id_(ship_id) { 11 | for (int i = 0; i < g_berths.size(); i++) 12 | berths_id_.push_back(i); 13 | } 14 | 15 | ShipPlanMaker::ShipPlanMaker(int ship_id, std::vector berths_id) 16 | : ship_id_(ship_id), berths_id_(berths_id) {} 17 | 18 | void ShipPlanMaker::Clear() { 19 | permutation_.clear(); 20 | } 21 | 22 | void ShipPlanMaker::ResetBerthIDs(std::vector berths_id) { 23 | berths_id_.assign(berths_id.begin(), berths_id.end()); 24 | Clear(); 25 | } 26 | 27 | void ShipPlanMaker::RandomGlobal() { 28 | int n = berths_id_.size(); 29 | 30 | permutation_.clear(); 31 | 32 | for (int i = 0; i < n; ++i) { 33 | permutation_.push_back(i); // 生成初始序列 1, 2, ..., n 34 | } 35 | 36 | for (int i = 1; i < n; i++) { 37 | int p = rand() % i; 38 | 39 | std::swap(permutation_[p], permutation_[i]); 40 | } 41 | 42 | // std::random_device rd; 43 | // std::mt19937 g(rd()); 44 | 45 | // std::shuffle(permutation_.begin(), permutation_.end(), g); // 随机打乱序列 46 | 47 | permutation_.resize(rand() % n + 1); // 只取前 k 个数作为排列 48 | } 49 | 50 | void ShipPlanMaker::RandomLocal() { 51 | int n = permutation_.size(); 52 | int m = berths_id_.size(); 53 | 54 | if (rand() % 10 < 8 && n > 1) { 55 | int u = rand() % n; 56 | int v = rand() % (n - 1); 57 | 58 | v += (v >= u); 59 | 60 | // fprintf(stderr, "swap pair (%d, %d)\n", u, v); 61 | 62 | std::swap(permutation_[u], permutation_[v]); 63 | } else if (rand() % 10 < 6 && n < m) { 64 | std::vector appear(m, 0); 65 | int p; 66 | 67 | for (int i : permutation_) 68 | appear[i] = 1; 69 | 70 | for (p = 0; p < m && appear[p]; p++) 71 | ; 72 | 73 | // fprintf(stderr, "add %d\n", p); 74 | 75 | permutation_.push_back(p); 76 | } else { 77 | if (n > 0) 78 | permutation_.pop_back(); 79 | else 80 | RandomLocal(); 81 | } 82 | } 83 | 84 | void ShipPlanMaker::GetLocalBest() { 85 | std::vector local_best(permutation_); 86 | double local_best_value = CalcValue(); 87 | 88 | for (int i = 0; i < 100; i++) { 89 | RandomLocal(); 90 | 91 | double current_value = CalcValue(); 92 | 93 | if (current_value < local_best_value) { 94 | permutation_.assign(local_best.begin(), local_best.end()); 95 | } else { 96 | local_best_value = current_value; 97 | local_best.assign(permutation_.begin(), permutation_.end()); 98 | } 99 | } 100 | } 101 | 102 | void ShipPlanMaker::GetGlobalBestTSP() { 103 | Clear(); 104 | std::vector global_best(permutation_); 105 | double global_best_value = CalcValue(); 106 | 107 | for (int i = 0; i < 100; i++) { 108 | RandomGlobal(); 109 | GetLocalBest(); 110 | 111 | double current_value = CalcValue(); 112 | 113 | if (current_value > global_best_value) { 114 | global_best.assign(permutation_.begin(), permutation_.end()); 115 | global_best_value = current_value; 116 | } 117 | } 118 | 119 | permutation_.assign(global_best.begin(), global_best.end()); 120 | } 121 | 122 | void ShipPlanMaker::GetGlobalBestForce() { 123 | std::vector global_best; 124 | double global_best_value = 0; 125 | 126 | for (int k = 0; k < (1 << berths_id_.size()); k++) { 127 | permutation_.clear(); 128 | 129 | for (int i = 0; i < berths_id_.size(); i++) 130 | if ((k >> i) & 1) permutation_.push_back(i); 131 | 132 | do { 133 | double current_value = CalcValue(); 134 | 135 | if (current_value > global_best_value) { 136 | global_best.assign(permutation_.begin(), permutation_.end()); 137 | global_best_value = current_value; 138 | } 139 | } while (std::next_permutation(permutation_.begin(), permutation_.end())); 140 | } 141 | 142 | permutation_.assign(global_best.begin(), global_best.end()); 143 | } 144 | 145 | void ShipPlanMaker::GetGlobalBest() { 146 | if (berths_id_.size() < 9) { 147 | GetGlobalBestForce(); 148 | } else { 149 | GetGlobalBestTSP(); 150 | } 151 | } 152 | 153 | int ShipPlanMaker::GetNextBerth() { 154 | GetGlobalBest(); 155 | 156 | return (permutation_.size()) ? berths_id_[permutation_.front()] : -1; 157 | } 158 | 159 | double ShipPlanMaker::CalcValue() { 160 | int cframe = g_frame_id; 161 | 162 | // Ship& ship = g_ships[ship_id_]; 163 | Position cpos = g_ships[ship_id_].GetPostion(); 164 | int cdir = g_ships[ship_id_].GetDir(); 165 | int ccap = g_ships[ship_id_].GetCap(); 166 | int cvalue = g_ships[ship_id_].GetValue(); 167 | static int recover_time = 10; 168 | 169 | // cframe += g_map.GetDeliveryPointsDist(cpos, cdir); 170 | 171 | for (int i : permutation_) { 172 | // transfer to real berth id 173 | i = berths_id_[i]; 174 | // go to berth 175 | cframe += g_berths[i].GetOceanDistance(cpos, cdir) + recover_time; 176 | cpos = g_berths[i].core_pos_; 177 | cdir = -1; 178 | 179 | // load cargo 180 | int take_cargo_num = std::min(ccap, g_berths[i].GetCargoNum()); 181 | // cvalue += take_cargo_num; 182 | cvalue += g_berths[i].CalcValue(take_cargo_num) * (!!take_cargo_num); 183 | cframe += take_cargo_num / g_berths[i].velocity_ + (take_cargo_num % g_berths[i].velocity_ != 0); 184 | ccap -= take_cargo_num; 185 | } 186 | 187 | // go to delivery point 188 | cframe += g_map.GetDeliveryPointsDist(cpos, cdir); 189 | 190 | return (cframe > MAX_FRAME_NUM - 6) ? -INF : 1.0 * cvalue / (cframe - g_frame_id + 1); 191 | } 192 | 193 | void ShipPlanMaker::Print() { 194 | int cframe = g_frame_id; 195 | 196 | Ship &ship = g_ships.front(); 197 | Position cpos = ship.GetPostion(); 198 | int cdir = ship.GetDir(); 199 | int ccap = ship.GetCap(); 200 | int cvalue = ship.GetValue(); 201 | static int recover_time = 10; 202 | 203 | std::cerr << "frame: " << g_frame_id << std::endl; 204 | 205 | for (int i : permutation_) { 206 | i = berths_id_[i]; 207 | 208 | // go to berth 209 | cframe += g_berths[i].GetOceanDistance(cpos, cdir) + recover_time; 210 | cpos = g_berths[i].core_pos_; 211 | cdir = -1; 212 | 213 | // load cargo 214 | int take_cargo_num = std::min(ccap, g_berths[i].GetCargoNum()); 215 | cvalue += g_berths[i].CalcValue(take_cargo_num) * (!!take_cargo_num); 216 | cframe += take_cargo_num / g_berths[i].velocity_ + (take_cargo_num % g_berths[i].velocity_ != 0); 217 | ccap -= take_cargo_num; 218 | 219 | fprintf(stderr, "p frame %d: berth %d; cap %d; value %d\n", cframe, i, ccap, cvalue); 220 | } 221 | 222 | // go to delivery point 223 | cframe += g_map.GetDeliveryPointsDist(cpos, cdir); 224 | 225 | fprintf(stderr, "p frame %d: sent; cap %d; value %d; v: %.3f\n", cframe, ccap, cvalue, 226 | 1.0 * cvalue / (cframe - g_frame_id + 1)); 227 | } 228 | 229 | void OceanScheduler::ShipDFS(double now, int j, double &sum, std::vector &tmp, std::vector &res) { 230 | if (j >= g_ships.size()) { 231 | if (now > sum) { 232 | sum = now; 233 | res = tmp; 234 | } 235 | return; 236 | } 237 | if (g_ships[j].GetStatus() != 2 && g_ships[j].GetCap() * g_caplim >= g_ship_capacity) { 238 | for (auto &i : g_berths) { 239 | bool used = false; 240 | for (auto k : tmp) { 241 | if (k == i.id_) { 242 | used = true; 243 | break; 244 | } 245 | } 246 | if (used) continue; 247 | tmp.push_back(i.id_); 248 | // std::cerr<<'/'< max_cargo_num) { 356 | max_cargo_num = cur_cargo_num; 357 | max_set_id = set_id; 358 | } 359 | } 360 | 361 | // max_set_id = 0; 362 | 363 | ship_berth_set_[ship_id] = max_set_id; 364 | 365 | if (max_set_id != -1) { 366 | used_set_[max_set_id] = 1; 367 | } 368 | } 369 | 370 | int ShipsPlanMaker::GetBerthSetCargoNum(int set_id) { 371 | int sum = 0; 372 | 373 | for (int bid : g_berth_group_manager.GetAllBerthGroup()[set_id].group_member_) { 374 | sum += g_berths[bid].GetCargoNum(); 375 | } 376 | 377 | return sum; 378 | } 379 | 380 | int ShipsPlanMaker::StepToBerthID(int step, int set_id) { 381 | std::vector berth_set; 382 | for (auto &berth_id : g_berth_group_manager.GetAllBerthGroup()[set_id].group_member_) { 383 | if (g_berths[berth_id].GetShipNum() == 0) { 384 | berth_set.push_back(berth_id); 385 | } 386 | } 387 | 388 | int set_size = berth_set.size(); 389 | // int set_size = berth_sets_[set_id].size(); 390 | 391 | if (set_size <= 1) { 392 | return 0; 393 | } 394 | 395 | int k = step % (set_size * 2 - 2); 396 | 397 | if (k >= set_size) { 398 | k = (set_size * 2 - 2) - k; 399 | } 400 | 401 | return berth_set[k]; 402 | } 403 | 404 | int ShipsPlanMaker::GetNextBerthTemp(int ship_id) { 405 | if (g_ships[ship_id].GetCap() == 0) { 406 | return -1; 407 | } 408 | if (g_ships[ship_id].GetCaNum() && 409 | g_map.GetDeliveryPointsDist(g_ships[ship_id].GetPostion(), g_ships[ship_id].GetDir()) > 410 | MAX_FRAME_NUM - 50 - g_frame_id) { 411 | return -1; 412 | } 413 | int id = ship_tar_hash[ship_id % g_berth_group_manager.GetAllBerthGroup().size()]; 414 | int tarber = *g_berth_group_manager.GetAllBerthGroup()[id].group_member_.begin(); 415 | int ttvl = -INF; 416 | if (g_ships[ship_id].GetTar() != -1 && g_berths[g_ships[ship_id].GetTar()].GetShipNum() == 0) { 417 | tarber = g_ships[ship_id].GetTar(); 418 | ttvl = ttvl = g_berths[tarber].tot_value * 1.2; 419 | } 420 | for (auto &berth : g_berth_group_manager.GetAllBerthGroup()[id].group_member_) { 421 | if (g_berths[berth].GetShipNum() == 0) { 422 | if (g_berths[berth].tot_value > ttvl) { 423 | tarber = berth; 424 | ttvl = g_berths[berth].tot_value; 425 | } 426 | } 427 | } 428 | return tarber; 429 | } 430 | 431 | int ShipsPlanMaker::GetNextBerth(int ship_id) { 432 | if (g_ships[ship_id].GetCap() == 0) { 433 | return -1; 434 | } 435 | 436 | int set_id = ship_berth_set_[ship_id]; 437 | 438 | if (set_id == -1 || GetBerthSetCargoNum(set_id) == 0) { 439 | SetBerthSet(ship_id); 440 | } 441 | 442 | if (set_id == -1) { 443 | return -1; 444 | } else { 445 | int cur_next_berth_id = StepToBerthID(step_[ship_id], set_id); 446 | 447 | step_[ship_id] += (g_berths[cur_next_berth_id].GetCargoNum() == 0); 448 | 449 | return StepToBerthID(step_[ship_id], set_id); 450 | } 451 | 452 | std::cerr << "next berth ok" << std::endl; 453 | } 454 | 455 | void ShipsPlanMaker::Print() { 456 | std::cerr << "set info" << std::endl; 457 | 458 | std::cerr << "set cargo num: "; 459 | for (int i = 0; i < g_berth_group_manager.GetAllBerthGroup().size(); i++) { 460 | std::cerr << GetBerthSetCargoNum(i) << " "; 461 | } 462 | std::cerr << std::endl; 463 | 464 | std::cerr << "ship set: "; 465 | for (int i = 0; i < GetShipNum(); i++) { 466 | std::cerr << ship_berth_set_[i] << " "; 467 | } 468 | std::cerr << std::endl; 469 | } 470 | 471 | OceanScheduler g_ocean_scheduler; 472 | ShipsPlanMaker sspm; -------------------------------------------------------------------------------- /src/land_controller.cpp: -------------------------------------------------------------------------------- 1 | #include "land_controller.hpp" 2 | 3 | 4 | RobotMotionState::RobotMotionState(Position pos, std::map dire_val) 5 | : position_(pos), dire_val_(dire_val) {} 6 | 7 | void LandController::ControlRobots() { 8 | #ifdef LOCAL_TIMER 9 | Timer("LandController::ControlRobots", 2); 10 | #endif 11 | std::vector motion_states = GetRobotMotionState(); 12 | std::vector> groups = GetRobotGroups(); 13 | 14 | for (const auto &group : groups) { 15 | #ifdef USE_DFS 16 | // 如果该组只有一个机器人,直接选择最好的方向 17 | if (group.size() == 1) { 18 | int x = group[0]; 19 | std::map dire_val_ = motion_states[x].dire_val_; 20 | assert(!dire_val_.empty()); 21 | auto best = dire_val_.begin(); 22 | for (auto it = dire_val_.begin(); it != dire_val_.end(); it++) { 23 | if (it->second > best->second) { 24 | best = it; 25 | } else if (it->second == best->second && rand() % 2) { 26 | // 加入随机化 27 | best = it; 28 | } 29 | } 30 | g_robots[x].SetMoveDirection(best->first); 31 | } 32 | 33 | // 如果该组有多个机器人,那么调用 AvoidCrash,递归搜索 34 | if (group.size() >= 2) { 35 | std::vector group_states; 36 | for (int id : group) { 37 | group_states.push_back(motion_states[id]); 38 | } 39 | std::vector directions = AvoidCrash(group_states); 40 | for (int i = 0; i < group.size(); i++) { 41 | int id = group[i]; 42 | g_robots[id].SetMoveDirection(directions[i]); 43 | } 44 | } 45 | 46 | #endif 47 | 48 | #ifdef DONT_USE_DFS 49 | for (int i = 0; i < group.size(); i++) { 50 | int x = group[i]; 51 | std::map dire_val_ = motion_states[x].dire_val_; 52 | assert(dire_val_.size() > 1); 53 | auto best = dire_val_.begin(); 54 | for (auto it = dire_val_.begin(); it != dire_val_.end(); it++) { 55 | if (it->second > best->second) { 56 | best = it; 57 | } else if (it->second == best->second && rand() % 2) { 58 | // 加入随机化 59 | best = it; 60 | } 61 | } 62 | g_robots[x].SetMoveDirection(best->first); 63 | } 64 | #endif 65 | } 66 | 67 | #ifdef DONT_USE_DFS 68 | for (int i = 0; i < g_robot_cnt; i++) { 69 | WorseAvoidCrash(i, 0); 70 | } 71 | #endif 72 | } 73 | 74 | std::map LandController::GetDirectionMap(const Robot &robot) { 75 | std::map direction_map; 76 | 77 | // 如果正在等待大模型,那么必须原地不动! 78 | if (robot.IsWaitingForLLM()) { 79 | direction_map[4] = 1e7; 80 | return direction_map; 81 | } 82 | 83 | // 这个机器人漫无目的,去哪里都行 84 | if (robot.FullCargo() && robot.GetTargetBerthID() == -1 || 85 | !robot.FullCargo() && robot.GetCargoPtr() == nullptr) { 86 | for (int i = 0; i < 5; i++) { 87 | Position cur_pos = robot.GetPosition(); 88 | Position nxt_pos = cur_pos + DIR[i]; 89 | if (g_map.IsField(nxt_pos) && !dangerous_position_->Get(nxt_pos)) { 90 | direction_map[i] = 0; 91 | } 92 | } 93 | 94 | return direction_map; 95 | } 96 | 97 | // 获取这个机器人的目的地的距离矩阵 98 | auto get_dist_grid = [&]() -> const Grid & { 99 | if (robot.GetAddtionalTarget() != nullptr) { 100 | return robot.GetAddtionalTarget()->GetDistanceGrid(); 101 | } 102 | if (robot.FullCargo()) { 103 | int berth_id = robot.GetTargetBerthID(); 104 | return g_berths[berth_id].GetLandDistanceGrid(); 105 | } else { 106 | CargoPtr cargo_ptr = robot.GetCargoPtr(); 107 | return cargo_ptr->GetDistanceGrid(); 108 | } 109 | }; 110 | 111 | // 目标的距离矩阵 112 | const Grid &distance_grid = get_dist_grid(); 113 | 114 | // 临时 BFS 的最大距离,单个机器人搜的面积不会超过 4 * MAX_BFS_DEPTH * MAX_BFS_DEPTH 115 | const int MAX_BFS_DEPTH = 7; 116 | 117 | auto bfs = [&](Position start_pos, int max_depth) -> int { 118 | // 记录每个点与机器人之间的距离 119 | std::map dist_robot; 120 | dist_robot[start_pos] = 0; 121 | 122 | // 维护 BFS 时的一个状态 123 | struct BFSState { 124 | Position position_; 125 | int dist_; 126 | }; 127 | 128 | std::queue que; 129 | que.push(BFSState{start_pos, 0}); 130 | int res = INF; 131 | 132 | #ifdef LOCAL_TEST 133 | std::set st; 134 | #endif 135 | 136 | while (!que.empty()) { 137 | auto [cur_pos, cur_dist] = que.front(); 138 | #ifdef LOCAL_TEST 139 | if (res == distance_grid.Get(cur_pos)) { 140 | st.insert(cur_pos); 141 | } else if (res > distance_grid.Get(cur_pos)) { 142 | st.clear(); 143 | st.insert(cur_pos); 144 | } 145 | #endif 146 | res = std::min(res, distance_grid.Get(cur_pos)); 147 | if (res == 0) { 148 | return res; 149 | } 150 | que.pop(); 151 | if (dist_robot[cur_pos] < cur_dist) { 152 | continue; 153 | } 154 | 155 | for (int i = 0; i < 4; i++) { 156 | Position nxt_pos = cur_pos + DIR[i]; 157 | int nxt_dist = cur_dist + 1; 158 | if (nxt_dist > max_depth) { 159 | continue; 160 | } 161 | if (!g_map.IsField(nxt_pos) || dangerous_position_->Get(nxt_pos)) { 162 | continue; 163 | } 164 | if (!dist_robot.count(nxt_pos) || nxt_dist < dist_robot[nxt_pos]) { 165 | dist_robot[nxt_pos] = nxt_dist; 166 | que.push({nxt_pos, nxt_dist}); 167 | } 168 | } 169 | } 170 | 171 | #ifdef LOCAL_TEST 172 | /** 173 | * @test 测试 st 中的所有点,到目标的距离都是 res 174 | */ 175 | for (auto pos : st) { 176 | assert(distance_grid.Get(pos) == res); 177 | } 178 | #endif 179 | 180 | return res; 181 | }; 182 | 183 | Position cur_pos = robot.GetPosition(); 184 | #ifdef LOCAL 185 | assert(g_map.IsField(cur_pos)); 186 | assert(direction_map.empty()); 187 | #endif 188 | direction_map[4] = 0; 189 | int cur_dist = distance_grid.Get(cur_pos); 190 | 191 | for (int i = 0; i < 5; i++) { 192 | Position nxt_pos = cur_pos + DIR[i]; 193 | if (!g_map.IsField(nxt_pos) || dangerous_position_->Get(nxt_pos)) { 194 | continue; 195 | } 196 | 197 | int bfs_depth = (cur_dist <= MAX_BFS_DEPTH + 3 ? 0 : MAX_BFS_DEPTH - 1); 198 | int benefit = bfs(nxt_pos, bfs_depth); 199 | if (benefit == 0) { 200 | benefit = MAX_BFS_DEPTH; 201 | } else { 202 | benefit = cur_dist - benefit; 203 | } 204 | // direction_map[i] = benefit; 205 | // 这里使用了有货让无货机制 206 | direction_map[i] = benefit * (robot.FullCargo() ? 1 : 100); 207 | } 208 | 209 | for (auto [d, v] : direction_map) { 210 | if (d != 4) { 211 | direction_map[d] -= direction_map[4]; 212 | } 213 | } 214 | direction_map[4] = 0; 215 | 216 | return direction_map; 217 | } 218 | 219 | std::vector LandController::GetRobotMotionState() { 220 | // 更新 dangerous_position 221 | dangerous_position_->Set(0); 222 | 223 | for (const auto &[id, other_robot] : g_other_robots) { 224 | #ifdef LOCAL 225 | for (const auto &robot : g_robots) { 226 | assert(other_robot.position_ != robot.GetPosition()); 227 | } 228 | #endif 229 | Position cur_pos = other_robot.position_; 230 | 231 | for (int i = -ROBOT_DANGEROUS_RANGE; i <= ROBOT_DANGEROUS_RANGE; i++) { 232 | for (int j = -ROBOT_DANGEROUS_RANGE; j <= ROBOT_DANGEROUS_RANGE; j++) { 233 | Position nxt_pos = cur_pos + Position{i, j}; 234 | 235 | if (!g_map.IsLandMainLine(nxt_pos)) { 236 | dangerous_position_->Set(nxt_pos, 1); 237 | } 238 | } 239 | } 240 | } 241 | 242 | std::vector motion_states; 243 | for (auto &robot : g_robots) { 244 | std::map dire_map = GetDirectionMap(robot); 245 | motion_states.emplace_back(robot.GetPosition(), dire_map); 246 | } 247 | 248 | return motion_states; 249 | } 250 | 251 | std::vector> LandController::GetRobotGroups() { 252 | DisjointSetUnion dsu(g_robots.size()); 253 | 254 | // 判断一个位置是否绝对安全,不需要防碰撞(避免泊位附近主干道的拥堵) 255 | auto check_safe = [&](const Position &pos) -> bool { 256 | for (int i = 0; i < 5; i++) { 257 | const Position nxt_pos = pos + DIR[i]; 258 | if (g_map.IsField(nxt_pos) && !g_map.IsLandMainLine(nxt_pos)) { 259 | return false; 260 | } 261 | } 262 | return true; 263 | }; 264 | 265 | // 将曼哈顿距离不超过 3 的机器人用并查集两两合并 266 | for (int i = 0; i < g_robots.size(); i++) { 267 | const auto &[xi, yi] = g_robots[i].GetPosition(); 268 | if (check_safe(Position{xi, yi})) { 269 | continue; 270 | } 271 | 272 | for (int j = i + 1; j < g_robots.size(); j++) { 273 | const auto &[xj, yj] = g_robots[j].GetPosition(); 274 | if (check_safe(Position{xj, yj})) { 275 | continue; 276 | } 277 | 278 | if (std::abs(xi - xj) + std::abs(yi - yj) <= 3) { 279 | dsu.Merge(i, j); 280 | } 281 | } 282 | } 283 | return dsu.GetSets(); 284 | } 285 | 286 | std::vector LandController::AvoidCrash(const std::vector &motion_state_vec) { 287 | int n = motion_state_vec.size(); 288 | std::vector res_dir(n, 4); 289 | std::vector cur_dir(n); 290 | std::vector cur_pos(n); 291 | std::vector nxt_pos(n); 292 | std::vector res_nxt_pos(n); 293 | 294 | for (int i = 0; i < n; i++) { 295 | cur_pos[i] = motion_state_vec[i].position_; 296 | } 297 | double best_val = -INF; 298 | 299 | auto dfs = [&](auto self, int id, double cur_val) -> void { 300 | if (id == n) { 301 | if (std::count(cur_dir.begin(), cur_dir.end(), 4) != n) { 302 | if (cur_val > best_val || (cur_val == best_val && rand() % 2)) { 303 | best_val = cur_val; 304 | res_dir = cur_dir; 305 | res_nxt_pos = nxt_pos; 306 | } 307 | } 308 | return; 309 | } 310 | 311 | for (auto [dir, val] : motion_state_vec[id].dire_val_) { 312 | nxt_pos[id] = cur_pos[id] + DIR[dir]; 313 | cur_dir[id] = dir; 314 | bool success = true; 315 | for (int i = 0; i < id; i++) { 316 | if (g_map.CheckLandCollision(cur_pos[i], nxt_pos[i], cur_pos[id], nxt_pos[id])) { 317 | success = false; 318 | break; 319 | } 320 | } 321 | if (success) { 322 | self(self, id + 1, cur_val + (0.01 * id + 1) * val); 323 | } 324 | } 325 | }; 326 | 327 | dfs(dfs, 0, 0); 328 | 329 | #ifdef LOCAL 330 | /** 331 | * @test 验证运动控制的合法性:不会发生碰撞 332 | */ 333 | for (int i = 0; i < n; i++) { 334 | for (int j = i + 1; j < n; j++) { 335 | if (g_map.CheckLandCollision(cur_pos[i], res_nxt_pos[i], cur_pos[j], res_nxt_pos[j])) { 336 | fprintf(stderr, 337 | "i = %d, j = %d, cur_pos[i]: (%d, %d), nxt_pos[i]: (%d, %d), " 338 | "cur_pos[j]: (%d, %d), nxt_pos[j]: (%d, %d)\n", 339 | i, j, cur_pos[i][0], cur_pos[i][1], res_nxt_pos[i][0], res_nxt_pos[i][1], 340 | cur_pos[j][0], cur_pos[j][1], res_nxt_pos[j][0], res_nxt_pos[j][1]); 341 | std::cerr << "failed" << std::endl; 342 | } 343 | assert(!g_map.CheckLandCollision(cur_pos[i], res_nxt_pos[i], cur_pos[j], res_nxt_pos[j])); 344 | } 345 | } 346 | // assert(std::count(res_dir.begin(), res_dir.end(), 4) != n); 347 | #endif 348 | 349 | return res_dir; 350 | } 351 | 352 | 353 | void LandController::WorseAvoidCrash(int i, int c) { 354 | if (c > 30) { 355 | return; 356 | } 357 | const int maxrc = 30; 358 | std::array cur_pos; 359 | std::array nxt_pos; 360 | std::array direction; 361 | 362 | for (int k = 0; k < g_robot_cnt; k++) { 363 | cur_pos[k] = g_robots[k].GetPosition(); 364 | } 365 | for (int j = 0; j < g_robot_cnt; j++) { 366 | if (j == i) { 367 | continue; 368 | } 369 | 370 | for (int k = 0; k < g_robot_cnt; k++) { 371 | direction[k] = DIR[g_robots[k].GetMoveDirection()]; 372 | nxt_pos[k] = cur_pos[k] + direction[k]; 373 | } 374 | 375 | // j 追尾 i 376 | if (nxt_pos[j] == cur_pos[i] && nxt_pos[i] == cur_pos[i]) { 377 | bool success = false; 378 | for (int d = 0; d < 4; d++) { 379 | if (d == g_robots[i].GetMoveDirection()) { 380 | continue; 381 | } 382 | auto tmp_pos = cur_pos[j] + DIR[d]; 383 | if (g_map.IsField(tmp_pos) && tmp_pos != cur_pos[i] && tmp_pos != nxt_pos[i]) { 384 | g_robots[j].SetMoveDirection(d); 385 | success = true; 386 | break; 387 | } 388 | } 389 | 390 | // 否则stop 391 | if (!success) { 392 | g_robots[j].SetMoveDirection(4); 393 | // g_robots[j].SetMoveDirection(g_robots[j].GetMoveDirection()^1); 394 | } 395 | WorseAvoidCrash(j, c + 1); 396 | continue; 397 | } 398 | 399 | // 挨在一起迎面撞,j 让出身位,i 等待 400 | if (nxt_pos[i] == cur_pos[j] && nxt_pos[j] == cur_pos[i]) { 401 | // 先尝试向侧边避让 402 | bool success = false; 403 | for (int d = 0; d < 4; d++) { 404 | if (d == g_robots[i].GetMoveDirection()) { 405 | continue; 406 | } 407 | auto tmp_pos = cur_pos[j] + DIR[d]; 408 | if (g_map.IsField(tmp_pos) && tmp_pos != cur_pos[i] && tmp_pos != nxt_pos[i]) { 409 | g_robots[j].SetMoveDirection(d); 410 | success = true; 411 | break; 412 | } 413 | } 414 | // 否则向后退让 415 | if (!success) { 416 | g_robots[j].SetMoveDirection(g_robots[i].GetMoveDirection()); 417 | } 418 | WorseAvoidCrash(j, c + 1); 419 | continue; 420 | } 421 | 422 | // i 追 j 423 | if (nxt_pos[i] == cur_pos[j] && nxt_pos[j] != cur_pos[j]) { 424 | WorseAvoidCrash(j, c + 1); 425 | continue; 426 | } 427 | } 428 | 429 | for (int j = 0; j < g_robot_cnt; j++) { 430 | if (j == i) { 431 | continue; 432 | } 433 | 434 | for (int k = 0; k < g_robot_cnt; k++) { 435 | direction[k] = DIR[g_robots[k].GetMoveDirection()]; 436 | nxt_pos[k] = cur_pos[k] + direction[k]; 437 | } 438 | 439 | // 直角撞,或迎面间隔撞,j 等 i 先走 440 | if (nxt_pos[i] == nxt_pos[j]) { 441 | g_robots[j].SetMoveDirection(4); 442 | WorseAvoidCrash(j, c + 1); 443 | continue; 444 | } 445 | } 446 | } 447 | 448 | LandController g_land_controller; -------------------------------------------------------------------------------- /src/map.cpp: -------------------------------------------------------------------------------- 1 | #include "map.hpp" 2 | 3 | #include 4 | 5 | ConnectedComponent::ConnectedComponent(const std::set &berth_set, 6 | const std::vector &robot_buy_pos, 7 | const std::vector &ship_buy_pos, 8 | int land_area, 9 | double berth_ocean_dist) 10 | : berth_set_(berth_set), 11 | robot_buy_pos_(robot_buy_pos), 12 | ship_buy_pos_(ship_buy_pos), 13 | land_area_(land_area), 14 | berth_ocean_dist_(berth_ocean_dist) {} 15 | 16 | bool ConnectedComponent::operator>(const ConnectedComponent &other) const { 17 | if (this->land_area_ != other.land_area_) { 18 | return this->land_area_ > other.land_area_; 19 | } 20 | return this->berth_ocean_dist_ < other.berth_ocean_dist_; 21 | } 22 | 23 | Map::Map() { 24 | berth_id_grid_.Set(-1); 25 | } 26 | 27 | void Map::Init() { 28 | for (int i = 0; i < MAP_SIZE; i++) { 29 | std::string line; 30 | std::cin >> line; 31 | 32 | for (int j = 0; j < MAP_SIZE; j++) { 33 | map_[i][j] = line[j]; 34 | 35 | if (map_[i][j] == 'T') { 36 | delivery_points_.push_back(Position{i, j}); 37 | } 38 | if (map_[i][j] == 'R') { 39 | robot_buy_pos_.push_back(Position{i, j}); 40 | } 41 | if (map_[i][j] == 'S') { 42 | ship_buy_pos_.push_back(Position{i, j}); 43 | } 44 | } 45 | 46 | std::string ok; 47 | std::cin >> ok; 48 | assert(ok == "OK"); 49 | std::cout << ok << std::endl; 50 | } 51 | 52 | FindDeleveryShortestPath(); 53 | 54 | unsigned long long hash = 0; 55 | for (int i = 0; i < MAP_SIZE; i++) { 56 | for (int j = 0; j < MAP_SIZE; j++) { 57 | hash = (hash * 377 + map_[i][j]); 58 | } 59 | } 60 | 61 | hash_value_ = hash % 37; 62 | } 63 | 64 | void Map::InitAllBerths(std::queue berth_metadata_que) { 65 | auto start = std::chrono::steady_clock::now(); 66 | 67 | std::mutex metadata_mtx; 68 | std::vector> tmp_berths(berth_metadata_que.size()); 69 | 70 | // 双核并行初始化所有泊位 71 | auto init_function = [&]() -> void { 72 | while (true) { 73 | std::optional berth_metadata; 74 | { 75 | std::lock_guard lg(metadata_mtx); 76 | if (berth_metadata_que.empty()) { 77 | return; 78 | } 79 | berth_metadata = berth_metadata_que.front(); 80 | berth_metadata_que.pop(); 81 | } 82 | 83 | Grid dist_grid; 84 | std::set area; 85 | auto [id, pos, vel] = berth_metadata.value(); 86 | GenerateBerth(id, pos, vel, dist_grid, area); 87 | tmp_berths[id].emplace(id, pos, vel, std::move(dist_grid), std::move(area)); 88 | { 89 | std::lock_guard lg(cerr_mtx); 90 | fprintf(stderr, "finish init %d", id); 91 | std::cerr << std::endl; 92 | } 93 | } 94 | }; 95 | 96 | // 两个线程分别绑定到两个 CPU 上 97 | std::thread init_thread0(init_function); 98 | std::thread init_thread1(init_function); 99 | 100 | pthread_t handle0 = init_thread0.native_handle(); 101 | pthread_t handle1 = init_thread1.native_handle(); 102 | 103 | #ifdef LOCAL 104 | fprintf(stderr, "handle0 = %ld\n", handle0); 105 | fprintf(stderr, "handle1 = %ld\n", handle1); 106 | #endif 107 | 108 | #if !defined(_WIN32) && !defined(_WIN64) 109 | cpu_set_t cpu_set0; 110 | CPU_ZERO(&cpu_set0); 111 | CPU_SET(0, &cpu_set0); 112 | pthread_setaffinity_np(handle0, sizeof(cpu_set0), &cpu_set0); 113 | 114 | cpu_set_t cpu_set1; 115 | CPU_ZERO(&cpu_set1); 116 | CPU_SET(1, &cpu_set1); 117 | pthread_setaffinity_np(handle1, sizeof(cpu_set1), &cpu_set1); 118 | #endif 119 | 120 | init_thread0.join(); 121 | init_thread1.join(); 122 | 123 | for (auto &it : tmp_berths) { 124 | g_berths.emplace_back(std::move(it.value())); 125 | } 126 | 127 | nearest_berth_dist_.Set(INF); 128 | 129 | // 初始化每个点到泊位的距离 130 | for (int i = 0; i < MAP_SIZE; i++) { 131 | for (int j = 0; j < MAP_SIZE; j++) { 132 | for (int k = 0; k < g_berths.size(); k++) { 133 | int dist = g_berths[k].dist_grid_.Get(Position{i, j}); 134 | if (dist < nearest_berth_dist_[i][j]) { 135 | nearest_berth_id_[i][j] = k; 136 | nearest_berth_dist_[i][j] = dist; 137 | } 138 | } 139 | } 140 | } 141 | 142 | auto end = std::chrono::steady_clock::now(); 143 | auto duration = std::chrono::duration_cast(end - start); 144 | std::cerr << "init berth using " << duration.count() << " ms\n"; 145 | } 146 | 147 | void Map::GenerateBerth( 148 | int berth_id, const Position &core_pos, int velocity, Grid &dist_grid, std::set &area) { 149 | std::queue que; // 广义泊位 150 | que.push(core_pos); 151 | berth_id_grid_.Set(core_pos, berth_id); // 广义泊位 152 | std::vector src{core_pos}; // 狭义泊位 153 | 154 | while (!que.empty()) { 155 | Position cur_pos = que.front(); 156 | que.pop(); 157 | if (area.count(cur_pos)) continue; 158 | area.insert(cur_pos); 159 | 160 | for (int i = 0; i < 4; i++) { 161 | Position nxt_pos = cur_pos + DIR[i]; 162 | 163 | if (IN_MAP(nxt_pos[0], nxt_pos[1]) && berth_id_grid_.Get(nxt_pos) == -1 && !area.count(nxt_pos)) { 164 | char type = map_[nxt_pos[0]][nxt_pos[1]]; 165 | if (type == 'B' || type == 'K') { 166 | que.push(nxt_pos); 167 | berth_id_grid_.Set(nxt_pos, berth_id); 168 | } 169 | if (type == 'B') { 170 | src.push_back(nxt_pos); 171 | } 172 | } 173 | } 174 | } 175 | 176 | MultisourceBFS(std::move(src), dist_grid, BERTH_BFS_MAX_DEPTH); 177 | } 178 | 179 | void Map::MultisourceBFS(const std::vector &src, Grid &dist_grid, int max_depth) noexcept { 180 | dist_grid.Set(INF); 181 | std::queue> que; 182 | 183 | for (const auto &[x, y] : src) { 184 | if (!IsField({x, y})) { 185 | continue; 186 | } 187 | 188 | dist_grid[x][y] = 0; 189 | que.push(std::array{0, x, y}); 190 | } 191 | 192 | while (!que.empty()) { 193 | const auto [d, x, y] = que.front(); 194 | que.pop(); 195 | if (d > dist_grid[x][y]) { 196 | continue; 197 | } 198 | 199 | for (int i = 0; i < 4; i++) { 200 | int xx = x + DX[i]; 201 | int yy = y + DY[i]; 202 | int dist = dist_grid[x][y] + 1; 203 | 204 | if (IsField({xx, yy})) { 205 | if (dist_grid[xx][yy] > dist && dist <= max_depth) { 206 | dist_grid[xx][yy] = dist; 207 | que.push(std::array{dist, xx, yy}); 208 | } 209 | } 210 | } 211 | } 212 | } 213 | 214 | std::pair Map::FindNearestBerth(const Position &pos) const { 215 | return std::pair{nearest_berth_id_.Get(pos), nearest_berth_dist_.Get(pos)}; 216 | } 217 | 218 | void Map::BuyRobot(int race, int berth_id) { 219 | static int idx = 0; 220 | int sz = connnected_component_.robot_buy_pos_.size(); 221 | idx = rand() % sz; 222 | auto [x, y] = connnected_component_.robot_buy_pos_[idx]; 223 | std::cout << "lbot " << x << " " << y << ' ' << race << "\n"; 224 | } 225 | 226 | void Map::BuyShip(int berth_id) { 227 | // static int idx = 0; 228 | // int sz = connnected_component_.ship_buy_pos_.size(); 229 | // idx = (idx + 1) % sz; 230 | g_berth_group_manager.ActivateOneGroup(ship_tar_hash[g_ship_cnt]); 231 | Position p = {-1, -1}; 232 | for (auto pos : ship_buy_pos_) { 233 | if (p == Position{-1, -1} || 234 | g_berths[berth_id].GetOceanDistance(pos, -1) < g_berths[berth_id].GetOceanDistance(p, -1)) { 235 | p = pos; 236 | } 237 | } 238 | auto [x, y] = p; 239 | // auto [x, y] = connnected_component_.ship_buy_pos_[idx]; 240 | std::cout << "lboat " << x << " " << y << "\n"; 241 | } 242 | 243 | void Map::FindConnectedComponent() { 244 | // 先把泊位都激活,方便计算距离 245 | for (auto &berth : g_berths) { 246 | berth.Activate(); 247 | } 248 | 249 | // 枚举所有的买船点和泊位,用并查集维护其连通性 250 | DisjointSetUnion dsu(ship_buy_pos_.size() + g_berths.size()); 251 | for (int i = 0; i < ship_buy_pos_.size(); i++) { 252 | for (int j = 0; j < g_berths.size(); j++) { 253 | if (g_berths[j].GetOceanDistance(ship_buy_pos_[i], -1) < 1e5) { 254 | dsu.Merge(i, ship_buy_pos_.size() + j); 255 | } 256 | } 257 | } 258 | 259 | auto connected_block = dsu.GetSets(); 260 | std::vector connected_components; 261 | 262 | // 选择一个最佳的连通块 263 | for (auto &block : connected_block) { 264 | // 记录;这个连通块里面有哪些泊位 265 | std::set berth_set; 266 | std::vector ship_buy_pos; 267 | for (int x : block) { 268 | if (x >= ship_buy_pos_.size()) { 269 | berth_set.insert(x - ship_buy_pos_.size()); 270 | } else { 271 | ship_buy_pos.push_back(ship_buy_pos_[x]); 272 | } 273 | } 274 | 275 | // 如果这个连通块没有买船点或者泊位,就跳过 276 | if (ship_buy_pos.empty() || berth_set.empty()) { 277 | continue; 278 | } 279 | 280 | // 如果与送货点不连通,就跳过 281 | if (GetDeliveryPointsDist(ship_buy_pos[0], 0) > 1e5) { 282 | continue; 283 | } 284 | 285 | // BFS 计算这个连通块的陆地面积 286 | Grid visit; 287 | visit.Set(0); 288 | std::queue que; 289 | que.push(robot_buy_pos_[0]); 290 | int land_area = 0; 291 | 292 | while (!que.empty()) { 293 | auto cur_pos = que.front(); 294 | visit.Set(cur_pos, 1); 295 | land_area += 1; 296 | que.pop(); 297 | for (int i = 0; i < 5; i++) { 298 | Position nxt_pos = cur_pos + DIR[i]; 299 | if (!IsField(nxt_pos) || visit.Get(nxt_pos) == 1) { 300 | continue; 301 | } 302 | que.push(nxt_pos); 303 | visit.Set(nxt_pos, 1); 304 | } 305 | } 306 | 307 | // 计算泊位到送货点的平均距离 308 | double berth_ocean_dist = 0; 309 | for (int berth_id : berth_set) { 310 | Position core_pos = g_berths[berth_id].core_pos_; 311 | berth_ocean_dist += GetDeliveryPointsDist(core_pos, -1); 312 | } 313 | berth_ocean_dist /= berth_set.size(); 314 | connected_components.emplace_back(berth_set, robot_buy_pos_, ship_buy_pos, land_area, 315 | berth_ocean_dist); 316 | } 317 | 318 | // 选择最优连通块,并启动相应泊位 319 | std::sort(connected_components.begin(), connected_components.end(), std::greater()); 320 | this->connnected_component_ = std::move(connected_components[0]); 321 | 322 | // 将连通块以外的泊位全都失活 323 | for (int i = 0; i < g_berths.size(); i++) { 324 | if (connnected_component_.berth_set_.count(i) == 0) { 325 | g_berths[i].Deactivate(); 326 | } 327 | } 328 | } 329 | 330 | bool Map::IsField(const Position &pos) const noexcept { 331 | const auto &[x, y] = pos; 332 | const char &c = map_[x][y]; 333 | return IN_MAP(x, y) && c != '*' && c != '#' && c != '~' && c != 'S' && c != 'K' && c != 'T'; 334 | } 335 | 336 | bool Map::IsLandMainLine(const Position &pos) const noexcept { 337 | const auto &[x, y] = pos; 338 | const char &c = map_[x][y]; 339 | return IN_MAP(x, y) && (c == '>' || c == 'R' || c == 'c' || c == 'B'); 340 | } 341 | 342 | bool Map::PointInOcean(const Position &pos) const noexcept { 343 | const auto &[x, y] = pos; 344 | const char &c = map_[x][y]; 345 | return IN_MAP(x, y) && c != '#' && c != '.' && c != '>' && c != 'R'; 346 | } 347 | 348 | bool Map::PointInMainChannel(const Position &pos) const noexcept { 349 | const auto &[x, y] = pos; 350 | const char &c = map_[x][y]; 351 | return IN_MAP(x, y) && (c == '~' || c == 'S' || c == 'c' || c == 'B' || c == 'K'); 352 | } 353 | 354 | bool Map::ShipInOcean(const Position &pos, int dir) const noexcept { 355 | bool in = true; 356 | switch (dir) { 357 | case 0: 358 | for (int i = 0; i < 2; i++) { 359 | for (int j = 0; j < 3; j++) { 360 | in &= PointInOcean(pos + Position{i, j}); 361 | } 362 | } 363 | break; 364 | 365 | case 1: 366 | for (int i = -1; i < 1; i++) { 367 | for (int j = -2; j < 1; j++) { 368 | in &= PointInOcean(pos + Position{i, j}); 369 | } 370 | } 371 | break; 372 | 373 | case 2: 374 | for (int i = -2; i < 1; i++) { 375 | for (int j = 0; j < 2; j++) { 376 | in &= PointInOcean(pos + Position{i, j}); 377 | } 378 | } 379 | break; 380 | 381 | case 3: 382 | for (int i = 0; i < 3; i++) { 383 | for (int j = -1; j < 1; j++) { 384 | in &= PointInOcean(pos + Position{i, j}); 385 | } 386 | } 387 | break; 388 | } 389 | return in; 390 | } 391 | 392 | bool Map::ShipInMainChannel(const Position &pos, int dir) const noexcept { 393 | bool in = false; 394 | switch (dir) { 395 | case 0: 396 | for (int i = 0; i < 2; i++) { 397 | for (int j = 0; j < 3; j++) { 398 | in |= PointInMainChannel(pos + Position{i, j}); 399 | } 400 | } 401 | break; 402 | 403 | case 1: 404 | for (int i = -1; i < 1; i++) { 405 | for (int j = -2; j < 1; j++) { 406 | in |= PointInMainChannel(pos + Position{i, j}); 407 | } 408 | } 409 | break; 410 | 411 | case 2: 412 | for (int i = -2; i < 1; i++) { 413 | for (int j = 0; j < 2; j++) { 414 | in |= PointInMainChannel(pos + Position{i, j}); 415 | } 416 | } 417 | break; 418 | 419 | case 3: 420 | for (int i = 0; i < 3; i++) { 421 | for (int j = -1; j < 1; j++) { 422 | in |= PointInMainChannel(pos + Position{i, j}); 423 | } 424 | } 425 | break; 426 | } 427 | return in; 428 | } 429 | 430 | void Map::FindOceanShortestPath(const std::vector &src, 431 | std::array &ocean_dist_grid) const { 432 | // 直接跑 dijkstra 最短路 433 | for (int i = 0; i < 4; i++) { 434 | ocean_dist_grid[i].Set(INF); 435 | } 436 | std::priority_queue>> q; 437 | 438 | for (const auto &i : src) { 439 | for (int j = 0; j < 4; j++) { 440 | if (ShipInOcean(i, j)) { 441 | ocean_dist_grid[j].Set(i, 0); 442 | q.push({0, {i, j}}); 443 | } 444 | } 445 | } 446 | 447 | while (!q.empty()) { 448 | auto [dis, pad] = q.top(); 449 | dis = -dis; 450 | const auto [p, d] = pad; 451 | q.pop(); 452 | 453 | if (ocean_dist_grid[d].Get(p) < dis) { 454 | continue; 455 | } 456 | 457 | std::pair nxt[3]; 458 | nxt[0] = {p + DIR[d ^ 1], d}; 459 | nxt[1] = {p + DIR[d] + DIR[d ^ ((d >> 1) | 2)], d ^ 3 ^ (d >> 1)}; 460 | nxt[2] = {p + DIR[d ^ 3 ^ (d >> 1)] + DIR[d ^ 3 ^ (d >> 1)], d ^ ((d >> 1) | 2)}; 461 | 462 | for (int i = 0; i < 3; i++) { 463 | int nxtdis = dis + 1 + ShipInMainChannel(p, d); 464 | 465 | if (ShipInOcean(nxt[i].first, nxt[i].second) && 466 | nxtdis < ocean_dist_grid[nxt[i].second].Get(nxt[i].first)) { 467 | ocean_dist_grid[nxt[i].second].Set(nxt[i].first, nxtdis); 468 | q.push({-nxtdis, nxt[i]}); 469 | } 470 | } 471 | } 472 | } 473 | 474 | void Map::FindDeleveryShortestPath() { 475 | FindOceanShortestPath(delivery_points_, delivery_point_dist_); 476 | } 477 | 478 | int Map::GetDeliveryPointsDist(const Position &pos, int dir) const noexcept { 479 | if (dir == -1) { 480 | int mi = INF; 481 | for (int i = 0; i < 4; i++) { 482 | mi = std::min(mi, delivery_point_dist_[i].Get(pos)); 483 | } 484 | return mi; 485 | } 486 | 487 | return delivery_point_dist_[dir].Get(pos); 488 | } 489 | 490 | int Map::GetDeliveryPointsMarch(const Position &pos, int dir) const noexcept { 491 | int dis = delivery_point_dist_[dir].Get(pos); 492 | std::vector res{3}; 493 | 494 | if (delivery_point_dist_[dir].Get(pos + DIR[dir]) < dis) { 495 | res.clear(); 496 | res.push_back(2); 497 | dis = delivery_point_dist_[dir].Get(pos + DIR[dir]); 498 | } else if (delivery_point_dist_[dir].Get(pos + DIR[dir]) == dis) { 499 | res.push_back(2); 500 | } 501 | 502 | int tmp_dir = dir ^ (2 | (dir >> 1)); 503 | Position tmp_pos = pos + DIR[dir] + DIR[dir ^ 3 ^ (dir >> 1)]; 504 | if (delivery_point_dist_[tmp_dir].Get(tmp_pos) < dis) { 505 | res.clear(); 506 | res.push_back(1); 507 | dis = delivery_point_dist_[tmp_dir].Get(tmp_pos); 508 | } else if (delivery_point_dist_[tmp_dir].Get(tmp_pos) == dis) { 509 | res.push_back(1); 510 | } 511 | 512 | tmp_dir = dir ^ 3 ^ (dir >> 1); 513 | tmp_pos = pos + DIR[dir] + DIR[dir]; 514 | if (delivery_point_dist_[tmp_dir].Get(tmp_pos) < dis) { 515 | res.clear(); 516 | res.push_back(0); 517 | dis = delivery_point_dist_[tmp_dir].Get(tmp_pos); 518 | } else if (delivery_point_dist_[tmp_dir].Get(tmp_pos) == dis) { 519 | res.push_back(0); 520 | } 521 | 522 | return res[rand() % res.size()]; 523 | } 524 | 525 | bool Map::CheckLandCollision(const Position &cur_x, 526 | const Position &nxt_x, 527 | const Position &cur_y, 528 | const Position &nxt_y) const noexcept { 529 | const static std::set land_main{'>', 'R', 'c', 'B'}; 530 | for (const auto &pos : {cur_x, nxt_x, cur_y, nxt_y}) { 531 | if (!IsField(pos)) { 532 | return false; 533 | } 534 | } 535 | 536 | auto check = [&](const Position &pos) -> bool { return land_main.count(map_[pos[0]][pos[1]]) == 0; }; 537 | 538 | // 下一个位置重合 539 | if (nxt_x == nxt_y && check(nxt_x)) { 540 | return true; 541 | } 542 | // 面对面迎面撞 543 | if (nxt_y == cur_x && nxt_x == cur_y && (check(cur_x) || check(cur_y))) { 544 | return true; 545 | } 546 | 547 | return false; 548 | } 549 | 550 | int Map::Manhattan(const Position &x, const Position &y) const noexcept { 551 | return abs(x[0] - y[0]) + abs(x[1] - y[1]); 552 | } 553 | 554 | Map g_map; -------------------------------------------------------------------------------- /src/land_scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "land_scheduler.hpp" 2 | 3 | 4 | ScheduleGraph::ScheduleGraph(int robot_cnt, int cargo_cnt, int berth_cnt) { 5 | #ifdef LOCAL_TIMER 6 | Timer timer("ScheduleGraph Constructor", 1); 7 | #endif // LOCAL_TIMER 8 | 9 | this->robot_cnt_ = robot_cnt; 10 | this->cargo_cnt_ = cargo_cnt; 11 | this->berth_cnt_ = berth_cnt; 12 | this->ver_cnt_ = robot_cnt + cargo_cnt + berth_cnt; 13 | 14 | adj_.resize(ver_cnt_); 15 | for (auto &line : benefit_matrix_) { 16 | std::fill(line.begin(), line.end(), 0); 17 | } 18 | } 19 | 20 | void ScheduleGraph::Init(int robot_cnt, int cargo_cnt, int berth_cnt) { 21 | source_ = -1; 22 | sink_ = -1; 23 | ver_cnt_ = robot_cnt + cargo_cnt + berth_cnt; 24 | robot_cnt_ = robot_cnt; 25 | cargo_cnt_ = cargo_cnt; 26 | berth_cnt_ = berth_cnt; 27 | berth_group_cnt_ = 0; 28 | 29 | already_in_group_.clear(); 30 | tmp_group_limitation_.clear(); 31 | cargo_berth_.clear(); 32 | 33 | adj_.resize(ver_cnt_); 34 | for (auto &line : adj_) { 35 | line.clear(); 36 | } 37 | 38 | edge_.clear(); 39 | dis_.clear(); 40 | incf_.clear(); 41 | pre_.clear(); 42 | 43 | for (auto &line : benefit_matrix_) { 44 | std::fill(line.begin(), line.end(), 0); 45 | } 46 | } 47 | 48 | void ScheduleGraph::SetMisionBenifit(int robot_id, int cargo_id, double benefit) noexcept { 49 | #ifdef LOCAL 50 | assert(robot_id < robot_cnt_); 51 | assert(cargo_id < cargo_cnt_); 52 | #endif 53 | benefit_matrix_[robot_id][cargo_id] = benefit; 54 | AddEdge(GetRobotID(robot_id), GetCargoID(cargo_id), 1, benefit); 55 | } 56 | 57 | void ScheduleGraph::SetCargoBerth(int cargo_id, int berth_id) noexcept { 58 | #ifdef LOCAL 59 | assert(cargo_id < cargo_cnt_); 60 | assert(berth_id < berth_cnt_); 61 | #endif 62 | assert(cargo_berth_.count(cargo_id) == 0); 63 | cargo_berth_.insert(cargo_id); 64 | AddEdge(GetCargoID(cargo_id), GetBerthID(berth_id), 1, 0); 65 | } 66 | 67 | double ScheduleGraph::GetMissionBenifit(int robot_id, int cargo_id) noexcept { 68 | #ifdef LOCAL 69 | assert(robot_id < robot_cnt_); 70 | assert(cargo_id < cargo_cnt_); 71 | #endif 72 | return benefit_matrix_[robot_id][cargo_id]; 73 | } 74 | 75 | void ScheduleGraph::SetBerthGroupLimitation(const std::set &berth_group, int limitation) noexcept { 76 | int berth_group_id = berth_group_cnt_; 77 | adj_.emplace_back(); 78 | for (int berth_id : berth_group) { 79 | // 首先减去已经取到货的机器人数量 80 | for (const auto &robot : g_robots) { 81 | if (robot.FullCargo()) { 82 | int target_berth_id = robot.GetTargetBerthID(); 83 | if (target_berth_id == berth_id) { 84 | limitation -= 1; 85 | } 86 | } 87 | } 88 | assert(already_in_group_.count(berth_id) == 0); 89 | already_in_group_.insert(berth_id); 90 | AddEdge(GetBerthID(berth_id), GetBerthGroupID(berth_group_id), INF, 0); 91 | } 92 | 93 | tmp_group_limitation_[berth_group_id] = limitation; 94 | berth_group_cnt_ += 1; 95 | ver_cnt_ += 1; 96 | } 97 | 98 | std::map ScheduleGraph::MatchRobotCargo() noexcept { 99 | source_ = ver_cnt_; 100 | sink_ = ver_cnt_ + 1; 101 | ver_cnt_ += 2; 102 | adj_.emplace_back(); 103 | adj_.emplace_back(); 104 | dis_.resize(ver_cnt_); 105 | incf_.resize(ver_cnt_); 106 | pre_.resize(ver_cnt_); 107 | 108 | for (int i = 0; i < robot_cnt_; i++) { 109 | AddEdge(source_, GetRobotID(i), 1, 0); 110 | } 111 | for (auto [berth_group_id, limit] : tmp_group_limitation_) { 112 | AddEdge(GetBerthGroupID(berth_group_id), sink_, limit, 0); 113 | } 114 | 115 | int flow = 0; 116 | double cost = 0; 117 | std::map, int> match; 118 | 119 | while (SPFA()) { 120 | int tmp = incf_[sink_]; 121 | assert(tmp == 1); // 在本问题中,每次的增广流量一定是 1。 122 | flow += tmp; 123 | cost += tmp * dis_[sink_]; 124 | for (int i = sink_, j; i != source_; i = j) { 125 | // i 从汇点 t 往回找,边的方向是 j->i,流量为 tmp 126 | edge_[pre_[i]].cap_ -= tmp; 127 | edge_[pre_[i] ^ 1].cap_ += tmp; 128 | j = edge_[pre_[i] ^ 1].to_; 129 | 130 | // 寻找机器人——货物匹配,注意辨别反向边 131 | if (j < robot_cnt_ && i >= robot_cnt_ && i < robot_cnt_ + cargo_cnt_) { 132 | match[std::pair{j, i}] += tmp; 133 | } 134 | if (i < robot_cnt_ && j >= robot_cnt_ && j < robot_cnt_ + cargo_cnt_) { 135 | match[std::pair{i, j}] -= tmp; 136 | } 137 | } 138 | } 139 | 140 | std::map ans; 141 | #ifdef LOCAL_TEST 142 | /** 143 | * @test 检查一下返回的匹配是否不会重合 144 | */ 145 | std::set st_u, st_v; 146 | #endif 147 | for (auto [p, v] : match) { 148 | if (v != 0) { 149 | assert(v == 1); 150 | auto [u, v] = p; 151 | ans[u] = v - robot_cnt_; 152 | #ifdef LOCAL_TEST 153 | assert(st_u.count(u) == 0); 154 | assert(st_v.count(v) == 0); 155 | st_u.insert(u); 156 | st_v.insert(v); 157 | #endif 158 | } 159 | } 160 | 161 | return ans; 162 | } 163 | 164 | void ScheduleGraph::AddEdge(int u, int v, int cap, double cost) noexcept { 165 | adj_[u].push_back(edge_.size()); 166 | edge_.push_back(Edge{v, cap, cost}); 167 | adj_[v].push_back(edge_.size()); 168 | edge_.push_back(Edge{u, 0, -cost}); 169 | } 170 | 171 | bool ScheduleGraph::SPFA() noexcept { 172 | int s = source_; 173 | int t = sink_; 174 | std::fill(dis_.begin(), dis_.end(), -INF); 175 | std::fill(incf_.begin(), incf_.end(), 0); 176 | 177 | std::vector inq(ver_cnt_ + 1, false); 178 | std::queue que; 179 | que.push(s); 180 | dis_[s] = 0; 181 | inq[s] = true; 182 | incf_[s] = INF; 183 | 184 | while (!que.empty()) { 185 | int u = que.front(); 186 | que.pop(); 187 | inq[u] = false; 188 | 189 | for (int i : adj_[u]) { 190 | auto [v, cap, co] = edge_[i]; 191 | if (cap > 0 && dis_[u] + co > dis_[v] + EPS) { 192 | dis_[v] = dis_[u] + co; 193 | pre_[v] = i; 194 | incf_[v] = std::min(cap, incf_[u]); 195 | 196 | if (!inq[v]) { 197 | que.push(v); 198 | inq[v] = true; 199 | } 200 | } 201 | } 202 | } 203 | 204 | return incf_[t] > 0; 205 | } 206 | 207 | int ScheduleGraph::GetRobotID(int robot_id) const noexcept { 208 | return robot_id; 209 | } 210 | 211 | int ScheduleGraph::GetCargoID(int cargo_id) const noexcept { 212 | return robot_cnt_ + cargo_id; 213 | } 214 | 215 | int ScheduleGraph::GetBerthID(int berth_id) const noexcept { 216 | return robot_cnt_ + cargo_cnt_ + berth_id; 217 | } 218 | 219 | int ScheduleGraph::GetBerthGroupID(int berth_group_id) const noexcept { 220 | return robot_cnt_ + cargo_cnt_ + berth_cnt_ + berth_group_id; 221 | } 222 | 223 | 224 | void LandScheduler::ScheduleRobots() { 225 | #ifdef LOCAL_TIMER 226 | Timer timer("LandScheduler::ScheduleRobots", 12); 227 | #endif 228 | ScheduleBusyRobots(); 229 | ScheduleFreeRobots(); 230 | } 231 | 232 | void LandScheduler::ScheduleBusyRobots() { 233 | for (auto &robot : g_robots) { 234 | if (!robot.FullCargo() || robot.IsWaitingForLLM()) { 235 | continue; 236 | } 237 | 238 | int target_berth_id = -1; 239 | int dist_to_berth = INF; 240 | 241 | for (const auto &berth_group : g_berth_group_manager.GetActivatedBerthGroup()) { 242 | for (int id : berth_group.group_member_) { 243 | const auto &berth = g_berths[id]; 244 | int cur_dist = berth.GetLandDistance(robot.GetPosition()); 245 | 246 | if (dist_to_berth > cur_dist) { 247 | dist_to_berth = cur_dist; 248 | target_berth_id = berth.id_; 249 | } 250 | } 251 | } 252 | 253 | if (target_berth_id == -1) { 254 | for (const auto &berth : g_berths) { 255 | int cur_dist = berth.GetLandDistance(robot.GetPosition()); 256 | 257 | if (dist_to_berth > cur_dist) { 258 | dist_to_berth = cur_dist; 259 | target_berth_id = berth.id_; 260 | } 261 | } 262 | } 263 | 264 | // fprintf(stderr, "robotid %d, dist %d\n", robot.id_, dist_to_berth); 265 | 266 | assert(target_berth_id >= 0 && target_berth_id < g_berths.size()); 267 | robot.SetTargetBerthID(target_berth_id); 268 | } 269 | 270 | #ifdef LOCAL_TEST 271 | for (const auto &robot : g_robots) { 272 | if (robot.FullCargo()) { 273 | assert(robot.GetTargetBerthID() != -1); 274 | assert(robot.GetCargoPtr() != nullptr); 275 | assert(robot.GetCargoPtr()->value_ > 0); 276 | assert(robot.GetCargoPtr()->IsDeleted()); 277 | } 278 | } 279 | #endif 280 | } 281 | 282 | void LandScheduler::ScheduleFreeRobots() { 283 | #ifdef LOCAL_TIMER 284 | Timer timer("LandScheduler::ScheduleFreeRobots", 13); 285 | #endif // LOCAL_TIMER 286 | 287 | for (auto &robot : g_robots) { 288 | // 如果它上一帧设定的目标,已经被别的机器人拿走了, 289 | if (!robot.FullCargo() && robot.GetCargoPtr() != nullptr && robot.GetCargoPtr()->IsDeleted() && 290 | !robot.IsWaitingForLLM()) { 291 | robot.SetCargoPtr(nullptr); 292 | } 293 | } 294 | 295 | cargo_ptr_vec_ = g_cargo_manager.GetAllCargoPtr(); 296 | graph_ptr_->Init(static_cast(g_robots.size()), static_cast(cargo_ptr_vec_.size()), 297 | static_cast(g_berths.size() + 1)); 298 | 299 | // std::cerr << "MatchCargoBerth" << std::endl; 300 | MatchCargoBerth(); 301 | 302 | // std::cerr << "CalcRobotCargoBenefit" << std::endl; 303 | CalcRobotCargoBenefit(); 304 | 305 | // std::cerr << "DivideBerthGroups" << std::endl; 306 | DivideBerthGroups(); 307 | 308 | // std::cerr << "UpdateMatch" << std::endl; 309 | UpdateMatch(); 310 | 311 | // std::cerr << "Finish" << std::endl; 312 | #ifdef LOCAL_TEST 313 | /** 314 | * @test 验证匹配不会重复 315 | */ 316 | std::set st; 317 | for (const auto &robot : g_robots) { 318 | if (!robot.FullCargo() && !robot.IsWaitingForLLM() && robot.GetCargoPtr() != nullptr) { 319 | auto ptr = robot.GetCargoPtr(); 320 | assert(!ptr->IsDeleted()); 321 | assert(st.count(ptr) == 0); 322 | st.insert(ptr); 323 | } 324 | } 325 | #endif 326 | } 327 | 328 | int LandScheduler::RoNumAroundCargo(CargoPtr cp, int dis) { 329 | int res = 0; 330 | for (auto &[id, other_robot] : g_other_robots) { 331 | if (other_robot.cargo_num_ < other_robot.volume_ && cp->GetDistance(other_robot.position_) < dis) { 332 | res++; 333 | } 334 | } 335 | return res; 336 | } 337 | 338 | void LandScheduler::Occupy() { 339 | std::map flag; 340 | for (const auto &cargo : cargo_ptr_vec_) { 341 | if (cargo->value_ >= 1400) { 342 | std::priority_queue> heap; 343 | for (auto &robot : g_robots) { 344 | if (flag[robot.id_]) { 345 | continue; 346 | } 347 | heap.push({-cargo->GetDistance(robot.GetPosition()), robot.id_}); 348 | } 349 | 350 | if (!heap.empty()) { 351 | auto [dis, id] = heap.top(); 352 | dis = -dis; 353 | if (RoNumAroundCargo(cargo, dis) == 0) { 354 | flag[id] = true; 355 | if (g_robots[id].FullCargo()) { 356 | g_robots[id].SetAddtionalTarget(cargo); 357 | // break; 358 | } 359 | } 360 | } 361 | } 362 | } 363 | } 364 | 365 | void LandScheduler::MatchCargoBerth() noexcept { 366 | std::fill(cargo_berth_dist_.begin(), cargo_berth_dist_.end(), INF); 367 | const auto &activated_berth_groups = g_berth_group_manager.GetActivatedBerthGroup(); 368 | 369 | for (const auto &cargo : cargo_ptr_vec_) { 370 | const int cargo_id = cargo->GetID(); 371 | 372 | // 虚拟货物向虚拟泊位连边 373 | if (cargo->IsVirtual()) { 374 | graph_ptr_->SetCargoBerth(cargo_id, VIRTUAL_BERTH_ID); 375 | continue; 376 | } 377 | 378 | // 其他货物找最近的泊位 379 | int target_berth_id = -1; 380 | int distance = INF; // 货物到泊位的距离 381 | 382 | for (const auto &berth_group : activated_berth_groups) { 383 | for (int x : berth_group.group_member_) { 384 | const auto &berth = g_berths[x]; 385 | const int cur_dist = berth.GetLandDistance(cargo->position_); 386 | if (cur_dist < distance) { 387 | distance = cur_dist; 388 | target_berth_id = berth.id_; 389 | } 390 | } 391 | } 392 | 393 | cargo_berth_dist_[cargo_id] = distance; 394 | if (target_berth_id != -1) { 395 | graph_ptr_->SetCargoBerth(cargo_id, target_berth_id); 396 | } 397 | } 398 | } 399 | 400 | void LandScheduler::CalcRobotCargoBenefit() noexcept { 401 | #ifdef LOCAL_TIMER 402 | Timer timer("LandScheduler::CalcRobotCargoBenefit", 10); 403 | #endif // LOCAL_TIMER 404 | 405 | // 暂存货物收益,选比较优的若干个进行匹配 406 | static std::vector> benefit_vec; 407 | 408 | // 记录自己的机器人都在哪些位置 409 | static std::set self_robot_position; 410 | self_robot_position.clear(); 411 | for (const auto &robot : g_robots) { 412 | self_robot_position.insert(robot.GetPosition()); 413 | } 414 | 415 | // 枚举所有机器人——货物组合,计算收益 416 | for (const auto &robot : g_robots) { 417 | if (robot.FullCargo()) { 418 | continue; 419 | } 420 | 421 | benefit_vec.clear(); 422 | 423 | // 机器人已经走过的距离 424 | int distance3 = INF; 425 | for (auto &berth : g_berths) { 426 | distance3 = std::min(distance3, berth.GetLandDistance(robot.GetPosition())); 427 | } 428 | 429 | for (const auto &cargo : cargo_ptr_vec_) { 430 | const int cargo_id = cargo->GetID(); 431 | 432 | // 如果是虚拟点 433 | if (cargo->IsVirtual()) { 434 | double distance = cargo->GetDistance(robot.GetPosition()); 435 | graph_ptr_->SetMisionBenifit(robot.id_, cargo->GetID(), 20.0 / (distance + 200)); 436 | continue; 437 | } 438 | 439 | // ! 如果货物为普通货物,或者货物到机器人的距离大于了 MAX_ROBOT_CARGO_MATCH_DISTANCE 就直接跳过 440 | if (cargo->value_ < MIN_ADVANCED_CARGO_VALUE || 441 | cargo->GetDistance(robot.GetPosition()) > MAX_ROBOT_CARGO_MATCH_DISTANCE) { 442 | continue; 443 | } 444 | 445 | // 如果货物已经被占用,就放弃 446 | if (cargo->IsOccupied() || 447 | self_robot_position.count(cargo->position_) && robot.GetPosition() != cargo->position_) { 448 | continue; 449 | } 450 | 451 | double distance1 = cargo->GetDistance(robot.GetPosition()); // 机器人到货物的距离 452 | double distance2 = cargo_berth_dist_[cargo->GetID()]; // 货物到泊位的距离 453 | 454 | // 如果货物寿命不足,就放弃 455 | if (cargo->GetRemainingLife() < 200 + distance1) { 456 | continue; 457 | } 458 | 459 | // 如果货物到不了泊位,就放弃 460 | if (distance2 > 1e5) { 461 | continue; 462 | } 463 | 464 | // 整个任务的总距离 465 | double distance = 0.2 * distance2 + distance1 + 50; 466 | double benefit = 1.0 * cargo->value_ / distance; 467 | benefit_vec.push_back({benefit, cargo}); 468 | } 469 | 470 | // 对于该机器人,选前若干优的货物进行匹配 471 | int cargo_num = std::min(static_cast(benefit_vec.size()), MAX_CARGO_NUM_FOR_ROBOT_MATCH); 472 | std::partial_sort(benefit_vec.begin(), benefit_vec.begin() + cargo_num, benefit_vec.end(), 473 | std::greater<>()); 474 | 475 | for (int i = 0; i < cargo_num; i++) { 476 | const auto &[benefit, cargo_ptr] = benefit_vec[i]; 477 | graph_ptr_->SetMisionBenifit(robot.id_, cargo_ptr->GetID(), benefit); 478 | } 479 | } 480 | } 481 | 482 | void LandScheduler::DivideBerthGroups() { 483 | #ifdef DONT_USE_MFMC_LIMITATIONO 484 | std::set tmp; 485 | for (int i = 0; i < g_berths.size(); i++) { 486 | tmp.insert(i); 487 | } 488 | graph_ptr_->SetBerthGroupLimitation(tmp, 100); 489 | return; 490 | #endif 491 | 492 | const auto &activated_berth_groups = g_berth_group_manager.GetActivatedBerthGroup(); 493 | int activated_group_num = activated_berth_groups.size(); 494 | 495 | for (const auto &group : activated_berth_groups) { 496 | graph_ptr_->SetBerthGroupLimitation(group.group_member_, g_robot_cnt / activated_group_num + 10); 497 | // std::cerr << "Limitation: "; 498 | // for (int x : group.group_member_) { 499 | // std::cerr << x << " "; 500 | // } 501 | // std::cerr << "num = " << g_robot_cnt / activated_group_num + 5 << std::endl; 502 | } 503 | 504 | std::set virtual_berth = {VIRTUAL_BERTH_ID}; 505 | graph_ptr_->SetBerthGroupLimitation(virtual_berth, INF); 506 | } 507 | 508 | 509 | void LandScheduler::UpdateMatch() { 510 | #ifdef LOCAL_TIMER 511 | Timer timer("LandScheduler::UpdateMatch()", 3); 512 | #endif // LOCAL_TIMER 513 | 514 | // 这一轮的匹配 515 | std::map match_result = graph_ptr_->MatchRobotCargo(); 516 | std::vector new_target(g_robots.size(), -1); 517 | 518 | for (int i = 0; i < g_robots.size(); i++) { 519 | if (match_result.count(i)) { 520 | new_target[i] = match_result[i]; 521 | } else { 522 | new_target[i] = -1; 523 | } 524 | } 525 | 526 | #ifdef DONT_USE_ROBOT_AVOID_SWING 527 | // 弃用机器人防摆头算法 528 | for (auto &robot : g_robots) { 529 | // ! 正在等待大模型的机器人,一定不要修改 530 | if (robot.FullCargo() || robot.IsWaitingForLLM()) { 531 | continue; 532 | } 533 | if (match_result.count(robot.id_)) { 534 | int cargo_id = (match_result[robot.id_]); 535 | robot.SetCargoPtr(cargo_ptr_vec_[cargo_id]); 536 | } else { 537 | robot.SetCargoPtr(nullptr); 538 | } 539 | } 540 | return; 541 | #endif 542 | 543 | // 记录上一轮的匹配 544 | 545 | std::vector old_target(g_robots.size(), -1); 546 | std::unordered_set ptr_set; 547 | for (auto ptr : cargo_ptr_vec_) { 548 | ptr_set.insert(ptr); 549 | } 550 | 551 | for (auto &robot : g_robots) { 552 | // ! 正在等待大模型的机器人,一定不要修改 553 | if (robot.FullCargo() || robot.IsWaitingForLLM()) { 554 | continue; 555 | } 556 | 557 | auto ptr = robot.GetCargoPtr(); 558 | 559 | // 这里非常重要。如果这个货物指针指向的货物是非法的,那么一定要提前删掉。 560 | if (ptr_set.count(ptr) == 0) { 561 | ptr = nullptr; 562 | robot.SetCargoPtr(nullptr); 563 | } 564 | if (ptr != nullptr && ptr->IsDeleted()) { 565 | ptr = nullptr; 566 | robot.SetCargoPtr(nullptr); 567 | } 568 | 569 | if (ptr != nullptr && !ptr->IsDeleted()) { 570 | old_target[robot.id_] = robot.GetCargoPtr()->GetID(); 571 | } 572 | } 573 | 574 | #ifdef LOCAL_TEST 575 | /** 576 | * @test 验证所有返回的匹配,不重不漏。验证旧匹配合法 577 | */ 578 | std::set stx, sty; 579 | for (auto [x, y] : match_result) { 580 | assert(stx.count(x) == 0 && sty.count(y) == 0); 581 | stx.insert(x); 582 | sty.insert(y); 583 | } 584 | 585 | for (int i = 0; i < g_robots.size(); i++) { 586 | if (g_robots[i].FullCargo() || g_robots[i].GetCargoPtr() == nullptr || 587 | g_robots[i].GetCargoPtr()->IsDeleted()) { 588 | continue; 589 | } 590 | 591 | for (int j = i + 1; j < g_robots.size(); j++) { 592 | if (g_robots[j].FullCargo() || g_robots[j].GetCargoPtr() == nullptr || 593 | g_robots[j].GetCargoPtr()->IsDeleted()) { 594 | continue; 595 | } 596 | 597 | assert(g_robots[i].GetCargoPtr()->GetID() != g_robots[j].GetCargoPtr()->GetID()); 598 | assert(old_target[i] != old_target[j] || old_target[i] == -1); 599 | assert(new_target[i] != new_target[j] || new_target[i] == -1); 600 | } 601 | } 602 | #endif 603 | 604 | // 并查集,将所有目标相关的机器人化为一个集合 605 | 606 | DisjointSetUnion dsu(g_robots.size()); 607 | 608 | for (int i = 0; i < g_robots.size(); i++) { 609 | if (g_robots[i].FullCargo() || g_robots[i].IsWaitingForLLM()) { 610 | continue; 611 | } 612 | for (int j = i + 1; j < g_robots.size(); j++) { 613 | if (g_robots[i].FullCargo() || g_robots[i].IsWaitingForLLM()) { 614 | continue; 615 | } 616 | if (old_target[i] == new_target[j] && old_target[i] != -1) { 617 | dsu.Merge(i, j); 618 | } 619 | if (old_target[j] == new_target[i] && old_target[j] != -1) { 620 | dsu.Merge(i, j); 621 | } 622 | } 623 | } 624 | 625 | // 根据并查集,决定每个组是否改变决策 626 | 627 | auto robot_groups = dsu.GetSets(); 628 | 629 | for (const auto &group : robot_groups) { 630 | // ! 正在等待大模型的机器人,一定不要修改 631 | if (std::any_of(group.begin(), group.end(), [&](int x) { return g_robots[x].IsWaitingForLLM(); })) { 632 | continue; 633 | } 634 | 635 | double old_benefit = 0; 636 | double new_benefit = 0; 637 | for (int robot_id : group) { 638 | old_benefit += graph_ptr_->GetMissionBenifit(robot_id, old_target[robot_id]); 639 | new_benefit += graph_ptr_->GetMissionBenifit(robot_id, new_target[robot_id]); 640 | } 641 | 642 | 643 | // TODO 这里的阈值, 不一定是最合理的. 644 | if ((new_benefit - old_benefit) / (old_benefit + EPS) > -INF) { 645 | for (int robot_id : group) { 646 | auto &robot = g_robots[robot_id]; 647 | int cargo_id = new_target[robot_id]; 648 | if (cargo_id != -1) { 649 | robot.SetCargoPtr(cargo_ptr_vec_[cargo_id]); 650 | } else { 651 | robot.SetCargoPtr(nullptr); 652 | } 653 | } 654 | } 655 | } 656 | 657 | #ifdef LOCAL_TEST 658 | /** 659 | * @test 验证本轮的匹配不会发生冲突 660 | */ 661 | int schedule_cnt = 0; 662 | for (int i = 0; i < g_robots.size(); i++) { 663 | if (g_robots[i].FullCargo()) { 664 | schedule_cnt += 1; 665 | } 666 | 667 | if (g_robots[i].FullCargo() || g_robots[i].GetCargoPtr() == nullptr || 668 | g_robots[i].GetCargoPtr()->IsDeleted()) { 669 | continue; 670 | } 671 | for (int j = i + 1; j < g_robots.size(); j++) { 672 | if (g_robots[j].FullCargo() || g_robots[j].GetCargoPtr() == nullptr || 673 | g_robots[j].GetCargoPtr()->IsDeleted()) { 674 | continue; 675 | } 676 | assert(g_robots[i].GetCargoPtr()->GetID() != g_robots[j].GetCargoPtr()->GetID()); 677 | } 678 | if (g_robots[i].GetCargoPtr() != nullptr) { 679 | schedule_cnt += 1; 680 | } 681 | } 682 | 683 | // fprintf(stderr, "frame %d : %d g_robots has target.", g_frame_id, schedule_cnt); 684 | // std::cerr << std::endl; 685 | #endif 686 | } 687 | 688 | LandScheduler g_land_scheduler; --------------------------------------------------------------------------------