use std::collections::{BTreeMap, BTreeSet};
use super::ekmf::Matrix;
use super::{Connection, Tile, TokenSpace};
use n18hex::{HexFace, RotateCW};
pub type TokenIndex = BTreeMap<TokenSpace, usize>;
fn city_face_connections(
tile: &Tile,
rotn: &RotateCW,
) -> BTreeMap<usize, BTreeSet<HexFace>> {
let mut city_faces: BTreeMap<usize, BTreeSet<HexFace>> = BTreeMap::new();
let city_count = tile.cities().len();
for ix in 0..city_count {
let start = Connection::City { ix };
let faces: BTreeSet<HexFace> = tile
.connected_faces(start)
.iter()
.map(|&face| face + rotn)
.collect();
city_faces.insert(ix, faces);
}
city_faces
}
fn token_face_connections(
tile: &Tile,
rotn: &RotateCW,
tokens: &TokenIndex,
) -> BTreeMap<usize, BTreeSet<HexFace>> {
let mut token_faces: BTreeMap<usize, BTreeSet<HexFace>> = BTreeMap::new();
for (ix, (token_space, _token)) in tokens.iter().enumerate() {
let start = Connection::City {
ix: token_space.city_ix(),
};
let want_faces: BTreeSet<HexFace> = tile
.connected_faces(start)
.iter()
.map(|&face| face + rotn)
.collect();
token_faces.insert(ix, want_faces);
}
token_faces
}
fn valid_cities(
token_faces: BTreeMap<usize, BTreeSet<HexFace>>,
city_faces: BTreeMap<usize, BTreeSet<HexFace>>,
) -> BTreeMap<usize, Vec<usize>> {
token_faces
.into_iter()
.map(|(token_ix, want_faces)| {
let city_ixs: Vec<usize> = city_faces
.iter()
.filter_map(|(&city_ix, city_faces)| {
if want_faces.is_subset(city_faces) {
Some(city_ix)
} else {
None
}
})
.collect();
(token_ix, city_ixs)
})
.collect()
}
fn flow_matrix(
tile: &Tile,
token_cities: BTreeMap<usize, Vec<usize>>,
) -> Matrix {
let token_count = token_cities.len();
let city_count = tile.cities().len();
let n = token_count + city_count + 2;
let mut capacities = Matrix::square(n);
for (token_ix, city_ixs) in token_cities.iter() {
let source_ix = token_ix + 1;
capacities[(0, source_ix)] = 1;
for city_ix in city_ixs {
let dest_ix = city_ix + token_count + 1;
capacities[(source_ix, dest_ix)] = 1;
}
}
for ix in 0..city_count {
let node_ix = ix + token_count + 1;
capacities[(node_ix, n - 1)] = tile.cities()[ix].tokens.count();
}
capacities
}
fn flow_matrix_tokens(
flow_mat: Matrix,
tokens: &TokenIndex,
tile: &Tile,
) -> Option<TokenIndex> {
let token_count = tokens.len();
let city_count = tile.cities().len();
let mut tokens_table: TokenIndex = BTreeMap::new();
let tokens_iter = tokens.values();
for (token_n, token) in tokens_iter.enumerate() {
let token_ix = token_n + 1;
let city_n = (0..city_count).find_map(|city_n| {
let city_ix = city_n + token_count + 1;
if flow_mat[(token_ix, city_ix)] == 1 {
Some(city_n)
} else {
None
}
});
if let Some(n) = city_n {
let tok_spaces = tile.city_token_spaces(n);
let free_space_opt =
tok_spaces.iter().find(|ts| !tokens_table.contains_key(ts));
if let Some(tok_space) = free_space_opt {
tokens_table.insert(*tok_space, *token);
} else {
panic!("Token #{} has no token space?", token_n);
}
} else {
panic!("Token #{} -> NOWHERE", token_n);
}
}
Some(tokens_table)
}
pub fn try_placing_tokens(
orig_tile: &Tile,
orig_rotn: &RotateCW,
tokens: &TokenIndex,
new_tile: &Tile,
new_rotn: &RotateCW,
) -> Option<TokenIndex> {
if tokens.is_empty() {
return None;
}
let token_faces = token_face_connections(orig_tile, orig_rotn, tokens);
let city_faces = city_face_connections(new_tile, new_rotn);
let token_cities = valid_cities(token_faces, city_faces);
let capacities = flow_matrix(new_tile, token_cities);
let (net_flow, flow_mat) = capacities.max_flow_mat();
if net_flow != tokens.len() {
return None;
}
flow_matrix_tokens(flow_mat, tokens, new_tile)
}