Add some types to be serialized over IO

This commit is contained in:
tali 2022-12-16 10:18:15 -05:00
parent d30f413949
commit 370a0329b9
4 changed files with 201 additions and 1 deletions

1
Cargo.lock generated
View File

@ -33,6 +33,7 @@ dependencies = [
"ahash",
"hashbrown",
"mino",
"serde",
]
[[package]]

View File

@ -5,10 +5,12 @@ version = "0.1.0"
edition = "2021"
[features]
default = []
default = ["io"]
io = ["serde"]
[dependencies]
mino = { path = "../mino" }
ahash = "0.8"
hashbrown = "0.13"
serde = { version = "1.0", features = ["derive"], optional = true }

194
fish/src/io.rs Normal file
View File

@ -0,0 +1,194 @@
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use mino::srs::PieceType;
use mino::MatBuf;
use mino::{srs::Piece, Loc};
use serde::{Deserialize, Serialize};
/// Deserializable description of an initial game state. This is intentionally similar to
/// the TBP "start" message.
#[derive(Clone, Debug, Deserialize)]
pub struct InputState {
#[serde(flatten)]
pub queue: InputQueue,
#[allow(dead_code)]
#[serde(flatten)]
_attack: AttackState,
// XXX(iitalics): TBP uses the terminology "board" but we use "matrix".
#[serde(rename = "board")]
pub matrix: InputMatrix,
}
/// Deserializable description of the queue.
#[derive(Clone, Debug, Deserialize)]
pub struct InputQueue {
#[serde(deserialize_with = "deserialize_hold")]
pub hold: Option<PieceType>,
#[serde(deserialize_with = "deserialize_previews")]
#[serde(rename = "queue")]
pub previews: Vec<PieceType>,
}
#[derive(Clone, Debug, Deserialize)]
#[allow(dead_code)]
struct AttackState {
combo: u32,
back_to_back: u32,
// TODO: use this?
}
// use FromStr to deserialize a PieceType
fn deserialize_hold<'de, D>(de: D) -> Result<Option<PieceType>, D::Error>
where
D: serde::Deserializer<'de>,
{
Option::<String>::deserialize(de)?
.map(|s| s.parse())
.transpose()
.map_err(serde::de::Error::custom)
}
fn deserialize_previews<'de, D>(de: D) -> Result<Vec<PieceType>, D::Error>
where
D: serde::Deserializer<'de>,
{
Vec::<String>::deserialize(de)?
.iter()
.map(|s| s.parse())
.collect::<Result<Vec<_>, _>>()
.map_err(serde::de::Error::custom)
}
/// Deserializable description of the game matrix.
#[derive(Clone, Debug)]
pub struct InputMatrix {
pub cells: Box<[[Option<Color>; 10]; 40]>,
}
impl InputMatrix {
/// Converts this matrix to a [`MatBuf`], which discards color information but is
/// stored more efficiently.
pub fn to_mat(&self) -> MatBuf {
let mut mat = MatBuf::new();
for (y, row) in self.cells.iter().enumerate() {
for (x, cell) in row.iter().enumerate() {
if cell.is_some() {
mat.set(x as i16, y as i16);
}
}
}
mat
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug, Deserialize)]
#[repr(u8)]
pub enum Color {
I = 1,
J = 2,
L = 3,
O = 4,
S = 5,
T = 6,
Z = 7,
G = 8,
}
impl<'de> serde::de::Deserialize<'de> for InputMatrix {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
// XXX(iitalics): serde doesn't let you deserialize a [T; 40] so we have to go
// through Vec first and then check the length :|
let cells: Vec<_> = serde::de::Deserialize::deserialize(de)?;
let cells: [_; 40] = cells
.try_into()
.map_err(|_| serde::de::Error::custom("board must contain 40 rows"))?;
Ok(Self {
cells: Box::new(cells),
})
}
}
#[derive(Clone, Debug, Serialize, Default)]
pub struct OutputMoves {
pub moves: Vec<OutputMove>,
}
#[derive(Clone, Debug, Serialize)]
pub struct OutputMove {
pub location: OutputLocation,
pub spin: Spin,
}
#[derive(Clone, Debug, Serialize)]
pub struct OutputLocation {
#[serde(serialize_with = "serialize_piece_type")]
#[serde(rename = "type")]
pub ty: PieceType,
#[serde(serialize_with = "serialize_loc")]
#[serde(flatten)]
pub location: Loc,
}
impl From<Piece> for OutputLocation {
fn from(pc: Piece) -> Self {
Self {
ty: pc.ty,
location: pc.loc,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Spin {
#[default]
None,
Mini,
Full,
}
fn serialize_piece_type<S>(ty: &PieceType, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
ty.as_char().serialize(ser)
}
fn serialize_loc<S>(loc: &Loc, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut ser = ser.serialize_struct("Location", 3)?;
ser.serialize_field("x", &loc.x)?;
ser.serialize_field("y", &loc.y)?;
let r = ["north", "east", "south", "west"][loc.r as usize];
ser.serialize_field("orientation", r)?;
ser.end()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_color_repr() {
assert_eq!(
core::mem::size_of::<Option<Color>>(),
core::mem::size_of::<u8>()
);
assert_eq!(
core::mem::size_of::<[Option<Color>; 10]>(),
core::mem::size_of::<[u8; 10]>()
);
assert_eq!(
unsafe { core::mem::transmute::<Option<Color>, u8>(None) },
0u8
);
}
}

View File

@ -4,4 +4,7 @@ extern crate alloc;
pub mod find;
#[cfg(feature = "io")]
pub mod io;
pub use find::find_locations;