Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/detect win #7

Merged
merged 12 commits into from
Nov 9, 2019
80 changes: 47 additions & 33 deletions src/display/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::model::{AreaId, Game, Suit};

use super::{
blank::BlankWidget, card::CARD_SIZE, geometry, help::HelpWidget, stack::StackWidget,
DisplayState, Widget,
win::WinWidget, DisplayState, Widget,
};

lazy_static! {
Expand All @@ -29,30 +29,21 @@ pub struct GameWidgetState {
}

#[derive(Debug)]
pub struct GameWidget<'a, 'g>
where
'g: 'a,
{
pub area_ids: Vec<AreaId>,
pub struct GameWidget<'a> {
pub area_ids: &'a [AreaId],
pub bounds: geometry::Rect<u16>,
pub game: &'a Game<'g>,
pub game: &'a Game,
pub display_state: DisplayState,
pub widget_state: &'a GameWidgetState,
}

impl<'a, 'g> Widget for GameWidget<'a, 'g>
where
'g: 'a,
{
impl<'a> Widget for GameWidget<'a> {
fn bounds(&self) -> geometry::Rect<u16> {
self.bounds
}
}

impl<'a, 'g> fmt::Display for GameWidget<'a, 'g>
where
'g: 'a,
{
impl<'a> fmt::Display for GameWidget<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let full_refresh_required = self.is_full_refresh_required();

Expand All @@ -66,19 +57,24 @@ where

match self.display_state {
DisplayState::Playing => {
let game_area_ids = self.game.area_ids();

let area_ids = if full_refresh_required {
self.game.area_ids()
&game_area_ids
} else {
self.area_ids.clone()
self.area_ids
};

for area_id in area_ids {
self.write_area(area_id, fmt)?;
self.write_area(*area_id, fmt)?;
}
}
DisplayState::HelpMessageOpen => {
self.write_help(fmt)?;
}
DisplayState::WinMessageOpen => {
self.write_win(fmt)?;
}
_ => {}
}

Expand All @@ -90,10 +86,7 @@ where
}
}

impl<'a, 'g> GameWidget<'a, 'g>
where
'g: 'a,
{
impl<'a> GameWidget<'a> {
fn is_full_refresh_required(&self) -> bool {
let state = self.widget_state.cell.borrow();

Expand All @@ -103,12 +96,21 @@ where
.unwrap_or(true);
let display_state_changed = state
.prev_display_state
.map(|prev_display_state| prev_display_state != self.display_state)
.map(|prev_display_state| {
prev_display_state != self.display_state && self.state_requires_refresh()
})
.unwrap_or(true);

bounds_changed || display_state_changed
}

fn state_requires_refresh(&self) -> bool {
match self.display_state {
DisplayState::WinMessageOpen => false,
_ => true,
}
}

fn write_area(&self, area_id: AreaId, fmt: &mut fmt::Formatter) -> fmt::Result {
let bounds = bounds_for_area(area_id, self.bounds);

Expand All @@ -124,18 +126,20 @@ where
write!(fmt, "{}", blank_widget)?;
}

let stack = self.game.stack(area_id);

let stack_widget = StackWidget {
bounds,
stack: &stack,
};
if let Some(stack) = self.game.stack(area_id) {
let stack_widget = StackWidget {
bounds,
stack: &stack,
};

let new_bounds = stack_widget.bounds();
write!(fmt, "{}", stack_widget)?;
let new_bounds = stack_widget.bounds();
write!(fmt, "{}", stack_widget)?;

// Ignore return value, because we don't need the old value.
let _ = bounds_cache.insert(area_id, new_bounds);
// Ignore return value, because we don't need the old value.
bounds_cache.insert(area_id, new_bounds);
} else {
bounds_cache.remove(&area_id);
}

Ok(())
}
Expand All @@ -149,6 +153,16 @@ where

Ok(())
}

fn write_win(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let widget = WinWidget {
bounds: self.bounds,
};

write!(fmt, "{}", widget)?;

Ok(())
}
}

fn bounds_for_area(area_id: AreaId, widget_bounds: geometry::Rect<u16>) -> geometry::Rect<u16> {
Expand Down
2 changes: 2 additions & 0 deletions src/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ pub mod geometry;
pub mod help;
pub mod selector;
pub mod stack;
pub mod win;

#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum DisplayState {
Playing,
HelpMessageOpen,
Quitting,
WinMessageOpen,
}

pub trait Widget: fmt::Display {
Expand Down
54 changes: 54 additions & 0 deletions src/display/win.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::fmt;

use super::{
frame::{self, FrameWidget, Title},
geometry, Widget,
};

lazy_static! {
static ref CONTENT_SIZE: geometry::Size2D<u16> = geometry::size2(20, 3);
static ref BORDER: geometry::SideOffsets2D<u16> = geometry::SideOffsets2D::new_all_same(1);
static ref PADDING: geometry::SideOffsets2D<u16> = geometry::SideOffsets2D::new(1, 2, 1, 2);
}

#[derive(Debug)]
pub struct WinWidget {
pub bounds: geometry::Rect<u16>,
}

impl Widget for WinWidget {
fn bounds(&self) -> geometry::Rect<u16> {
let left_offset = self.bounds.size.width.saturating_sub(CONTENT_SIZE.width) / 2;
let top_offset = self.bounds.size.height.saturating_sub(CONTENT_SIZE.height) / 2;
let offset: geometry::Vector2D<u16> = geometry::vec2(left_offset, top_offset);

let inner_origin = self.bounds.origin + offset;
let inner_bounds = geometry::Rect::new(inner_origin, *CONTENT_SIZE);

inner_bounds.outer_rect(*BORDER + *PADDING)
}
}

impl fmt::Display for WinWidget {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let frame_bounds = self.bounds();
let inner_bounds = frame_bounds.inner_rect(*BORDER + *PADDING);

let frame_display = FrameWidget {
bounds: frame_bounds,
top_title: Some(Title::center("Y O U W I N !")),
bottom_title: None,
frame_style: &frame::DOUBLE,
};

write!(fmt, "{}", frame_display)?;

let goto_line1 = geometry::goto(inner_bounds.origin);
let goto_line2 = geometry::goto(inner_bounds.origin + geometry::vec2(0, 2));

write!(fmt, "{}Congratulations!", goto_line1)?;
write!(fmt, "{}New Game? (Y/N)", goto_line2)?;

Ok(())
}
}