Skip to content

Commit 412e30c

Browse files
author
Ferdinand Schober
committed
optimize success chances as well
1 parent e8fe070 commit 412e30c

4 files changed

Lines changed: 45 additions & 22 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ game = ["dep:solitaire-game"]
3838
count-allocs = []
3939

4040
[profile.release]
41-
lto = "fat"
41+
lto = "thin"
4242
strip = "symbols"
4343
panic = "abort"
4444
codegen-units = 1

solitaire-game/src/solver.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ fn calculate_random_move_chances(
8484
let feasible = feasible.0.clone();
8585
let wake = wake.clone();
8686
let task = thread_pool.spawn(async move {
87-
let feasible = feasible.iter().copied().collect();
88-
let p_random_chance = solitaire_solver::calculate_p_random_chance_success(feasible);
87+
let feasible = feasible;
88+
let p_random_chance = solitaire_solver::calculate_p_random_chance_success(feasible.into_iter());
8989

9090
let mut command_queue = CommandQueue::default();
9191
command_queue.push(move |world: &mut World| {
Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,59 @@
1+
use crate::Dir;
2+
13
use super::{
24
Board,
35
hash::{CustomHashMap as HashMap, CustomHashSet as HashSet},
46
};
57

68
/// calculate the chances of winning the game by chosing possible moves at random
7-
pub fn calculate_p_random_chance_success(feasible: Vec<Board>) -> HashMap<Board, f64> {
8-
let feasible: HashSet<_> = feasible.into_iter().collect();
9+
pub fn calculate_p_random_chance_success(feasible: impl Iterator<Item = Board>) -> HashMap<Board, f64> {
910
let mut chances = HashMap::default();
1011
chances.insert(Board::solved(), 1.0);
11-
for i in 2..=(Board::SLOTS - 1) {
12-
let feasible_with_i_pegs = feasible
13-
.iter()
14-
.copied()
15-
.filter(|b| b.count_pegs() == i)
16-
.collect::<Vec<_>>();
17-
for constellation in feasible_with_i_pegs {
18-
let legal_moves = constellation.get_legal_moves();
12+
13+
let mut index: HashSet<Board> = HashSet::default();
14+
let mut by_pegs: Vec<Vec<Board>> = vec![Vec::new(); Board::SLOTS + 2];
15+
for board in feasible {
16+
index.insert(board);
17+
by_pegs[board.count_pegs()].push(board);
18+
}
19+
20+
for layer in by_pegs.iter().skip(2) {
21+
for board in layer {
22+
let mut buffer = [Board::empty(); MAX_MOVES];
23+
let successors = successors_of(*board, &mut buffer);
1924

2025
// we assume each legal move has equal chance of being taken (1 / n)
2126
// p_success = sum(moves, P(move) * P(success | move))
2227
// P(success | move) = 0.0 if infeasible, else lookup
23-
let p_move = 1.0 / legal_moves.len() as f64;
28+
let p_move = 1.0 / successors.len() as f64;
2429

2530
let mut p_success = 0.0;
2631

27-
for mov in legal_moves {
28-
let c_new = constellation.mov(mov).normalize();
29-
p_success += if feasible.contains(&c_new) {
30-
p_move * *chances.get(&c_new).expect("already present")
32+
for succ in successors {
33+
p_success += if index.contains(&succ) {
34+
p_move * *chances.get(&succ).expect("already present")
3135
} else {
3236
p_move * 0.0
3337
};
3438
}
3539

36-
chances.insert(constellation, p_success);
40+
chances.insert(*board, p_success);
3741
}
3842
}
3943
chances
4044
}
45+
46+
const MAX_MOVES: usize = 76;
47+
48+
fn successors_of(board: Board, buffer: &mut [Board; MAX_MOVES]) -> &[Board] {
49+
let syms = board.symmetries();
50+
let mut len = 0;
51+
for dir in Dir::enumerate() {
52+
for idx in board.mov_pattern_mask(dir) {
53+
debug_assert!(len < MAX_MOVES, "more than {MAX_MOVES} moves from one board");
54+
buffer[len] = Board::normalize_after_move(&syms, idx, dir);
55+
len += 1;
56+
}
57+
}
58+
&buffer[..len]
59+
}

src/main.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,16 +178,20 @@ fn main() {
178178
solitaire_solver::calculate_all_solutions_naive();
179179
}
180180
Command::CalculateRandomChanceSuccessRatio => {
181-
let feasible = solitaire_solver::calculate_feasible_set(None);
181+
let feasible = solitaire_solver::calculate_feasible_set(args.threads);
182182
let start = std::time::Instant::now();
183-
let feasible = feasible.into_iter().collect();
183+
let feasible: Vec<_> = feasible.into_iter().collect();
184184
let success_probabilities =
185-
solitaire_solver::calculate_p_random_chance_success(feasible);
185+
solitaire_solver::calculate_p_random_chance_success(feasible.into_iter());
186186
let p = *success_probabilities.get(&Board::default()).unwrap();
187187
let percentage = p * 100.;
188188

189189
println!("took {:?}", start.elapsed());
190190
println!("success probability when chosing moves at random: {percentage}%");
191+
let (b, p) = success_probabilities.iter().map(|(b, p)| (*b, *p)).fold((Board::default(), f64::INFINITY), |(b1, p1), (b2, p2)| if p2 < p1 { (b2, p2) } else { (b1, p1) });
192+
let perc = p * 100.;
193+
println!("minimum success chance: \n{b} ({perc}%)");
194+
191195
}
192196
Command::CalculateSingle => {
193197
let solution = solitaire_solver::calculate_first_solution();

0 commit comments

Comments
 (0)