import planetwars-rules
This commit is contained in:
parent
931bf0fbed
commit
b7ab700a57
8 changed files with 561 additions and 0 deletions
5
Cargo.toml
Normal file
5
Cargo.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"planetwars-rules",
|
||||||
|
]
|
2
planetwars-rules/.gitignore
vendored
Normal file
2
planetwars-rules/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
11
planetwars-rules/Cargo.toml
Normal file
11
planetwars-rules/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "planetwars-rules"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Ilion Beyst <ilion.beyst@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
84
planetwars-rules/src/config.rs
Normal file
84
planetwars-rules/src/config.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
use super::protocol as proto;
|
||||||
|
use super::rules::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub map_file: String,
|
||||||
|
pub max_turns: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn create_state(&self, num_players: usize) -> PwState {
|
||||||
|
let planets = self.load_map(num_players);
|
||||||
|
let players = (0..num_players)
|
||||||
|
.map(|player_num| Player {
|
||||||
|
id: player_num + 1,
|
||||||
|
alive: true,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
PwState {
|
||||||
|
players: players,
|
||||||
|
planets: planets,
|
||||||
|
expeditions: Vec::new(),
|
||||||
|
expedition_num: 0,
|
||||||
|
turn_num: 0,
|
||||||
|
max_turns: self.max_turns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_map(&self, num_players: usize) -> Vec<Planet> {
|
||||||
|
let map = self.read_map().expect("[PLANET_WARS] reading map failed");
|
||||||
|
|
||||||
|
return map
|
||||||
|
.planets
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(num, planet)| {
|
||||||
|
let mut fleets = Vec::new();
|
||||||
|
let owner = planet.owner.and_then(|owner_num| {
|
||||||
|
// in the current map format, player numbers start at 1.
|
||||||
|
// TODO: we might want to change this.
|
||||||
|
// ignore players that are not in the game
|
||||||
|
if owner_num > 0 && owner_num <= num_players {
|
||||||
|
Some(owner_num - 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if planet.ship_count > 0 {
|
||||||
|
fleets.push(Fleet {
|
||||||
|
owner: owner,
|
||||||
|
ship_count: planet.ship_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Planet {
|
||||||
|
id: num,
|
||||||
|
name: planet.name,
|
||||||
|
x: planet.x,
|
||||||
|
y: planet.y,
|
||||||
|
fleets: fleets,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_map(&self) -> io::Result<Map> {
|
||||||
|
let mut file = File::open(&self.map_file)?;
|
||||||
|
let mut buf = String::new();
|
||||||
|
file.read_to_string(&mut buf)?;
|
||||||
|
let map = serde_json::from_str(&buf)?;
|
||||||
|
return Ok(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Map {
|
||||||
|
pub planets: Vec<proto::Planet>,
|
||||||
|
}
|
111
planetwars-rules/src/lib.rs
Normal file
111
planetwars-rules/src/lib.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod protocol;
|
||||||
|
pub mod rules;
|
||||||
|
pub mod serializer;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
pub use rules::{PwState, Dispatch};
|
||||||
|
pub use protocol::CommandError;
|
||||||
|
pub use config::Config as PwConfig;
|
||||||
|
|
||||||
|
pub struct PlanetWars {
|
||||||
|
/// Game state
|
||||||
|
state: rules::PwState,
|
||||||
|
/// Map planet names to their ids
|
||||||
|
planet_map: HashMap<String, usize>
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn state<'a>(&'a self) -> &'a PwState {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a command
|
||||||
|
pub fn execute_command(
|
||||||
|
&mut self,
|
||||||
|
player_num: usize,
|
||||||
|
cmd: &protocol::Command
|
||||||
|
) -> Result<(), CommandError>
|
||||||
|
{
|
||||||
|
let dispatch = self.parse_command(player_num, cmd)?;
|
||||||
|
self.state.dispatch(&dispatch);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check the given command for validity.
|
||||||
|
/// If it is valid, return an internal representation of the dispatch
|
||||||
|
/// described by the command.
|
||||||
|
pub fn parse_command(&self, player_id: usize, cmd: &protocol::Command)
|
||||||
|
-> Result<Dispatch, CommandError>
|
||||||
|
{
|
||||||
|
let origin_id = *self
|
||||||
|
.planet_map
|
||||||
|
.get(&cmd.origin)
|
||||||
|
.ok_or(CommandError::OriginDoesNotExist)?;
|
||||||
|
|
||||||
|
let target_id = *self
|
||||||
|
.planet_map
|
||||||
|
.get(&cmd.destination)
|
||||||
|
.ok_or(CommandError::DestinationDoesNotExist)?;
|
||||||
|
|
||||||
|
if self.state.planets[origin_id].owner() != Some(player_id - 1) {
|
||||||
|
println!("owner was {:?}", self.state.planets[origin_id].owner());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
79
planetwars-rules/src/protocol.rs
Normal file
79
planetwars-rules/src/protocol.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Expedition {
|
||||||
|
pub id: u64,
|
||||||
|
pub ship_count: u64,
|
||||||
|
pub origin: String,
|
||||||
|
pub destination: String,
|
||||||
|
pub owner: usize,
|
||||||
|
pub turns_remaining: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Planet {
|
||||||
|
pub ship_count: u64,
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
pub owner: Option<usize>,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Action {
|
||||||
|
#[serde(rename = "moves")]
|
||||||
|
pub commands: Vec<Command>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Command {
|
||||||
|
pub origin: String,
|
||||||
|
pub destination: String,
|
||||||
|
pub ship_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct State {
|
||||||
|
pub planets: Vec<Planet>,
|
||||||
|
pub expeditions: Vec<Expedition>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct GameInfo {
|
||||||
|
pub players: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum CommandError {
|
||||||
|
NotEnoughShips,
|
||||||
|
OriginNotOwned,
|
||||||
|
ZeroShipMove,
|
||||||
|
OriginDoesNotExist,
|
||||||
|
DestinationDoesNotExist,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PlayerCommand {
|
||||||
|
pub command: Command,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub error: Option<CommandError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[serde(tag = "type", content = "value")]
|
||||||
|
pub enum PlayerAction {
|
||||||
|
Timeout,
|
||||||
|
ParseError(String),
|
||||||
|
Commands(Vec<PlayerCommand>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[serde(tag = "type", content = "content")]
|
||||||
|
pub enum ServerMessage {
|
||||||
|
/// Game state in current turn
|
||||||
|
GameState(State),
|
||||||
|
/// The action that was performed
|
||||||
|
PlayerAction(PlayerAction),
|
||||||
|
/// The game is over, and this is the concluding state.
|
||||||
|
FinalState(State),
|
||||||
|
}
|
193
planetwars-rules/src/rules.rs
Normal file
193
planetwars-rules/src/rules.rs
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/// The planet wars game rules.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PwState {
|
||||||
|
pub players: Vec<Player>,
|
||||||
|
pub planets: Vec<Planet>,
|
||||||
|
pub expeditions: Vec<Expedition>,
|
||||||
|
// How many expeditions were already dispatched.
|
||||||
|
// This is needed for assigning expedition identifiers.
|
||||||
|
pub expedition_num: u64,
|
||||||
|
pub turn_num: u64,
|
||||||
|
pub max_turns: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Player {
|
||||||
|
pub id: usize,
|
||||||
|
pub alive: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Fleet {
|
||||||
|
pub owner: Option<usize>,
|
||||||
|
pub ship_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Planet {
|
||||||
|
pub id: usize,
|
||||||
|
pub name: String,
|
||||||
|
pub fleets: Vec<Fleet>,
|
||||||
|
pub x: f64,
|
||||||
|
pub y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Expedition {
|
||||||
|
pub id: u64,
|
||||||
|
pub origin: usize,
|
||||||
|
pub target: usize,
|
||||||
|
pub fleet: Fleet,
|
||||||
|
pub turns_remaining: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Dispatch {
|
||||||
|
pub origin: usize,
|
||||||
|
pub target: usize,
|
||||||
|
pub ship_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PwState {
|
||||||
|
pub fn dispatch(&mut self, dispatch: &Dispatch) {
|
||||||
|
let distance = self.planets[dispatch.origin]
|
||||||
|
.distance(&self.planets[dispatch.target]);
|
||||||
|
|
||||||
|
let origin = &mut self.planets[dispatch.origin];
|
||||||
|
origin.fleets[0].ship_count -= dispatch.ship_count;
|
||||||
|
|
||||||
|
let expedition = Expedition {
|
||||||
|
id: self.expedition_num,
|
||||||
|
origin: dispatch.origin,
|
||||||
|
target: dispatch.target,
|
||||||
|
turns_remaining: distance,
|
||||||
|
fleet: Fleet {
|
||||||
|
owner: origin.owner(),
|
||||||
|
ship_count: dispatch.ship_count,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// increment counter
|
||||||
|
self.expedition_num += 1;
|
||||||
|
self.expeditions.push(expedition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play one step of the game
|
||||||
|
pub fn step(&mut self) {
|
||||||
|
self.turn_num += 1;
|
||||||
|
|
||||||
|
// Initially mark all players dead, re-marking them as alive once we
|
||||||
|
// encounter a sign of life.
|
||||||
|
for player in self.players.iter_mut() {
|
||||||
|
player.alive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.step_expeditions();
|
||||||
|
self.resolve_combat();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn repopulate(&mut self) {
|
||||||
|
for planet in self.planets.iter_mut() {
|
||||||
|
if planet.owner().is_some() {
|
||||||
|
planet.fleets[0].ship_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_expeditions(&mut self) {
|
||||||
|
let mut i = 0;
|
||||||
|
let exps = &mut self.expeditions;
|
||||||
|
while i < exps.len() {
|
||||||
|
// compare with 1 to avoid issues with planet distance 0
|
||||||
|
if exps[i].turns_remaining <= 1 {
|
||||||
|
// remove expedition from expeditions, and add to fleet
|
||||||
|
let exp = exps.swap_remove(i);
|
||||||
|
let planet = &mut self.planets[exp.target];
|
||||||
|
planet.orbit(exp.fleet);
|
||||||
|
} else {
|
||||||
|
exps[i].turns_remaining -= 1;
|
||||||
|
if let Some(owner_num) = exps[i].fleet.owner {
|
||||||
|
// owner has an expedition in progress; this is a sign of life.
|
||||||
|
self.players[owner_num].alive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// proceed to next expedition
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_combat(&mut self) {
|
||||||
|
for planet in self.planets.iter_mut() {
|
||||||
|
planet.resolve_combat();
|
||||||
|
if let Some(owner_num) = planet.owner() {
|
||||||
|
// owner owns a planet; this is a sign of life.
|
||||||
|
self.players[owner_num].alive = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_finished(&self) -> bool {
|
||||||
|
let remaining = self.players.iter().filter(|p| p.alive).count();
|
||||||
|
return remaining < 2 || self.turn_num >= self.max_turns;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn living_players(&self) -> Vec<usize> {
|
||||||
|
self.players
|
||||||
|
.iter()
|
||||||
|
.filter_map(|p| if p.alive { Some(p.id) } else { None })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Planet {
|
||||||
|
pub fn owner(&self) -> Option<usize> {
|
||||||
|
self.fleets.first().and_then(|f| f.owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ship_count(&self) -> u64 {
|
||||||
|
self.fleets.first().map_or(0, |f| f.ship_count)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a fleet orbit this planet.
|
||||||
|
fn orbit(&mut self, fleet: Fleet) {
|
||||||
|
// If owner already has a fleet present, merge
|
||||||
|
for other in self.fleets.iter_mut() {
|
||||||
|
if other.owner == fleet.owner {
|
||||||
|
other.ship_count += fleet.ship_count;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else, add fleet to fleets list
|
||||||
|
self.fleets.push(fleet);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_combat(&mut self) {
|
||||||
|
// The player owning the largest fleet present will win the combat.
|
||||||
|
// Here, we resolve how many ships he will have left.
|
||||||
|
// note: in the current implementation, we could resolve by doing
|
||||||
|
// winner.ship_count -= second_largest.ship_count, but this does not
|
||||||
|
// allow for simple customizations (such as changing combat balance).
|
||||||
|
|
||||||
|
self.fleets
|
||||||
|
.sort_by(|a, b| a.ship_count.cmp(&b.ship_count).reverse());
|
||||||
|
while self.fleets.len() > 1 {
|
||||||
|
let fleet = self.fleets.pop().unwrap();
|
||||||
|
// destroy some ships
|
||||||
|
for other in self.fleets.iter_mut() {
|
||||||
|
other.ship_count -= fleet.ship_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove dead fleets
|
||||||
|
while self.fleets.last().map(|f| f.ship_count) == Some(0) {
|
||||||
|
self.fleets.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn distance(&self, other: &Planet) -> u64 {
|
||||||
|
let dx = self.x - other.x;
|
||||||
|
let dy = self.y - other.y;
|
||||||
|
return (dx.powi(2) + dy.powi(2)).sqrt().ceil() as u64;
|
||||||
|
}
|
||||||
|
}
|
76
planetwars-rules/src/serializer.rs
Normal file
76
planetwars-rules/src/serializer.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use super::protocol as proto;
|
||||||
|
use super::rules::{Expedition, Planet, PwState};
|
||||||
|
|
||||||
|
/// Serialize given gamestate
|
||||||
|
pub fn serialize(state: &PwState) -> proto::State {
|
||||||
|
serialize_rotated(state, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize given gamestate with player numbers rotated by given offset.
|
||||||
|
pub fn serialize_rotated(state: &PwState, offset: usize) -> proto::State {
|
||||||
|
let serializer = Serializer::new(state, offset);
|
||||||
|
serializer.serialize_state()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Serializer<'a> {
|
||||||
|
state: &'a PwState,
|
||||||
|
player_num_offset: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Serializer<'a> {
|
||||||
|
fn new(state: &'a PwState, offset: usize) -> Self {
|
||||||
|
Serializer {
|
||||||
|
state: state,
|
||||||
|
player_num_offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_state(&self) -> proto::State {
|
||||||
|
proto::State {
|
||||||
|
planets: self
|
||||||
|
.state
|
||||||
|
.planets
|
||||||
|
.iter()
|
||||||
|
.map(|planet| self.serialize_planet(planet))
|
||||||
|
.collect(),
|
||||||
|
expeditions: self
|
||||||
|
.state
|
||||||
|
.expeditions
|
||||||
|
.iter()
|
||||||
|
.map(|exp| self.serialize_expedition(exp))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the player number for given player id.
|
||||||
|
/// Player numbers are 1-based (as opposed to player ids), They will also be
|
||||||
|
/// rotated based on the number offset for this serializer.
|
||||||
|
fn player_num(&self, player_id: usize) -> usize {
|
||||||
|
let num_players = self.state.players.len();
|
||||||
|
let rotated_id =
|
||||||
|
(player_id + num_players - self.player_num_offset) % num_players;
|
||||||
|
// protocol player ids start at 1
|
||||||
|
return rotated_id + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_planet(&self, planet: &Planet) -> proto::Planet {
|
||||||
|
proto::Planet {
|
||||||
|
name: planet.name.clone(),
|
||||||
|
x: planet.x,
|
||||||
|
y: planet.y,
|
||||||
|
owner: planet.owner().map(|id| self.player_num(id)),
|
||||||
|
ship_count: planet.ship_count(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_expedition(&self, exp: &Expedition) -> proto::Expedition {
|
||||||
|
proto::Expedition {
|
||||||
|
id: exp.id,
|
||||||
|
owner: self.player_num(exp.fleet.owner.unwrap()),
|
||||||
|
ship_count: exp.fleet.ship_count,
|
||||||
|
origin: self.state.planets[exp.origin as usize].name.clone(),
|
||||||
|
destination: self.state.planets[exp.target as usize].name.clone(),
|
||||||
|
turns_remaining: exp.turns_remaining,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue