serde impl (#16)

Closes #15

Reviewed-on: #16
Co-authored-by: Rhys Lloyd <krakow20@gmail.com>
Co-committed-by: Rhys Lloyd <krakow20@gmail.com>
This commit was merged in pull request #16.
This commit is contained in:
2026-06-09 17:15:24 +00:00
committed by Quaternions
parent ccfcfd6ad3
commit d67645854a
11 changed files with 437 additions and 19 deletions
+53
View File
@@ -2,11 +2,18 @@ pub type Rng = rand::rngs::StdRng;
use card_game::{Card, Game, Pile, Rank, Stack};
#[cfg(test)]
mod test;
// test readme
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
struct ReadmeDoctests;
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum DrawStockConfig {
#[default]
@@ -14,6 +21,10 @@ pub enum DrawStockConfig {
DrawThree = 3,
}
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum MoveFromFoundationConfig {
#[default]
@@ -21,6 +32,10 @@ pub enum MoveFromFoundationConfig {
Disallowed,
}
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[derive(Clone, Copy, Debug)]
pub struct ScoringConfig {
pub move_to_foundation: i32,
@@ -44,6 +59,10 @@ impl Default for ScoringConfig {
}
}
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[derive(Clone, Debug, Default)]
pub struct KlondikeConfig {
pub draw_stock: DrawStockConfig,
@@ -379,6 +398,10 @@ const fn sum(n: usize) -> usize {
const STOCK: usize = 52 - sum(TABLEAUS);
const NUM_RANKS: usize = Rank::RANKS.len();
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct KlondikeState {
stock: Pile<STOCK, STOCK>,
@@ -613,6 +636,11 @@ fn test_klondike_iter() {
assert_eq!(KlondikeIter::new().count(), 721);
}
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize),
serde(transparent)
)]
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Klondike {
state: KlondikeState,
@@ -797,3 +825,28 @@ impl Game for Klondike {
})
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for KlondikeInstruction {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
// There is 721 klondike instructions
let instruction_id = KlondikeIter::new()
.position(|instruction| &instruction == self)
.unwrap();
serializer.serialize_u16(instruction_id as u16)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for KlondikeInstruction {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let instruction_id = u16::deserialize(deserializer)?;
let instruction = KlondikeIter::new().nth(instruction_id as usize).unwrap();
Ok(instruction)
}
}
@@ -0,0 +1,5 @@
---
source: klondike/src/test.rs
expression: serialized
---
{"config":{"inner":{"draw_stock":"DrawOne","move_from_foundation":"Allowed","scoring":{"move_to_foundation":10,"flip_up_bonus":5,"move_to_tableau":5,"move_from_foundation":-15,"recycle":0}},"undo_penalty":-15,"solve_moves_budget":100000,"solve_states_budget":100000},"initial_state":{"stock":{"face_down":[52,51,21,59,36,45,49,50,22,3,58,39,41,61,13,57,34,56,28,1,19,12,17,5],"face_up":[]},"foundations":[[],[],[],[]],"tableau1":{"face_down":[],"face_up":[40]},"tableau2":{"face_down":[9],"face_up":[4]},"tableau3":{"face_down":[35,8],"face_up":[60]},"tableau4":{"face_down":[11,27,44],"face_up":[25]},"tableau5":{"face_down":[6,7,23,18],"face_up":[29]},"tableau6":{"face_down":[2,55,10,42,24],"face_up":[54]},"tableau7":{"face_down":[26,43,38,53,37,33],"face_up":[20]}},"instructions":[336,100,720,619,606,6,720,19,16,388,497,580,388,720,139,720,19,720,17,31,720,720,720,7,720,720,523,458,720,720,720,720,720,720,720,720,43,43,720,720,720,139,720,19,720,43,720,43,720,720,720,720,720,720,720,720,720,623,619,720,720,720,720,720,720,720,720,720,720,693,281,333,253,209,113,87,720,720,720,720,720,720,720,720,75,223,619,533,74,2,720,116,331,279,279,305,305,29,25,24,12,37,25,222,24,42,126,471,720,720,720,362,292,331,253,720,19,720,183,151,12,67,336,61,93,235,720,7,2,0,7,38,26,14,27,15,26,36,0,7,3,6,18,43,36,24,12,0,1,19,38,26,39,27,15,38,26,36]}
@@ -0,0 +1,6 @@
---
source: klondike/src/test.rs
expression: serialized
extension: bin
snapshot_kind: binary
---
+55
View File
@@ -0,0 +1,55 @@
use crate::Klondike;
use card_game::Session;
#[test]
fn test_is_winnable() {
// is winnable
let solution_result = Session::new_default(Klondike::with_seed(124)).solve();
if let Ok(Some(solution)) = solution_result {
let win_moves = solution.clean_solution();
// for (i, ins) in win_moves.into_iter().enumerate() {
// println!("{i} = {:?}", ins.instruction());
// }
println!("Game is winnable with {} moves", win_moves.len());
} else {
println!("Game is not winnable");
}
}
#[cfg(feature = "serde")]
#[test]
fn test_json() {
let mut session = Session::new_default(Klondike::with_seed(124));
let solution_result = session.solve();
if let Ok(Some(solution)) = solution_result {
for snapshot in solution.clean_solution() {
session.process_instruction(snapshot.instruction().clone());
}
}
let serialized = serde_json::to_string(&session).unwrap();
println!("serialized = {serialized}");
let round_trip_session: Session<Klondike> = serde_json::from_str(&serialized).unwrap();
let serialized2 = serde_json::to_string(&round_trip_session).unwrap();
assert_eq!(serialized, serialized2);
insta::assert_snapshot!(serialized);
}
#[cfg(feature = "serde")]
#[test]
fn test_rmp() {
let mut session = Session::new_default(Klondike::with_seed(124));
let solution_result = session.solve();
if let Ok(Some(solution)) = solution_result {
for snapshot in solution.clean_solution() {
session.process_instruction(snapshot.instruction().clone());
}
}
let serialized = rmp_serde::to_vec(&session).unwrap();
println!("serialized.len() = {}", serialized.len());
let round_trip_session: Session<Klondike> = rmp_serde::from_slice(&serialized).unwrap();
let serialized2 = rmp_serde::to_vec(&round_trip_session).unwrap();
assert_eq!(serialized, serialized2);
insta::assert_binary_snapshot!("save_rmp.bin", serialized);
}