Skip to content

Commit

Permalink
game: allow defender to position guards
Browse files Browse the repository at this point in the history
  • Loading branch information
lewis-weinberger committed Apr 6, 2023
1 parent ac21f3d commit 7bda30a
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
*.log
8 changes: 4 additions & 4 deletions src/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ pub const MAP: &str = "

pub const PLAYERS: usize = 4;

pub const DEFENDER: usize = 3;
pub const POSITIONS: [Option<Point>; 4] =
[Some((40, 1)), Some((1, 5)), Some((45, 45)), Some((1, 40))];

pub const POSITIONS: [Option<Point>; 4] = [Some((40, 1)), Some((1, 5)), Some((45, 45)), None];

pub const TARGETS: [Option<Point>; 4] = [Some((1, 46)), Some((45, 45)), Some((1, 1)), None];
pub const TARGETS: [Option<Point>; 4] =
[Some((1, 46)), Some((45, 45)), Some((1, 1)), Some((42, 1))];

pub const GUARDS: [Option<(Point, Direction)>; 5] = [
Some(((15, 12), Direction::Up)),
Expand Down
23 changes: 19 additions & 4 deletions src/game.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{defaults, Cli, Config, MsgToClient, MsgToServer, Result, UIBackend, UserInterface};
use rand::{
distributions::{Distribution, Standard},
random, Rng,
random, thread_rng, Rng,
};
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
Expand Down Expand Up @@ -260,11 +260,16 @@ impl Game {
let config = Config::new();
let player = 0;

let defender = defaults::DEFENDER;
// Currently use test defaults
let map = defaults::MAP.into();
let positions = defaults::POSITIONS.to_vec();
let mut positions = defaults::POSITIONS.to_vec();
let mut targets = defaults::TARGETS.to_vec();
let guards = defaults::GUARDS.to_vec();
let targets = defaults::TARGETS.to_vec();

let mut rng = thread_rng();
let defender = rng.gen_range(0..config.players);
positions[defender] = None;
targets[defender] = None;

Game {
address,
Expand Down Expand Up @@ -362,6 +367,16 @@ impl Game {
})
}

/// Guard placement for defending player
pub fn place_guards<T: UIBackend>(&mut self, ui: &mut UserInterface<T>) -> Result<MsgToServer> {
ui.place_guards(self)?;
Ok(MsgToServer {
new: self.positions[self.player],
guards: self.guards.clone(),
quit: self.quit,
})
}

/// Tiles within guard's line-of-sight
pub fn view_cone(&self, guard: usize) -> Vec<(Point, Tile)> {
let mut cone = HashSet::new();
Expand Down
37 changes: 31 additions & 6 deletions src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ impl ClientThread {
// Send initial game state
serialize_into(&self.stream, &game)?;

if game.player == game.defender {
// If this is the defender we wait to receive
// their choice of guard positions
let msg = deserialize_from(&self.stream)?;
self.tx.send(msg)?;
}

loop {
// Send latest info to client
let msg = self.rx.recv()?;
Expand Down Expand Up @@ -102,7 +109,7 @@ pub struct Server {
}

impl Server {
pub fn new(game: Game) -> Result<Self> {
pub fn new(mut game: Game) -> Result<Self> {
info!(
"Address: {}, players: {}",
game.address, game.config.players
Expand All @@ -116,6 +123,11 @@ impl Server {
clients.push(ClientHandle::new(&listener, g)?);
}

// Update guards' positions from defending player
let msg = clients[game.defender].rx.recv()?;
info!("Received guard positions from defender!");
game.update(msg, game.defender);

Ok(Server { clients, game })
}

Expand Down Expand Up @@ -160,17 +172,28 @@ pub struct Client<T: UIBackend> {
}

impl<T: UIBackend> Client<T> {
pub fn new(address: &str, ui: UserInterface<T>) -> Result<Self> {
pub fn new(address: &str, mut ui: UserInterface<T>) -> Result<Self> {
let stream = TcpStream::connect(address)?;
stream.set_read_timeout(Some(Duration::from_millis(100)))?;
let game = deserialize_from(&stream)?;
let mut game: Game = deserialize_from(&stream)?;
info!("Connected to {}. Waiting for server...", address);

// Defender sets positions of their guards
if game.player == game.defender {
let msg = game.place_guards(&mut ui)?;
serialize_into(&stream, &msg)?;
}

// Display splash screen
ui.splash()?;

Ok(Client { stream, game, ui })
}

pub fn run(&mut self) -> Result<()> {
self.stream
.set_read_timeout(Some(Duration::from_millis(100)))?;
let quit: Status;
let mut begun = false;
loop {
// Receive update from server
if let Ok(msg) = deserialize_from::<&TcpStream, MsgToClient>(&self.stream) {
Expand All @@ -179,14 +202,16 @@ impl<T: UIBackend> Client<T> {
quit = msg.quit;
break;
}
begun = true;

// Send back update if it's our turn
if msg.turn {
let msg = self.game.play(msg.defender, &mut self.ui)?;
serialize_into(&self.stream, &msg)?;
}
} else {
self.ui.idle()?;
} else if self.ui.idle(begun)? {
self.ui.reset();
return Ok(());
}
}
self.ui.reset();
Expand Down
87 changes: 82 additions & 5 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod term;

use crate::{Game, Point, Result, Status};
use rand::{thread_rng, Rng};
use std::time::{Duration, Instant};

pub enum Key {
Expand Down Expand Up @@ -70,7 +71,7 @@ impl<T: UIBackend> UserInterface<T> {
}
}

fn defender(&mut self, game: &mut Game, key: Key) -> isize {
fn defender(&mut self, game: &mut Game, key: Key, set: &mut bool) -> isize {
match key {
Key::Tab => loop {
self.guard = (self.guard + 1) % game.guards.len();
Expand All @@ -90,6 +91,9 @@ impl<T: UIBackend> UserInterface<T> {
'[' => game.rotate_guard(self.guard, false),
']' => game.rotate_guard(self.guard, true),
'.' => (),
' ' => {
*set = true;
}
_ => return 0,
},
}
Expand Down Expand Up @@ -254,7 +258,8 @@ impl<T: UIBackend> UserInterface<T> {
.input(Duration::from_millis(game.config.input_timeout))?
{
actions -= if defender {
self.defender(game, k)
let mut x = true;
self.defender(game, k, &mut x)
} else {
self.player(game, k)
};
Expand Down Expand Up @@ -291,16 +296,88 @@ impl<T: UIBackend> UserInterface<T> {
Ok(())
}

/// Event loop to for placing guard positions
pub fn place_guards(&mut self, game: &mut Game) -> Result<()> {
let mut remaining: usize = game.config.num_guards;
self.guard = 0;
let mut final_choice = vec![];

// Hide player positions
let players = game.positions.clone();
game.positions = vec![None; game.config.players];

self.display(game, true)?;
while remaining > 0 {
self.message(&format!("{} guards remaining to place", remaining))?;

if let Some(k) = self
.backend
.input(Duration::from_millis(game.config.input_timeout))?
{
let mut done = false;
let _ = self.defender(game, k, &mut done);
if done {
final_choice.push(game.guards[self.guard]);
game.guards[self.guard] = None;
remaining -= 1;
self.guard = game.guards.iter().position(|&x| x.is_some()).unwrap_or(0);
}
} else {
continue;
}

self.display(game, true)?;
}
game.guards = final_choice;
game.positions = players;

Ok(())
}

/// Splash screen
pub fn splash(&mut self) -> Result<()> {
const SPLASH: &str = "██ ██ █████ ███ ██ ███████ ██████
██ ██ ██ ██ ████ ██ ███ ██ ██
███████ ███████ ██ ██ ██ ███ ██ ██
██ ██ ██ ██ ██ ██ ██ ███ ██ ██
██ ██ ██ ██ ██ ████ ███████ ██████
Version: 0.1.0
";

self.backend.clear()?;
let mut p = (5, 5);
for line in SPLASH.lines() {
self.backend.draw(p, line, Colour::Red)?;
p.1 += 1;
}
self.backend.flush()?;
Ok(())
}

/// De-initialise the user interface
pub fn reset(&mut self) {
self.backend.reset();
}

/// Idle screen
pub fn idle(&mut self) -> Result<()> {
pub fn idle(&mut self, begun: bool) -> Result<bool> {
// Consume accidental input
let _ = self.backend.input(Duration::from_millis(100))?;
self.message("Waiting for other players...")
if let Some(Key::Char('q')) = self.backend.input(Duration::from_millis(100))? {
return Ok(true);
}
if begun {
// Draw @s at random points on the screen
let mut rng = thread_rng();
let size = self.backend.size();
for _ in 0..(size.0 / 3) {
let x = rng.gen_range(0..size.0);
let y = rng.gen_range(0..(size.1 - 1));
self.backend.draw((x, y), "@", Colour::Grey)?;
}
}
self.message("Waiting for other players...")?;
Ok(false)
}
}

Expand Down
1 change: 1 addition & 0 deletions src/ui/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,6 @@ impl UIBackend for Terminal {
fn reset(&mut self) {
execute!(self.stdout, MoveTo(0, self.size.1 - 1), Show).ok();
terminal::disable_raw_mode().ok();
println!();
}
}

0 comments on commit 7bda30a

Please sign in to comment.