Skip to content

Instantly share code, notes, and snippets.

@Patryk27
Created June 26, 2023 19:37
Show Gist options
  • Save Patryk27/df2294ecdb63856f881adcd027ab641e to your computer and use it in GitHub Desktop.
Save Patryk27/df2294ecdb63856f881adcd027ab641e to your computer and use it in GitHub Desktop.
use core::fmt::{self, Write};
use rand::{seq::SliceRandom, Rng};
use yew::prelude::*;
#[derive(Debug)]
struct App {
players: [Player; 4],
cards: Vec<Card>,
turn: usize,
revealed: bool,
}
impl Component for App {
type Message = ModelMessage;
type Properties = ();
fn create(_: &Context<Self>) -> Self {
let mut cards = Card::all();
cards.shuffle(&mut rand::thread_rng());
let mut players = [
Player::default(),
Player::default(),
Player::default(),
Player::default(),
];
for (card_idx, card) in cards.into_iter().enumerate() {
players[card_idx % 4].cards.push(card);
}
Self {
players,
cards: Default::default(),
turn: 1,
revealed: false,
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
ModelMessage::NextCard => {
let player = &mut self.players[self.turn];
let card_idx = rand::thread_rng().gen_range(0..player.cards.len());
self.cards.push(player.cards.remove(card_idx));
self.turn = (self.turn + 1) % 4;
}
ModelMessage::RevealCards => {
self.revealed = true;
}
ModelMessage::HideCards => {
self.revealed = false;
}
ModelMessage::Restart => {
*self = <Self as Component>::create(ctx);
}
}
true
}
fn view(&self, ctx: &Context<Self>) -> Html {
let done = self.players.iter().all(|player| player.cards.is_empty());
let mut board = String::new();
for n in 1..4 {
_ = writeln!(board, "Player {}:", n);
_ = writeln!(board, "{}", self.players[n].cards(self.revealed));
_ = writeln!(board);
}
_ = writeln!(board, "You:");
_ = writeln!(board, "{}", self.players[0].cards(true));
_ = writeln!(board);
_ = writeln!(board, "Latest:");
if let Some(card) = self.cards.last() {
let player = (self.turn + 3) % 4;
let player = if player == 0 {
"you".to_owned()
} else {
format!("player {}", player)
};
_ = writeln!(board, "{} ({})", card, player);
} else {
_ = writeln!(board, "-");
}
if self.revealed {
_ = writeln!(board);
_ = writeln!(board, "Cards so far:");
for card in &self.cards {
_ = write!(board, "{} ", card);
}
_ = writeln!(board);
}
html! {
<div class="container">
<pre class="board">
{ board }
</pre>
<div class="actions">
if done {
<button
onclick={ctx.link().callback(|_| ModelMessage::Restart)}>
{ "Restart" }
</button>
} else {
<button
onclick={ctx.link().callback(|_| ModelMessage::NextCard)}>
{ "Next Card" }
</button>
if self.revealed {
<button
onclick={ctx.link().callback(|_| ModelMessage::HideCards)}>
{ "Hide Cards" }
</button>
} else {
<button
onclick={ctx.link().callback(|_| ModelMessage::RevealCards)}>
{ "Reveal Cards" }
</button>
}
}
</div>
</div>
}
}
}
enum ModelMessage {
NextCard,
RevealCards,
HideCards,
Restart,
}
#[derive(Clone, Debug, Default)]
struct Player {
cards: Vec<Card>,
}
impl Player {
fn cards(&self, revealed: bool) -> String {
if revealed {
self.cards
.iter()
.map(|card| card.to_string())
.collect::<Vec<_>>()
.join(" ")
} else {
"▯ ".repeat(self.cards.len()).trim().to_owned()
}
}
}
#[derive(Copy, Clone, Debug)]
struct Card {
rank: &'static str,
color: &'static str,
}
impl Card {
fn all() -> Vec<Self> {
let mut cards = Vec::new();
for rank in Self::ranks() {
for color in Self::colors() {
cards.push(Card { rank, color });
}
}
cards
}
fn ranks() -> [&'static str; 13] {
[
"A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3", "2",
]
}
fn colors() -> [&'static str; 4] {
["♤", "♡", "♢", "♧"]
}
}
impl fmt::Display for Card {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.rank, self.color)
}
}
fn main() {
yew::Renderer::<App>::new().render();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment