implement staging api

This commit is contained in:
2026-07-07 18:44:50 -07:00
parent a511679a47
commit 8c64583c00
5 changed files with 65 additions and 56 deletions
+3 -1
View File
@@ -21,7 +21,9 @@ fn play_to_win(rng: &mut Rng) -> Option<KlondikeStats> {
return None;
}
game.process_instruction(&mut stats, &CONFIG, instruction);
game.stage_instruction(&mut stats, &CONFIG, instruction)
.unwrap()
.commit();
}
game.is_win().then_some(stats)
}
+8 -5
View File
@@ -210,7 +210,7 @@ fn find_valid_instruction(
});
let instruction =
KlondikeInstruction::DstTableau(DstTableau { tableau, src });
if state.is_instruction_valid(config, instruction) {
if state.is_instruction_valid(config, &instruction) {
return Some(instruction);
}
}
@@ -228,7 +228,7 @@ fn find_valid_instruction(
_ => return None,
};
state
.is_instruction_valid(config, instruction)
.is_instruction_valid(config, &instruction)
.then_some(instruction)
}
@@ -277,13 +277,16 @@ fn main() -> Result<(), std::io::Error> {
.state()
.get_auto_move(&session.config().inner)
{
session.process_instruction(instruction);
session.stage_instruction(instruction).unwrap().commit();
} else {
println!("No valid moves!");
}
}
SessionInstruction::Stock => {
session.process_instruction(KlondikeInstruction::RotateStock)
session
.stage_instruction(KlondikeInstruction::RotateStock)
.unwrap()
.commit();
}
SessionInstruction::Klondike(naive_instruction) => {
if let Some(instruction) = find_valid_instruction(
@@ -291,7 +294,7 @@ fn main() -> Result<(), std::io::Error> {
session.state().state(),
naive_instruction,
) {
session.process_instruction(instruction);
session.stage_instruction(instruction).unwrap().commit();
} else {
println!("Invalid move!");
}
+1 -1
View File
@@ -16,7 +16,7 @@ let mut session = Session::new_default(game);
// play game a bit
while let Some(instruction) = session.state().state().get_auto_move(&config) {
session.process_instruction(instruction);
session.stage_instruction(instruction).unwrap().commit();
// quit after 200 moves or win
if session.is_win() || 200 < session.stats().stats().moves() {
+45 -47
View File
@@ -1,6 +1,6 @@
pub type Rng = rand::rngs::StdRng;
use card_game::{Card, Game, Pile, Rank, Stack};
use card_game::{Card, Game, InstructionConsumer, Pile, Rank, Stack, StagedInstruction};
#[cfg(test)]
mod test;
@@ -621,7 +621,7 @@ impl KlondikeState {
pub fn is_instruction_valid(
&self,
config: &KlondikeConfig,
instruction: KlondikeInstruction,
instruction: &KlondikeInstruction,
) -> bool {
match instruction {
// Stock -> Stock draws a card or resets the stock
@@ -830,54 +830,11 @@ impl Game for Klondike {
let state = self.state.clone();
let config = config.clone();
KlondikeIter::new()
.filter(move |&instruction| state.is_instruction_valid(&config, instruction))
.filter(move |instruction| state.is_instruction_valid(&config, instruction))
}
fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool {
fn is_instruction_valid(&self, config: &Self::Config, instruction: &Self::Instruction) -> bool {
self.state.is_instruction_valid(config, instruction)
}
fn process_instruction(
&mut self,
stats: &mut Self::Stats,
config: &Self::Config,
instruction: Self::Instruction,
) {
stats.increment_moves();
match instruction {
// Reset the stock if it's empty
KlondikeInstruction::RotateStock => {
if self.state.stock.face_down().is_empty() {
self.state.stock.flip_it_and_reverse_it();
stats.increment_recycle_count();
} else {
for _ in 0..config.draw_stock as usize {
self.state.stock.flip_up();
}
}
}
// Move a card from anywhere to a foundation
KlondikeInstruction::DstFoundation(DstFoundation { src, foundation }) => {
stats.increment_move_to_foundation();
let (card, did_flip_up) = self.state.take_top_card_flip_up(src);
if did_flip_up {
stats.increment_flip_up_bonus();
}
self.state.extend_foundation(foundation, card);
}
// Move a stack of cards from anywhere to a tableau
KlondikeInstruction::DstTableau(DstTableau { src, tableau }) => {
match src {
KlondikePileStack::Stock => stats.increment_move_to_tableau(),
KlondikePileStack::Foundation(_) => stats.increment_move_from_foundation(),
KlondikePileStack::Tableau(_) => {}
}
let (cards, did_flip_up) = self.state.take_stack_flip_up(src);
if did_flip_up {
stats.increment_flip_up_bonus();
}
self.state.extend_tableau(tableau, cards);
}
}
}
fn is_win(&self) -> bool {
// all foundations contain all ranks
self.state.foundations.iter().all(|foundation| {
@@ -889,6 +846,47 @@ impl Game for Klondike {
})
}
}
impl InstructionConsumer for Klondike {
fn process_instruction(valid_instruction: StagedInstruction<'_, Self>) {
let (game, stats, config, instruction) = valid_instruction.consume();
stats.increment_moves();
match instruction {
// Reset the stock if it's empty
KlondikeInstruction::RotateStock => {
if game.state.stock.face_down().is_empty() {
game.state.stock.flip_it_and_reverse_it();
stats.increment_recycle_count();
} else {
for _ in 0..config.draw_stock as usize {
game.state.stock.flip_up();
}
}
}
// Move a card from anywhere to a foundation
KlondikeInstruction::DstFoundation(DstFoundation { src, foundation }) => {
stats.increment_move_to_foundation();
let (card, did_flip_up) = game.state.take_top_card_flip_up(src);
if did_flip_up {
stats.increment_flip_up_bonus();
}
game.state.extend_foundation(foundation, card);
}
// Move a stack of cards from anywhere to a tableau
KlondikeInstruction::DstTableau(DstTableau { src, tableau }) => {
match src {
KlondikePileStack::Stock => stats.increment_move_to_tableau(),
KlondikePileStack::Foundation(_) => stats.increment_move_from_foundation(),
KlondikePileStack::Tableau(_) => {}
}
let (cards, did_flip_up) = game.state.take_stack_flip_up(src);
if did_flip_up {
stats.increment_flip_up_bonus();
}
game.state.extend_tableau(tableau, cards);
}
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for KlondikeInstruction {
+8 -2
View File
@@ -23,7 +23,10 @@ fn test_json() {
let solution_result = session.solve();
if let Ok(Some(solution)) = solution_result {
for snapshot in solution.clean_solution() {
session.process_instruction(snapshot.instruction().clone());
session
.stage_instruction(snapshot.instruction().clone())
.unwrap()
.commit();
}
}
let serialized = serde_json::to_string(&session).unwrap();
@@ -42,7 +45,10 @@ fn test_rmp() {
let solution_result = session.solve();
if let Ok(Some(solution)) = solution_result {
for snapshot in solution.clean_solution() {
session.process_instruction(snapshot.instruction().clone());
session
.stage_instruction(snapshot.instruction().clone())
.unwrap()
.commit();
}
}
let serialized = rmp_serde::to_vec(&session).unwrap();