use crate::map::{Room, VertexKey, WumpusMap};
use crate::{dprint, Args};
use anyhow::Result;
use rand::rngs::ThreadRng;
use rand::seq::SliceRandom;
use slotmap::Key;
#[derive(Debug)]
struct WumpusLocation {
wumpus: VertexKey,
wumpus_halo: Vec<VertexKey>,
}
impl WumpusLocation {
fn new() -> Self {
let wumpus = VertexKey::null();
let wumpus_halo = Vec::new();
Self {
wumpus,
wumpus_halo,
}
}
fn set_location(&mut self, location: VertexKey, wumpus_map: &WumpusMap) -> Result<()> {
self.wumpus = location;
self.wumpus_halo.clear();
let vertex = wumpus_map.try_get_vertex(&self.wumpus)?;
self.wumpus_halo.extend_from_slice(vertex.neighbors());
Ok(())
}
fn is_near(&self, key: &VertexKey, wumpus_map: &WumpusMap) -> bool {
if self.wumpus_halo.contains(key) {
return true;
}
self.wumpus_halo
.iter()
.any(|v| wumpus_map.get_vertex(v).has_neighbor(key))
}
}
#[derive(Debug)]
pub struct Things {
pub(crate) player: VertexKey,
wumpus_location: WumpusLocation,
bats: Vec<VertexKey>,
pits: Vec<VertexKey>,
pub(crate) wumpus_movement_likelihood: u32,
}
impl Things {
pub fn _new(conf: &Args, wumpus_map: &WumpusMap) -> Self {
Self::try_new(conf, wumpus_map).unwrap()
}
pub fn try_new(conf: &Args, wumpus_map: &WumpusMap) -> Result<Self> {
fn place_player(
wumpus_location: &WumpusLocation,
conf: &Args,
vertices: &[VertexKey],
rng: &mut ThreadRng,
wumpus_map: &WumpusMap,
) -> VertexKey {
let tunnels = conf.tunnels as f32;
let rooms = conf.rooms as f32;
let allow_wumpocalypse = conf.hard && (tunnels / rooms < 0.4);
loop {
let player = vertices.choose(rng).expect("Bug: `Vertices` is empty");
if *player != wumpus_location.wumpus
&& !(allow_wumpocalypse && wumpus_location.is_near(player, wumpus_map))
{
return player.clone();
}
}
}
let wumpus_movement_likelihood = 2;
let bat_count = conf.bats;
let pit_count = conf.pits;
let vertices = wumpus_map.as_slice();
let mut rng = rand::thread_rng();
let mut bats_and_pits: Vec<VertexKey> = vertices
.choose_multiple(&mut rng, (bat_count + pit_count) as usize)
.cloned()
.collect();
let bats = bats_and_pits.split_off(pit_count as usize);
let pits = bats_and_pits;
dprint!("<pit in rooms {:?}>\n", pits);
dprint!("<bat in rooms {:?}>\n", bats);
let wumpus = vertices
.choose(&mut rng)
.expect("Bug: `Vertices` empty")
.clone();
let mut wumpus_location = WumpusLocation::new();
wumpus_location.set_location(wumpus, wumpus_map)?;
dprint!("<wumpus in room {:?}>\n", wumpus_location);
wumpus_location.set_location(wumpus, wumpus_map)?;
let player = place_player(&wumpus_location, conf, vertices, &mut rng, wumpus_map);
Ok(Self {
wumpus_location,
bats,
pits,
player,
wumpus_movement_likelihood,
})
}
pub fn wumpus_is_near(&self, wumpus_map: &WumpusMap) -> bool {
self.wumpus_location.is_near(&self.player, wumpus_map)
}
pub fn bat_is_near(&self, wumpus_map: &WumpusMap) -> bool {
let player = wumpus_map
.try_get_vertex(&self.player)
.expect(format!("Bug: no vertex found for player {:?}", &self.player).as_str());
player.neighbors().iter().any(|v| self.bats.contains(v))
}
pub fn pit_is_near(&self, wumpus_map: &WumpusMap) -> bool {
let player = wumpus_map
.try_get_vertex(&self.player)
.expect(format!("Bug: no vertex found for player {:?}", &self.player).as_str());
player.neighbors().iter().any(|v| self.pits.contains(v))
}
pub fn wump_room<'a>(&self, wumpus_map: &'a WumpusMap) -> Result<&'a Room> {
wumpus_map.try_get_room(&self.wumpus_location.wumpus)
}
pub fn set_wumpus_location(&mut self, key: &VertexKey, wumpus_map: &WumpusMap) -> Result<()> {
self.wumpus_location.set_location(*key, wumpus_map)
}
pub fn player_wumpus_collision(&self) -> bool {
self.player == self.wumpus_location.wumpus
}
pub fn get_wumpus_location(&self) -> VertexKey {
self.wumpus_location.wumpus
}
pub fn set_player_location(&mut self, key: &VertexKey) {
self.player = *key;
}
pub fn player_pit_collision(&self) -> bool {
self.pits.contains(&self.player)
}
pub fn player_bat_collision(&self) -> bool {
self.bats.contains(&self.player)
}
}
pub(crate) fn initialize_things_in_cave(wumpus_map: &WumpusMap, conf: &Args) -> Result<Things> {
let things = Things::try_new(conf, wumpus_map)?;
Ok(things)
}