chiark / gitweb /
fastsplit_delete: Implement, and use in currency
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 1 May 2022 13:43:04 +0000 (14:43 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 1 May 2022 13:43:04 +0000 (14:43 +0100)
Amazingly, this seems to work at least in my basic ad-hoc test.
The log message needs a little work.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/currency.rs
src/fastsplit.rs

index 408f1334c654e2e0fa9cb292745ce8e8390a5896..ed8d3ac5c7d3b445332eff73694d94556e2992fb 100644 (file)
@@ -164,4 +164,121 @@ impl PieceTrait for Banknote {
       default()
     ))
   })}))}
+
+  #[throws(IE)]
+  fn held_change_hook(&self,
+                      _ig: &InstanceRef,
+                      gplayers: &GPlayers,
+                      ipieces: &IPieces,
+                      goccults: &GOccults,
+                      gpieces: &mut GPieces,
+                      tpiece: PieceId,
+                      was_held: Option<PlayerId>)
+                      -> OpHookThunk {
+    let missing_e = || internal_error_bydebug(&(was_held, tpiece));
+
+    let tself = self;
+    let tgpc = gpieces.get(tpiece).ok_or_else(missing_e)?;
+    let tipc = ipieces.get(tpiece).ok_or_else(missing_e)?;
+
+    if_let!{ Some(player) = was_held; else return Ok(default()) }
+    if tgpc.held.is_some() { /*wat*/ return default(); }
+    let gpl = gplayers.get(player);
+
+    // Occultation is not yet supported here.  When implementing
+    // occultation, delete this and fix all the things.
+    let show = ShowUnocculted::new_visible();
+
+    let merge_with = gpieces.iter().filter_map(|(mpiece, mgpc)|{
+      if mpiece == tpiece { throw!() }
+      let mipc = ipieces.get(mpiece)?;
+
+      // Our position is within its bbox
+      if ! mipc.show(show).abs_bbox(mgpc).ok()?.contains(tgpc.pos) { throw!() }
+
+      // It's a banknote
+      let mself: &Banknote = mipc.p.show(show)
+        .downcast_piece_fastsplit().ok()?;
+
+      // Of our currency
+      if mself.currency != tself.currency { throw!() }
+      let currency = &mself.currency;
+
+      if mgpc.occult.passive_occid().is_some() {
+        // We don't do occultation yet.  But, anyway, we don't want to
+        // deal with this since it might mean we're totally invisible
+        // to our player!  When we do support this, call
+        // Occultation::get_kind ?
+        throw!();
+      }
+
+      // We are in the ellipse inscribed in its bbox
+      let delta = (tgpc.pos - mgpc.pos).ok()?.promote();
+      let bbox_sz = mipc.show(show).bbox_approx().ok()?;
+      let dist2: f64 = (0..2).map(|i| {
+        // The bbox may not be centred.  We imagine a quarter ellipse
+        // inscribed in each corner, with the centre at the nominal position.
+        let delta = delta.coords[i];
+        let cnr = if delta < 0. { bbox_sz.tl() } else { bbox_sz.br() };
+        let rel = delta / (cnr.coords[i] as f64);
+        rel*rel
+      }).sum();
+      if ! (dist2 <= 1.) { throw!() }
+
+      Some((mpiece,mgpc,currency))
+    });
+
+    if_let!{
+      Some((mpiece,mgpc,currency)) =
+        merge_with.at_most_one().ok().flatten();
+      else return Ok(default());
+    }
+
+    let tqty = tgpc.xdata_exp::<Value>()?.qty;
+    let mqty = mgpc.xdata_exp::<Value>()?.qty;
+    if_let!{
+      Some(new_qty) = mqty.checked_add(tqty);
+      else return Ok(default()); // arithmetic overflow!
+    }
+
+    let logents = vec![ LogEntry { html: hformat!(
+      "{} deposited {} {}{}, giving {}{}",
+      match gpl {
+        Some(gpl) => gpl.nick.to_html(),
+        None => Html::lit("Departing player").into(),
+      },
+      tipc.p.show(show).describe_html(tgpc, goccults)?,
+      tqty, currency,
+      new_qty, currency,
+    )}];
+
+  OpHookThunk::Reborrow(Box::new(move |igg: &mut InstanceGuard, (_player,)| {
+
+    let (puo, uu_d) = igg.fastsplit_delete(show, tpiece, &logents)?;
+    // commitment point
+  Ok(((move ||{
+    let ig = &mut **igg;
+
+    let () = (||{
+      // None of these situations ought to happen, really, but the
+      // callback structure means it isn't 100% possible to rule them out.
+      let mgpc = ig.gs.pieces.get_mut(mpiece).ok_or("tpiece vanished")?;
+      let mvalue = mgpc.xdata_mut_exp::<Value>().map_err(|_|"xdata vanished")?;
+      mvalue.qty = mvalue.qty.checked_add(tqty).ok_or("overflow")?;
+      if mvalue.qty != new_qty { throw!("modified value") }
+      Ok::<_,&'static str>(())
+    })().unwrap_or_else(|m|{
+      warn!("during dorp-and-merge of currency {tpiece:?} into {mpiece:?}: {m}");
+    });
+
+    vec![Box::new(move |prepub: &mut PrepareUpdatesBuffer| {
+      prepub.piece_update_image(mpiece, &None).unwrap_or_else(
+        |e| error!("currency image update failed: {} {:?}", &e, &e));
+      prepub.piece_update(tpiece, &None, puo.into());
+      prepub.log_updates(logents);
+      prepub.add_unprepared(uu_d);
+    }) as _]
+
+  }))()) // <- no ?
+  }))}
 }
index 005cf69a0caf04daca8ada99a9b83177de2a299a..8d1acb42a3acce6d209764a9c62fab506c61b920 100644 (file)
@@ -166,6 +166,44 @@ impl InstanceGuard<'_> {
       (t_pu, unprepared)
     })()// <- no ?, infallible (to avoid having not completed implementation
   }
+
+  #[throws(IE)]
+  pub fn fastsplit_delete(&mut self,
+                          show: ShowUnocculted,
+                          piece: PieceId,
+                          _proof_that_caller_handles_logging: &Vec<LogEntry>)
+                          -> (PieceUpdateOp<(),()>, UnpreparedUpdates)
+  {
+    let missing_e = || internal_error_bydebug(&piece);
+
+    // See reasoning in fastsplit_split
+    let modperm = self.modify_pieces_not_necessarily_saving_aux();
+
+    let gpc = self.gs.pieces.get(piece).ok_or_else(missing_e)?;
+    let ipc = self.ipieces.get(piece).ok_or_else(missing_e)?;
+    let _p: &Piece = ipc.p.show(show).downcast_piece()?;
+
+    let _fsid: &FastSplitId = gpc.fastsplit.as_ref()
+      .ok_or_else(|| internal_error_bydebug(gpc))?;
+    // We allow merging things with different FastSplitIds.  The
+    // FastSplitId identifies not the "kind of piece", but simply
+    // ancestry.  For example, all banknotes split from the same
+    // original note will have the same id, but otherwise, different
+    // banknotes even of the same currency have different ids.
+
+    match ToRecalculate::with(|mut to_permute| {
+      let r = self.delete_piece(modperm,&mut to_permute, piece,|_,_,_,_|());
+      (r, to_permute.implement(self))
+    }) {
+      (Err(e), uu_p) => {
+        PrepareUpdatesBuffer::only_unprepared(self, uu_p);
+        throw!(e);
+      },
+      (Ok(((), puo, uu_d)), uu_p) => {
+        (puo, chain!(uu_d, uu_p).collect())
+      },
+    }
+  }
 }
 
 impl IFastSplits {