mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-12 07:40:50 -08:00
rust port
This commit is contained in:
9
50_Horserace/rust/Cargo.toml
Normal file
9
50_Horserace/rust/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
57
50_Horserace/rust/src/game.rs
Normal file
57
50_Horserace/rust/src/game.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use crate::{horses::Horses, players::Players};
|
||||
|
||||
pub struct Game {
|
||||
horses: Horses,
|
||||
players: Players,
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new() -> Self {
|
||||
Game {
|
||||
horses: Horses::new(),
|
||||
players: Players::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play(&mut self) -> bool {
|
||||
self.horses.randomize_odds();
|
||||
self.horses.print_table();
|
||||
|
||||
self.players.make_bets();
|
||||
|
||||
println!("\n1 2 3 4 5 6 7 8");
|
||||
|
||||
for _ in 1..=7 {
|
||||
self.horses.advance();
|
||||
self.draw();
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
|
||||
let winner = self.horses.print_placements();
|
||||
self.players.process_winner(winner);
|
||||
|
||||
return self.players.prompt_next_round();
|
||||
}
|
||||
|
||||
pub fn draw(&self) {
|
||||
println!("=============");
|
||||
println!("XXXXSTARTXXXX");
|
||||
for row in 1..=28 {
|
||||
let neighbors = self.horses.get_at(row);
|
||||
|
||||
match neighbors.len() {
|
||||
0 => println!(),
|
||||
1 => println!("{}", neighbors[0].no),
|
||||
_ => {
|
||||
for h in neighbors {
|
||||
print!("{} ", h.no);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("XXXXFINISHXXXX");
|
||||
}
|
||||
}
|
||||
114
50_Horserace/rust/src/horses.rs
Normal file
114
50_Horserace/rust/src/horses.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use rand::Rng;
|
||||
|
||||
pub struct Horse {
|
||||
pub name: String,
|
||||
pub no: u8,
|
||||
pub odd: f32,
|
||||
pub position: u8,
|
||||
}
|
||||
|
||||
impl Horse {
|
||||
fn new(name: &str, no: u8) -> Self {
|
||||
Horse {
|
||||
name: name.to_string(),
|
||||
no,
|
||||
odd: 0.,
|
||||
position: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Horses {
|
||||
horses: [Horse; 8],
|
||||
}
|
||||
|
||||
impl Horses {
|
||||
pub fn new() -> Self {
|
||||
Horses {
|
||||
horses: [
|
||||
Horse::new("JOE MAW", 1),
|
||||
Horse::new("L.B.J.", 2),
|
||||
Horse::new("MR.WASHBURN", 3),
|
||||
Horse::new("MISS KAREN", 4),
|
||||
Horse::new("JOLLY", 5),
|
||||
Horse::new("HORSE", 6),
|
||||
Horse::new("JELLY DO NOT", 7),
|
||||
Horse::new("MIDNIGHT", 8),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn randomize_odds(&mut self) {
|
||||
let mut odds = Vec::new();
|
||||
|
||||
for _ in 1..=8 {
|
||||
odds.push(rand::thread_rng().gen_range(1.0..=10.));
|
||||
}
|
||||
|
||||
let total: f32 = odds.iter().sum();
|
||||
|
||||
for (i, o) in odds.iter().enumerate() {
|
||||
let o = total / o;
|
||||
self.horses[i].odd = o;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn advance(&mut self) {
|
||||
for h in self.horses.iter_mut() {
|
||||
let distance = rand::thread_rng().gen_range(1..=100);
|
||||
let scale = h.odd.ceil() as i32;
|
||||
|
||||
let dt = if distance < 10 {
|
||||
1
|
||||
} else if distance < scale + 17 {
|
||||
2
|
||||
} else if distance < scale + 37 {
|
||||
3
|
||||
} else if distance < scale + 57 {
|
||||
4
|
||||
} else if distance < scale + 77 {
|
||||
5
|
||||
} else if distance < scale + 92 {
|
||||
6
|
||||
} else {
|
||||
7
|
||||
};
|
||||
|
||||
h.position += dt as u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_at(&self, row: usize) -> Vec<&Horse> {
|
||||
self.horses
|
||||
.iter()
|
||||
.filter(|h| h.position == row as u8)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn print_table(&self) {
|
||||
println!("HORSE\t\tNUMBER\t\tODDS\t\t\n");
|
||||
for horse in self.horses.iter() {
|
||||
let (h, n, o) = (horse.name.clone(), horse.no, horse.odd);
|
||||
|
||||
if h.len() > 7 {
|
||||
println!("{}\t{}\t\t{:.2} :1", h, n, o);
|
||||
} else {
|
||||
println!("{}\t\t{}\t\t{:.2} :1", h, n, o);
|
||||
}
|
||||
}
|
||||
println!("-----------------------------------------\n")
|
||||
}
|
||||
|
||||
pub fn print_placements(&mut self) -> u8 {
|
||||
self.horses.sort_by(|a, b| b.position.cmp(&a.position));
|
||||
|
||||
println!("\nTHE RACE RESULTS ARE:\n");
|
||||
|
||||
for (i, h) in self.horses.iter_mut().enumerate() {
|
||||
println!("{} PLACE HORSE NO. {}\t\tAT {:.2} :1", i + 1, h.no, h.odd);
|
||||
h.position = 0;
|
||||
}
|
||||
|
||||
self.horses[0].no
|
||||
}
|
||||
}
|
||||
15
50_Horserace/rust/src/main.rs
Normal file
15
50_Horserace/rust/src/main.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::game::Game;
|
||||
|
||||
mod game;
|
||||
mod horses;
|
||||
mod players;
|
||||
mod util;
|
||||
|
||||
fn main() {
|
||||
let mut game = Game::new();
|
||||
let mut again = true;
|
||||
|
||||
while again {
|
||||
again = game.play();
|
||||
}
|
||||
}
|
||||
154
50_Horserace/rust/src/players.rs
Normal file
154
50_Horserace/rust/src/players.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use crate::util::{self, PromptResult};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Player {
|
||||
pub name: String,
|
||||
pub money: u32,
|
||||
pub playing: bool,
|
||||
pub horse_no: u8,
|
||||
pub bet: u32,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
fn new(name: String) -> Self {
|
||||
Player {
|
||||
name,
|
||||
money: 100000,
|
||||
playing: true,
|
||||
horse_no: 0,
|
||||
bet: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Players {
|
||||
players: Vec<Player>,
|
||||
}
|
||||
|
||||
impl Players {
|
||||
pub fn new() -> Self {
|
||||
let players;
|
||||
|
||||
loop {
|
||||
if let PromptResult::Numeric(n) = util::prompt(Some(true), "HOW MANY WANT TO BET?") {
|
||||
if n <= 0 {
|
||||
println!("THERE CAN'T BE (LESS THAN) ZERO PLAYERS!");
|
||||
} else {
|
||||
println!("WHEN ? APPEARS, TYPE NAME");
|
||||
players = Players::generate_players(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Players { players }
|
||||
}
|
||||
|
||||
pub fn make_bets(&mut self) {
|
||||
println!("PLACE YOUR BETS...HORSE # THEN AMOUNT");
|
||||
|
||||
for p in self.players.iter_mut() {
|
||||
if !p.playing {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = format!("{}?", p.name);
|
||||
|
||||
'prompt: loop {
|
||||
if let PromptResult::Normal(response) = util::prompt(None, name.as_str()) {
|
||||
let response: Vec<&str> = response.trim().split(",").collect();
|
||||
|
||||
for (i, n) in response.iter().enumerate() {
|
||||
if let Ok(n) = n.parse::<i32>() {
|
||||
if n.is_negative() {
|
||||
println!("YOU CAN'T ENTER A NEGATIVE NUMBER!")
|
||||
} else {
|
||||
match i {
|
||||
0 => {
|
||||
if n > 8 {
|
||||
println!("INVALID HORSE #")
|
||||
} else {
|
||||
p.horse_no = n as u8;
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
if n == 0 {
|
||||
println!("YOU CAN'T BET NOTHING!");
|
||||
} else if n > p.money as i32 {
|
||||
println!("YOU DON'T HAVE ENOUGH MONEY!")
|
||||
} else {
|
||||
p.bet = n as u32;
|
||||
break 'prompt;
|
||||
}
|
||||
}
|
||||
_ => println!("YOU CAN'T ENTER MORE THAN 2 NUMBERS!"),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("ONLY ENTER NUMBERS PLEASE!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_players(n: i32) -> Vec<Player> {
|
||||
let mut players: Vec<Player> = Vec::new();
|
||||
|
||||
for _ in 0..n {
|
||||
loop {
|
||||
if let PromptResult::Normal(name) = util::prompt(None, "?") {
|
||||
let name = name.trim().to_uppercase();
|
||||
|
||||
if name.is_empty() {
|
||||
println!("NAME CAN'T BE EMPTY!");
|
||||
} else if let Some(_) = players.iter().find(|p| p.name == name) {
|
||||
println!("THERE IS ALREADY A PLAYER WITH THAT NAME!");
|
||||
} else {
|
||||
players.push(Player::new(name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
players
|
||||
}
|
||||
|
||||
pub fn process_winner(&mut self, no: u8) {
|
||||
println!();
|
||||
for p in self.players.iter_mut() {
|
||||
if !p.playing {
|
||||
continue;
|
||||
}
|
||||
|
||||
if p.horse_no == no {
|
||||
p.money += p.bet;
|
||||
println!("{} WON ${}! THEY HAVE ${}.", p.name, p.bet, p.money);
|
||||
} else {
|
||||
p.money -= p.bet;
|
||||
println!("{} LOST ${}. THEY HAVE ${} LEFT.", p.name, p.bet, p.money);
|
||||
}
|
||||
p.bet = 0;
|
||||
p.horse_no = 0;
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn prompt_next_round(&mut self) -> bool {
|
||||
for p in self.players.iter_mut() {
|
||||
let msg = format!("{}, DO YOU WANT TO BET ON THE NEXT RACE?", p.name);
|
||||
if let PromptResult::YesNo(yes) = util::prompt(Some(false), msg.as_str()) {
|
||||
p.playing = yes;
|
||||
}
|
||||
}
|
||||
|
||||
if let None = self.players.iter().find(|p| p.playing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
40
50_Horserace/rust/src/util.rs
Normal file
40
50_Horserace/rust/src/util.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use std::io;
|
||||
|
||||
pub enum PromptResult {
|
||||
Normal(String),
|
||||
YesNo(bool),
|
||||
Numeric(i32),
|
||||
}
|
||||
|
||||
pub fn prompt(is_numeric: Option<bool>, msg: &str) -> PromptResult {
|
||||
use PromptResult::*;
|
||||
|
||||
println!("{msg}");
|
||||
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
|
||||
io::stdin()
|
||||
.read_line(&mut input)
|
||||
.expect("Failed to read input.");
|
||||
|
||||
if let Some(is_numeric) = is_numeric {
|
||||
let input = input.trim();
|
||||
|
||||
if is_numeric {
|
||||
if let Ok(n) = input.parse::<i32>() {
|
||||
return Numeric(n);
|
||||
}
|
||||
println!("PLEASE ENTER A VALID NUMBER!");
|
||||
} else {
|
||||
match input.to_uppercase().as_str() {
|
||||
"YES" | "Y" => return YesNo(true),
|
||||
"NO" | "N" => return YesNo(false),
|
||||
_ => println!("PLEASE ENTER (Y)ES OR (N)O."),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Normal(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user