You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:25:55