I might just throw out the TempFile thing - should actually save them somewhere. As I think it would likely be useful for testing too
900 lines
26 KiB
Rust
900 lines
26 KiB
Rust
use card_stuffs::{self, CardPosition};
|
|
use core::panic;
|
|
use std::io;
|
|
|
|
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
|
use ratatui::{
|
|
layout::{Constraint, Flex, Layout, Rect},
|
|
style::{Color, Style, Stylize},
|
|
text::Line,
|
|
widgets::{Block, BorderType, Borders, Clear, Paragraph, Wrap},
|
|
DefaultTerminal, Frame,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct App {
|
|
cards: card_stuffs::Klondike,
|
|
// There aren't pretty... not sure what else I can do about that though...
|
|
highlighted_card: card_stuffs::CardPosition,
|
|
// I should think about making this a Vec so I can highlight a whole stack which is about to move
|
|
selected_card: Option<card_stuffs::CardPosition>,
|
|
exit: bool,
|
|
show_help: bool,
|
|
show_exit: bool,
|
|
}
|
|
|
|
impl Default for App {
|
|
fn default() -> Self {
|
|
Self {
|
|
cards: card_stuffs::Klondike::default(),
|
|
highlighted_card: card_stuffs::CardPosition::TopWaste,
|
|
selected_card: None,
|
|
exit: false,
|
|
show_exit: false,
|
|
show_help: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
const CARD_HEIGHT: u16 = 11;
|
|
const CARD_WIDTH: u16 = 15;
|
|
|
|
fn draw_waste(
|
|
cards_in_waste: &Vec<card_stuffs::Card>,
|
|
area: Rect,
|
|
frame: &mut Frame,
|
|
highlight: bool,
|
|
selected: bool,
|
|
) {
|
|
let horizontal = Layout::horizontal([
|
|
Constraint::Length(3),
|
|
Constraint::Length(3),
|
|
Constraint::Length(CARD_WIDTH),
|
|
]);
|
|
|
|
let [w1, w2, top_waste] = horizontal.areas(area);
|
|
|
|
// There must be a better way to do all of this
|
|
if cards_in_waste.is_empty() {
|
|
frame.render_widget(empty_pile(highlight, selected), top_waste);
|
|
}
|
|
if cards_in_waste.len() >= 3 {
|
|
frame.render_widget(
|
|
partially_covered_card(&cards_in_waste[cards_in_waste.len() - 3]),
|
|
w1,
|
|
);
|
|
frame.render_widget(
|
|
partially_covered_card(&cards_in_waste[cards_in_waste.len() - 2]),
|
|
w2,
|
|
);
|
|
frame.render_widget(
|
|
card_widget(
|
|
&cards_in_waste[cards_in_waste.len() - 1],
|
|
true,
|
|
highlight,
|
|
false,
|
|
),
|
|
top_waste,
|
|
);
|
|
} else if cards_in_waste.len() == 2 {
|
|
frame.render_widget(
|
|
partially_covered_card(&cards_in_waste[cards_in_waste.len() - 2]),
|
|
w2,
|
|
);
|
|
frame.render_widget(
|
|
card_widget(
|
|
&cards_in_waste[cards_in_waste.len() - 1],
|
|
true,
|
|
highlight,
|
|
false,
|
|
),
|
|
top_waste,
|
|
);
|
|
} else if cards_in_waste.len() == 1 {
|
|
frame.render_widget(
|
|
card_widget(
|
|
&cards_in_waste[cards_in_waste.len() - 1],
|
|
true,
|
|
highlight,
|
|
false,
|
|
),
|
|
top_waste,
|
|
);
|
|
}
|
|
}
|
|
|
|
impl App {
|
|
/// runs the application's main loop until the user quits
|
|
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
|
|
while !self.exit {
|
|
terminal.draw(|frame| self.draw(frame))?;
|
|
self.handle_events()?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn draw(&self, frame: &mut Frame) {
|
|
let area = frame.area();
|
|
let vertical = Layout::vertical([
|
|
Constraint::Length(1),
|
|
Constraint::Min(0),
|
|
Constraint::Length(1),
|
|
]);
|
|
let [title_bar, main_area, status_bar] = vertical.areas(area);
|
|
|
|
frame.render_widget(
|
|
Block::new()
|
|
.borders(Borders::TOP)
|
|
.border_type(BorderType::Thick)
|
|
.title(Line::from("Legends of Soltar").bold()),
|
|
title_bar,
|
|
);
|
|
|
|
let status_bar_info = format!(
|
|
"Cards Per-Draw: {} ---- Times Through Deck: {} ---- Press 'h' for Help ",
|
|
self.cards.num_cards_turned, self.cards.current_num_passes_through_deck
|
|
);
|
|
frame.render_widget(
|
|
Block::new().borders(Borders::TOP).title(status_bar_info),
|
|
status_bar,
|
|
);
|
|
|
|
let vertical = Layout::vertical([Constraint::Length(CARD_HEIGHT), Constraint::Min(0)]);
|
|
let [dwf_area, piles_area] = vertical.areas(main_area);
|
|
|
|
let horizontal = Layout::horizontal([
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(3 + 3 + CARD_WIDTH), // for 2 cards shown underneath
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
])
|
|
.flex(Flex::SpaceAround);
|
|
|
|
let [deck_area, waste_area, fa, fb, fc, fd] = horizontal.areas(dwf_area);
|
|
let foundation_areas = [fa, fb, fc, fd];
|
|
|
|
frame.render_widget(deck_widget(&self.cards.deck), deck_area);
|
|
|
|
{
|
|
let highlight = self.highlighted_card == CardPosition::TopWaste;
|
|
let selected = {
|
|
match self.selected_card {
|
|
None => false,
|
|
Some(pos) => pos == CardPosition::TopWaste,
|
|
}
|
|
};
|
|
draw_waste(&self.cards.waste, waste_area, frame, highlight, selected);
|
|
}
|
|
|
|
for (i, fa) in foundation_areas.iter().enumerate() {
|
|
let highlight = self.highlighted_card == CardPosition::Foundation(i);
|
|
let selected = {
|
|
match self.selected_card {
|
|
None => false,
|
|
Some(pos) => pos == CardPosition::Foundation(i),
|
|
}
|
|
};
|
|
frame.render_widget(empty_pile(highlight, selected), *fa);
|
|
}
|
|
|
|
let horizontal = Layout::horizontal([
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
Constraint::Length(CARD_WIDTH),
|
|
])
|
|
.flex(Flex::SpaceAround);
|
|
let pileses: [Rect; 7] = horizontal.areas(piles_area);
|
|
|
|
for pile in 0..card_stuffs::NUM_PILES_KLONDIKE {
|
|
let mut constraints = Vec::new();
|
|
for card in 0..(card_stuffs::NUM_PILES_KLONDIKE + 13) {
|
|
match self.cards.piles[pile].get(card) {
|
|
Some(_) => {
|
|
if card == self.cards.piles[pile].len() - 1 {
|
|
constraints.push(Constraint::Length(CARD_HEIGHT));
|
|
} else {
|
|
constraints.push(Constraint::Length(2));
|
|
}
|
|
}
|
|
None => {
|
|
constraints.push(Constraint::Length(0));
|
|
}
|
|
}
|
|
}
|
|
|
|
let vertical = Layout::vertical(constraints);
|
|
let card_display: [Rect; card_stuffs::NUM_PILES_KLONDIKE + 13] =
|
|
vertical.areas(pileses[pile]);
|
|
|
|
for (i, card) in card_display.iter().enumerate() {
|
|
match self.cards.piles[pile].get(i) {
|
|
None => break,
|
|
Some(c) => {
|
|
let is_top_card = i == self.cards.piles[pile].len() - 1;
|
|
let highlight = self.highlighted_card == CardPosition::Pile(pile, i);
|
|
let selected = {
|
|
match self.selected_card {
|
|
None => false,
|
|
Some(pos) => pos == CardPosition::Pile(pile, i),
|
|
}
|
|
};
|
|
let a_card = match c.visible {
|
|
true => card_widget(c, is_top_card, highlight, selected),
|
|
false => card_widget(c, is_top_card, highlight, selected),
|
|
};
|
|
frame.render_widget(&a_card, *card)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if self.show_help {
|
|
show_help(frame, &area);
|
|
} else if self.show_exit {
|
|
show_exit(frame, &area);
|
|
}
|
|
}
|
|
|
|
fn handle_events(&mut self) -> io::Result<()> {
|
|
match event::read()? {
|
|
// it's important to check that the event is a key press event as
|
|
// crossterm also emits key release and repeat events on Windows.
|
|
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
|
|
self.handle_key_event(key_event)
|
|
}
|
|
_ => {}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_key_event(&mut self, key_event: KeyEvent) {
|
|
match key_event.code {
|
|
KeyCode::Char('q') => {
|
|
if self.show_exit {
|
|
self.exit()
|
|
}
|
|
self.show_exit = true;
|
|
}
|
|
KeyCode::Char('b') => self.show_exit = false,
|
|
KeyCode::Char('d') => self.cards.deck_to_waste(),
|
|
KeyCode::Char('w') => self.cards.waste_to_deck(),
|
|
KeyCode::Char('1') => self.cards.num_cards_turned = 1,
|
|
KeyCode::Char('3') => self.cards.num_cards_turned = 3,
|
|
KeyCode::Char('h') => self.show_help = !self.show_help, // toggle
|
|
KeyCode::Char(' ') => self.select_card(),
|
|
KeyCode::Left => {
|
|
self.highlighted_card =
|
|
handle_move_highlighted(&self.highlighted_card, Direction::Left, &self.cards)
|
|
}
|
|
KeyCode::Right => {
|
|
self.highlighted_card =
|
|
handle_move_highlighted(&self.highlighted_card, Direction::Right, &self.cards)
|
|
}
|
|
KeyCode::Up => {
|
|
self.highlighted_card =
|
|
handle_move_highlighted(&self.highlighted_card, Direction::Up, &self.cards)
|
|
}
|
|
KeyCode::Down => {
|
|
self.highlighted_card =
|
|
handle_move_highlighted(&self.highlighted_card, Direction::Down, &self.cards)
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn exit(&mut self) {
|
|
self.exit = true;
|
|
}
|
|
|
|
fn select_card(&mut self) {
|
|
self.selected_card = Some(self.highlighted_card);
|
|
// FIXME - actually moving the selected card to next position
|
|
// FIXME - don't allow selection of empty pile/foundation
|
|
}
|
|
}
|
|
|
|
fn main() -> io::Result<()> {
|
|
let mut terminal = ratatui::init();
|
|
let app_result = App::default().run(&mut terminal);
|
|
ratatui::restore();
|
|
app_result
|
|
}
|
|
|
|
enum Direction {
|
|
Up,
|
|
Down,
|
|
Left,
|
|
Right,
|
|
}
|
|
|
|
fn handle_move_highlighted(
|
|
current_position: &CardPosition,
|
|
direction: Direction,
|
|
cards: &card_stuffs::Klondike,
|
|
) -> CardPosition {
|
|
match current_position {
|
|
CardPosition::TopWaste => match direction {
|
|
Direction::Up | Direction::Left => CardPosition::TopWaste,
|
|
Direction::Right => CardPosition::Foundation(0),
|
|
Direction::Down => {
|
|
let lowest_shown_card = cards.clone().lowest_visible_card_in_pile_from_index(1, 0);
|
|
CardPosition::Pile(1, lowest_shown_card)
|
|
}
|
|
},
|
|
CardPosition::Pile(p, i) => {
|
|
match direction {
|
|
Direction::Down => {
|
|
if *i == cards.piles[*p].len() - 1 {
|
|
CardPosition::Pile(*p, *i)
|
|
} else {
|
|
CardPosition::Pile(*p, *i + 1)
|
|
}
|
|
}
|
|
Direction::Up => {
|
|
let lowest_shown_card =
|
|
cards.clone().lowest_visible_card_in_pile_from_index(*p, 0);
|
|
if *i == lowest_shown_card {
|
|
match *p {
|
|
0 | 1 => CardPosition::TopWaste,
|
|
2 | 3 => CardPosition::Foundation(0),
|
|
4 => CardPosition::Foundation(1),
|
|
5 => CardPosition::Foundation(2),
|
|
6 => CardPosition::Foundation(3),
|
|
_ => panic!("Should be on Pile over 6"),
|
|
}
|
|
} else {
|
|
CardPosition::Pile(*p, *i - 1)
|
|
}
|
|
}
|
|
Direction::Left => {
|
|
if *p == 0 {
|
|
CardPosition::Pile(*p, *i)
|
|
} else {
|
|
let lowest_shown_card = cards
|
|
.clone()
|
|
.lowest_visible_card_in_pile_from_index(*p - 1, 0);
|
|
if lowest_shown_card <= *i && cards.piles[*p].len() <= *i {
|
|
CardPosition::Pile(*p - 1, *i) // CHECK
|
|
} else {
|
|
CardPosition::Pile(*p - 1, lowest_shown_card)
|
|
}
|
|
}
|
|
}
|
|
Direction::Right => {
|
|
if *p == 6 {
|
|
CardPosition::Pile(*p, *i)
|
|
} else {
|
|
let lowest_shown_card = cards
|
|
.clone()
|
|
.lowest_visible_card_in_pile_from_index(*p + 1, 0);
|
|
if lowest_shown_card <= *i && cards.piles[*p].len() <= *i {
|
|
CardPosition::Pile(*p + 1, *i) // CHECK
|
|
} else {
|
|
CardPosition::Pile(*p + 1, lowest_shown_card)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CardPosition::Foundation(f) => match direction {
|
|
Direction::Up => CardPosition::Foundation(*f),
|
|
Direction::Left => {
|
|
if *f == 0 {
|
|
CardPosition::TopWaste
|
|
} else {
|
|
CardPosition::Foundation(f - 1)
|
|
}
|
|
}
|
|
Direction::Right => {
|
|
if *f >= 3 {
|
|
CardPosition::Foundation(3)
|
|
} else {
|
|
CardPosition::Foundation(*f + 1)
|
|
}
|
|
}
|
|
Direction::Down => match f {
|
|
0 => {
|
|
let i = cards.clone().lowest_visible_card_in_pile_from_index(3, 0);
|
|
CardPosition::Pile(3, i)
|
|
}
|
|
1 => {
|
|
let i = cards.clone().lowest_visible_card_in_pile_from_index(4, 0);
|
|
CardPosition::Pile(4, i)
|
|
}
|
|
2 => {
|
|
let i = cards.clone().lowest_visible_card_in_pile_from_index(5, 0);
|
|
CardPosition::Pile(5, i)
|
|
}
|
|
3 => {
|
|
let i = cards.clone().lowest_visible_card_in_pile_from_index(6, 0);
|
|
CardPosition::Pile(6, i)
|
|
}
|
|
_ => panic!("Can't be on a foundation this high"),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
fn show_help(frame: &mut Frame, area: &Rect) {
|
|
let block = Block::bordered().title("Help");
|
|
let text = "You are playing \"Legends of Soltar\" - a Klondike thingy
|
|
Press 'q' to Quit
|
|
Press '1' or '3' to change the number of cards you draw from the deck
|
|
Press 'd' to draw from your deck
|
|
Press 'w' to put the waste pile back into the deck (you can only do this when the waste is empty)";
|
|
let p = Paragraph::new(text).wrap(Wrap { trim: true });
|
|
let vertical = Layout::vertical([Constraint::Max(10)]).flex(Flex::Center);
|
|
let horizontal = Layout::horizontal([Constraint::Percentage(70)]).flex(Flex::Center);
|
|
let [area] = vertical.areas(*area);
|
|
let [area] = horizontal.areas(area);
|
|
frame.render_widget(Clear, area);
|
|
frame.render_widget(p.block(block), area);
|
|
}
|
|
|
|
fn show_exit(frame: &mut Frame, area: &Rect) {
|
|
let block = Block::bordered().title("Exit?");
|
|
let text = "Really want to exit Legend of Soltar?
|
|
Press 'q' to Quit or 'b' to go back";
|
|
let p = Paragraph::new(text).wrap(Wrap { trim: true });
|
|
let vertical = Layout::vertical([Constraint::Max(10)]).flex(Flex::Center);
|
|
let horizontal = Layout::horizontal([Constraint::Percentage(70)]).flex(Flex::Center);
|
|
let [area] = vertical.areas(*area);
|
|
let [area] = horizontal.areas(area);
|
|
frame.render_widget(Clear, area);
|
|
frame.render_widget(p.block(block), area);
|
|
}
|
|
|
|
fn card_widget<'a>(
|
|
card: &'a card_stuffs::Card,
|
|
top: bool,
|
|
highlight: bool,
|
|
select: bool,
|
|
) -> Paragraph<'a> {
|
|
if !card.visible {
|
|
return facedown_card(top);
|
|
}
|
|
|
|
let card_image = card_paragraph(&card);
|
|
|
|
let card_style = match card.suit.colour() {
|
|
card_stuffs::Colour::Black => Style::new().black().bg(Color::White),
|
|
card_stuffs::Colour::Red => Style::new().red().bg(Color::White),
|
|
};
|
|
|
|
let mut borders = Borders::TOP | Borders::LEFT | Borders::RIGHT;
|
|
if top {
|
|
borders |= Borders::BOTTOM;
|
|
}
|
|
|
|
let mut border_style = Style::new().white().on_black();
|
|
if highlight {
|
|
border_style = border_style.fg(Color::Blue);
|
|
} else if select {
|
|
border_style = border_style.fg(Color::Green);
|
|
}
|
|
|
|
Paragraph::new(card_image).style(card_style).block(
|
|
Block::new()
|
|
.style(border_style)
|
|
.borders(borders)
|
|
.border_type(BorderType::Rounded),
|
|
)
|
|
}
|
|
|
|
fn deck_widget(cards_in_deck: &Vec<card_stuffs::Card>) -> Paragraph<'static> {
|
|
let card_image = format!(
|
|
"#############\n\
|
|
#############\n\
|
|
### Cards ###\n\
|
|
### Left ###\n\
|
|
#############\n\
|
|
#### {:02} #####\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############",
|
|
cards_in_deck.len()
|
|
);
|
|
|
|
Paragraph::new(card_image).block(
|
|
Block::new()
|
|
.borders(Borders::ALL)
|
|
.border_type(BorderType::Rounded),
|
|
)
|
|
}
|
|
|
|
fn partially_covered_card(card: &card_stuffs::Card) -> Paragraph {
|
|
let card_image = format!("{value}{suit}", value = card.value, suit = card.suit);
|
|
let card_style = match card.suit.colour() {
|
|
card_stuffs::Colour::Black => Style::new().black().bg(Color::White),
|
|
card_stuffs::Colour::Red => Style::new().red().bg(Color::White),
|
|
};
|
|
let borders = Borders::TOP | Borders::LEFT | Borders::BOTTOM;
|
|
let border_style = Style::new().white().on_black();
|
|
Paragraph::new(card_image).style(card_style).block(
|
|
Block::new()
|
|
.style(border_style)
|
|
.borders(borders)
|
|
.border_type(BorderType::Rounded),
|
|
)
|
|
}
|
|
|
|
fn facedown_card(top: bool) -> Paragraph<'static> {
|
|
let hidden_card = format!(
|
|
"#############\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############\n\
|
|
#############"
|
|
);
|
|
|
|
let mut borders = Borders::TOP | Borders::LEFT | Borders::RIGHT;
|
|
if top {
|
|
borders |= Borders::BOTTOM;
|
|
}
|
|
|
|
Paragraph::new(hidden_card).block(
|
|
Block::new()
|
|
.borders(borders)
|
|
.border_type(BorderType::Rounded),
|
|
)
|
|
}
|
|
|
|
fn empty_pile(highlight: bool, selected: bool) -> Paragraph<'static> {
|
|
// made using https://www.asciiart.eu/
|
|
let hidden_card = format!(
|
|
"
|
|
XX XX
|
|
XX XX
|
|
|
|
X X
|
|
X X
|
|
X X
|
|
XXXXXXX"
|
|
);
|
|
|
|
let mut border_style = Style::new();
|
|
if highlight {
|
|
border_style = border_style.fg(Color::Blue);
|
|
} else if selected {
|
|
border_style = border_style.fg(Color::Green);
|
|
}
|
|
|
|
Paragraph::new(hidden_card).block(
|
|
Block::new()
|
|
.style(border_style)
|
|
.borders(Borders::ALL)
|
|
.border_type(BorderType::Rounded),
|
|
)
|
|
}
|
|
|
|
fn card_paragraph(c: &card_stuffs::Card) -> String {
|
|
match c.value {
|
|
card_stuffs::Value::Ace => {
|
|
/*
|
|
XX
|
|
X
|
|
X X
|
|
X X
|
|
X X
|
|
XXXXXXX
|
|
X X
|
|
X X
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
{suit} {suit}
|
|
{suit} {suit}
|
|
{suit}{suit}{suit}{suit}{suit}{suit}{suit}
|
|
{suit} {suit}
|
|
{suit} {suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Two => {
|
|
/*
|
|
XX
|
|
X
|
|
XXX
|
|
X
|
|
|
|
X
|
|
XXX
|
|
X
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit}
|
|
{suit}{suit}{suit}
|
|
{suit}
|
|
|
|
{suit}
|
|
{suit}{suit}{suit}
|
|
{suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Three => {
|
|
/*
|
|
XX X
|
|
XXX
|
|
X
|
|
X
|
|
XXX
|
|
X
|
|
X
|
|
XXX
|
|
X XX
|
|
*/
|
|
format!(
|
|
"{value}{suit} {suit}
|
|
{suit}{suit}{suit}
|
|
{suit}
|
|
{suit}
|
|
{suit}{suit}{suit}
|
|
{suit}
|
|
{suit}
|
|
{suit}{suit}{suit}
|
|
{suit} {value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Four => {
|
|
format!(
|
|
"{value}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Five => {
|
|
format!(
|
|
"{value}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{suit}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Six => {
|
|
/*
|
|
XX
|
|
|
|
XX XX
|
|
|
|
XX XX
|
|
|
|
XX XX
|
|
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Seven => {
|
|
/*
|
|
XX
|
|
XX XX
|
|
|
|
XX
|
|
|
|
XX XX
|
|
|
|
XX XX
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{suit}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
|
|
{suit}{suit} {suit}{suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Eight => {
|
|
/*
|
|
XX
|
|
|
|
X X
|
|
X
|
|
X X
|
|
X
|
|
X X
|
|
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
|
|
{suit} {suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Nine => {
|
|
/*
|
|
XX
|
|
X X
|
|
|
|
X X
|
|
X
|
|
X X
|
|
|
|
X X
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit} {suit}
|
|
|
|
{suit} {suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
|
|
{suit} {suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Ten => {
|
|
/*
|
|
XX
|
|
X X
|
|
X
|
|
X X
|
|
|
|
X X
|
|
X
|
|
X X
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit} {suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
|
|
{suit} {suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Jack => {
|
|
/*
|
|
XX
|
|
|
|
XXXXX
|
|
X
|
|
X
|
|
X X
|
|
XX
|
|
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
|
|
{suit}{suit}{suit}{suit}{suit}
|
|
{suit}
|
|
{suit}
|
|
{suit} {suit}
|
|
{suit}{suit}
|
|
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::Queen => {
|
|
/*
|
|
XX
|
|
XXXX
|
|
X X
|
|
X X
|
|
X X
|
|
X XX
|
|
XXXXX
|
|
X
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit}{suit}{suit}{suit}
|
|
{suit} {suit}
|
|
{suit} {suit}
|
|
{suit} {suit}
|
|
{suit} {suit}
|
|
{suit}{suit}{suit}{suit}{suit}
|
|
{suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
card_stuffs::Value::King => {
|
|
/*
|
|
XX
|
|
X X
|
|
X XX
|
|
X XX
|
|
XX
|
|
X XX
|
|
X XX
|
|
X X
|
|
XX
|
|
*/
|
|
format!(
|
|
"{value}{suit}
|
|
{suit} {suit}
|
|
{suit} {suit}{suit}
|
|
{suit} {suit}{suit}
|
|
{suit}{suit}
|
|
{suit} {suit}{suit}
|
|
{suit} {suit}{suit}
|
|
{suit} {suit}
|
|
{value}{suit}",
|
|
value = c.value,
|
|
suit = c.suit
|
|
)
|
|
}
|
|
}
|
|
}
|