2021年美赛A题真菌元胞自动机Python实现
import matplotlib.pyplot as plt import random import numpy as np import matplotlib.animation as animation def save_fungi_ca_gif(): # save the gif file to the path target_gif_path = "E:/engineering space/figure/gif format/" target_gif_name = "aggressive_ca.gif" target_gif_full = target_gif_path + target_gif_name anim_1.save(target_gif_full, writer='pillow') # save the animation print('Saved') def calculate_random_neighbour(stay, left, up, right, down, no_reproduce): # get the relative location of the newly total_rate = stay + left + up + right + down + no_reproduce # reproduced fungi cell if total_rate == 0.0: total_rate = 1 cap1 = stay / total_rate # cap 1 to 5 is the boundary of probability area cap2 = cap1 + left / total_rate cap3 = cap2 + up / total_rate cap4 = cap3 + right / total_rate cap5 = cap4 + down / total_rate rnd = random.random() if 0 <= rnd < cap1: return 0 elif cap1 <= rnd < cap2: # divide the range into 6 parts return 1 elif cap2 <= rnd < cap3: # randomly select one part return 2 elif cap3 <= rnd < cap4: return 3 elif cap4 <= rnd < cap5: return 4 else: return 5 def get_extension_parameters(i_get_extension_parameters): # get the extension parameters extension_parameters = extension_rate_list[fungi_list[i_get_extension_parameters][3]] return extension_parameters def get_random_neighbour(i_get_random_neighbour): # calculate the density of neighbour mesh as well as self mesh extension_rate_get_random_neighbour = get_extension_parameters(i_get_random_neighbour) stay_density = my_board[fungi_list[i_get_random_neighbour][0], fungi_list[i_get_random_neighbour][1]] / one_mesh_max stay_rate = extension_rate_get_random_neighbour - stay_density if fungi_list[i_get_random_neighbour][1] != 0: left_density = my_board[fungi_list[i_get_random_neighbour][0], fungi_list[i_get_random_neighbour][1] - 1] \ / one_mesh_max left_rate = extension_rate_get_random_neighbour * ( 1 - left_density + 0.1) # further calculate the transferring rate of # the new fungi else: left_density = 0.5 left_rate = 0 if fungi_list[i_get_random_neighbour][0] != 0: up_density = my_board[fungi_list[i_get_random_neighbour][0] - 1, fungi_list[i_get_random_neighbour][1]] \ / one_mesh_max up_rate = extension_rate_get_random_neighbour * (1 - up_density + 0.1) else: up_density = 0.5 up_rate = 0 if fungi_list[i_get_random_neighbour][1] != patch_size - 1: right_density = my_board[ fungi_list[i_get_random_neighbour][0], fungi_list[i_get_random_neighbour][1] + 1] \ / one_mesh_max right_rate = extension_rate_get_random_neighbour * (1 - right_density + 0.1) else: right_density = 0.5 right_rate = 0 if fungi_list[i_get_random_neighbour][0] != patch_size - 1: down_density = my_board[fungi_list[i_get_random_neighbour][0] + 1, fungi_list[i_get_random_neighbour][1]] \ / one_mesh_max down_rate = extension_rate_get_random_neighbour * (1 - down_density + 0.1) else: down_density = 0.5 down_rate = 0 total_density = stay_density + left_density + up_density + right_density + down_density no_reproduce_rate = total_density / 8 neighbour_location = calculate_random_neighbour(stay_rate, left_rate, up_rate, right_rate, down_rate, no_reproduce_rate) return neighbour_location def reproduce_one_cell(i_reproduce_one_cell): # reproduce a single fungi cell iterations = 0 while True: # loop until the random neighbour meets the boundary conditions iterations += 1 # print('iteration:', iterations) reproduce_location = get_random_neighbour(i_reproduce_one_cell) # the followings are boundary conditions if iterations < 3: if reproduce_location == 0: # stay in one mesh current_mesh_num = my_board[fungi_list[i_reproduce_one_cell][0], fungi_list[i_reproduce_one_cell][1]] if current_mesh_num < one_mesh_max: # haven't arrived the maximum of one mesh break # this is the location wanted else: # print(iterations) # how many loops have been taken continue # random again if reproduce_location == 1: # go to left mesh if fungi_list[i_reproduce_one_cell][1] == 0: # reaches the left boundary # print(iterations) continue # random again elif my_board[fungi_list[i_reproduce_one_cell][0], fungi_list[i_reproduce_one_cell][1] - 1] \ < one_mesh_max: break # this is the location wanted else: continue if reproduce_location == 2: # go to up mesh if fungi_list[i_reproduce_one_cell][0] == 0: # reaches the up boundary # print(iterations) continue # random again elif my_board[fungi_list[i_reproduce_one_cell][0] - 1, fungi_list[i_reproduce_one_cell][1]] \ < one_mesh_max: break # this is the location wanted else: continue if reproduce_location == 3: # go to right mesh if fungi_list[i_reproduce_one_cell][1] == patch_size - 1: # reaches the right boundary # print(iterations) continue # random again elif my_board[fungi_list[i_reproduce_one_cell][0], fungi_list[i_reproduce_one_cell][1] + 1] \ < one_mesh_max: break # this is the location wanted else: continue if reproduce_location == 4: # go to down mesh if fungi_list[i_reproduce_one_cell][0] == patch_size - 1: # reaches the down boundary continue # print(iterations) elif my_board[fungi_list[i_reproduce_one_cell][0] + 1, fungi_list[i_reproduce_one_cell][1]] \ < one_mesh_max: break # this is the location wanted else: continue else: reproduce_location = 5 break global fungi_num # take reproducing activity if reproduce_location != 5: if reproduce_location == 0: fungi_list.append([fungi_list[i_reproduce_one_cell][0], fungi_list[i_reproduce_one_cell][1], 0, fungi_list[ i_reproduce_one_cell][3]]) # print('Stayed') if reproduce_location == 1: fungi_list.append([fungi_list[i_reproduce_one_cell][0], fungi_list[i_reproduce_one_cell][1] - 1, 0, fungi_list[i_reproduce_one_cell][3]]) if reproduce_location == 2: fungi_list.append([fungi_list[i_reproduce_one_cell][0] - 1, fungi_list[i_reproduce_one_cell][1], 0, fungi_list[i_reproduce_one_cell][3]]) if reproduce_location == 3: fungi_list.append([fungi_list[i_reproduce_one_cell][0], fungi_list[i_reproduce_one_cell][1] + 1, 0, fungi_list[i_reproduce_one_cell][3]]) if reproduce_location == 4: fungi_list.append([fungi_list[i_reproduce_one_cell][0] + 1, fungi_list[i_reproduce_one_cell][1], 0, fungi_list[i_reproduce_one_cell][3]]) fungi_list[i_reproduce_one_cell][2] += 1 # the reproduced time of a fungi if fungi_list[i_reproduce_one_cell][2] == reproduce_time_to_die_list[fungi_list[i_reproduce_one_cell][3]]: # when reproduced an exact times to die my_board[fungi_list[i_reproduce_one_cell][0]][fungi_list[i_reproduce_one_cell][1]] -= 1 # remove fungi # in number board del fungi_list[i_reproduce_one_cell] # the death of a fungi fungi_num = len(fungi_list) update_board(fungi_num) # get a board according to the new fungi reproduced else: fungi_list[i_reproduce_one_cell][2] += 1 # the life span of a fungi def reproduce_round(): # reproduce all the current fungi cells for i_reproduce_round in range(fungi_num): reproduce_one_cell(i_reproduce_round) # reproduce a single fungi cell def update_board(fungi_num_update_board): # get a number board according to the new fungi my_board[fungi_list[fungi_num_update_board - 1][0]][fungi_list[fungi_num_update_board - 1][1]] += 1 def get_rgb_board(): paint_rgb_board = [[[1.0 for k in range(3)] for j in range(patch_size)] for i in range(patch_size)] # initialize global red_fungi_num global green_fungi_num global blue_fungi_num red_fungi_num = 0 green_fungi_num = 0 blue_fungi_num = 0 # color board for i_paint_rgb_board in range(fungi_num): if fungi_list[i_paint_rgb_board][3] == 0: red_fungi_num += 1 paint_rgb_board[fungi_list[i_paint_rgb_board][0]][fungi_list[i_paint_rgb_board][1]][1] -= (0.99 / one_mesh_max) paint_rgb_board[fungi_list[i_paint_rgb_board][0]][fungi_list[i_paint_rgb_board][1]][2] -= (0.99 / one_mesh_max) elif fungi_list[i_paint_rgb_board][3] == 1: green_fungi_num += 1 paint_rgb_board[fungi_list[i_paint_rgb_board][0]][fungi_list[i_paint_rgb_board][1]][0] -= (0.99 / one_mesh_max) paint_rgb_board[fungi_list[i_paint_rgb_board][0]][fungi_list[i_paint_rgb_board][1]][2] -= (0.99 / one_mesh_max) else: blue_fungi_num += 1 paint_rgb_board[fungi_list[i_paint_rgb_board][0]][fungi_list[i_paint_rgb_board][1]][0] -= (0.99 / one_mesh_max) paint_rgb_board[fungi_list[i_paint_rgb_board][0]][fungi_list[i_paint_rgb_board][1]][1] -= (0.99 / one_mesh_max) return paint_rgb_board def attack_round(): global fungi_num for i_attack_round in range(fungi_num): if i_attack_round == fungi_num - 1: break if fungi_list[i_attack_round][2] >= reproduce_time_to_die_list[fungi_list[i_attack_round][3]]: # when reproduced an exact times to die my_board[fungi_list[i_attack_round][0]][fungi_list[i_attack_round][1]] -= 1 # remove fungi in number board del fungi_list[i_attack_round] # the death of a fungi fungi_num = len(fungi_list) # update_board(fungi_num) # get a board according to the new fungi reproduced else: continue def update(): # update the board information and fungi list in a round reproduce_round() # reproduce all the current fungi cells rgb_board_update = get_rgb_board() # get a color board after a round of reproduction attack_round() return rgb_board_update def calculate_initial_position(): # calculate the initial position of cells for fair competition radius_calculate_initial_position = patch_size / 6 # distance between initial cells middle_point_initial_position = patch_size / 2 for i_calculate_initial_position in range(initial_fungi_num): rotation_degree = i_calculate_initial_position / initial_fungi_num * 2 * np.pi x_initial_position = middle_point_initial_position + np.cos(rotation_degree) * radius_calculate_initial_position y_initial_position = middle_point_initial_position + np.sin(rotation_degree) * radius_calculate_initial_position x_initial_list.append(int(round(x_initial_position))) y_initial_list.append(int(round(y_initial_position))) def animate_func(frame_sequence): # depict the animation ax_1.imshow(update()) update_extension_rate() # ax_1.text(patch_size / 12, patch_size / 12, frame_sequence, color='white', size=20) print('total:', fungi_num, 'red:', red_fungi_num, 'green:', green_fungi_num, 'blue:', blue_fungi_num) print('frame number:', frame_sequence + 1) def update_extension_rate(): global extension_rate_list red_fungi_rate = red_fungi_num / fungi_num green_fungi_rate = green_fungi_num / fungi_num blue_fungi_rate = blue_fungi_num / fungi_num if red_fungi_rate <= rate_limit: extension_rate_list[0] = fungi_num / red_fungi_num # total_extension_rate = sum(extension_rate_list) # extension_rate_list = extension_rate_list / total_extension_rate elif green_fungi_rate <= rate_limit: extension_rate_list[1] = fungi_num / green_fungi_num # total_extension_rate = sum(extension_rate_list) # extension_rate_list = extension_rate_list / total_extension_rate elif blue_fungi_rate <= rate_limit: extension_rate_list[2] = fungi_num / blue_fungi_num # total_extension_rate = sum(extension_rate_list) # extension_rate_list = extension_rate_list / total_extension_rate if __name__ == '__main__': patch_size = 25 # size of our patch # frame_number = 30 # number of frames one_mesh_max = 7 # the maximum number of fungi cells in a single mesh initial_fungi_num = 3 # the initial number of fungi cells as well as species fungi_list = [] # list of cells my_board = np.zeros((patch_size, patch_size)) # initialize number board x_initial_list = [] # the x, y position of initial cells y_initial_list = [] # calculate_initial_position() # calculate the initial position of cells for fair competition red_fungi_num = 1 # number of each species of fungi green_fungi_num = 1 blue_fungi_num = 1 rate_limit = 0.01 for i_initial in range(initial_fungi_num): # initialize fungi list x_initial = np.random.randint(patch_size) # int(patch_size / 10) + i_initial * int(8 * patch_size / 10) # y_initial = np.random.randint(patch_size) # x_initial # # fungi_list.append([x_initial_list[i_initial], y_initial_list[i_initial], 0, i_initial]) # the indexes are # x, y, fungi_list.append([x_initial, y_initial, 0, i_initial]) # reproduce time and species num update_board(i_initial + 1) fungi_num = initial_fungi_num extension_rate_list = [0.6, 0.2, 0.2] # extension_rate_list = np.array(extension_rate_list) # different species of fungi have different extension rate attack_parameter_list = [0.3, 0.1, 0.5] # density parameter of different species of fungi reproduce_time_to_die_list = [2, 2, 2] # each cell reproduce two times until death rgb_board = [[[1.0 for k in range(3)] for j in range(patch_size)] for i in range(patch_size)] # initialize color # board fig_1 = plt.figure(1) # create a plot ax_1 = fig_1.add_subplot(1, 1, 1) # create an axes # ims = [] # prepared for the frames to put in # anim_1 = animation.ArtistAnimation(fig_1, ims, repeat=False) # create an animation # save_fungi_ca_gif() # save the gif file anim_2 = animation.FuncAnimation(fig_1, animate_func) plt.show() # print(my_board)