blob: 74059459a3ee73b107c11f66cc29778ba0f63500 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl_fuchsia_game_tennis as fidl_tennis;
use fidl_fuchsia_game_tennis::GameState;
use fuchsia_syslog::{fx_log, fx_log_info};
use futures::prelude::*;
use parking_lot::Mutex;
use std::sync::{Arc, Weak};
const BOARD_HEIGHT: f64 = 10.0;
const BOARD_WIDTH: f64 = 20.0;
const PADDLE_SPEED: f64 = 0.4; // distance paddle travels per step
const PADDLE_SIZE: f64 = 1.0; // vertical height of paddle
const BALL_SPEEDUP_MULTIPLIER: f64 = 1.05; // speed multiplier applied on every paddle bounce
const MAX_BOUNCE_ANGLE: f64 = 1.3; // in radians, bounce angle when hitting very top edge of paddle
pub struct Game {
state: GameState,
player_1: Option<Player>,
player_2: Option<Player>,
ball_dx: f64,
ball_dy: f64,
}
#[derive(Clone)]
struct Player {
pub name: String,
pub state: Arc<Mutex<PlayerState>>,
}
#[derive(Clone)]
pub enum PlayerState {
Up,
Down,
Stop,
}
fn calc_paddle_movement(pos: &mut f64, state: &PlayerState) {
let player_delta = match state {
PlayerState::Up => PADDLE_SPEED * -1.0,
PlayerState::Down => PADDLE_SPEED,
PlayerState::Stop => 0.0,
};
let new_paddle_location = *pos + player_delta;
if new_paddle_location >= 0.0 && new_paddle_location < BOARD_HEIGHT {
*pos = new_paddle_location;
}
}
impl Game {
/// return clone of internal state
pub fn state(&self) -> GameState {
fidl_tennis::GameState {
ball_x: self.state.ball_x,
ball_y: self.state.ball_y,
player_1_y: self.state.player_1_y,
player_2_y: self.state.player_2_y,
player_1_score: self.state.player_1_score,
player_2_score: self.state.player_2_score,
player_1_name: self.state.player_1_name.clone(),
player_2_name: self.state.player_2_name.clone(),
time: self.state.time,
game_num: self.state.game_num,
}
}
pub fn new() -> Game {
Game {
player_1: None,
player_2: None,
ball_dx: 0.0,
ball_dy: 0.0,
state: GameState {
ball_x: 0.0,
ball_y: 0.0,
game_num: 0,
player_1_y: 0.0,
player_2_y: 0.0,
player_1_score: 0,
player_2_score: 0,
player_1_name: "".to_string(),
player_2_name: "".to_string(),
time: 0,
},
}
}
pub fn players_ready(&self) -> bool {
return self.player_1.is_some() && self.player_2.is_some();
}
pub fn register_new_paddle(&mut self, player_name: String) -> Arc<Mutex<PlayerState>> {
let paddle = Player {
name: player_name.clone(),
state: Arc::new(Mutex::new(PlayerState::Stop)),
};
let res = paddle.state.clone();
if self.player_1.is_none() {
self.player_1 = Some(paddle);
self.state.player_1_name = player_name;
} else if self.player_2.is_none() {
self.player_2 = Some(paddle);
self.state.player_2_name = player_name;
} else {
panic!("too many clients connected");
}
return res;
}
pub fn step(&mut self) {
if self.players_ready() && self.state.game_num == 0 {
self.new_game();
} else if !self.players_ready() {
// game has not started yet
return;
}
fx_log_info!("new step");
self.state.time += 1;
calc_paddle_movement(
&mut self.state.player_1_y,
&self.player_1.as_mut().unwrap().state.lock(),
);
calc_paddle_movement(
&mut self.state.player_2_y,
&self.player_2.as_mut().unwrap().state.lock(),
);
let mut new_ball_x = self.state.ball_x + self.ball_dx;
let mut new_ball_y = self.state.ball_y + self.ball_dy;
// reflect off the top/bottom of the board
if new_ball_y <= 0.0 || new_ball_y > BOARD_HEIGHT {
self.ball_dy = -self.ball_dy;
new_ball_y = self.state.ball_y;
fx_log_info!("bounce off top or bottom");
}
// reflect off the left/right of the board, if a paddle is in the way
if new_ball_x <= 0.0 {
// we're about to go off of the left side
if new_ball_y > self.state.player_1_y + (PADDLE_SIZE / 2.0)
|| new_ball_y < self.state.player_1_y - (PADDLE_SIZE / 2.0) {
// player 1 missed, so player 2 gets a point and we reset
self.state.player_2_score += 1;
self.new_game();
return;
} else {
self.ball_dx = -self.ball_dx;
new_ball_x = self.state.ball_x;
fx_log_info!("bounce off left");
}
}
if new_ball_x > BOARD_WIDTH {
// we're about to go off of the right side
if new_ball_y > self.state.player_2_y + (PADDLE_SIZE / 2.0)
|| new_ball_y < self.state.player_2_y - (PADDLE_SIZE / 2.0) {
// player 2 missed, so player 1 gets a point and we reset
self.state.player_1_score += 1;
self.new_game();
return;
} else {
self.ball_dx = -self.ball_dx;
new_ball_x = self.state.ball_x;
fx_log_info!("bounce off right");
}
}
self.state.ball_x = new_ball_x;
self.state.ball_y = new_ball_y;
}
fn new_game(&mut self) {
self.player_1.as_mut().map(|player| {
*player.state.lock() = PlayerState::Stop;
});
self.player_2.as_mut().map(|player| {
*player.state.lock() = PlayerState::Stop;
});
self.ball_dx = 0.5; // TODO randomize?
self.ball_dy = 0.5; // TODO randomize?
self.state.ball_x = BOARD_WIDTH / 2.0;
self.state.ball_y = BOARD_HEIGHT / 2.0;
self.state.game_num += 1;
//self.state.player_1_y = BOARD_HEIGHT / 2.0;
//self.state.player_2_y = BOARD_HEIGHT / 2.0;
self.state.time = 0;
}
}