Minor cleanup & utilities
parent
89aba4f930
commit
4ee7f8a9ac
|
@ -195,7 +195,7 @@ impl Human {
|
||||||
|
|
||||||
impl Agent for Human {
|
impl Agent for Human {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"Player"
|
"Human"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn player(&self) -> Player {
|
fn player(&self) -> Player {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::num::NonZeroU8;
|
use std::{mem::swap, num::NonZeroU8};
|
||||||
|
|
||||||
use crate::{board::Board, util::Symb};
|
use crate::{board::Board, util::Symb};
|
||||||
|
|
||||||
/// Returns an iterator of (sort, coords, char_idx, f32) for each empty slot in the listed partials.
|
/// Returns an iterator of (idx, coords, char_idx, f32) for each empty slot in the listed partials.
|
||||||
/// - sort is the index of this slot.
|
/// - idx is the index of this slot in the board string.
|
||||||
/// - coords are the coordinate of this slot's partial
|
/// - coords are the coordinate of this slot's partial
|
||||||
/// - char_idx is the index of this slot in its partial
|
/// - char_idx is the index of this slot in its partial
|
||||||
/// - f32 is the influence of this slot
|
/// - f32 is the influence of this slot
|
||||||
|
@ -51,17 +51,14 @@ pub fn free_slots_by_influence(board: &Board) -> Option<Vec<(usize, f32)>> {
|
||||||
Some(slots)
|
Some(slots)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the maximum possible value of the given board
|
/// Find the minimum or maximum possible value of the given board,
|
||||||
#[allow(dead_code)]
|
/// without adding any operations. Returns None if we couldn't find
|
||||||
pub fn maximize_value(board: &Board) -> Option<Board> {
|
/// a best board.
|
||||||
|
pub fn best_board_noop(board: &Board, minimize: bool) -> Option<Board> {
|
||||||
let n_free = board.get_board().iter().filter(|x| x.is_none()).count();
|
let n_free = board.get_board().iter().filter(|x| x.is_none()).count();
|
||||||
|
|
||||||
// Assume we have 10 or fewer available slots
|
// TODO: fix zero division
|
||||||
if n_free >= 10 {
|
let available_numbers = (1..=9)
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
|
|
||||||
let available_numbers = (0..=9)
|
|
||||||
.map(|x| match x {
|
.map(|x| match x {
|
||||||
0 => Symb::Zero,
|
0 => Symb::Zero,
|
||||||
x => Symb::Number(NonZeroU8::new(x).unwrap()),
|
x => Symb::Number(NonZeroU8::new(x).unwrap()),
|
||||||
|
@ -69,19 +66,36 @@ pub fn maximize_value(board: &Board) -> Option<Board> {
|
||||||
.filter(|x| !board.contains(*x))
|
.filter(|x| !board.contains(*x))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if n_free > available_numbers.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
let slots = free_slots_by_influence(board)?;
|
let slots = free_slots_by_influence(board)?;
|
||||||
|
|
||||||
let all_symbols = {
|
let all_symbols = {
|
||||||
// We need this many from the bottom, and this many from the top.
|
let mut a = {
|
||||||
let neg_count = slots.iter().filter(|(_, x)| *x <= 0.0).count();
|
// Number of slots we want to minimize
|
||||||
let pos_count = slots.iter().filter(|(_, x)| *x > 0.0).count();
|
let mut neg_count = slots.iter().filter(|(_, x)| *x <= 0.0).count();
|
||||||
|
// Number of slots we want to maximize
|
||||||
|
let mut pos_count = slots.iter().filter(|(_, x)| *x > 0.0).count();
|
||||||
|
|
||||||
let mut a_iter = available_numbers
|
if minimize {
|
||||||
.iter()
|
swap(&mut neg_count, &mut pos_count);
|
||||||
.take(neg_count)
|
}
|
||||||
.chain(available_numbers.iter().rev().take(pos_count).rev());
|
|
||||||
|
|
||||||
let mut g = slots
|
available_numbers
|
||||||
|
.iter()
|
||||||
|
.take(neg_count)
|
||||||
|
.chain(available_numbers.iter().rev().take(pos_count).rev())
|
||||||
|
.collect_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
if !minimize {
|
||||||
|
a.reverse();
|
||||||
|
}
|
||||||
|
let mut a_iter = a.into_iter();
|
||||||
|
|
||||||
|
slots
|
||||||
// Group slots with equal weights
|
// Group slots with equal weights
|
||||||
// and count the number of elements in each group
|
// and count the number of elements in each group
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -101,37 +115,32 @@ pub fn maximize_value(board: &Board) -> Option<Board> {
|
||||||
// of this set of sets
|
// of this set of sets
|
||||||
.multi_cartesian_product()
|
.multi_cartesian_product()
|
||||||
.map(|x| x.iter().flatten().cloned().collect_vec())
|
.map(|x| x.iter().flatten().cloned().collect_vec())
|
||||||
.map(|v| slots.iter().zip(v).collect_vec())
|
// Finally, attach the coordinate of each slot to each symbol
|
||||||
.collect_vec();
|
.map(|v| slots.iter().map(|x| x.0).zip(v).collect_vec())
|
||||||
|
.collect_vec()
|
||||||
// Sort these vectors so the order of values
|
|
||||||
// matches the order of empty slots
|
|
||||||
g.iter_mut()
|
|
||||||
.for_each(|v| v.sort_by(|(a, _), (b, _)| b.0.partial_cmp(&a.0).unwrap()));
|
|
||||||
g.into_iter()
|
|
||||||
.map(|v| v.into_iter().map(|(_, s)| s).collect_vec())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut best_board = None;
|
let mut best_board = None;
|
||||||
let mut best_value = None;
|
let mut best_value = None;
|
||||||
for i in all_symbols {
|
for i_iter in all_symbols {
|
||||||
let mut i_iter = i.iter();
|
let mut tmp_board = board.clone();
|
||||||
let filled = Board::from_board(board.get_board().map(|x| match x {
|
for (i, s) in i_iter {
|
||||||
None => i_iter.next().cloned(),
|
tmp_board.get_board_mut()[i] = Some(s);
|
||||||
_ => x,
|
}
|
||||||
}));
|
|
||||||
|
|
||||||
let val = filled.evaluate();
|
let val = tmp_board.evaluate();
|
||||||
|
|
||||||
if let Some(val) = val {
|
if let Some(val) = val {
|
||||||
if let Some(best) = best_value {
|
if minimize {
|
||||||
if val > best {
|
if best_value.is_none() || val < best_value.unwrap() {
|
||||||
best_value = Some(val);
|
best_value = Some(val);
|
||||||
best_board = Some(filled)
|
best_board = Some(tmp_board)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
best_value = Some(val);
|
if best_value.is_none() || val > best_value.unwrap() {
|
||||||
best_board = Some(filled)
|
best_value = Some(val);
|
||||||
|
best_board = Some(tmp_board)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::fmt::{Display, Write};
|
use std::fmt::{Debug, Display, Write};
|
||||||
use termion::color::{self, Color};
|
use termion::color::{self, Color};
|
||||||
|
|
||||||
use super::{PlayerAction, TreeElement};
|
use super::{PlayerAction, TreeElement};
|
||||||
|
@ -74,6 +74,12 @@ impl Display for Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for Board {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Board {
|
impl Board {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
@ -93,6 +99,24 @@ impl Board {
|
||||||
&mut self.board
|
&mut self.board
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the index of the ith empty slot
|
||||||
|
pub fn ith_empty_slot(&self, mut idx: usize) -> Option<usize> {
|
||||||
|
for (i, c) in self.board.iter().enumerate() {
|
||||||
|
if c.is_none() {
|
||||||
|
if idx == 0 {
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
idx -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx == 0 {
|
||||||
|
Some(self.board.len() - 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_done(&self) -> bool {
|
pub fn is_done(&self) -> bool {
|
||||||
self.free_spots == 0
|
self.free_spots == 0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue