Add MoveFromFoundationConfig option to KlondikeConfig (#12)
Closes #11 Reviewed-on: #12 Co-authored-by: Rhys Lloyd <krakow20@gmail.com> Co-committed-by: Rhys Lloyd <krakow20@gmail.com>
This commit was merged in pull request #12.
This commit is contained in:
+10
-4
@@ -10,7 +10,10 @@ pub trait Game {
|
||||
type Stats;
|
||||
type Config;
|
||||
type Instruction;
|
||||
fn possible_instructions(&self) -> impl Iterator<Item = Self::Instruction> + use<Self>;
|
||||
fn possible_instructions(
|
||||
&self,
|
||||
config: &Self::Config,
|
||||
) -> impl Iterator<Item = Self::Instruction> + use<Self>;
|
||||
fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool;
|
||||
fn process_instruction(
|
||||
&mut self,
|
||||
@@ -377,7 +380,7 @@ where
|
||||
.process_instruction(&mut self.stats, &self.config, SessionInstruction::Undo)
|
||||
}
|
||||
pub fn possible_instructions(&self) -> impl Iterator<Item = G::Instruction> + use<G> {
|
||||
self.state.state.possible_instructions()
|
||||
self.state.state.possible_instructions(&self.config)
|
||||
}
|
||||
pub fn process_instruction(&mut self, instruction: G::Instruction) {
|
||||
self.state.process_instruction(
|
||||
@@ -399,9 +402,12 @@ where
|
||||
type Stats = SessionStats<G::Stats>;
|
||||
type Config = G::Config;
|
||||
type Instruction = SessionInstruction<G::Instruction>;
|
||||
fn possible_instructions(&self) -> impl Iterator<Item = Self::Instruction> + use<G> {
|
||||
fn possible_instructions(
|
||||
&self,
|
||||
config: &Self::Config,
|
||||
) -> impl Iterator<Item = Self::Instruction> + use<G> {
|
||||
self.state
|
||||
.possible_instructions()
|
||||
.possible_instructions(config)
|
||||
.map(SessionInstruction::InnerInstruction)
|
||||
}
|
||||
fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool {
|
||||
|
||||
@@ -9,9 +9,10 @@ fn play_to_win(rng: &mut Rng) -> Option<KlondikeStats> {
|
||||
let mut stats = KlondikeStats::new();
|
||||
const CONFIG: KlondikeConfig = KlondikeConfig {
|
||||
draw_stock: klondike::DrawStockConfig::DrawOne,
|
||||
move_from_foundation: klondike::MoveFromFoundationConfig::Allowed,
|
||||
};
|
||||
// play game a bit
|
||||
while let Some(instruction) = game.get_auto_move()
|
||||
while let Some(instruction) = game.get_auto_move(&CONFIG)
|
||||
&& !game.is_win()
|
||||
{
|
||||
// quit before 250 moves
|
||||
|
||||
@@ -274,7 +274,7 @@ fn main() -> Result<(), std::io::Error> {
|
||||
}
|
||||
}
|
||||
SessionInstruction::Auto => {
|
||||
if let Some(instruction) = session.state().get_auto_move() {
|
||||
if let Some(instruction) = session.state().get_auto_move(session.config()) {
|
||||
session.process_instruction(instruction);
|
||||
} else {
|
||||
println!("No valid moves!");
|
||||
|
||||
+3
-2
@@ -7,14 +7,15 @@ Klondike
|
||||
|
||||
```rust
|
||||
use card_game::Session;
|
||||
use klondike::Klondike;
|
||||
use klondike::{Klondike, KlondikeConfig};
|
||||
|
||||
// create game session
|
||||
let game = Klondike::with_seed(123);
|
||||
let config = KlondikeConfig::default();
|
||||
let mut session = Session::new_default(game);
|
||||
|
||||
// play game a bit
|
||||
while let Some(instruction) = session.state().get_auto_move() {
|
||||
while let Some(instruction) = session.state().get_auto_move(&config) {
|
||||
session.process_instruction(instruction);
|
||||
|
||||
// quit after 200 moves or win
|
||||
|
||||
+31
-9
@@ -14,9 +14,17 @@ pub enum DrawStockConfig {
|
||||
DrawThree = 3,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum MoveFromFoundationConfig {
|
||||
#[default]
|
||||
Allowed,
|
||||
Disallowed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct KlondikeConfig {
|
||||
pub draw_stock: DrawStockConfig,
|
||||
pub move_from_foundation: MoveFromFoundationConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
@@ -459,7 +467,11 @@ impl KlondikeState {
|
||||
Tableau::Tableau7 => self.tableau7.extend(cards),
|
||||
}
|
||||
}
|
||||
pub fn is_instruction_valid(&self, instruction: KlondikeInstruction) -> bool {
|
||||
pub fn is_instruction_valid(
|
||||
&self,
|
||||
config: &KlondikeConfig,
|
||||
instruction: KlondikeInstruction,
|
||||
) -> bool {
|
||||
match instruction {
|
||||
// Stock -> Stock draws a card or resets the stock
|
||||
KlondikeInstruction::RotateStock => {
|
||||
@@ -488,6 +500,11 @@ impl KlondikeState {
|
||||
}
|
||||
// other = move to tableau
|
||||
KlondikeInstruction::DstTableau(dst_tableau) => {
|
||||
if config.move_from_foundation == MoveFromFoundationConfig::Disallowed
|
||||
&& let KlondikePileStack::Foundation(_) = dst_tableau.src
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// get the cards
|
||||
if let Some(src_card) = self.stack_bottom_card(dst_tableau.src) {
|
||||
match self.top_card(dst_tableau.tableau) {
|
||||
@@ -620,15 +637,15 @@ impl Klondike {
|
||||
}
|
||||
}
|
||||
/// A single move that usually makes progress towards a winning game
|
||||
pub fn get_auto_move(&self) -> Option<KlondikeInstruction> {
|
||||
self.possible_instructions()
|
||||
pub fn get_auto_move(&self, config: &KlondikeConfig) -> Option<KlondikeInstruction> {
|
||||
self.possible_instructions(config)
|
||||
.filter(|ins| !ins.is_useless())
|
||||
.min_by_key(|ins| self.instruction_priority(ins))
|
||||
}
|
||||
/// A list of possible moves with useless moves filtered out and sorted by a simple priority function
|
||||
pub fn get_sorted_moves(&self) -> Vec<KlondikeInstruction> {
|
||||
pub fn get_sorted_moves(&self, config: &KlondikeConfig) -> Vec<KlondikeInstruction> {
|
||||
let mut useful_moves: Vec<_> = self
|
||||
.possible_instructions()
|
||||
.possible_instructions(config)
|
||||
.filter(|ins| !ins.is_useless())
|
||||
.collect();
|
||||
useful_moves.sort_by_key(|ins| self.instruction_priority(ins));
|
||||
@@ -640,12 +657,17 @@ impl Game for Klondike {
|
||||
type Stats = KlondikeStats;
|
||||
type Config = KlondikeConfig;
|
||||
type Instruction = KlondikeInstruction;
|
||||
fn possible_instructions(&self) -> impl Iterator<Item = Self::Instruction> + use<> {
|
||||
fn possible_instructions(
|
||||
&self,
|
||||
config: &Self::Config,
|
||||
) -> impl Iterator<Item = Self::Instruction> + use<> {
|
||||
let state = self.state.clone();
|
||||
KlondikeIter::new().filter(move |&instruction| state.is_instruction_valid(instruction))
|
||||
let config = config.clone();
|
||||
KlondikeIter::new()
|
||||
.filter(move |&instruction| state.is_instruction_valid(&config, instruction))
|
||||
}
|
||||
fn is_instruction_valid(&self, _config: &Self::Config, instruction: Self::Instruction) -> bool {
|
||||
self.state.is_instruction_valid(instruction)
|
||||
fn is_instruction_valid(&self, config: &Self::Config, instruction: Self::Instruction) -> bool {
|
||||
self.state.is_instruction_valid(config, instruction)
|
||||
}
|
||||
fn process_instruction(
|
||||
&mut self,
|
||||
|
||||
Reference in New Issue
Block a user