melvin_ob/objective/
beacon_objective_done.rs1use super::BeaconObjective;
2use crate::util::Vec2D;
3use crate::http_handler::{
4 http_client::HTTPClient,
5 http_request::{
6 beacon_position_put::BeaconPositionRequest, request_common::NoBodyHTTPRequestType,
7 },
8};
9use crate::{error, obj};
10use chrono::{DateTime, Utc};
11use fixed::types::I32F32;
12use rand::Rng;
13use std::io::{Error, ErrorKind};
14use std::sync::Arc;
15
16#[derive(Clone)]
21pub struct BeaconObjectiveDone {
22 id: usize,
24 name: String,
26 start: DateTime<Utc>,
28 end: DateTime<Utc>,
30 guesses: Vec<Vec2D<I32F32>>,
32 submitted: bool,
34}
35
36impl BeaconObjectiveDone {
37 const MAP_WIDTH_RANGE: std::ops::Range<u32> = 0..21600;
39 const MAP_HEIGHT_RANGE: std::ops::Range<u32> = 0..10800;
41 const MIN_DISTANCE_RAND_GUESSES: f32 = 75.0;
43
44 pub fn id(&self) -> usize { self.id }
46 pub fn name(&self) -> &str { &self.name }
48 pub fn start(&self) -> DateTime<Utc> { self.start }
50 pub fn end(&self) -> DateTime<Utc> { self.end }
52 pub fn guesses(&self) -> &Vec<Vec2D<I32F32>> { &self.guesses }
54 pub fn submitted(&self) -> bool { self.submitted }
56 pub fn set_submitted(&mut self) { self.submitted = true }
58
59 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
65 pub async fn guess_max(&self, client: Arc<HTTPClient>) {
66 obj!(
67 "Guessing max for {}: {} guesses...",
68 self.id,
69 self.guesses.len()
70 );
71 let id_u16 = self.id() as u16;
72 let guess_cloned = self.guesses().clone();
73 for (i, guess) in guess_cloned.iter().enumerate() {
74 let width = guess.x().abs().to_num::<u32>();
75 let height = guess.y().abs().to_num::<u32>();
76 let req = BeaconPositionRequest { beacon_id: id_u16, width, height };
77 obj!("Sending request for beacon {id_u16} with width {width} and height {height}...");
78 if self.submit_guess(req, client.clone(), guess, i).await.is_err() {
79 return;
80 };
81 }
82 }
83
84 #[allow(clippy::cast_possible_truncation)]
90 pub async fn randomize_no_meas_guesses(&self, client: Arc<HTTPClient>) {
91 if !self.guesses.is_empty() {
92 obj!("Guesses are provided already, skipping randomization.");
93 return self.guess_max(client).await;
94 }
95 obj!("No guesses for {}, randomizing 10 guesses.", self.id);
96
97 let random_guesses = Self::generate_random_guesses();
98 for (i, guess) in random_guesses.iter().enumerate() {
99 let guess_req = BeaconPositionRequest {
100 beacon_id: self.id as u16,
101 width: guess.x().abs().to_num::<u32>(),
102 height: guess.y().abs().to_num::<u32>(),
103 };
104 let res = self.submit_guess(guess_req, Arc::clone(&client), guess, i).await;
105 match res {
106 Ok(done) => {
107 if done.is_some() { return; };
108 }
109 Err(_) => return,
110 }
111 }
112 }
113
114 async fn submit_guess(
129 &self,
130 req: BeaconPositionRequest,
131 client: Arc<HTTPClient>,
132 guess: &Vec2D<I32F32>,
133 guess_num: usize,
134 ) -> Result<Option<()>, Error> {
135 if let Ok(msg) = req.send_request(&client).await {
136 if msg.is_success() {
137 obj!(
138 "And Rohan will answer! Mustered Rohirrim {} at {}!",
139 req.beacon_id,
140 guess
141 );
142 return Ok(Some(()));
143 } else if msg.is_fail() {
144 obj!(
145 "What can men do against such reckless hate? Still searching beacon {} after {} tries!",
146 req.beacon_id,
147 guess_num
148 );
149 return Ok(None);
150 } else if msg.is_last() {
151 obj!(
152 "Where was Gondor when the Westfold fell! Could not find beacon {} after {} tries!",
153 req.beacon_id,
154 guess_num
155 );
156 return Err(Error::new(ErrorKind::Other, "Beacon over!"));
157 } else if msg.is_unknown() {
158 obj!("Beacon {} is unknown!", req.beacon_id);
159 return Err(Error::new(ErrorKind::Other, "Beacon unknown!"));
160 }
161 obj!("Unknown Message: {}! Returning!", msg.msg());
162 return Err(Error::new(ErrorKind::Other, "Unknown Message!"));
163 }
164 error!("Unnoticed HTTP Error in submit_guess()");
165 Err(Error::new(ErrorKind::Other, "HTTP Error!"))
166 }
167
168 fn generate_random_guesses() -> Vec<Vec2D<I32F32>> {
175 let mut rng = rand::rng();
176 let mut random_guesses = Vec::new();
177 while random_guesses.len() <= 10 {
178 let random_width = rng.random_range(Self::MAP_WIDTH_RANGE);
179 let random_height = rng.random_range(Self::MAP_HEIGHT_RANGE);
180 let rand_guess = Vec2D::new(
181 I32F32::from_num(random_width),
182 I32F32::from_num(random_height),
183 );
184
185 let too_close = random_guesses.iter().any(|prev_guesses: &Vec2D<I32F32>| {
186 prev_guesses.euclid_distance(&rand_guess)
187 <= I32F32::from_num(Self::MIN_DISTANCE_RAND_GUESSES)
188 });
189
190 if too_close {
191 continue;
192 }
193
194 random_guesses.push(rand_guess);
195 }
196 random_guesses
197 }
198}
199
200impl From<BeaconObjective> for BeaconObjectiveDone {
201 fn from(obj: BeaconObjective) -> Self {
207 let guesses =
208 if let Some(meas) = obj.measurements() { meas.pack_perfect_circles() } else { vec![] };
209 Self {
210 id: obj.id(),
211 name: String::from(obj.name()),
212 start: obj.start(),
213 end: obj.end(),
214 guesses,
215 submitted: false,
216 }
217 }
218}