Hello, World!
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
148
Cargo.lock
generated
Normal file
148
Cargo.lock
generated
Normal file
@@ -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",
|
||||
]
|
||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "dos"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.9.2"
|
||||
248
src/main.rs
Normal file
248
src/main.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user