diff --git a/simulator/src/smart_dealer.rs b/simulator/src/smart_dealer.rs index e8cf607..be8d5cf 100644 --- a/simulator/src/smart_dealer.rs +++ b/simulator/src/smart_dealer.rs @@ -8,17 +8,6 @@ pub struct Deal { impl Deal { pub fn deal(setup: &Setup, rng: &mut impl Rng) -> Deal { - let mut tries = 0; - loop { - if let Some(d) = Self::deal1(setup, rng) { - println!("Generated ({} tries)", tries); - return d; - } - tries += 1; - } - } - - fn deal1(setup: &Setup, rng: &mut impl Rng) -> Option { let n_usable_cards = setup.ruleset.usable_n_cards(); // there are n - 1 final slots, because we don't use the middle one @@ -39,41 +28,10 @@ impl Deal { let mut wells = Self::generate_wells(setup, rng); let mut pops = Self::plan_pops(&wells, rng); - while let Some(w) = pops.pop() { - let is_arcana = w >= setup.ruleset.n_suits as usize; - - let mut exclude = None; - if !is_arcana { - // 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 !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 - } - - let card = wells[w].pop().expect("card must be present"); - - 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 - const MAX_I: i32 = 48; - for i in 0..MAX_I+1 { - let src = rng.gen_range(0..slots.len()); - if let Some(accepted_card) = Self::pop_accepted_card(setup, src, &mut slots) { - 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 - } - } + while let Some((w, n)) = pops.pop() { + for _ in 0..n { + let card = wells[w].pop().expect("card must be present"); + Self::find_home(setup, rng, card, None, None, &mut slots, &max_height, true); } } @@ -82,24 +40,28 @@ impl Deal { for i in 0..slots.len() { while slots[i].len() > max_height[i] { let card = slots[i].pop().expect("must be a card"); - if !Self::find_home(setup, rng, 0.0, card, None, None, &mut slots, &max_height) { - return None - } + Self::find_home(setup, rng, card, None, None, &mut slots, &max_height, false); } } slots.remove(0); // get rid of the auxiliary slot + let mut shuffles = 0; for s in 0..slots.len() { - if setup.deck.instantly_accepted.contains(slots[s].last().expect("each slot must contain cards")) { - return None; + let extra_shuf = shuffles + 1; + while setup.deck.instantly_accepted.contains(slots[s].last().expect("each slot must contain cards")) { + slots[s].shuffle(rng); + shuffles = extra_shuf } } + for _ in shuffles..2 { + slots.choose_mut(rng).unwrap().shuffle(rng); + } for s in slots.iter() { assert_eq!(tower_height, s.len()); } - return Some(Deal { slots } ) + return Deal { slots } } fn generate_wells(setup: &Setup, rng: &mut impl Rng) -> Vec> { @@ -132,7 +94,7 @@ impl Deal { wells } - fn plan_pops(wells: &[Vec], rng: &mut impl Rng) -> Vec { + fn plan_pops(wells: &[Vec], rng: &mut impl Rng) -> Vec<(usize, usize)> { let mut pops = vec![]; for (well, contents) in wells.iter().enumerate() { for _ in 0..contents.len() { @@ -140,67 +102,58 @@ impl Deal { } } pops.shuffle(rng); - pops + + let mut rle = vec![]; + let mut current: Option = None; + let mut count = 0; + while let Some(nxt) = pops.pop() { + if current == Some(nxt) && count < 4 { + count += 1; + } else { + if let Some(prev) = current { + rle.push((prev, count)) + } + current = Some(nxt); + count = 1; + } + } + + if let Some(prev) = current { + if count > 0 { + rle.push((prev, count)) + } + } + + rle } - fn peek2(stack: &Vec) -> (Option, Option) { - let mut iter = stack.iter().rev(); - let top = iter.next().cloned(); - let second_to_top = iter.next().cloned(); - return (second_to_top, top); - } - - fn find_home(setup: &Setup, rng: &mut impl Rng, acceptor_odds: f64, card: Card, source: Option, exclude: Option, slots: &mut [Vec], max_height: &[usize]) -> bool { + fn find_home(setup: &Setup, rng: &mut impl Rng, card: Card, source: Option, exclude: Option, slots: &mut [Vec], max_height: &[usize], allow_too_tall: bool) { // if a card is sitting on an acceptor, it could have been moved there // from somewhere else let mut acceptors = vec![]; - // if a card is sitting on a start point, it could not have been moved - // there from somewhere else - let mut start_points = vec![]; - for s in 0..slots.len() { if Some(s) == exclude { // don't place it here, ever - } else if slots[s].len() == max_height[s] - 1 && setup.deck.instantly_accepted.contains(&card) { + } else if false { // 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 { - if accepts(setup, slots[s].last().cloned(), card) { + if allow_too_tall && accepts(setup, slots[s].last().cloned(), card) { + acceptors.push(s); + } else if slots[s].len() < max_height[s] { acceptors.push(s); } - - if slots[s].len() < max_height[s] { - start_points.push(s); - } } } - let mut acceptor = acceptors.choose(rng).cloned(); - let start_point = start_points.choose(rng).cloned(); - if !rng.gen_bool(acceptor_odds) { - // don't use an acceptor even though we could - acceptor = None; - } + let acceptor = acceptors.choose(rng).cloned(); - if let Some(a) = acceptor.or(start_point).or(source) { + if let Some(a) = acceptor.or(source) { slots[a].push(card); - return true; } else { - return false; + panic!("should never happen") } } - - fn pop_accepted_card(setup: &Setup, src: usize, slots: &mut [Vec]) -> Option { - let (card0, card1) = Self::peek2(&slots[src]); - - if let Some(c1) = card1 { - if accepts(&setup, card0, c1) { - slots[src].pop().expect("we just peeked at this"); - return Some(c1); - } - } - None - } } fn accepts(setup: &Setup, prev: Option, next: Card) -> bool {