A simple actor-based simulation of tag, implemented in rust.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
3.1 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. pub type Distance = isize;
  2. #[derive(Clone)]
  3. pub struct Position {
  4. pub x: Distance,
  5. pub y: Distance,
  6. }
  7. impl From<(Distance, Distance)> for Position {
  8. fn from((x, y): (Distance, Distance)) -> Self {
  9. Self { x, y }
  10. }
  11. }
  12. #[derive(Debug)]
  13. pub struct Direction {
  14. pub x: Distance,
  15. pub y: Distance,
  16. }
  17. impl From<(Distance, Distance)> for Direction {
  18. fn from((x, y): (Distance, Distance)) -> Self {
  19. Self { x, y }
  20. }
  21. }
  22. pub type AgentId = usize;
  23. pub struct WorldView {
  24. pub self_id: AgentId,
  25. pub tagged: AgentId,
  26. pub tagged_by: Option<AgentId>,
  27. pub bounds_distance: (Distance, Distance, Distance, Distance),
  28. pub other_agents: Vec<(Direction, AgentId)>,
  29. }
  30. #[derive(Debug)]
  31. pub enum Move {
  32. Noop, // Why not an Option?
  33. TryTag(AgentId),
  34. TryMove(Direction),
  35. }
  36. pub trait Agent {
  37. fn next_move(&self, view: WorldView) -> Move;
  38. }
  39. pub struct NullAgent;
  40. impl Agent for NullAgent {
  41. fn next_move(&self, _view: WorldView) -> Move {
  42. Move::Noop
  43. }
  44. }
  45. use std::cell::RefCell;
  46. pub struct ScriptedAgent {
  47. moves: RefCell<Vec<Move>>,
  48. }
  49. impl ScriptedAgent {
  50. pub fn new(mut moves: Vec<Move>) -> Self {
  51. moves.reverse();
  52. Self {
  53. moves: RefCell::new(moves),
  54. }
  55. }
  56. }
  57. impl Agent for ScriptedAgent {
  58. fn next_move(&self, _view: WorldView) -> Move {
  59. self
  60. .moves
  61. .borrow_mut()
  62. .pop()
  63. .expect("ScriptedAgent has no moves left to make")
  64. }
  65. }
  66. pub struct SimpleAgent;
  67. fn rand(exclusive_upper_bound: u32) -> u32 {
  68. std::time::SystemTime::now()
  69. .duration_since(std::time::UNIX_EPOCH)
  70. .unwrap()
  71. .subsec_micros()
  72. % exclusive_upper_bound
  73. }
  74. fn random_move() -> Move {
  75. Move::TryMove(
  76. match rand(8) {
  77. 0 => (-1, -1),
  78. 1 => (0, -1),
  79. 2 => (1, -1),
  80. 3 => (1, 0),
  81. 4 => (1, 1),
  82. 5 => (0, 1),
  83. 6 => (-1, 1),
  84. 7 => (-1, 0),
  85. _ => unreachable!(),
  86. }
  87. .into(),
  88. )
  89. }
  90. fn random_move_within(
  91. (top, right, bottom, left): (Distance, Distance, Distance, Distance),
  92. ) -> Move {
  93. loop {
  94. let mv = random_move();
  95. if let Move::TryMove(Direction { x, y }) = mv {
  96. if top + y > 0 && bottom - y > 0 && left + x > 0 && right - x > 0 {
  97. return mv;
  98. }
  99. }
  100. }
  101. }
  102. impl Agent for SimpleAgent {
  103. fn next_move(&self, view: WorldView) -> Move {
  104. if view.self_id == view.tagged {
  105. let mut cur = None;
  106. for (dist, id) in view.other_agents {
  107. if Some(id) == view.tagged_by {
  108. continue;
  109. }
  110. if dist.x.abs() <= 1 && dist.y.abs() <= 1 {
  111. cur = Some((id, 0, dist));
  112. break;
  113. }
  114. let total_dist = dist.x.abs() + dist.y.abs();
  115. let cur_dist = if let Some((_, dist, _)) = cur {
  116. dist
  117. } else {
  118. isize::MAX
  119. };
  120. if total_dist < cur_dist {
  121. cur = Some((id, total_dist, dist));
  122. }
  123. }
  124. match cur {
  125. Some((other, total_dist, dist)) => {
  126. if total_dist == 0 {
  127. Move::TryTag(other)
  128. } else {
  129. Move::TryMove((dist.x.signum(), dist.y.signum()).into())
  130. }
  131. }
  132. None => random_move_within(view.bounds_distance),
  133. }
  134. } else {
  135. random_move_within(view.bounds_distance)
  136. }
  137. }
  138. }