upd
This commit is contained in:
parent
21135d817e
commit
1234473369
6 changed files with 154 additions and 47 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[registries.crates-io]
|
||||
protocol = "sparse"
|
30
flake.lock
30
flake.lock
|
@ -6,11 +6,11 @@
|
|||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1685254813,
|
||||
"narHash": "sha256-Pod+U90fDJJml5cwoOvx/KKBF4HmWtK9Cttql5sfwFQ=",
|
||||
"lastModified": 1685514167,
|
||||
"narHash": "sha256-urRxF0ZGSNeZjM4kALNg3wTh7fBscbqQmS6S/HU7Wms=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "2804d7ee704057959d831b038dea0e6845b18658",
|
||||
"rev": "3abfea51663583186f687c49a157eab1639349ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -24,11 +24,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1681202837,
|
||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||
"lastModified": 1685518550,
|
||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -38,11 +38,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1685168767,
|
||||
"narHash": "sha256-wQgnxz0PdqbyKKpsWl/RU8T8QhJQcHfeC6lh1xRUTfk=",
|
||||
"lastModified": 1685383865,
|
||||
"narHash": "sha256-3uQytfnotO6QJv3r04ajSXbEFMII0dUtw0uqYlZ4dbk=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e10802309bf9ae351eb27002c85cfdeb1be3b262",
|
||||
"rev": "5e871d8aa6f57cc8e0dc087d1c5013f6e212b4ce",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -54,11 +54,11 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1685325175,
|
||||
"narHash": "sha256-QAKsxoePrMUoHRYpfbxBeVXDKoRJ8S864/UMjML+JiU=",
|
||||
"lastModified": 1685555835,
|
||||
"narHash": "sha256-P2BQb6kdLfinxkJALnqI0GdlEjBBK9BuGAHpCtNYjl8=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4295779fbaacb7158d5f32c4aa2740ef7b56d91b",
|
||||
"rev": "254264957ed99b982312504783c2e6036822f826",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -77,11 +77,11 @@
|
|||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1685177170,
|
||||
"narHash": "sha256-bRURsRZZmBZtQo8OHD/PRslGQC04wed6lWroQaAPSPg=",
|
||||
"lastModified": 1685465261,
|
||||
"narHash": "sha256-aJ2nUinUrNcFi+pb47bS5IIAeSiUEEPLJY8W4Q8Pcjk=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "f6e3a87bf9478574f8c64ac2efec125bc19b1c64",
|
||||
"rev": "d2b3caa5b5694125fad04a9699e919444439f6a2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -21,7 +21,8 @@ async fn main() -> Result<()> {
|
|||
|
||||
let web_app_handle = tokio::spawn(web_app());
|
||||
|
||||
let (game_result, web_result) = tokio::join!(game_loop_handle, web_app_handle);
|
||||
let (game_result, web_result) =
|
||||
tokio::join!(game_loop_handle, web_app_handle);
|
||||
game_result??;
|
||||
web_result??;
|
||||
|
||||
|
|
42
src/map.rs
42
src/map.rs
|
@ -1,15 +1,34 @@
|
|||
use std::{collections::HashSet, path::Path};
|
||||
use std::{collections::HashSet, ops::Deref, path::Path, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
use image::{Rgb, RgbImage};
|
||||
use imageproc::{
|
||||
drawing::{draw_filled_circle_mut, draw_filled_rect_mut, draw_line_segment_mut},
|
||||
drawing::{
|
||||
draw_filled_circle_mut, draw_filled_rect_mut, draw_line_segment_mut,
|
||||
},
|
||||
rect::Rect,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nalgebra::Vector2;
|
||||
use rand::Rng;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MapRef(Arc<Map>);
|
||||
|
||||
impl MapRef {
|
||||
pub fn new(map: Map) -> Self {
|
||||
Self(Arc::new(map))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MapRef {
|
||||
type Target = Map;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Map {
|
||||
points: Vec<Vector2<f64>>,
|
||||
|
||||
|
@ -80,7 +99,10 @@ impl Map {
|
|||
|(top_left, bottom_right), point| {
|
||||
(
|
||||
Vector2::new(top_left.x.min(point.x), top_left.y.min(point.y)),
|
||||
Vector2::new(bottom_right.x.max(point.x), bottom_right.y.max(point.y)),
|
||||
Vector2::new(
|
||||
bottom_right.x.max(point.x),
|
||||
bottom_right.y.max(point.y),
|
||||
),
|
||||
)
|
||||
},
|
||||
);
|
||||
|
@ -105,16 +127,18 @@ impl Map {
|
|||
);
|
||||
|
||||
let to_image_space_f32 = |p: &Vector2<f64>| {
|
||||
let image_x = ((p.x - top_left.x) * image_width as f64 / space_width) as f32;
|
||||
let image_y =
|
||||
image_height as f32 - ((p.y - bottom_right.y) * image_height as f64 / space_height) as f32;
|
||||
let image_x =
|
||||
((p.x - top_left.x) * image_width as f64 / space_width) as f32;
|
||||
let image_y = image_height as f32
|
||||
- ((p.y - bottom_right.y) * image_height as f64 / space_height) as f32;
|
||||
(image_x, image_y)
|
||||
};
|
||||
|
||||
let to_image_space_i32 = |p: &Vector2<f64>| {
|
||||
let image_x = ((p.x - top_left.x) * image_width as f64 / space_width) as i32;
|
||||
let image_y =
|
||||
image_height as i32 - ((p.y - bottom_right.y) * image_height as f64 / space_height) as i32;
|
||||
let image_x =
|
||||
((p.x - top_left.x) * image_width as f64 / space_width) as i32;
|
||||
let image_y = image_height as i32
|
||||
- ((p.y - bottom_right.y) * image_height as f64 / space_height) as i32;
|
||||
(image_x, image_y)
|
||||
};
|
||||
|
||||
|
|
116
src/state.rs
116
src/state.rs
|
@ -3,8 +3,11 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::map::{Map, MapOptionsBuilder, MapRef};
|
||||
|
||||
/// The entire game state at a particular point in time (keyframe)
|
||||
pub struct GameState {
|
||||
/// When the game started
|
||||
|
@ -16,68 +19,141 @@ pub struct GameState {
|
|||
/// How long it's been since the beginning of the game
|
||||
elapsed_time: Duration,
|
||||
|
||||
empires: HashMap<EmpireId, EmpireState>,
|
||||
galaxy_state: GalaxyState,
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
pub fn new() -> GameState {
|
||||
GameState {
|
||||
pub fn new() -> Result<GameState> {
|
||||
let rng = rand::thread_rng();
|
||||
let map = Map::generate(MapOptionsBuilder::default().build()?, rng);
|
||||
|
||||
Ok(GameState {
|
||||
start_time: Instant::now(),
|
||||
elapsed_time: Duration::new(0, 0),
|
||||
empires: HashMap::default(),
|
||||
}
|
||||
galaxy_state: GalaxyState::new(map),
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute when the next keyframe would be based on the current knowledge of
|
||||
/// the game, and what the state would look like at that keyframe.
|
||||
///
|
||||
/// This process must be _entirely_ deterministic.
|
||||
pub fn next_keyframe(&self) -> PendingGameState {
|
||||
/// This process must be _entirely_ atomic and deterministic.
|
||||
pub fn next_keyframe(&self) -> NextKeyframe {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Interpolate two frames to find out what the frame at the given timestamp
|
||||
/// would look like.
|
||||
///
|
||||
/// This process must be _entirely_ deterministic.
|
||||
pub fn interpolate(from: &GameState, to: &GameState, at: Duration) -> GameState {
|
||||
debug_assert_eq!(from.start_time, to.start_time);
|
||||
|
||||
/// This process must be _entirely_ atomic and deterministic.
|
||||
pub fn evaluate(from: &Self, at: Duration) -> GameState {
|
||||
GameState {
|
||||
start_time: from.start_time,
|
||||
elapsed_time: at,
|
||||
empires: todo!(),
|
||||
galaxy_state: GalaxyState::evaluate(&from.galaxy_state, at),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A game state that is pending several non-deterministic operations to be applied
|
||||
pub struct PendingGameState {
|
||||
/// A game state that is pending several non-deterministic operations to be
|
||||
/// applied
|
||||
pub struct NextKeyframe {
|
||||
inner: GameState,
|
||||
|
||||
pending_operations: HashSet<GameStateChange>,
|
||||
|
||||
intermediate_events: HashSet<IntermediateEvent>,
|
||||
}
|
||||
|
||||
/// Mostly notifications and stuff
|
||||
pub enum IntermediateEvent {}
|
||||
|
||||
pub enum GameStateChange {
|
||||
/// A new empire joins the galaxy
|
||||
CreateEmpire,
|
||||
}
|
||||
|
||||
impl PendingGameState {
|
||||
impl NextKeyframe {
|
||||
/// Resolve all the pending operations into a keyframe
|
||||
pub fn resolve(self, _rand: impl Rng) -> GameState {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct EmpireId(usize);
|
||||
|
||||
pub struct ResourceId(usize);
|
||||
|
||||
pub struct MetricId(usize);
|
||||
pub struct FleetId(usize);
|
||||
pub struct StarSystemId(usize);
|
||||
|
||||
pub struct EmpireState {
|
||||
resources: HashMap<ResourceId, ResourceInfo>,
|
||||
pub struct GalaxyState {
|
||||
empires: HashMap<EmpireId, EmpireState>,
|
||||
map: MapRef,
|
||||
}
|
||||
|
||||
pub struct ResourceInfo {}
|
||||
impl GalaxyState {
|
||||
pub fn new(map: Map) -> Self {
|
||||
GalaxyState {
|
||||
empires: HashMap::default(),
|
||||
map: MapRef::new(map),
|
||||
}
|
||||
}
|
||||
|
||||
/// Interpolate two GalaxyStates
|
||||
pub fn evaluate(from: &Self, at: Duration) -> Self {
|
||||
// Interpolate each empire separately
|
||||
let empire_keys = {
|
||||
let mut keys = from.empires.keys().collect::<Vec<_>>();
|
||||
keys.sort();
|
||||
keys
|
||||
};
|
||||
|
||||
let new_empires = empire_keys
|
||||
.into_iter()
|
||||
.map(|empire_id| {
|
||||
let from_empire = from.empires.get(&empire_id).expect("must exist");
|
||||
let new_empire = EmpireState::evaluate(from_empire, at);
|
||||
(*empire_id, new_empire)
|
||||
})
|
||||
.collect();
|
||||
|
||||
GalaxyState {
|
||||
empires: new_empires,
|
||||
map: from.map.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmpireState {
|
||||
id: EmpireId,
|
||||
|
||||
resources: HashMap<ResourceId, ResourceState>,
|
||||
|
||||
fleets: HashMap<FleetId, FleetState>,
|
||||
}
|
||||
|
||||
impl EmpireState {
|
||||
/// Interpolate empire states
|
||||
pub fn evaluate(from: &Self, at: Duration) -> Self {
|
||||
// Increase resources using the specified equation
|
||||
let new_resources = from
|
||||
.resources
|
||||
.iter()
|
||||
.map(|(resource_id, resource_state)| {});
|
||||
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResourceState {}
|
||||
|
||||
pub struct FleetState {
|
||||
owned_by_empire: EmpireId,
|
||||
command_queue: Vec<FleetCommand>,
|
||||
}
|
||||
|
||||
pub enum FleetCommand {
|
||||
GoToSystem(StarSystemId),
|
||||
AttackFleet(FleetId),
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
use std::{net::SocketAddr, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use axum::{routing::get, Router, Server};
|
||||
|
||||
pub async fn web_app() -> Result<()> {
|
||||
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
|
||||
|
||||
// run it with hyper on localhost:3000
|
||||
Server::bind(&"0.0.0.0:3000".parse().unwrap())
|
||||
// run it with hyper on localhost
|
||||
let addr = SocketAddr::from_str("0.0.0.0:8392")?;
|
||||
println!("Running a web server on {addr:?}");
|
||||
Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue