This commit is contained in:
Michael Zhang 2023-07-16 17:15:39 -05:00
parent 21135d817e
commit 1234473369
6 changed files with 154 additions and 47 deletions

2
.cargo/config.toml Normal file
View file

@ -0,0 +1,2 @@
[registries.crates-io]
protocol = "sparse"

View file

@ -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": {

View file

@ -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??;

View file

@ -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)
};

View file

@ -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),
}

View file

@ -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?;