chiark / gitweb /
clock: Much wip code, compiles but doesn't do anything yet
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 16 Mar 2021 19:45:03 +0000 (19:45 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 19 Mar 2021 20:05:30 +0000 (20:05 +0000)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
specs/penultima.game.toml
src/clock.rs
src/prelude.rs
src/spec.rs

index d55e4f92492d72d0ce967f5aa78386aa2bde2e78..082cb03345c77866cecc5e64e85eae66f91da57a 100644 (file)
@@ -196,3 +196,4 @@ shape.xy = [20,10]
 [[pieces]]
 pos = [240, 100]
 type = "ChessClock"
+time = 900
index 8d859e67b5faa75690a20f3833f88cf1f2aed358..eb0d264084ce8abd64e985819d7ec7d98f38fb54 100644 (file)
 // SPDX-License-Identifier: AGPL-3.0-or-later
 // There is NO WARRANTY.
 
+#![allow(dead_code)]
+
 use crate::prelude::*;
 use shapelib::Rectangle;
 
-const W: Coord = 50;
-const H: Coord = 20;
-const OUTLINE: Rectangle = Rectangle { xy: PosC([W as f64, H as f64]) };
+// ==================== users ====================
+
+struct UserInfo {
+  idchar: char,
+}
+
+const N: usize = 2;
+
+type Time = i32;
+
+#[derive(Copy,Clone,Serialize,Deserialize)]
+#[derive(Eq,Ord,PartialEq,PartialOrd,Hash)]
+#[serde(try_from="u8", into="u8")]
+struct User(bool);
+
+impl<T> Index<User> for [T;2] {
+  type Output = T;
+  fn index(&self, index: User) -> &T { &self[index.0 as usize] }
+}  
+impl<T> IndexMut<User> for [T;2] {
+  fn index_mut(&mut self, index: User) -> &mut T { &mut self[index.0 as usize] }
+}  
+
+const USERINFOS: [UserInfo; N] = [
+  UserInfo { idchar: 'x' },
+  UserInfo { idchar: 'y' },
+];
+
+const USERS: [User; N] = [ User(false), User(true) ];
+
+impl fmt::Debug for User {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    f.write_char(USERINFOS[*self].idchar)
+  }
+}
+
+#[derive(Debug,Clone,Copy,Error,Serialize,Deserialize)]
+struct UserOutOfRangeError;
+display_as_debug!{UserOutOfRangeError}
+
+impl TryFrom<u8> for User {
+  type Error = UserOutOfRangeError;
+  #[throws(UserOutOfRangeError)]
+  fn try_from(u: u8) -> User { User(match u {
+    0 => false,
+    1 => true,
+    _ => throw!(UserOutOfRangeError),
+  }) }
+}
+
+impl From<User> for u8 {
+  fn from(user: User) -> u8 { user.0 as u8 }
+}
+
+// ==================== state ====================
 
 #[derive(Debug,Clone,Serialize,Deserialize)]
 pub struct ChessClock { // spec
+  time: Time,
+  #[serde(default)] per_move: usize,
 }
 
 #[derive(Debug,Serialize,Deserialize)]
 struct Clock { // state
+  spec: ChessClock,
+}
+
+#[derive(Debug,Serialize,Deserialize,Default)]
+struct State {
+  users: [UState; 2],
+  #[serde(skip)] running: Option<Running>,
 }
 
+#[typetag::serde(name="Hand")]
+impl PieceXData for State {
+  fn dummy() -> Self { default() }
+}
+
+
+#[derive(Debug,Default,Serialize,Deserialize)]
+struct UState {
+  player: PlayerId,
+  remaining: Time, // -ve means flag
+}
+
+#[derive(Debug,Serialize,Deserialize)]
+struct Running {
+  user: User,
+  #[serde(with="timespec_serde")] next_wakeup: TimeSpec,
+}
+
+mod timespec_serde {
+  use super::*;
+
+  #[derive(Serialize, Deserialize)]
+  struct Timespec(i64, u32);
+
+  #[throws(S::Error)]
+  pub fn serialize<S:Serializer>(v: &TimeSpec, s: S) -> S::Ok {
+    let v = Timespec(v.tv_sec().into(), v.tv_nsec().try_into().unwrap());
+    Serialize::serialize(&v, s)?
+  }
+  #[throws(D::Error)]
+  pub fn deserialize<'de, D:Deserializer<'de>>(d: D) -> TimeSpec {
+    let Timespec(sec, nsec) = Deserialize::deserialize(d)?;
+    libc::timespec { tv_sec: sec.into(), tv_nsec: nsec.into() }.into()
+  }
+}
+
+// ==================== rendering, abstract ====================
+
+#[derive(Debug)]
+struct URender<'r> {
+  st: URenderState,
+  remaining: Time, // always >=0
+  nick: &'r str,
+}
+
+#[derive(Debug,Copy,Clone)]
+#[derive(Eq,Ord,PartialEq,PartialOrd,Hash)]
+enum URenderState {
+  Running,
+  ActiveHeld,
+  Inactive,
+  Stopped,
+  Reset,
+  Flag,
+  PMissing,
+}
+use URenderState as URS;
+
+impl Clock {
+  fn urender<'r>(&self, state: &State, gplayers: &'r GPlayers, gpc: &GPiece)
+                 -> [URender<'r>; N]
+  {
+    let mut r: [URender;N] = izip!(
+      USERS.iter(),
+      state.users.iter()
+    ).map(|(&user, ustate)| {
+      let nick = gplayers.get(ustate.player).map(|gpl| gpl.nick.as_str());
+      let (st, remaining, nick) =
+        if ustate.remaining < 0 {
+          (URS::Flag, 0, nick.unwrap_or(""))
+        } else if let Some(nick) = nick {
+          (
+            if let Some(running) = &state.running {
+              if running.user != user {
+                URS::Inactive
+              } else if gpc.held.is_some() {
+                URS::ActiveHeld
+              } else {
+                URS::Running
+              }
+            } else if ustate.remaining == self.spec.time {
+              URS::Reset
+            } else {
+              URS::Stopped
+            },
+            ustate.remaining,
+            nick
+          )
+        } else {
+          (URS::PMissing, ustate.remaining, "")
+        };
+
+      URender { st, remaining, nick }
+    })
+      .collect::<ArrayVec<_>>()
+      .into_inner().unwrap();
+
+    if r.iter().filter(|ur| ur.st == URS::Reset).count() == 1 {
+      for ur in &mut r {
+        if ur.st == URS::Reset { ur.st = URS::Stopped }
+      }
+    }
+
+    r
+  }
+}
+
+// ==================== rendering ====================
+
+const W: Coord = 50;
+const H: Coord = 20;
+const OUTLINE: Rectangle = Rectangle { xy: PosC([W as f64, H as f64]) };
+
+
+// ==================== piece management, loading, etc. ====================
+
 #[typetag::serde]
 impl PieceSpec for ChessClock {
   #[throws(SpecError)]
   fn load(&self, _: usize, _gpc: &mut GPiece) -> PieceSpecLoaded {
-    let clock = Clock {
+    if self.time <= 0 { throw!(SpecError::NegativeTimeout) }
 
+    let clock = Clock {
+      spec: self.clone(),
     };
     PieceSpecLoaded {
       p: Box::new(clock),
@@ -50,8 +231,16 @@ impl PieceTrait for Clock {
   fn svg_piece(&self, f: &mut Html, _gpc: &GPiece, _gs: &GameState, id: VisiblePieceId) {
     dbgc!("rendering", id);
     write!( &mut f.0, r##"
-        <rect fill="black" width="10" height="10"/>
-    "##)?;
+        <rect fill="white" stroke="black" width="{}" height="{}"/>
+    "##, W, H
+    )?;
+/*
+    let urs = 
+
+    dbg
+    for (i,u) in USERS.iter().enumerate() {
+      
+    }*/
   }
 
   #[throws(IE)]
index 1cdfe54c3c4c1ffbe60ab6e85603b84e7a550c33..2108ff23404e623c276577e03b2d6e220cb9d830 100644 (file)
@@ -32,7 +32,7 @@ pub use std::iter::repeat_with;
 pub use std::marker::PhantomData;
 pub use std::mem;
 pub use std::num::{NonZeroUsize, TryFromIntError, Wrapping};
-pub use std::ops::{Deref, DerefMut, Index};
+pub use std::ops::{Deref, DerefMut, Index, IndexMut};
 pub use std::os::linux::fs::MetadataExt; // todo why linux for st_mode??
 pub use std::os::unix;
 pub use std::os::unix::ffi::OsStrExt;
index 9d31a824de5e957142c5345348b6a6df1182a0f0..bb963c56c388b1e66f4cebc0cf4837591cee0688 100644 (file)
@@ -83,6 +83,7 @@ pub enum SpecError {
   InconsistentFacesEdgecoloursCount,
   SpecifiedWidthOfNoEdges,
   UnsupportedShape,
+  NegativeTimeout,
 }
 display_as_debug!{SpecError}