Further improvements to the smart dealer

This commit is contained in:
Pyrex 2024-02-07 18:36:08 -08:00
parent 89b0dce6b9
commit 0b3d45378f
4 changed files with 77 additions and 39 deletions

View File

@ -1,6 +1,6 @@
progression={ progression={
-- level 1 -- level 1
--ruleset:new(5,1,9,0), -- ruleset:new(5,1,9,0),
-- level 2 -- level 2
-- ruleset:new(5,2,9,0), -- ruleset:new(5,2,9,0),
-- level 3 -- level 3

View File

@ -80,13 +80,21 @@ impl<'a> Board<'a> {
} }
pub fn display(&self) { pub fn display(&self) {
fn display_cards(setup: &Setup, cards: &[Card]) {
print!("- ");
for c in cards {
let meta = setup.deck.cards[c.0 as usize];
print!("{}{:02} ", meta.suit as char, meta.rank);
}
print!("\n");
}
println!("Wells:"); println!("Wells:");
for w in &self.wells { for w in &self.wells {
println!("- {:?}", w.contents); display_cards(self.setup, &w.contents);
} }
println!("\nSlots:"); println!("\nSlots:");
for s in &self.slots { for s in &self.slots {
println!("- {:?}", s.contents); display_cards(self.setup, &s.contents);
} }
} }

View File

@ -12,20 +12,20 @@ mod zobrist;
fn main() { fn main() {
/*
let ruleset = Ruleset { let ruleset = Ruleset {
n_slots: 11, n_slots: 11,
n_suits: 5, n_suits: 5,
n_cards_per_suit: 10, n_cards_per_suit: 10,
n_arcana: 25 n_arcana: 25
}; };
/* */
let ruleset = Ruleset { let ruleset = Ruleset {
n_slots: 11, n_slots: 11,
n_suits: 4, n_suits: 4,
n_cards_per_suit: 13, n_cards_per_suit: 13,
n_arcana: 22 n_arcana: 22
}; };
*/
/* /*
let ruleset = Ruleset { let ruleset = Ruleset {
n_slots: 5, n_slots: 5,
@ -51,11 +51,25 @@ fn main() {
}; };
*/ */
let setup = ruleset.compile().expect("compilation should succeed"); let setup = ruleset.compile().expect("compilation should succeed");
let mut board = Board::new(&setup); /*
board.deal(Deal::deal(&setup, &mut rand::thread_rng())); for _ in 0..10000 {
board.display(); Deal::deal(&setup, &mut rand::thread_rng());
}
*/
println!("is_winnable: {}", is_winnable(board)); let mut winnable = 0;
let mut total = 0;
loop {
let mut board = Board::new(&setup);
board.deal(Deal::deal(&setup, &mut rand::thread_rng()));
board.display();
if is_winnable(board) {
winnable += 1;
}
total += 1;
println!("winnable: {}/{} ({}%)", winnable, total, (100.0 * winnable as f32)/(total as f32));
}
// println!("Legal moves: {:#?}", board.legal_moves()); // println!("Legal moves: {:#?}", board.legal_moves());

View File

@ -17,10 +17,11 @@ impl Deal {
fn deal1(setup: &Setup, rng: &mut impl Rng) -> Option<Deal> { fn deal1(setup: &Setup, rng: &mut impl Rng) -> Option<Deal> {
// don't use the middle slot // don't use the middle slot
let n_slots = (setup.ruleset.n_slots - 1) as usize; let n_slots = (setup.ruleset.n_slots - 1) as usize;
let aux_slot = n_slots;
let n_usable_cards = setup.ruleset.usable_n_cards(); let n_usable_cards = setup.ruleset.usable_n_cards();
let tower_height = n_usable_cards as usize / n_slots; let tower_height = n_usable_cards as usize / n_slots;
let mut slots: Vec<Vec<Card>> = vec![vec![]; n_slots]; let mut slots: Vec<Vec<Card>> = vec![vec![]; n_slots+1];
let split_point = let split_point =
if setup.ruleset.n_arcana == 0 { 0 } if setup.ruleset.n_arcana == 0 { 0 }
@ -53,37 +54,41 @@ impl Deal {
} }
pops.shuffle(rng); pops.shuffle(rng);
while let Some(w) = pops.pop() { fn find_home(card: Card, exclude: Option<usize>, setup: &Setup, slots: &mut [Vec<Card>], aux_slot: usize, tower_height: usize, rng: &mut impl Rng) {
fn find_home(card: Card, exclude: Option<usize>, setup: &Setup, slots: &mut [Vec<Card>], tower_height: usize, rng: &mut impl Rng) { let mut acceptors = vec![];
let mut acceptors = vec![]; let mut not_full = vec![];
let mut not_full = vec![]; for s in 0..slots.len() {
for s in 0..slots.len() { let not_too_tall = slots[s].len() < if s == aux_slot { 1 } else { tower_height };
if Some(s) != exclude && slots[s].len() < tower_height { if Some(s) != exclude {
if accepts(setup, slots[s].last().cloned(), card) { if not_too_tall && accepts(setup, slots[s].last().cloned(), card) {
acceptors.push(s) acceptors.push(s)
} }
if not_too_tall {
not_full.push(s); not_full.push(s);
} }
} }
acceptors.shuffle(rng);
not_full.shuffle(rng);
if rng.gen_bool(0.5) && acceptors.len() > 0 {
let a = acceptors.first().unwrap();
slots[*a].push(card);
} else if let Some(a) = not_full.first() {
slots[*a].push(card);
} else if let Some(e) = exclude {
slots[e].push(card)
} else {
panic!("should not ever happen")
}
} }
acceptors.shuffle(rng);
not_full.shuffle(rng);
if rng.gen_bool(0.5) && acceptors.len() > 0 {
let a = acceptors.first().unwrap();
slots[*a].push(card);
} else if let Some(a) = not_full.first() {
slots[*a].push(card);
} else if let Some(e) = exclude {
slots[e].push(card)
} else {
panic!("should not ever happen")
}
}
while let Some(w) = pops.pop() {
let card = virtual_wells[w].pop().expect("card must be present"); let card = virtual_wells[w].pop().expect("card must be present");
find_home(card, None, setup, &mut slots, tower_height, rng); find_home(card, None, setup, &mut slots, aux_slot, tower_height, rng);
// move any card that is on an acceptor to a random slot // move any card that is on an acceptor to a random slot
for _ in 0..4 { for _ in 0..15 {
let mut sources: Vec<usize> = (0..slots.len()).collect(); let mut sources: Vec<usize> = (0..slots.len()).collect();
sources.shuffle(rng); sources.shuffle(rng);
for src in sources { for src in sources {
@ -94,13 +99,19 @@ impl Deal {
if let Some(t) = top { if let Some(t) = top {
if accepts(&setup, second_to_top, t) { if accepts(&setup, second_to_top, t) {
slots[src].pop(); slots[src].pop();
find_home(t, Some(src), setup, &mut slots, tower_height, rng); find_home(t, Some(src), setup, &mut slots, aux_slot, tower_height, rng);
} }
} }
} }
} }
} }
if let Some(top) = slots[n_slots].pop() {
assert!(slots[n_slots].len() == 0);
find_home(top, Some(n_slots), setup, &mut slots, aux_slot, tower_height, rng);
assert!(slots[n_slots].len() == 0);
}
let mut instantly_accepted = vec![]; let mut instantly_accepted = vec![];
for &a in &setup.deck.aces { for &a in &setup.deck.aces {
instantly_accepted.push(Card(a.0 + 1)) // twos instantly_accepted.push(Card(a.0 + 1)) // twos
@ -108,9 +119,7 @@ impl Deal {
instantly_accepted.push(Card(first_arcana)); instantly_accepted.push(Card(first_arcana));
instantly_accepted.push(Card(first_arcana + setup.ruleset.n_arcana - 1)); instantly_accepted.push(Card(first_arcana + setup.ruleset.n_arcana - 1));
// NOTE: We never used the free cell. The free cell can dig one deeper for s in 0..n_slots {
// So in theory most of these deals should be solvable using the freecell
for s in 0..slots.len() {
let mut iter = slots[s].iter().rev(); let mut iter = slots[s].iter().rev();
let last = iter.next().cloned(); let last = iter.next().cloned();
let second_to_last = iter.next().cloned(); let second_to_last = iter.next().cloned();
@ -132,6 +141,13 @@ impl Deal {
} }
} }
for s in 0..n_slots {
assert!(slots[s].len() == tower_height)
}
slots.pop(); // get rid of aux slot
return Some(Deal { slots }); return Some(Deal { slots });
} }