软件精英挑战赛


华为软件精英挑战赛——普朗克计划

2024华为软件精英挑战赛——普朗克计划

赛题:https://bbs.huaweicloud.com/forum/thread-0209145106256505005-1-1.html

解题思路

题目概括:地图随机生成价值不等的货物,控制10个机器人收集货物到港口;地图中会有10个固定港口存储机器人的货物,控制5艘轮船前往港口搬运货物到销售处获得金额。

我们队伍的思路是设计多个模块,运动模块控制机器人运动,包括上下左右、搬运、放等动作;目标选择模块帮助机器人选择目标货物和目标港口;路径规划模块帮助机器人进行路径查找;初始化模块获取初始化地图信息,预先查找部分路径和获取机器人状态;运输模块控制船收集买卖货物;防碰撞模块避免多个机器人之间互相碰撞与撞死;

具体实现

一、初始化模块

与判题器交互采用标准输入输出,在初始化阶段,我们获取地图信息,包括:障碍物、船、机器人、港口、机器人状态等信息。

def Init():

    for i in range(0, map_size):
        line = sys.stdin.readline().strip()
        ch.append(list(line))
        # line = input()
        # ch.append([c for c in line.split(sep=" ")]) # 读取地图
    for i in range(berth_num):
        line = input()
        berth_list = [int(c) for c in line.split(sep=" ")]
        id = berth_list[0]
        berths[id].x = berth_list[1]
        berths[id].y = berth_list[2]
        berths[id].transport_time = berth_list[3]
        if berths[id].transport_time > 1500:
            berth_final.append(id)
            berths[id].choose=1
            # None
        berths[id].loading_speed = berth_list[4]     #初始化泊位
    boat_capacity = int(input())            #初始化轮船容积
    okk = input()   #初始化完成,读取ok
    #确定船的坐标
    for i in range(berth_num):
        berth_x=berths[i].x
        berth_y=berths[i].y
        berth_con=[(berth_x,berth_y),(berth_x,berth_y+3),(berth_x+3,berth_y),(berth_x+3,berth_y+3)]
        berth_nums=[]
        index = 0
        for con in berth_con:
            nums = 0
            for x in range(-1,2):
                for y in range(-1,2):
                    if x==0 and y==0:
                        continue
                    xx = con[0]+x;
                    yy = con[1]+y;
                    if min(xx,yy) >= 0 and max(xx,yy) <= 199 and ch[xx][yy] == '.':
                        nums = nums+1
            berth_nums.append([index , nums])
            index = index + 1
        sorted_num = sorted(berth_nums, key=lambda x : x[1] , reverse=True)
        point_1 = berth_con[sorted_num[0][0]]
        point_2 = berth_con[sorted_num[1][0]]
        berths[i].x = (point_1[0]+point_2[0])//2
        berths[i].y = (point_1[1]+point_2[1])//2
                
    obstacles=get_obstacles(ch)

    # print(obstacles,file=sys.stderr)
    
    for obstacle in obstacles:
        obstacles_set.add(Node(obstacle[0],obstacle[1]))

    return obstacles ,boat_capacity

二、目标选择模块

2.1 定义状态表

总共10个机器人,每个机器人有7个信息需要存储:目标货物的x坐标目标货物的y坐标目标港口的id机器人此时的状态位(0表示闲置、1表示确定目标货物,需要寻找路径、2表示正在前往目标货物、3表示已经取到货物,需要搜索目标港口的路径、4表示正在前往目标港口)、目标货物的价格该机器人是否是初始的机器人上一次的目标港口

target_table = np.zeros((10,7),dtype=int)
target_table[:,6] = -1  #最初所有机器人都是初始机器人,当机器人完成第一次寻路之后即不是初始机器人

2.2 定义策略

选择货物的衡量标准是单位时间获得的金额大小即:$p=\frac{货物价值}{初始点到货物的距离+货物到港口的距离}$

公式中的距离我们采用真实距离,即真实路径的长度。但是如果是初始化的机器人,我们不采用真实路径长短,初始的货物很少,只要是机器人可达货物,即可前往。需要注意的是,如果某一个货物已经成为机器人的目标,则其他机器热无法选择该货物,我们将该货物到其他机器人的距离设置成99999

def robot2goods(goods,robots,berths,paths,now_frame,robot_to_init_goods,boat_capacity,berth_final):
    global target_table
    index_good_choosen = []
    if len(goods.available_goods) == 0:
        return []
    available_goods = np.array(goods.available_goods)
    #第一步计算出船厂与所有good的距离
    berth2good = np.zeros((10,len(goods.available_goods)),dtype=int)
    for index_berth,berth in enumerate(berths):
        for index_good in range(len(goods.available_goods)):
            berth2good[index_berth][index_good] = paths[index_berth][(goods.available_goods[index_good][0],goods.available_goods[index_good][1])]
    #当船厂号在最终不可达船厂号,设置为无限大
    
    
    ber2good_final = berth2good.copy()
    for i in range(10):
        if i in berth_final:
            ber2good_final[i,:] = 99999
    

    #获得所有物品的死亡帧
    death_good = np.array(available_goods[:,3]) + 1000
    #获取所有物品价值
    good_value = np.array(available_goods[:,2])
    #获得good到其最近船厂的距离和对应船厂id
    good2berth_id = np.argmin(ber2good_final,axis=0)#1*n
    # print(good2berth_id,file=sys.stderr)
    good2berth_value = np.amin(ber2good_final,axis=0)#1*n



    for index_robot,robot in enumerate(robots):
        #判断是否为初始机器人
        if target_table[index_robot][5] == 1:
            if target_table[index_robot][3] == 0 and robot.live and np.any(good2berth_value < 99999):
                #获取机器人所在的berth号

                berth_id = target_table[index_robot][2]
                robot2good = berth2good[berth_id]
                #确定机器人是否能拿到货物,不能拿到货的r2g距离设置成99999
                RobotArriveTime = robot2good + now_frame + 10
                CanNotReach = RobotArriveTime > death_good
                robot2good[CanNotReach] = 99999
                all_path_len = robot2good + good2berth_value 

                #当最后一轮时要考虑船厂死亡帧
                if now_frame > 12500:
                    for ind in range(10):
                        list_berths = np.where(good2berth_id == ind)[0]
                        if len(list_berths) > 0:
                            need_to_op = all_path_len[list_berths] + now_frame + 5
                            CanNotReach = need_to_op > berth_death[ind]
                            cannot = list_berths[CanNotReach]
                            all_path_len[cannot] = 99999

                m = np.divide(good_value, all_path_len)
                Choose_good_id = np.argmax(m)
                Choose_berth_id = good2berth_id[Choose_good_id]
                #如果选择的还是大,那么不选
                if robot2good[Choose_good_id]>=99999 or all_path_len[Choose_good_id]>=99999:
                    continue
                index_good_choosen.append(Choose_good_id)
                good2berth_value[Choose_good_id] = 99999
                target_table[index_robot][0] = available_goods[Choose_good_id][0]
                target_table[index_robot][1] = available_goods[Choose_good_id][1]
                target_table[index_robot][2] = Choose_berth_id
                target_table[index_robot][3] = 1
                target_table[index_robot][4] = available_goods[Choose_good_id][2]
                target_table[index_robot][6] = berth_id
        else:
            if target_table[index_robot][3] == 0 and robot.live and np.any(good2berth_value < 99999):
                

                second_goods2robot=robot_to_init_goods[index_robot]
                all_path_len = second_goods2robot + good2berth_value
                m = np.divide(good_value, all_path_len)
                Choose_good_id = np.argmax(m)

                #如果选择的还是大,那么不选
                if second_goods2robot[Choose_good_id]>=99999 or all_path_len[Choose_good_id]>=99999:
                    continue
                Choose_berth_id = good2berth_id[Choose_good_id]
                index_good_choosen.append(Choose_good_id)
                good2berth_value[Choose_good_id] = 99999
                target_table[index_robot][0] = available_goods[Choose_good_id][0]
                target_table[index_robot][1] = available_goods[Choose_good_id][1]
                target_table[index_robot][2] = Choose_berth_id
                target_table[index_robot][3] = 1
                target_table[index_robot][4] = available_goods[Choose_good_id][2]

    return index_good_choosen

三、路径规划模块

3.1 A*算法寻找路径

机器人路径规划问题中,我们采用常见的A*算法来实现对机器人路径优化。在算法中,我们采用欧氏距离作为启发式函数:

# A*算法
def astar(start, goal, obstacles):
    open_set = set()
    closed_set = set()
    current = start
    open_set.add(current)
    # counter = 1
    while open_set:
        # counter += 1
        # if counter >= 10000:
        #     return None
        current = min(open_set, key=lambda x: x.g + x.h)
        open_set.remove(current)
        closed_set.add(current)

        if get_distance(current, goal) ==0:
            path = []
            while current:
                path.append(current)
                current = current.parent
            return path[::-1] 

        for neighbor in get_neighbors(current, obstacles):
            if neighbor in closed_set:
                continue
            # tentative_g = current.g+ 1/3*get_distance(current, neighbor)
            tentative_g =  get_distance(current, neighbor)
            if neighbor not in open_set:
                open_set.add(neighbor)
                neighbor.h = get_distance(neighbor, goal)
            elif tentative_g >= neighbor.g:
                continue

            neighbor.parent = current
            neighbor.g = tentative_g

    return None


# 获取当前点的邻居点
def get_neighbors(node, obstacles):
    neighbors = []
    for x in range(-1, 2):
        for y in range(-1, 2):
            if x == 0 and y == 0 or(x*y!=0):
                continue
            if node.x + x <0 or node.x + x >200 or node.y + y <0 or node.y + y > 200 or Node(
                    node.x + x , node.y + y ) in obstacles:
                continue
            neighbor = Node(node.x + x , node.y + y )
            neighbors.append(neighbor)
    return neighbors
 

def get_distance(node1, node2):
    return math.sqrt((node1.x - node2.x) ** 2 + (node1.y - node2.y) ** 2)


def get_manhadundis(node1, node2):
    return abs(node1.x - node2.x) + abs(node1.y - node2.y)

A*算法查找路径效果如下:

A*

A可以很好的动态找到路径,但是经过多次优化,A\查找一次路径的时间还是很长:

考虑到机器人与判题器交互时间上限是15ms,如果多个机器人同时查找路径,那时间显然是不够的。考虑到除了初始化的机器人,即机器人在初始化之后每次运货都是从某个港口出发,再回到某个港口。因此很自然的想到,我们可以存储每个港口到所有位置的所有路径,机器人每次寻找路径只需要查表即可,于是改变思路,我们只在初始化阶段使用A*寻找路径。

3.2 广度优先搜索遍历,查找港口到所有点的路径。

def is_valid(x, y, rows, cols):
    return 0 <= x < rows and 0 <= y < cols

def find_paths(initial_positions, obstacle_coordinates, rows, cols):
    obstacles = set(obstacle_coordinates)
    paths = {}

    for start_x, start_y in initial_positions:
        visited = set()
        queue = deque([(start_x, start_y, [])])

        while queue:
            current_x, current_y, path = queue.popleft()

            if (current_x, current_y) not in visited:
                visited.add((current_x, current_y))

                if (current_x, current_y) not in obstacles:
                    current_path = path + [(current_x, current_y)]
                    paths[(start_x, start_y, current_x, current_y)] = current_path

                    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                        new_x, new_y = current_x + dx, current_y + dy

                        if is_valid(new_x, new_y, rows, cols) and (new_x, new_y) not in visited:
                            queue.append((new_x, new_y, current_path))

    return paths

这样我们只需要在初始化阶段遍历10次地图(因为每个港口都需要遍历一次地图)即可查找10个港口到所有点的路径表。经过我们的计算,在200200的地图中,可达点大约有25000,也就是每个港口我们要存储25000个路径,每个路径是一个长度平局为100的列表,经过我们的计算,这个路径表会占用存储将近*3GB。担心比赛的提交系统有存储上限,因此这个方案我们没有使用。

3.2 路径表转换方向表

我们为每一个港口存储一个200*200的方向表,每个位置存储01234、0表示该位置无法到达该港口,1表示该位置到达港口则需要机器人向下移动,以此类推如下图:绿色表示港口,黑色表示障碍物:

这样我们只需要存储10个200*200的表格,然后需要路径时,根据方向表格遍历提取路径,这样存储仅为:

在表格中提取路径时间为:

image-20240329150928478

同时,在广搜过程中,我们记录方向表和单通道。方向表为查找路径,单通道为防止机器人在单通道撞死。效果如下,红色表示从目标港口到目标货物提取的路径,黄色表示地图中的单通道:

四、运动模块

简单通过标准输出控制机器人和船运动:

def robots_movation(robot_num):
    for i in range(robot_num):
        print("move", i, 1)
        sys.stdout.flush()
def robot_up(robot_id):
    print("move", robot_id, 2)

def robot_down(robot_id):
    print("move", robot_id, 3)

def robot_left(robot_id):
    print("move", robot_id, 1)

def robot_right(robot_id):
    print("move", robot_id, 0)

def robot_get_goods(robot_id):
    print("get", robot_id)

def robot_pull_goods(robot_id):
    print("pull",robot_id)

def boat_ship(boat_id,berth_id):
    print("ship",boat_id,berth_id)

def boat_go_virtual(boat_id):
    print("go",boat_id)

五、防碰撞模块

防碰撞策略为:单通道进行锁死,非单通道采用优先停止,其次左右最后后退的策略防止机器人碰撞:

def detect_collision_1(paths , robots , track_index, connected_points , connected_locks):
    
    bot_num = 10
    collision_flag = True
    reback_status = [0] * 10
    now_position = set()
    robot_next_pos = [] # 存储机器人的位置信息
    robot_flag = [0] * 10 # 标记机器人下一个执行位置是否合法
    robot_next_pos_1 = set()
    waits = [[] for _ in range(10)]
    # nums = []

    #初始化robot_pos
    for index in range(bot_num):
        now_position.add(Node(robots[index].x , robots[index].y))
        if track_index[index] < len(paths[index]) and isinstance(paths[index][track_index[index]] , tuple) and len(paths[index][track_index[index]]) > 1:
            robot_flag[index] = 1
            robot_next_pos.append(Node(paths[index][track_index[index]][0],paths[index][track_index[index]][0]))
        else:
            robot_next_pos.append(Node(-1,-1))

    robot_next_pos_1 = set(robot_next_pos)
    if len(connected_locks) > 15:
        #进行单通道的解锁
        for index in range(len(connected_locks)):
            if connected_locks[index] != 0:
                pos = Node(robots[connected_locks[index]-1].x , robots[connected_locks[index]-1].y)
                if pos not in connected_points[index]:
                    connected_locks[index] = 0

        #进行单通道的上锁
        for index in range(bot_num):
            step = 0
            index_1 = max(0 , min(len(paths[index])-1 , track_index[index]+step))
            cancel_flag = True
            if index_1 < len(paths[index]) and isinstance(paths[index][index_1] , tuple) and len(paths[index][index_1]) > 1:
                pos = Node(paths[index][index_1][0] , paths[index][index_1][1])
                for i in range(len(connected_points)):
                    if pos in connected_points[i]:
                        cancel_flag = False
                        if connected_locks[i] == index+1:
                            continue
                        elif connected_locks[i] == 0:
                            connected_locks[i] = index+1
                        else:
                            # track_index[index] = max(0 , track_index[index]-1)
                            # 单通道门口执行左右移动
                            if direction_flag[index] == 0:
                                reback_status[index] = 2
                                left_right_flag = False # 判断其他两个方向有没有空地
                                wait_pos = [] #构建可选点,长度为1或者2
                                for dict in direction:
                                    pos = (robots[index].x+dict[0] , robots[index].y+dict[1])
                                    pos_1 = Node(robots[index].x+dict[0],robots[index].y+dict[1])
                                    if min(pos[0],pos[1]) >= 0 and max(pos[0] , pos[1]) <= 199 and pos != paths[index][track_index[index]] and pos_1 not in obstacles_set and pos_1 not in robot_next_pos_1 and pos_1 not in now_position: # 测试时修改为live_points_1,同时传入
                                        if track_index[index] >= 2 and pos == paths[index][track_index[index]-2]:
                                            continue
                                        wait_pos.append(pos)
                                        left_right_flag = True
                                if not left_right_flag: # 没有左右的执行回退
                                    track_index[index] = max(0 , track_index[index]-2)
                                    reback_status[index] = 3
                                else:
                                    pos = wait_pos[0]
                                    waits[index] = wait_pos
                                    paths[index].insert(track_index[index] , pos)
                                    paths[index].insert(track_index[index]+1 , (robots[index].x , robots[index].y))
                                    reback_status[index] = 2 # 发生过左右的情况
                                direction_flag[index] = 1
                            else:
                                reback_status[index] = 3
                                track_index[index] = max(0 , track_index[index]-2)
                        break
                if cancel_flag:
                    direction_flag[index] = 0
    

    while(collision_flag):
        collision_flag = False
        bots = list(itertools.combinations(robots, 2))
        random.shuffle(bots)

        for bot in bots:
            robot_1 = bot[0].id
            robot_2 = bot[1].id
            if len(paths[robot_1]) <= 1 or len(paths[robot_2]) <= 1 or track_index[robot_1] < 0 or track_index[robot_2] < 0 or track_index[robot_1] >= len(paths[robot_1]) or track_index[robot_2] >= len(paths[robot_2]):
                continue
            
            # 两种情况,直接冲突和交换冲突
            if (paths[robot_1][track_index[robot_1]][0] == paths[robot_2][track_index[robot_2]][0] and paths[robot_1][track_index[robot_1]][1] == paths[robot_2][track_index[robot_2]][1]) or ((robots[robot_1].x == paths[robot_2][track_index[robot_2]][0] and robots[robot_1].y == paths[robot_2][track_index[robot_2]][1]) and (robots[robot_2].x == paths[robot_1][track_index[robot_1]][0] and robots[robot_2].y == paths[robot_1][track_index[robot_1]][1])):
                
                target_robot = robot_1 if reback_status[robot_1] < reback_status[robot_2] else robot_2

                #编号小的保持不变,编号大的进行处理,即robot_2,这里规定0代表未发生碰撞,1代表停止,2代表其他两个方向寻路,==3代表回退
                reback_status[target_robot] = reback_status[target_robot] + 1
                if reback_status[target_robot] >= 4:# 已经进行过所有避障操作
                    continue
                elif reback_status[target_robot] == 3: # 已经产生了左右退的现象,弹出插入的点执行回退
                    if len(waits[target_robot]) == 2: # 如果还有备选位置,执行另一个躲避位置
                        paths[target_robot][track_index[target_robot]] = waits[target_robot][1]
                        waits[target_robot] = []
                        reback_status[target_robot] = 2
                    else:
                        del paths[target_robot][track_index[target_robot]]
                        track_index[target_robot] = max(0 , track_index[target_robot]-1)
                elif reback_status[target_robot] == 1 and paths[robot_1][track_index[robot_1]] == paths[robot_2][track_index[robot_2]]: #产生相撞于一点
                    track_index[target_robot] = max(0 , track_index[target_robot]-1)
                elif (reback_status[target_robot] == 1 and ((robots[robot_1].x , robots[robot_1].y) == paths[robot_2][track_index[robot_2]] and (robots[robot_2].x , robots[robot_2].y) == paths[robot_1][track_index[robot_1]])):
                    left_right_flag = False # 判断其他两个方向有没有空地
                    wait_pos = [] #构建可选点,长度为1或者2
                    for dict in direction:
                        pos = (robots[target_robot].x+dict[0] , robots[target_robot].y+dict[1])
                        pos_1 = Node(robots[target_robot].x+dict[0],robots[target_robot].y+dict[1])
                        if min(pos[0],pos[1]) >= 0 and max(pos[0] , pos[1]) <= 199 and pos != paths[target_robot][track_index[target_robot]] and pos_1 not in obstacles_set and pos_1 not in robot_next_pos_1 and pos_1 not in now_position: # 测试时修改为live_points_1,同时传入
                            if track_index[target_robot] >= 2 and pos == paths[target_robot][track_index[target_robot]-2]:
                                continue
                            wait_pos.append(pos)
                            left_right_flag = True
                    if not left_right_flag: # 没有左右的执行回退
                        track_index[target_robot] = max(0 , track_index[target_robot]-2)
                        reback_status[target_robot] = 3
                    else:
                        pos = wait_pos[0]
                        waits[target_robot] = wait_pos
                        paths[target_robot].insert(track_index[target_robot] , pos)
                        paths[target_robot].insert(track_index[target_robot]+1 , (robots[target_robot].x , robots[target_robot].y))
                        reback_status[target_robot] = 2 # 发生过左右的情况
                elif reback_status[target_robot] == 2:# 这里是对之前相撞于一点的情况进行处理
                    left_right_flag = False # 判断其他两个方向有没有空地
                    wait_pos = [] #构建可选点,长度为1或者2
                    for dict in direction:
                        pos = (robots[target_robot].x+dict[0] , robots[target_robot].y+dict[1])
                        pos_1 = Node(robots[target_robot].x+dict[0] , robots[target_robot].y+dict[1])
                        if min(pos[0],pos[1]) >= 0 and max(pos[0] , pos[1]) <= 199 and pos != paths[target_robot][track_index[target_robot]+1] and pos not in obstacles and pos_1 not in robot_next_pos_1 and pos_1 not in now_position: # 测试时修改为live_points_1,同时传入
                            if track_index[target_robot] >= 1 and pos == paths[target_robot][track_index[target_robot]-1]:
                                continue
                            wait_pos.append(pos)
                            left_right_flag = True
                    if not left_right_flag: # 没有左右的执行回退
                        track_index[target_robot] = max(0 , track_index[target_robot]-1)
                        reback_status[target_robot] = 3
                    else:
                        pos = wait_pos[0]
                        waits[target_robot] = wait_pos
                        paths[target_robot].insert(track_index[target_robot] , pos)
                        reback_status[target_robot] = 2 # 发生过左右的情况
                collision_flag = True

                    # # 更新robot_next_pos
                if isinstance(paths[target_robot][track_index[target_robot]] , tuple) and len(paths[target_robot][track_index[target_robot]]) > 1:
                    robot_next_pos[target_robot] = Node(paths[target_robot][track_index[target_robot]][0] , paths[target_robot][track_index[target_robot]][0])
                    robot_next_pos_1 = set(robot_next_pos)
    return track_index,paths,connected_locks

六、运输模块

五艘船选取港口的指标为:$p=\frac{船可以装的货物价值}{买卖点到港口的时间+装货时间+离开时间}$,五艘船轮流选择目标港口,到达港口之后如果可以装满,则装满离开,如果无法装满,则根据$p$考虑是否前往下一个港口继续装。

设置轮船的状态表,分别表示:目的港口id轮船状态位(0表示闲置,1表示确定好港口,2表示在去港口的路上,3表示正在装货,4表示准备离开港口,5表示正在回买卖点)、轮次船的转折次数

target_table_boat_berth = np.zeros((5,4),dtype=int)
target_table_boat_berth[:,0] = -1

运输选择:

def update_BoatStatus(boats,berths,capacity,now_frame):
    global target_table_boat_berth
    global berth_final
    global berth_death
    global berth_death_num
    for index_boat in range(5):
        #(共用)
        if target_table_boat_berth[index_boat][1] == 5 and boats[index_boat].update_status():
            target_table_boat_berth[index_boat][1] = 0
            target_table_boat_berth[index_boat][3] = 0
            #第五轮直接设置为转移过一次了,不会再转了
            # if target_table_boat_berth[index_boat][2] == 4:
            #     target_table_boat_berth[index_boat][3] = 1
            boats[index_boat].idle()
        #第一轮6000帧以内,涉及到它只回去一次,也可能回去两次。不管如何6000帧必须在虚拟点,并且前期就是要么装满走人,如果转移的话考虑船内装没装够0.9cap,装够的话去虚拟点卖就行。只有前两轮用这个
        if now_frame < 12000 :
            if target_table_boat_berth[index_boat][1] == 0:
                    #选取目标
                    berth_index = -1
                    #第一轮就正常选
                    if target_table_boat_berth[index_boat][2] == 0:
                        berth_index = select_first_two_first(berths,now_frame)
                    #只有能选的berth才会执行
                    if berth_index != -1:
                        target_table_boat_berth[index_boat][0] = berth_index
                        target_table_boat_berth[index_boat][1] = 1
                        berths[berth_index].choosen = 1
                    #如果没选到直接啥也不干轮次+1
                    # else:
                    #     target_table_boat_berth[index_boat][2] += 1
                #确定是否到船厂
            if target_table_boat_berth[index_boat][1] == 2 and boats[index_boat].update_status():
                target_table_boat_berth[index_boat][1] = 3
            #装货状态判断
            if target_table_boat_berth[index_boat][1] == 3:
                index_berth = target_table_boat_berth[index_boat][0]
                #判断是否可以继续装货
                if len(berths[index_berth].GoodsOfBerth) > 0 and boats[index_boat].inventory < capacity:
                    boats[index_boat].load(berths[index_berth].unload(capacity - boats[index_boat].inventory))
                #不能继续装货有两种情况,第一种是港口空了,第二种是船满了
                else:
                    #先解锁
                    berths[index_berth].choosen = 0
                    #如果是装满了就直接回去
                    if boats[index_boat].inventory >= capacity:
                        target_table_boat_berth[index_boat][1] = 4
                    #没装满。
                    else:
                        #如果装够0.9个cap直接走人就行了
                        if boats[index_boat].inventory >= 0.9 * capacity:
                            target_table_boat_berth[index_boat][1] = 4
                        #如果没装够0.9
                        else:
                            berth_index = select_first_two_second(berths,capacity - boats[index_boat].inventory,now_frame)
                            if berth_index != -1:
                                target_table_boat_berth[index_boat][0] = berth_index
                                target_table_boat_berth[index_boat][1] = 1
                                berths[berth_index].choosen = 1
                                target_table_boat_berth[index_boat][3] += 1 
                if (12000 - now_frame) <= berths[index_berth].transport_time:
                    target_table_boat_berth[index_boat][1] = 4

        else:
            #全新改版
            if 12000 <= now_frame <= 12005 :
                if target_table_boat_berth[index_boat][1] == 0:
                #只有最后一轮直接定
                        berth_index = select(berths,capacity)
                        target_table_boat_berth[index_boat][0] = berth_index
                        target_table_boat_berth[index_boat][1] = 1
                        berths[berth_index].choosen = 1
                        target_table_boat_berth[index_boat][2] = 4
                        #最后一轮直接加时间
                        if target_table_boat_berth[index_boat][2] == 4:
                            l_time = len(berths[berth_index].GoodsOfBerth) / berths[berth_index].loading_speed
                            #这样可以
                            berth_death[berth_index] = now_frame + berths[berth_index].transport_time + l_time + 5
                            berth_death_num += 1
            #确定是否到船厂(共用)
            if target_table_boat_berth[index_boat][1] == 2 and boats[index_boat].update_status():
                target_table_boat_berth[index_boat][1] = 3
            #装货状态
            if target_table_boat_berth[index_boat][1] == 3:
                index_berth = target_table_boat_berth[index_boat][0]
                #判断是否可以继续装货
                if len(berths[index_berth].GoodsOfBerth) > 0 and boats[index_boat].inventory < capacity:
                    boats[index_boat].load(berths[index_berth].unload(capacity - boats[index_boat].inventory))
                else:
                    #先解锁
                    berths[index_berth].choosen = 0
                    #如果是倒数第二轮最后走的时候不解锁
                    # if target_table_boat_berth[index_boat][2] == 3 and target_table_boat_berth[index_boat][3] == 1:
                    #     berths[index_berth].choosen = 1
                    #如果是装满了就直接回去(实际不会出现)
                    if boats[index_boat].inventory >= capacity:
                        target_table_boat_berth[index_boat][1] = 4
                        if target_table_boat_berth[index_boat][2] == 4:
                            if len(berth_final) == 9:
                                berth_final.clear()
                                berth_death[:] = 99999
                               # print('清空',file=sys.stderr)
                            #最多为4因此应该小于9,这样最后加完应该为9
                            if len(berth_final) < 9:
                                berth_final.append(target_table_boat_berth[index_boat][0])
                            # None
                    else:#没装满考虑怎么走
                    #如果没装满并且没有转移过,找下家
                        if target_table_boat_berth[index_boat][3] == 0:
                            if target_table_boat_berth[index_boat][2] == 4:
                                berth_final.append(target_table_boat_berth[index_boat][0])
                                #print(f'frame:{now_frame},boat_id:{index_boat},left:{boats[index_boat].inventory},boat_table:\n{target_table_boat_berth}',file=sys.stderr)
                            berth_index = select_two(berths,capacity - boats[index_boat].inventory,now_frame)
                            target_table_boat_berth[index_boat][0] = berth_index
                            target_table_boat_berth[index_boat][1] = 1
                            berths[berth_index].choosen = 1
                            target_table_boat_berth[index_boat][3] = 1 
                            if target_table_boat_berth[index_boat][2] == 4:
                                #记录船厂被锁时间
                                berth_death[berth_index] = 15000 - berths[berth_index].transport_time
                                berth_death_num += 1

                                if berth_death_num == 10:
                                    # 找到不是 999 的值的下标
                                    indices = np.where(berth_death != 99999)[0]
                                    # 通过下标获取对应的值
                                    values = berth_death[indices]
                                    # 找到最大值的下标
                                    max_index = np.argmax(values)
                                    # 在原始矩阵中找到最大值的位置
                                    max_value_index = indices[max_index]
                                    berth_death[max_value_index] = 99999
                                
                        #如果转移过只有呆满才能走
                        else: 
                            if (3000 - (now_frame % 3000)) <= berths[index_berth].transport_time:
                                target_table_boat_berth[index_boat][1] = 4  
                                if target_table_boat_berth[index_boat][2] == 4:
                                    if len(berth_final) == 9:
                                        berth_final.clear()
                                        berth_death[:] = 99999
                                        #print('清空',file=sys.stderr)
                                    #最多为4因此应该小于9,这样最后加完应该为9
                                    if len(berth_final) < 9:
                                        berth_final.append(target_table_boat_berth[index_boat][0])

七、总结

  • 采用静态和贪心的策略进行货物的选择,可能导致机器人运货量达不到最优;、

  • 防碰撞没有没有做到完美,机器人仍会出现卡死的情况

  • 整体代码链接:https://github.com/ghtll/huaweibisai2024


文章作者: ghtll
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ghtll !