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={
-- level 1
--ruleset:new(5,1,9,0),
-- ruleset:new(5,1,9,0),
-- level 2
-- ruleset:new(5,2,9,0),
-- level 3

View File

@ -80,13 +80,21 @@ impl<'a> Board<'a> {
}
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:");
for w in &self.wells {
println!("- {:?}", w.contents);
display_cards(self.setup, &w.contents);
}
println!("\nSlots:");
for s in &self.slots {
println!("- {:?}", s.contents);
display_cards(self.setup, &s.contents);
}
}

View File

@ -12,21 +12,21 @@ mod zobrist;
fn main() {
/*
let ruleset = Ruleset {
n_slots: 11,
n_suits: 5,
n_cards_per_suit: 10,
n_arcana: 25
};
/*
*/
let ruleset = Ruleset {
n_slots: 11,
n_suits: 4,
n_cards_per_suit: 13,
n_arcana: 22
};
*/
/*
/*
let ruleset = Ruleset {
n_slots: 5,
n_suits: 1,
@ -51,11 +51,25 @@ fn main() {
};
*/
let setup = ruleset.compile().expect("compilation should succeed");
let mut board = Board::new(&setup);
board.deal(Deal::deal(&setup, &mut rand::thread_rng()));
board.display();
/*
for _ in 0..10000 {
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());

View File

@ -17,10 +17,11 @@ impl Deal {
fn deal1(setup: &Setup, rng: &mut impl Rng) -> Option<Deal> {
// don't use the middle slot
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 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 =
if setup.ruleset.n_arcana == 0 { 0 }
@ -53,37 +54,41 @@ impl Deal {
}
pops.shuffle(rng);
while let Some(w) = pops.pop() {
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 not_full = vec![];
for s in 0..slots.len() {
if Some(s) != exclude && slots[s].len() < tower_height {
if accepts(setup, slots[s].last().cloned(), card) {
acceptors.push(s)
}
fn find_home(card: Card, exclude: Option<usize>, setup: &Setup, slots: &mut [Vec<Card>], aux_slot: usize, tower_height: usize, rng: &mut impl Rng) {
let mut acceptors = vec![];
let mut not_full = vec![];
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 {
if not_too_tall && accepts(setup, slots[s].last().cloned(), card) {
acceptors.push(s)
}
if not_too_tall {
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");
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
for _ in 0..4 {
for _ in 0..15 {
let mut sources: Vec<usize> = (0..slots.len()).collect();
sources.shuffle(rng);
for src in sources {
@ -94,13 +99,19 @@ impl Deal {
if let Some(t) = top {
if accepts(&setup, second_to_top, t) {
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![];
for &a in &setup.deck.aces {
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 + setup.ruleset.n_arcana - 1));
// NOTE: We never used the free cell. The free cell can dig one deeper
// So in theory most of these deals should be solvable using the freecell
for s in 0..slots.len() {
for s in 0..n_slots {
let mut iter = slots[s].iter().rev();
let 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 });
}