如何在R中实现小盒碰撞边界即停的Windows XP屏保式模拟?
R语言模拟Windows XP风格屏保:边界碰撞停止逻辑实现指导
我正在用R语言模拟类似Windows XP屏保的效果:一个3×3的小盒子在8×8的大网格内随机移动,初始中心位置为(4,4),每秒等概率向上下左右任一方向移动1单位,需要在小盒边缘首次触碰大网格边界时停止模拟。
已实现的模拟逻辑
步骤1:初始化数据与盒子
创建空数据框存储模拟结果,初始化小盒子中心位置:
centre <- c(4, 4) movement_df <- data.frame(iteration = integer(), top_left = character(), bottom_left = character(), top_right = character(), bottom_right = character(), centre = character())
步骤2:随机移动函数
编写函数处理小盒子的随机移动,并记录位置信息:
move_small_box <- function(centre, iteration, movement_df) { movement_df[nrow(movement_df) + 1, ] <- c(iteration, paste(centre[1] - 1, centre[2] - 1, sep = ","), paste(centre[1] + 1, centre[2] - 1, sep = ","), paste(centre[1] - 1, centre[2] + 1, sep = ","), paste(centre[1] + 1, centre[2] + 1, sep = ","), paste(centre[1], centre[2], sep = ",")) direction <- sample(c("up", "down", "left", "right"), 1) if (direction == "up") { centre[2] <- centre[2] - 1 } else if (direction == "down") { centre[2] <- centre[2] + 1 } else if (direction == "left") { centre[1] <- centre[1] - 1 } else if (direction == "right") { centre[1] <- centre[1] + 1 } return(list(centre, movement_df)) }
步骤3:迭代模拟(当前固定100次)
运行100次迭代模拟,输出部分结果:
for (iteration in 1:100) { result <- move_small_box(centre, iteration, movement_df) centre <- result[[1]] movement_df <- result[[2]] } print(movement_df)
输出示例:
iteration top_left bottom_left top_right bottom_right centre 1 1 3,3 5,3 3,5 5,5 4,4 2 2 2,3 4,3 2,5 4,5 3,4 3 3 1,3 3,3 1,5 3,5 2,4 4 4 0,3 2,3 0,5 2,5 1,4 5 5 1,3 3,3 1,5 3,5 2,4 6 6 1,2 3,2 1,4 3,4 2,3 7 7 2,2 4,2 2,4 4,4 3,3
当前问题
不清楚如何在小盒首次碰撞边界时停止模拟,求指导实现方法。
附注:轨迹可视化代码
中心轨迹图
library(ggplot2) library(gridExtra) # 拆分坐标字符串为数值 movement_df$bottom_left_x <- as.numeric(sapply(strsplit(movement_df$bottom_left, ","), "[", 1)) movement_df$bottom_left_y <- as.numeric(sapply(strsplit(movement_df$bottom_left, ","), "[", 2)) movement_df$bottom_right_x <- as.numeric(sapply(strsplit(movement_df$bottom_right, ","), "[", 1)) movement_df$bottom_right_y <- as.numeric(sapply(strsplit(movement_df$bottom_right, ","), "[", 2)) movement_df$top_left_x <- as.numeric(sapply(strsplit(movement_df$top_left, ","), "[", 1)) movement_df$top_left_y <- as.numeric(sapply(strsplit(movement_df$top_left, ","), "[", 2)) movement_df$top_right_x <- as.numeric(sapply(strsplit(movement_df$top_right, ","), "[", 1)) movement_df$top_right_y <- as.numeric(sapply(strsplit(movement_df$top_right, ","), "[", 2)) movement_df$centre_x <- as.numeric(sapply(strsplit(movement_df$centre, ","), "[", 1)) movement_df$centre_y <- as.numeric(sapply(strsplit(movement_df$centre, ","), "[", 2)) # 绘制中心轨迹 ggplot(movement_df, aes(x = centre_x, y = centre_y)) + geom_path() + geom_text(aes(label = iteration), position = position_jitter(width = 0.35, height = 0.35)) + labs(x = "X坐标", y = "Y坐标", title = "盒子中心移动轨迹") + theme_minimal()

坐标变化趋势图
# X坐标随迭代变化 plot_x <- ggplot(movement_df, aes(x = iteration)) + geom_line(aes(y = bottom_left_x, color = "bottom_left"), group = 1) + geom_line(aes(y = bottom_right_x, color = "bottom_right"), group = 1) + geom_line(aes(y = top_left_x, color = "top_left"), group = 1) + geom_line(aes(y = top_right_x, color = "top_right"), group = 1) + geom_line(aes(y = centre_x, color = "centre"), group = 1) + labs(x = "迭代次数", y = "X坐标", color = "位置", title = "小盒各点X坐标变化") + theme_minimal() # Y坐标随迭代变化 plot_y <- ggplot(movement_df, aes(x = iteration)) + geom_line(aes(y = bottom_left_y, color = "bottom_left"), group = 1) + geom_line(aes(y = bottom_right_y, color = "bottom_right"), group = 1) + geom_line(aes(y = top_left_y, color = "top_left"), group = 1) + geom_line(aes(y = top_right_y, color = "top_right"), group = 1) + geom_line(aes(y = centre_y, color = "centre"), group = 1) + labs(x = "迭代次数", y = "Y坐标", color = "位置", title = "小盒各点Y坐标变化") + theme_minimal() grid.arrange(plot_x, plot_y, ncol = 2)

解决方法
要实现碰撞边界时停止模拟,核心是每次移动后检查小盒边缘是否触碰大网格边界,如果触碰则终止循环。具体修改如下:
1. 定义边界检查函数
根据你的输出中出现的0,3,判断网格坐标范围为0~7,编写碰撞判断函数:
is_collided <- function(centre) { # 3×3盒子的边缘坐标:中心±1 left_edge <- centre[1] - 1 right_edge <- centre[1] + 1 top_edge <- centre[2] - 1 bottom_edge <- centre[2] + 1 # 检查是否触碰0~7的网格边界 return(left_edge < 0 || right_edge > 7 || top_edge < 0 || bottom_edge > 7) }
2. 修改循环逻辑,加入碰撞检查
将固定次数的for循环改为while循环,实时检查碰撞状态:
# 重新初始化 centre <- c(4, 4) movement_df <- data.frame(iteration = integer(), top_left = character(), bottom_left = character(), top_right = character(), bottom_right = character(), centre = character()) iteration <- 0 # 循环直到碰撞边界 while(TRUE) { iteration <- iteration + 1 # 记录当前位置 movement_df[nrow(movement_df) + 1, ] <- c(iteration, paste(centre[1] - 1, centre[2] - 1, sep = ","), paste(centre[1] + 1, centre[2] - 1, sep = ","), paste(centre[1] - 1, centre[2] + 1, sep = ","), paste(centre[1] + 1, centre[2] + 1, sep = ","), paste(centre[1], centre[2], sep = ",")) # 检查是否碰撞,碰撞则停止循环 if(is_collided(centre)) { break } # 未碰撞则执行随机移动 direction <- sample(c("up", "down", "left", "right"), 1) if (direction == "up") { centre[2] <- centre[2] - 1 } else if (direction == "down") { centre[2] <- centre[2] + 1 } else if (direction == "left") { centre[1] <- centre[1] - 1 } else if (direction == "right") { centre[1] <- centre[1] + 1 } } print(movement_df)
3. 可选:保留原函数结构的修改方案
如果想保留move_small_box函数,可让它返回碰撞状态,再在循环中判断:
# 修改移动函数,返回碰撞状态 move_small_box <- function(centre, iteration, movement_df) { # 记录当前位置 movement_df[nrow(movement_df) + 1, ] <- c(iteration, paste(centre[1] - 1, centre[2] - 1, sep = ","), paste(centre[1] + 1, centre[2] - 1, sep = ","), paste(centre[1] - 1, centre[2] + 1, sep = ","), paste(centre[1] + 1, centre[2] + 1, sep = ","), paste(centre[1], centre[2], sep = ",")) # 检查碰撞状态 collided <- is_collided(centre) if(collided) { return(list(centre = centre, movement_df = movement_df, collided = TRUE)) } # 未碰撞则移动 direction <- sample(c("up", "down", "left", "right"), 1) if (direction == "up") { centre[2] <- centre[2] - 1 } else if (direction == "down") { centre[2] <- centre[2] + 1 } else if (direction == "left") { centre[1] <- centre[1] - 1 } else if (direction == "right") { centre[1] <- centre[1] + 1 } return(list(centre = centre, movement_df = movement_df, collided = FALSE)) } # 循环调用函数 centre <- c(4,4) movement_df <- data.frame(iteration = integer(), top_left = character(), bottom_left = character(), top_right = character(), bottom_right = character(), centre = character()) iteration <- 0 while(TRUE) { iteration <- iteration +1 result <- move_small_box(centre, iteration, movement_df) centre <- result$centre movement_df <- result$movement_df if(result$collided) { break } }
以上两种方案都能实现小盒边缘首次触碰边界时立即停止模拟,同时完整记录移动轨迹。
内容的提问来源于stack exchange,提问作者Uk rain troll
相关产品推荐
相关产品推荐

