use piston_window::Context;
use piston_window::G2d;
use piston_window::Rectangle;
use piston_window::Transformed;
#[cfg(test)]
use quickcheck::Arbitrary;
#[cfg(test)]
use quickcheck::Gen;
use color;
const PLAYER_MARGIN: f64 = 10.0;
const SPEED: f64 = 150.0;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Movement {
Down,
None,
Up,
}
#[cfg(test)]
impl Arbitrary for Movement {
fn arbitrary<G: Gen>(g: &mut G) -> Movement {
let mode = g.gen_range(0, 3);
match mode {
0 => Movement::Down,
1 => Movement::Up,
_ => Movement::None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FieldSide {
Left,
Right,
}
impl FieldSide {
pub fn get_x_position(&self, player_width: f64, field_width: u32) -> f64 {
match *self {
FieldSide::Left => PLAYER_MARGIN,
FieldSide::Right => f64::from(field_width) - player_width - PLAYER_MARGIN,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct Player {
field_side: FieldSide,
movement: Movement,
position: (f64, f64),
score: isize,
size: (f64, f64),
speed: f64,
}
impl Player {
pub fn new(side: FieldSide, field_width: u32) -> Player {
let size: (f64, f64) = (10.0, 60.0);
let y: f64 = 0.0;
let x: f64 = side.get_x_position(size.0, field_width);
Player {
field_side: side,
movement: Movement::None,
position: (x, y),
score: 0,
size: (10.0, 60.0),
speed: SPEED,
}
}
pub fn change_speed(&mut self, amount: f64) {
self.speed += amount;
}
pub fn draw(&self, context: &Context, graphics: &mut G2d) {
let handle = Rectangle::new(color::WHITE);
let transformation = context.transform.trans(self.position.0, self.position.1);
handle.draw([0.0, 0.0, self.size.0, self.size.1], &context.draw_state, transformation, graphics);
}
#[inline]
pub fn get_bounding_box(&self) -> [f64; 4] {
[
self.position.0,
self.position.1,
self.position.0 + self.size.0,
self.position.1 + self.size.1
]
}
pub fn get_score(&self) -> isize {
self.score
}
pub fn set_movement(&mut self, movement: Movement) {
self.movement = movement;
}
pub fn update(&mut self, dt: f64, height: u32) {
match self.movement {
Movement::Down => {
self.position.1 += self.speed * dt;
if self.position.1 + self.size.1 > f64::from(height) {
self.position.1 = f64::from(height) - self.size.1;
}
},
Movement::Up => {
self.position.1 -= self.speed * dt;
if self.position.1 < 0.0 {
self.position.1 = 0.0;
}
},
_ => {},
}
}
pub fn update_score(&mut self, additional_points: isize) {
match self.score.checked_add(additional_points) {
Some(new_score) => self.score = new_score,
None => {
if additional_points >= 0 {
self.score = ::std::isize::MAX;
}
else {
self.score = ::std::isize::MIN;
}
}
}
self.speed = SPEED;
}
pub fn update_position(&mut self, new_field_width: u32) {
self.position.0 = self.field_side.get_x_position(self.size.0, new_field_width);
}
}
#[cfg(test)]
mod tests {
#![allow(trivial_casts)]
use quickcheck::TestResult;
use super::*;
#[test]
fn get_x_position_left() {
let side = FieldSide::Left;
let x: f64 = side.get_x_position(20.0, 50);
assert_eq!(x, PLAYER_MARGIN);
}
#[test]
fn get_x_position_right() {
let side = FieldSide::Right;
let x: f64 = side.get_x_position(20.0, 50);
assert_eq!(x, 30.0 - PLAYER_MARGIN);
}
#[test]
fn new() {
let player = Player::new(FieldSide::Left, 42);
assert_eq!(player.movement, Movement::None);
assert_eq!(player.position, (PLAYER_MARGIN, 0.0));
assert_eq!(player.score, 0);
assert_eq!(player.size, (10.0, 60.0));
assert_eq!(player.speed, 150.0);
}
#[test]
fn change_speed() {
let mut player = Player::new(FieldSide::Left, 42);
player.speed = 42.0;
player.change_speed(10.0);
assert_eq!(player.speed, 52.0);
}
#[test]
fn get_bounding_box() {
let player = Player::new(FieldSide::Left, 42);
let bounding_box = player.get_bounding_box();
assert_eq!(bounding_box[0], PLAYER_MARGIN);
assert_eq!(bounding_box[1], 0.0);
assert_eq!(bounding_box[2], PLAYER_MARGIN + 10.0);
assert_eq!(bounding_box[3], 60.0);
}
#[test]
fn get_score() {
let mut player = Player::new(FieldSide::Left, 42);
let score: isize = 42;
player.score = score;
assert_eq!(player.get_score(), score);
}
quickcheck! {
fn set_movement(movement: Movement) -> bool {
let mut player = Player::new(FieldSide::Left, 42);
player.set_movement(movement);
player.movement == movement
}
}
quickcheck! {
fn update(position: (f64, f64), dt: f64, height: u32, movement: Movement) -> TestResult {
if dt.is_sign_negative() || position.0.is_sign_negative() || position.1.is_sign_negative() {
return TestResult::discard();
}
let mut player = Player::new(FieldSide::Left, (position.1 * 2.0) as u32);
player.position = position;
player.set_movement(movement);
player.update(dt, height);
if (height as f64) < position.1 + player.size.1 {
return TestResult::discard();
}
match movement {
Movement::None => {
TestResult::from_bool(player.position == position)
},
Movement::Up => {
TestResult::from_bool(
player.position.0 == position.0 &&
player.position.1 >= 0.0 &&
player.position.1 <= position.1
)
},
Movement::Down => {
TestResult::from_bool(
player.position.0 == position.0 &&
player.position.1 >= position.1 &&
player.position.1 + player.size.1 <= (height as f64)
)
}
}
}
}
quickcheck! {
fn update_score(old_score: isize, additional_points: isize, speed: f64) -> bool {
let mut player = Player::new(FieldSide::Left, 42);
player.speed = speed;
player.score = old_score;
player.update_score(additional_points);
if additional_points == 0 {
return player.score == old_score &&
player.speed == SPEED;
} else if additional_points > 0 {
return old_score < player.score && player.score <= ::std::isize::MAX &&
player.speed == SPEED;
} else {
return ::std::isize::MIN <= player.score && player.score < old_score &&
player.speed == SPEED;
}
}
}
#[test]
fn update_score_upper_overflow() {
let mut player = Player::new(FieldSide::Left, 42);
player.score = ::std::isize::MAX;
player.update_score(1);
assert_eq!(player.score, ::std::isize::MAX);
}
#[test]
fn update_score_lower_overflow() {
let mut player = Player::new(FieldSide::Left, 42);
player.score = ::std::isize::MIN;
player.update_score(-1);
assert_eq!(player.score, ::std::isize::MIN);
}
#[test]
fn update_position() {
let mut player = Player::new(FieldSide::Right, 42);
player.update_position(60);
assert_eq!(player.position, (50.0 - PLAYER_MARGIN, 0.0));
}
}