Hello, World!

This commit is contained in:
2025-12-29 18:42:12 +01:00
commit b8ea661343
4 changed files with 404 additions and 0 deletions

248
src/main.rs Normal file
View File

@@ -0,0 +1,248 @@
use rand::{rng, seq::SliceRandom};
use std::{collections::VecDeque, mem};
#[derive(Debug, PartialEq, Eq)]
struct CardValue(u8);
#[derive(Debug)]
struct InvalidCardValue;
impl TryFrom<i32> for CardValue {
type Error = InvalidCardValue;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value < 0 || value > 9 {
Err(InvalidCardValue)
} else {
Ok(Self(value as _))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum CardColor {
Green,
Red,
Yellow,
Blue,
}
#[derive(Debug)]
enum ColoredKind {
Number(CardValue),
Draw2,
Reverse,
Skip,
}
#[derive(Debug)]
enum SpecialKind {
Wish,
Draw4,
}
#[derive(Debug)]
enum CardKind {
Color(CardColor, ColoredKind),
Special {
kind: SpecialKind,
chosen_color: Option<CardColor>,
},
}
#[derive(Debug)]
struct Card {
kind: CardKind,
}
impl Card {
fn can_stack(&self, other: &Card) -> bool {
use CardKind as CK;
match (&self.kind, &other.kind) {
(CK::Color(ca, _), CK::Color(cb, _)) => ca == cb,
(CK::Color(ca, _), CK::Special { chosen_color, .. }) => {
if let Some(cb) = chosen_color {
ca == cb
} else {
false
}
}
(CK::Special { .. }, _) => true,
}
}
fn colored(color: CardColor, kind: ColoredKind) -> Self {
Self {
kind: CardKind::Color(color, kind),
}
}
fn special(kind: SpecialKind) -> Self {
Self {
kind: CardKind::Special {
kind,
chosen_color: None,
},
}
}
fn trigger_pre_play(&self, player: &mut Player, state: &mut CardState) {
println!("trigger_pre_turn")
}
}
#[derive(Debug)]
enum CardChoice {
Play(usize),
Take,
}
#[derive(Debug)]
struct Player {
hand: VecDeque<Card>,
penalty: usize,
}
impl Default for Player {
fn default() -> Self {
Self {
hand: Default::default(),
penalty: 0,
}
}
}
impl Player {
fn choose_card(&self, state: &GameState) -> CardChoice {
for (i, card) in self.hand.iter().enumerate() {
if card.can_stack(&state.card_state.top_card) {
return CardChoice::Play(i);
}
}
return CardChoice::Take;
}
fn play_card(&mut self, i: usize, state: &mut CardState) {
let card = self
.hand
.swap_remove_front(i)
.expect("Card choice in range");
card.trigger_pre_play(self, state);
}
}
#[derive(Debug)]
struct CardState {
reserve_pile: VecDeque<Card>,
top_card: Card,
}
#[derive(Debug)]
struct GameState {
card_state: CardState,
players: Vec<Player>,
active_player_idx: usize,
}
fn create_reserve_pile() -> VecDeque<Card> {
let mut reserve_pile = VecDeque::with_capacity(52);
for color in [
CardColor::Yellow,
CardColor::Red,
CardColor::Green,
CardColor::Blue,
] {
for value in 1..=9 {
reserve_pile.push_back(Card::colored(
color.clone(),
ColoredKind::Number(value.try_into().unwrap()),
));
}
for _ in 0..2 {
reserve_pile.push_back(Card::colored(color.clone(), ColoredKind::Skip));
reserve_pile.push_back(Card::colored(color.clone(), ColoredKind::Reverse));
reserve_pile.push_back(Card::colored(color.clone(), ColoredKind::Draw2));
}
}
for _ in 0..4 {
reserve_pile.push_back(Card::special(SpecialKind::Draw4));
reserve_pile.push_back(Card::special(SpecialKind::Wish));
}
// Shuffle the reserve pile
let (first, []) = reserve_pile.as_mut_slices() else {
panic!("The pile was just created, so it must be contiguous!");
};
first.shuffle(&mut rng());
reserve_pile
}
impl GameState {
fn new(players: impl IntoIterator<Item = Player>) -> Self {
let mut reserve_pile = create_reserve_pile();
// Spread the cards. Might make the hand size adjustable later
let mut players: Vec<_> = players.into_iter().collect();
for _ in 0..7 {
for player in &mut players {
player.hand.push_back(reserve_pile.pop_front().unwrap());
}
}
let top_card = reserve_pile.pop_front().unwrap();
Self {
card_state: CardState {
reserve_pile,
top_card,
},
players,
active_player_idx: 0,
}
}
fn is_done(&self) -> bool {
self.players.iter().any(|p| p.hand.is_empty())
}
fn active_player(&self) -> &Player {
&self.players[self.active_player_idx]
}
fn active_player_mut(&mut self) -> &mut Player {
&mut self.players[self.active_player_idx]
}
fn do_turn(&mut self) {
let choice = self.active_player().choose_card(self);
match choice {
CardChoice::Play(i) => {
self.players[self.active_player_idx].play_card(i, &mut self.card_state);
}
CardChoice::Take => {
if let Some(card) = self.card_state.reserve_pile.pop_front() {
self.active_player_mut().hand.push_back(card);
} else {
self.active_player_mut().penalty += 1
};
}
};
self.active_player_idx = (self.active_player_idx + 1) % self.players.len();
}
}
fn main() {
let mut state = GameState::new((0..4).map(|_| Player::default()));
while !state.is_done() {
println!("{state:#?}");
state.do_turn();
}
}