commit b8ea661343dc871202af04621812183a6a7016ea Author: Weckyy702 <50154363+Weckyy702@users.noreply.github.com> Date: Mon Dec 29 18:42:12 2025 +0100 Hello, World! diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..df44589 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,148 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "dos" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..00abb38 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dos" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.9.2" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d26c223 --- /dev/null +++ b/src/main.rs @@ -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 for CardValue { + type Error = InvalidCardValue; + + fn try_from(value: i32) -> Result { + 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, + }, +} + +#[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, + 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, + top_card: Card, +} + +#[derive(Debug)] +struct GameState { + card_state: CardState, + players: Vec, + active_player_idx: usize, +} + +fn create_reserve_pile() -> VecDeque { + 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) -> 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(); + } +}