2021-12-20 11:47:24 +01:00
|
|
|
#[macro_use]
|
|
|
|
extern crate serde;
|
|
|
|
extern crate serde_json;
|
|
|
|
|
|
|
|
pub mod config;
|
|
|
|
pub mod protocol;
|
|
|
|
pub mod rules;
|
|
|
|
pub mod serializer;
|
|
|
|
|
|
|
|
pub use config::Config as PwConfig;
|
2021-12-21 21:47:48 +01:00
|
|
|
pub use protocol::CommandError;
|
|
|
|
pub use rules::{Dispatch, PwState};
|
|
|
|
use std::collections::HashMap;
|
2021-12-20 11:47:24 +01:00
|
|
|
|
|
|
|
pub struct PlanetWars {
|
|
|
|
/// Game state
|
|
|
|
state: rules::PwState,
|
|
|
|
/// Map planet names to their ids
|
2021-12-21 21:47:48 +01:00
|
|
|
planet_map: HashMap<String, usize>,
|
2021-12-20 11:47:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl PlanetWars {
|
|
|
|
pub fn create(config: PwConfig, num_players: usize) -> Self {
|
|
|
|
let state = config.create_state(num_players);
|
|
|
|
|
|
|
|
let planet_map = state
|
|
|
|
.planets
|
|
|
|
.iter()
|
|
|
|
.map(|p| (p.name.clone(), p.id))
|
|
|
|
.collect();
|
2021-12-21 21:47:48 +01:00
|
|
|
|
2021-12-20 11:47:24 +01:00
|
|
|
PlanetWars { state, planet_map }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Proceed to next turn
|
|
|
|
pub fn step(&mut self) {
|
|
|
|
self.state.repopulate();
|
|
|
|
self.state.step();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_finished(&self) -> bool {
|
|
|
|
self.state.is_finished()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serialize_state(&self) -> protocol::State {
|
|
|
|
serializer::serialize(&self.state)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn serialize_player_state(&self, player_id: usize) -> protocol::State {
|
|
|
|
serializer::serialize_rotated(&self.state, player_id - 1)
|
|
|
|
}
|
|
|
|
|
2022-03-13 15:20:03 +01:00
|
|
|
pub fn state(&self) -> &PwState {
|
2021-12-20 11:47:24 +01:00
|
|
|
&self.state
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute a command
|
|
|
|
pub fn execute_command(
|
|
|
|
&mut self,
|
|
|
|
player_num: usize,
|
2021-12-21 21:47:48 +01:00
|
|
|
cmd: &protocol::Command,
|
|
|
|
) -> Result<(), CommandError> {
|
2021-12-20 11:47:24 +01:00
|
|
|
let dispatch = self.parse_command(player_num, cmd)?;
|
|
|
|
self.state.dispatch(&dispatch);
|
2022-03-13 15:20:03 +01:00
|
|
|
Ok(())
|
2021-12-20 11:47:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Check the given command for validity.
|
|
|
|
/// If it is valid, return an internal representation of the dispatch
|
|
|
|
/// described by the command.
|
2021-12-21 21:47:48 +01:00
|
|
|
pub fn parse_command(
|
|
|
|
&self,
|
|
|
|
player_id: usize,
|
|
|
|
cmd: &protocol::Command,
|
|
|
|
) -> Result<Dispatch, CommandError> {
|
2021-12-20 11:47:24 +01:00
|
|
|
let origin_id = *self
|
2021-12-21 21:47:48 +01:00
|
|
|
.planet_map
|
|
|
|
.get(&cmd.origin)
|
|
|
|
.ok_or(CommandError::OriginDoesNotExist)?;
|
2021-12-20 11:47:24 +01:00
|
|
|
|
|
|
|
let target_id = *self
|
|
|
|
.planet_map
|
|
|
|
.get(&cmd.destination)
|
|
|
|
.ok_or(CommandError::DestinationDoesNotExist)?;
|
|
|
|
|
|
|
|
if self.state.planets[origin_id].owner() != Some(player_id - 1) {
|
|
|
|
return Err(CommandError::OriginNotOwned);
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.state.planets[origin_id].ship_count() < cmd.ship_count {
|
|
|
|
return Err(CommandError::NotEnoughShips);
|
|
|
|
}
|
|
|
|
|
|
|
|
if cmd.ship_count == 0 {
|
|
|
|
return Err(CommandError::ZeroShipMove);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Dispatch {
|
|
|
|
origin: origin_id,
|
|
|
|
target: target_id,
|
|
|
|
ship_count: cmd.ship_count,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Execute a dispatch.
|
|
|
|
/// This assumes the dispatch is valid. You should check this yourself
|
|
|
|
/// or use `parse_command` to obtain a valid dispatch.
|
|
|
|
pub fn execute_dispatch(&mut self, dispatch: &Dispatch) {
|
|
|
|
self.state.dispatch(dispatch);
|
|
|
|
}
|
2021-12-21 21:47:48 +01:00
|
|
|
}
|