Simplify the better dealer

This commit is contained in:
Pyrex 2024-02-07 20:05:00 -08:00
parent bff92e51ab
commit 37d39b4521
2 changed files with 41 additions and 40 deletions

View File

@ -11,6 +11,7 @@ pub struct Deck {
pub aces: Vec<Card>, pub aces: Vec<Card>,
pub suits: Vec<u8>, pub suits: Vec<u8>,
pub cards: Vec<CardMetadata>, pub cards: Vec<CardMetadata>,
pub instantly_accepted: Vec<Card>,
} }
pub struct Setup { pub struct Setup {
@ -89,6 +90,16 @@ impl Ruleset {
cards.push(CardMetadata {suit: b'a', rank}); cards.push(CardMetadata {suit: b'a', rank});
} }
Deck { aces, suits, cards }
// instantly accepted
let first_arcana = self.n_suits * self.n_cards_per_suit;
let mut instantly_accepted = vec![];
for a in aces.iter() {
instantly_accepted.push(Card(a.0 + 1)) // twos
}
instantly_accepted.push(Card(first_arcana));
instantly_accepted.push(Card(first_arcana + self.n_arcana - 1));
Deck { aces, suits, cards, instantly_accepted }
} }
} }

View File

@ -8,10 +8,13 @@ pub struct Deal {
impl Deal { impl Deal {
pub fn deal(setup: &Setup, rng: &mut impl Rng) -> Deal { pub fn deal(setup: &Setup, rng: &mut impl Rng) -> Deal {
let mut tries = 0;
loop { loop {
if let Some(d) = Self::deal1(setup, rng) { if let Some(d) = Self::deal1(setup, rng) {
println!("Generated ({} tries)", tries);
return d; return d;
} }
tries += 1;
} }
} }
@ -43,7 +46,9 @@ impl Deal {
if !is_arcana { if !is_arcana {
// we can't put a card in the well if it's blocked, so make sure it's unblocked // we can't put a card in the well if it's blocked, so make sure it's unblocked
if let Some(c) = slots[0].pop() { if let Some(c) = slots[0].pop() {
Self::find_home(setup, rng, 1.0, c, None, Some(0), &mut slots, &max_height); if !Self::find_home(setup, rng, 1.0, c, None, Some(0), &mut slots, &max_height) {
return None
}
} }
exclude = Some(0); // don't place the new card in the auxiliary slot exclude = Some(0); // don't place the new card in the auxiliary slot
@ -51,14 +56,23 @@ impl Deal {
let card = wells[w].pop().expect("card must be present"); let card = wells[w].pop().expect("card must be present");
Self::find_home(setup, rng, 1.0, card, None, exclude, &mut slots, &max_height); if !Self::find_home(setup, rng, 1.0, card, None, exclude, &mut slots, &max_height) {
return None;
}
// do some moves from acceptors to random slots // do some moves from acceptors to random slots
const MAX_I: i32 = 48; const MAX_I: i32 = 48;
for i in 0..MAX_I+1 { for i in 0..MAX_I+1 {
let src = rng.gen_range(0..slots.len()); let src = rng.gen_range(0..slots.len());
if let Some(accepted_card) = Self::pop_accepted_card(setup, src, &mut slots) { if let Some(accepted_card) = Self::pop_accepted_card(setup, src, &mut slots) {
Self::find_home(setup, rng, ((MAX_I - i) as f64)/(MAX_I as f64), accepted_card, Some(src), None, &mut slots, &max_height); let odds = ((MAX_I - i) as f64)/(MAX_I as f64);
if !Self::find_home(
setup, rng,
odds,
accepted_card, Some(src), None, &mut slots, &max_height
) {
return None
}
} }
} }
} }
@ -68,13 +82,17 @@ impl Deal {
for i in 0..slots.len() { for i in 0..slots.len() {
while slots[i].len() > max_height[i] { while slots[i].len() > max_height[i] {
let card = slots[i].pop().expect("must be a card"); let card = slots[i].pop().expect("must be a card");
Self::find_home(setup, rng, 0.0, card, None, None, &mut slots, &max_height); if !Self::find_home(setup, rng, 0.0, card, None, None, &mut slots, &max_height) {
return None
}
} }
} }
slots.remove(0); // get rid of the auxiliary slot slots.remove(0); // get rid of the auxiliary slot
if !Self::fix_instantly_accepted(setup, &mut slots) { for s in 0..slots.len() {
return None; if setup.deck.instantly_accepted.contains(slots[s].last().expect("each slot must contain cards")) {
return None;
}
} }
for s in slots.iter() { for s in slots.iter() {
@ -132,7 +150,7 @@ impl Deal {
return (second_to_top, top); return (second_to_top, top);
} }
fn find_home(setup: &Setup, rng: &mut impl Rng, acceptor_odds: f64, card: Card, source: Option<usize>, exclude: Option<usize>, slots: &mut [Vec<Card>], max_height: &[usize]) { fn find_home(setup: &Setup, rng: &mut impl Rng, acceptor_odds: f64, card: Card, source: Option<usize>, exclude: Option<usize>, slots: &mut [Vec<Card>], max_height: &[usize]) -> bool {
// if a card is sitting on an acceptor, it could have been moved there // if a card is sitting on an acceptor, it could have been moved there
// from somewhere else // from somewhere else
let mut acceptors = vec![]; let mut acceptors = vec![];
@ -144,6 +162,8 @@ impl Deal {
for s in 0..slots.len() { for s in 0..slots.len() {
if Some(s) == exclude { if Some(s) == exclude {
// don't place it here, ever // don't place it here, ever
} else if slots[s].len() == max_height[s] - 1 && setup.deck.instantly_accepted.contains(&card) {
// can't place an instantly accepted card at the bottom of a slot
} else { } else {
if accepts(setup, slots[s].last().cloned(), card) { if accepts(setup, slots[s].last().cloned(), card) {
acceptors.push(s); acceptors.push(s);
@ -164,42 +184,12 @@ impl Deal {
if let Some(a) = acceptor.or(start_point).or(source) { if let Some(a) = acceptor.or(start_point).or(source) {
slots[a].push(card); slots[a].push(card);
return true;
} else { } else {
panic!("should not ever happen"); return false;
} }
} }
fn fix_instantly_accepted(setup: &Setup, slots: &mut [Vec<Card>]) -> bool {
let first_arcana = setup.ruleset.n_suits * setup.ruleset.n_cards_per_suit;
let mut instantly_accepted = vec![];
for &a in &setup.deck.aces {
instantly_accepted.push(Card(a.0 + 1)) // twos
}
instantly_accepted.push(Card(first_arcana));
instantly_accepted.push(Card(first_arcana + setup.ruleset.n_arcana - 1));
for s in 0..slots.len() {
let (second_to_last, last) = Self::peek2(&slots[s]);
if let Some(last) = last {
if instantly_accepted.contains(&last) {
if let Some(c) = second_to_last {
if instantly_accepted.contains(&c) {
return false
}
let n = slots[s].len();
(slots[s][n-2],slots[s][n-1]) = (last, c);
}
else {
return false; // confusing nonsense situation
}
}
}
}
return true;
}
fn pop_accepted_card(setup: &Setup, src: usize, slots: &mut [Vec<Card>]) -> Option<Card> { fn pop_accepted_card(setup: &Setup, src: usize, slots: &mut [Vec<Card>]) -> Option<Card> {
let (card0, card1) = Self::peek2(&slots[src]); let (card0, card1) = Self::peek2(&slots[src]);