mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Merge branch 'coding-horror:main' into main
This commit is contained in:
9
10_Blackjack/rust/Cargo.toml
Normal file
9
10_Blackjack/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"
|
||||
3
10_Blackjack/rust/README.md
Normal file
3
10_Blackjack/rust/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
|
||||
634
10_Blackjack/rust/src/main.rs
Normal file
634
10_Blackjack/rust/src/main.rs
Normal file
@@ -0,0 +1,634 @@
|
||||
use rand::{prelude::{thread_rng, SliceRandom}};
|
||||
use std::{io, io::{stdout, Write}};
|
||||
|
||||
/**
|
||||
* todo list:
|
||||
*
|
||||
* allow splitting
|
||||
*/
|
||||
|
||||
|
||||
//DATA
|
||||
//enums
|
||||
enum PlayerType {
|
||||
Player,
|
||||
Dealer,
|
||||
}
|
||||
impl ToString for PlayerType {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
&PlayerType::Dealer => return String::from("Dealer"),
|
||||
&PlayerType::Player => return String::from("Player"),
|
||||
}
|
||||
}
|
||||
}
|
||||
enum Play {
|
||||
Stand,
|
||||
Hit,
|
||||
DoubleDown,
|
||||
Split,
|
||||
}
|
||||
impl ToString for Play {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
&Play::Hit => return String::from("Hit"),
|
||||
&Play::Stand => return String::from("Stand"),
|
||||
&Play::DoubleDown => return String::from("Double Down"),
|
||||
&Play::Split => return String::from("Split")
|
||||
}
|
||||
}
|
||||
}
|
||||
//structs
|
||||
struct CARD<'a> {
|
||||
name: &'a str,
|
||||
value: u8,
|
||||
}
|
||||
impl<'a> CARD<'a> {
|
||||
/**
|
||||
* creates a new card from the passed card name
|
||||
*/
|
||||
fn new(card_name: &str) -> CARD {
|
||||
return CARD { name: card_name, value: CARD::determine_value_from_name(card_name) };
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the value associated with a card with the passed name
|
||||
* return 0 if the passed card name doesn't exist
|
||||
*/
|
||||
fn determine_value_from_name(card_name: &str) -> u8 {
|
||||
//DATA
|
||||
let value:u8;
|
||||
|
||||
match card_name.to_ascii_uppercase().as_str() {
|
||||
"ACE" => value = 11,
|
||||
"2" => value = 2,
|
||||
"3" => value = 3,
|
||||
"4" => value = 4,
|
||||
"5" => value = 5,
|
||||
"6" => value = 6,
|
||||
"7" => value = 7,
|
||||
"8" => value = 8,
|
||||
"9" => value = 9,
|
||||
"10" => value = 10,
|
||||
"JACK" => value = 10,
|
||||
"QUEEN" => value = 10,
|
||||
"KING" => value = 10,
|
||||
_ => value = 0,
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
struct HAND<'a> {
|
||||
cards: Vec<CARD<'a>>,
|
||||
}
|
||||
impl<'a> HAND<'a> {
|
||||
/**
|
||||
* returns a new empty hand
|
||||
*/
|
||||
fn new() -> HAND<'a> {
|
||||
return HAND { cards: Vec::new()};
|
||||
}
|
||||
|
||||
/**
|
||||
* add a passed card to this hand
|
||||
*/
|
||||
fn add_card(&mut self, card: CARD<'a>) {
|
||||
self.cards.push(card);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the total points of the cards in this hand
|
||||
*/
|
||||
fn get_total(&self) -> usize {
|
||||
let mut total:usize = 0;
|
||||
for card in &self.cards {
|
||||
total += card.value as usize;
|
||||
}
|
||||
|
||||
//if there is an ACE, and the hand would otherwise bust, treat the ace like it's worth 1
|
||||
if total > 21 && self.cards.iter().any(|c| -> bool {*c.name == *"ACE"}) {
|
||||
total -= 10;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* adds the cards in hand into the discard pile
|
||||
*/
|
||||
fn discard_hand(&mut self, deck: &mut DECKS<'a>) {
|
||||
let len = self.cards.len();
|
||||
for _i in 0..len {
|
||||
deck.discard_pile.push(self.cards.pop().expect("hand empty"));
|
||||
}
|
||||
}
|
||||
}
|
||||
struct DECKS<'a> {
|
||||
deck: Vec<CARD<'a>>, //cards in the deck
|
||||
discard_pile: Vec<CARD<'a>> //decks discard pile
|
||||
}
|
||||
impl<'a> DECKS<'a> {
|
||||
/**
|
||||
* creates a new full and shuffled deck, and an empty discard pile
|
||||
*/
|
||||
fn new() -> DECKS<'a> {
|
||||
//returns a number of full decks of 52 cards, shuffles them
|
||||
//DATA
|
||||
let mut deck = DECKS{deck: Vec::new(), discard_pile: Vec::new()};
|
||||
let number_of_decks = 3;
|
||||
|
||||
//fill deck
|
||||
for _n in 0..number_of_decks { //fill deck with number_of_decks decks worth of cards
|
||||
for card_name in CARD_NAMES { //add 4 of each card, totaling one deck with 4 of each card
|
||||
deck.deck.push( CARD::new(card_name) );
|
||||
deck.deck.push( CARD::new(card_name) );
|
||||
deck.deck.push( CARD::new(card_name) );
|
||||
deck.deck.push( CARD::new(card_name) );
|
||||
}
|
||||
}
|
||||
|
||||
//shuffle deck
|
||||
deck.shuffle();
|
||||
|
||||
//return deck
|
||||
return deck;
|
||||
}
|
||||
|
||||
/**
|
||||
* shuffles the deck
|
||||
*/
|
||||
fn shuffle(&mut self) {
|
||||
self.deck.shuffle(&mut thread_rng());
|
||||
}
|
||||
|
||||
/**
|
||||
* draw card from deck, and return it
|
||||
* if deck is empty, shuffles discard pile into it and tries again
|
||||
*/
|
||||
fn draw_card(&mut self) -> CARD<'a> {
|
||||
match self.deck.pop() {
|
||||
Some(card) => return card,
|
||||
None => {
|
||||
let len = self.discard_pile.len();
|
||||
|
||||
if len > 0 {//deck is empty, shuffle discard pile into deck and try again
|
||||
println!("deck is empty, shuffling");
|
||||
for _i in 0..len {
|
||||
self.deck.push( self.discard_pile.pop().expect("discard pile empty") )
|
||||
}
|
||||
self.shuffle();
|
||||
return self.draw_card();
|
||||
} else { //discard pile and deck are empty, should never happen
|
||||
panic!("discard pile empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PLAYER<'a> {
|
||||
hand: HAND<'a>,
|
||||
balance: usize,
|
||||
bet: usize,
|
||||
wins: usize,
|
||||
player_type: PlayerType,
|
||||
index: usize,
|
||||
|
||||
}
|
||||
impl<'a> PLAYER<'a> {
|
||||
/**
|
||||
* creates a new player of the given type
|
||||
*/
|
||||
fn new(player_type: PlayerType, index: usize) -> PLAYER<'a> {
|
||||
return PLAYER { hand: HAND::new(), balance: STARTING_BALANCE, bet: 0, wins: 0, player_type: player_type, index: index};
|
||||
}
|
||||
|
||||
fn get_name(&self) -> String {
|
||||
format!("{}{}", self.player_type.to_string(),self.index)
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a bet from the player
|
||||
*/
|
||||
fn get_bet(&mut self) {
|
||||
if let PlayerType::Player = self.player_type {
|
||||
if self.balance < 1 {
|
||||
println!("{} is out of money :(", self.get_name());
|
||||
self.bet = 0;
|
||||
}
|
||||
self.bet = get_number_from_user_input(format!("{}\tBet?",self.get_name()).as_str(), 1, self.balance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a string of the players hand
|
||||
*
|
||||
* if player is a dealer, returns the first card in the hand followed by *'s for every other card
|
||||
* if player is a player, returns every card and the total
|
||||
*/
|
||||
fn hand_as_string(&self, hide_dealer:bool) -> String {
|
||||
if !hide_dealer {
|
||||
return format!(
|
||||
"{}\n\ttotal points = {}", //message
|
||||
{ //cards in hand
|
||||
let mut s:String = String::new();
|
||||
for cards_in_hand in self.hand.cards.iter().rev() {
|
||||
s += format!("{}\t", cards_in_hand.name).as_str();
|
||||
}
|
||||
s
|
||||
},
|
||||
self.hand.get_total() //total points in hand
|
||||
);
|
||||
}
|
||||
else {
|
||||
match &self.player_type {
|
||||
&PlayerType::Dealer => { //if this is a dealer
|
||||
return format!(
|
||||
"{}*",//message
|
||||
{ //*'s for other cards
|
||||
let mut s:String = String::new();
|
||||
let mut cards_in_hand = self.hand.cards.iter();
|
||||
cards_in_hand.next();//consume first card drawn
|
||||
for c in cards_in_hand.rev() {
|
||||
s += format!("{}\t", c.name).as_str();
|
||||
}
|
||||
s
|
||||
}
|
||||
);
|
||||
},
|
||||
&PlayerType::Player => { //if this is a player
|
||||
return format!(
|
||||
"{}\n\ttotal points = {}", //message
|
||||
{ //cards in hand
|
||||
let mut s:String = String::new();
|
||||
for cards_in_hand in self.hand.cards.iter().rev() {
|
||||
s += format!("{}\t", cards_in_hand.name).as_str();
|
||||
}
|
||||
s
|
||||
},
|
||||
self.hand.get_total() //total points in hand
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the players 'play'
|
||||
*/
|
||||
fn get_play(&self) -> Play {
|
||||
/*
|
||||
do different things depending on what type of player this is:
|
||||
if it's a dealer, use an algorithm to determine the play
|
||||
if it's a player, ask user for input
|
||||
*/
|
||||
match &self.player_type {
|
||||
&PlayerType::Dealer => {
|
||||
if self.hand.get_total() > 16 { // if total value of hand is greater than 16, stand
|
||||
return Play::Stand;
|
||||
} else { //otherwise hit
|
||||
return Play::Hit;
|
||||
}
|
||||
},
|
||||
&PlayerType::Player => {
|
||||
let valid_results:Vec<char>;
|
||||
if self.hand.cards.len() > 2 {//if there are more than 2 cards in the hand, at least one turn has happened, so splitting and doubling down are not allowed
|
||||
valid_results = vec!['s','S','h','H'];
|
||||
} else {
|
||||
valid_results = vec!['s','S','h','H','d','D','/'];
|
||||
}
|
||||
let play = get_char_from_user_input("\tWhat is your play?", &valid_results);
|
||||
match play {
|
||||
's' | 'S' => return Play::Stand,
|
||||
'h' | 'H' => return Play::Hit,
|
||||
'd' | 'D' => return Play::DoubleDown,
|
||||
'/' => return Play::Split,
|
||||
_ => panic!("get_char_from_user_input() returned invalid character"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GAME<'a> {
|
||||
players: Vec<PLAYER<'a>>, //last item in this is the dealer
|
||||
decks: DECKS<'a>,
|
||||
games_played:usize,
|
||||
}
|
||||
impl<'a> GAME<'a> {
|
||||
/**
|
||||
* creates a new game
|
||||
*/
|
||||
fn new(num_players:usize) -> GAME<'a> {
|
||||
//DATA
|
||||
let mut players: Vec<PLAYER> = Vec::new();
|
||||
|
||||
//add dealer
|
||||
players.push(PLAYER::new(PlayerType::Dealer,0));
|
||||
//create human player(s) (at least one)
|
||||
players.push(PLAYER::new(PlayerType::Player,1));
|
||||
for i in 2..=num_players { //one less than num_players players
|
||||
players.push(PLAYER::new(PlayerType::Player,i));
|
||||
}
|
||||
|
||||
//ask if they want instructions
|
||||
if let 'y'|'Y' = get_char_from_user_input("Do you want instructions? (y/n)", &vec!['y','Y','n','N']) {
|
||||
instructions();
|
||||
}
|
||||
println!();
|
||||
|
||||
//return a game
|
||||
return GAME { players: players, decks: DECKS::new(), games_played: 0}
|
||||
}
|
||||
|
||||
/**
|
||||
* prints the score of every player
|
||||
*/
|
||||
fn _print_stats(&self) {
|
||||
println!("{}", self.stats_as_string());
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a string of the wins, balance, and bets of every player
|
||||
*/
|
||||
fn stats_as_string(&self) -> String {
|
||||
format!("Scores:\n{}",{
|
||||
let mut s = String::new();
|
||||
self.players.iter().for_each(|p| {
|
||||
//format the presentation of player stats
|
||||
match p.player_type {
|
||||
PlayerType::Player => s+= format!("{} Wins:\t{}\t\tBalance:\t{}\t\tBet\t{}\n",p.get_name(),p.wins,p.balance,p.bet).as_str(),
|
||||
PlayerType::Dealer => s+= format!("{} Wins:\t{}\n",p.get_name(),p.wins).as_str()
|
||||
}
|
||||
});
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* plays a round of blackjack
|
||||
*/
|
||||
fn play_game(&mut self) {
|
||||
//DATA
|
||||
let scores;
|
||||
let game = self.games_played; //save it here so we don't have borrowing issues
|
||||
let mut player_hands_message: String = String::new();//cache it here so we don't have borrowing issues
|
||||
|
||||
//deal cards to each player
|
||||
for _i in 0..2 { // do this twice
|
||||
//draw card for each player
|
||||
self.players.iter_mut().for_each(|player| {player.hand.add_card( self.decks.draw_card() );});
|
||||
}
|
||||
|
||||
//get everyones bets
|
||||
self.players.iter_mut().for_each(|player| player.get_bet());
|
||||
scores = self.stats_as_string(); //save it here so we don't have borrowing issues later
|
||||
|
||||
//play game for each player
|
||||
for player in self.players.iter_mut() {
|
||||
//turn loop, ends when player finishes their turn
|
||||
loop{
|
||||
//clear screen
|
||||
clear();
|
||||
//print welcome
|
||||
welcome();
|
||||
//print game state
|
||||
println!("\n\t\t\tGame {}", game);
|
||||
//print scores
|
||||
println!("{}",scores);
|
||||
//print hands of all players
|
||||
print!("{}", player_hands_message);
|
||||
println!("{} Hand:\t{}", player.get_name(), player.hand_as_string(true));
|
||||
|
||||
if let PlayerType::Player = player.player_type { //player isn't the dealer
|
||||
if player.bet == 0 {//player is out of money
|
||||
break;//exit turn loop
|
||||
}
|
||||
}
|
||||
|
||||
//play through turn
|
||||
//check their hand value for a blackjack(21) or bust
|
||||
let score = player.hand.get_total();
|
||||
if score >= 21 {
|
||||
if score == 21 { // == 21
|
||||
println!("\tBlackjack! (21 points)");
|
||||
} else { // > 21
|
||||
println!("\tBust ({} points)", score);
|
||||
}
|
||||
break; //end turn
|
||||
}
|
||||
|
||||
//get player move
|
||||
let play = player.get_play();
|
||||
//process play
|
||||
match play {
|
||||
Play::Stand => {
|
||||
println!("\t{}", play.to_string());
|
||||
break; //end turn
|
||||
},
|
||||
Play::Hit => {
|
||||
println!("\t{}", play.to_string());
|
||||
//give them a card
|
||||
player.hand.add_card( self.decks.draw_card() );
|
||||
},
|
||||
Play::DoubleDown => {
|
||||
println!("\t{}", play.to_string());
|
||||
|
||||
//double their balance if there's enough money, othewise go all-in
|
||||
if player.bet * 2 < player.balance {
|
||||
player.bet *= 2;
|
||||
}
|
||||
else {
|
||||
player.bet = player.balance;
|
||||
}
|
||||
//give them a card
|
||||
player.hand.add_card( self.decks.draw_card() );
|
||||
},
|
||||
Play::Split => {
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//add player to score cache thing
|
||||
player_hands_message += format!("{} Hand:\t{}\n", player.get_name(), player.hand_as_string(true)).as_str();
|
||||
}
|
||||
|
||||
//determine winner
|
||||
let mut top_score = 0; //player with the highest points
|
||||
let mut num_winners = 1;
|
||||
for player in self.players.iter_mut().enumerate().filter( |x| -> bool {x.1.hand.get_total()<=21}) { //players_who_didnt_bust
|
||||
let score = player.1.hand.get_total();
|
||||
if score > top_score {
|
||||
top_score = score;
|
||||
num_winners = 1;
|
||||
} else if score == top_score {
|
||||
num_winners += 1;
|
||||
}
|
||||
}
|
||||
|
||||
//print winner(s)
|
||||
self.players.iter_mut().filter(|x|->bool{x.hand.get_total()==top_score}).for_each(|x| {//for each player with the top score
|
||||
print!("{} ", x.get_name());//print name
|
||||
x.wins += 1;//increment their wins
|
||||
});
|
||||
if num_winners > 1 {println!("all tie with {}\n\n\n", top_score);}
|
||||
else {println!("wins with {}!\n\n\n",top_score);}
|
||||
|
||||
//handle bets
|
||||
//remove money from losers
|
||||
self.players.iter_mut().filter(|p| p.hand.get_total()!=top_score).for_each( |p| p.balance -= p.bet); //for every player who didn't get the winning score, remove their bet from their balance
|
||||
//add money to winner
|
||||
self.players.iter_mut().filter(|p| p.hand.get_total()==top_score).for_each(|p| p.balance += p.bet); //for each player who got the winning score, add their bet to their balance
|
||||
|
||||
//discard hands
|
||||
self.players.iter_mut().for_each(|player| {player.hand.discard_hand(&mut self.decks);});
|
||||
|
||||
//increment games_played
|
||||
self.games_played += 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const CARD_NAMES: [&str;13] = ["ACE","2","3","4","5","6","7","8","9","10","JACK","QUEEN","KING"];
|
||||
const STARTING_BALANCE: usize = 100;
|
||||
|
||||
fn main() {
|
||||
//DATA
|
||||
let mut game: GAME;
|
||||
|
||||
//print welcome message
|
||||
welcome();
|
||||
|
||||
//create game
|
||||
game = GAME::new( get_number_from_user_input("How many players should there be (at least 1)?", 1, 7) );
|
||||
|
||||
//game loop, play game until user wants to stop
|
||||
loop {
|
||||
//play round
|
||||
game.play_game();
|
||||
|
||||
//ask if they want to play again
|
||||
match get_char_from_user_input("Play Again? (y/n)", &vec!['y','Y','n','N']) {
|
||||
'y' | 'Y' => continue,
|
||||
'n' | 'N' => break,
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* prints the welcome screen
|
||||
*/
|
||||
fn welcome() {
|
||||
//welcome message
|
||||
print!("
|
||||
BLACK JACK
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* prints the instructions
|
||||
*/
|
||||
fn instructions() {
|
||||
println!("
|
||||
THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE
|
||||
GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE
|
||||
PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE
|
||||
DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE
|
||||
FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE
|
||||
PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS
|
||||
STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/',
|
||||
INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE
|
||||
INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR
|
||||
'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING
|
||||
DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR
|
||||
BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'.
|
||||
NUMBER OF PLAYERS
|
||||
|
||||
NOTE:'/' (splitting) is not currently implemented, and does nothing
|
||||
|
||||
PRESS ENTER TO CONTINUE
|
||||
");
|
||||
io::stdin().read_line(&mut String::new()).expect("Failed to read line");
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a usize integer from user input
|
||||
*/
|
||||
fn get_number_from_user_input(prompt: &str, min:usize, max:usize) -> usize {
|
||||
//input loop
|
||||
return loop {
|
||||
let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
|
||||
|
||||
//print prompt
|
||||
println!("{}", prompt);
|
||||
stdout().flush().expect("Failed to flush to stdout.");
|
||||
//read user input from standard input, and store it to raw_input
|
||||
//raw_input.clear(); //clear input
|
||||
io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
|
||||
|
||||
//from input, try to read a number
|
||||
match raw_input.trim().parse::<usize>() {
|
||||
Ok(i) => {
|
||||
if i < min || i > max { //input out of desired range
|
||||
println!("INPUT OUT OF VALID RANGE. TRY AGAIN. {}-{}",min,max);
|
||||
continue; // run the loop again
|
||||
}
|
||||
else {
|
||||
break i;// this escapes the loop, returning i
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("INVALID INPUT. TRY AGAIN. {}", e.to_string().to_uppercase());
|
||||
continue; // run the loop again
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a character from user input
|
||||
* returns the first character they type
|
||||
*/
|
||||
fn get_char_from_user_input(prompt: &str, valid_results: &Vec<char>) -> char {
|
||||
//input loop
|
||||
return loop {
|
||||
let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
|
||||
|
||||
//print prompt
|
||||
println!("{}", prompt);
|
||||
stdout().flush().expect("Failed to flush to stdout.");
|
||||
//read user input from standard input, and store it to raw_input
|
||||
//raw_input.clear(); //clear input
|
||||
io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
|
||||
|
||||
//from input, try to read a valid character
|
||||
match raw_input.trim().chars().nth(0) {
|
||||
Some(i) => {
|
||||
if !valid_results.contains(&i) { //input out of desired range
|
||||
println!("INPUT IS NOT VALID CHARACTER. TRY AGAIN.");
|
||||
continue; // run the loop again
|
||||
}
|
||||
else {
|
||||
break i;// this escapes the loop, returning i
|
||||
}
|
||||
},
|
||||
None => {
|
||||
println!("INVALID INPUT. TRY AGAIN.");
|
||||
continue; // run the loop again
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* clear std out
|
||||
*/
|
||||
fn clear() {
|
||||
println!("\x1b[2J\x1b[0;0H");
|
||||
}
|
||||
441
27_Civil_War/python/Civilwar.py
Normal file
441
27_Civil_War/python/Civilwar.py
Normal file
@@ -0,0 +1,441 @@
|
||||
import math
|
||||
import random
|
||||
|
||||
|
||||
def tab(n):
|
||||
return " "*n
|
||||
|
||||
|
||||
battles = [["JULY 21, 1861. GEN. BEAUREGARD, COMMANDING THE SOUTH, MET",
|
||||
"UNION FORCES WITH GEN. MCDOWELL IN A PREMATURE BATTLE AT",
|
||||
"BULL RUN. GEN. JACKSON HELPED PUSH BACK THE UNION ATTACK."],
|
||||
|
||||
["APRIL 6-7, 1862. THE CONFEDERATE SURPRISE ATTACK AT",
|
||||
"SHILOH FAILED DUE TO POOR ORGANIZATION."],
|
||||
|
||||
["JUNE 25-JULY 1, 1862. GENERAL LEE (CSA) UPHELD THE",
|
||||
"OFFENSIVE THROUGHOUT THE BATTLE AND FORCED GEN. MCCLELLAN",
|
||||
"AND THE UNION FORCES AWAY FROM RICHMOND."],
|
||||
|
||||
["AUG 29-30, 1862. THE COMBINED CONFEDERATE FORCES UNDER LEE",
|
||||
"AND JACKSON DROVE THE UNION FORCES BACK INTO WASHINGTON."],
|
||||
|
||||
["SEPT 17, 1862. THE SOUTH FAILED TO INCORPORATE MARYLAND",
|
||||
"INTO THE CONFEDERACY."],
|
||||
|
||||
["DEC 13, 1862. THE CONFEDERACY UNDER LEE SUCCESSFULLY",
|
||||
"REPULSED AN ATTACK BY THE UNION UNDER GEN. BURNSIDE."],
|
||||
|
||||
["DEC 31, 1862. THE SOUTH UNDER GEN. BRAGG WON A CLOSE BATTLE."],
|
||||
|
||||
["MAY 1-6, 1863. THE SOUTH HAD A COSTLY VICTORY AND LOST",
|
||||
"ONE OF THEIR OUTSTANDING GENERALS, 'STONEWALL' JACKSON."],
|
||||
|
||||
["JULY 4, 1863. VICKSBURG WAS A COSTLY DEFEAT FOR THE SOUTH",
|
||||
"BECAUSE IT GAVE THE UNION ACCESS TO THE MISSISSIPPI."],
|
||||
|
||||
["JULY 1-3, 1863. A SOUTHERN MISTAKE BY GEN. LEE AT GETTYSBURG",
|
||||
"COST THEM ONE OF THE MOST CRUCIAL BATTLES OF THE WAR."],
|
||||
|
||||
["SEPT. 15, 1863. CONFUSION IN A FOREST NEAR CHICKAMAUGA LED",
|
||||
"TO A COSTLY SOUTHERN VICTORY."],
|
||||
|
||||
["NOV. 25, 1863. AFTER THE SOUTH HAD SIEGED GEN. ROSENCRANS'",
|
||||
"ARMY FOR THREE MONTHS, GEN. GRANT BROKE THE SIEGE."],
|
||||
|
||||
["MAY 5, 1864. GRANT'S PLAN TO KEEP LEE ISOLATED BEGAN TO",
|
||||
"FAIL HERE, AND CONTINUED AT COLD HARBOR AND PETERSBURG."],
|
||||
|
||||
["AUGUST, 1864. SHERMAN AND THREE VETERAN ARMIES CONVERGED",
|
||||
"ON ATLANTA AND DEALT THE DEATH BLOW TO THE CONFEDERACY."]]
|
||||
|
||||
historical_data = [
|
||||
[],
|
||||
["BULL RUN", 18000, 18500, 1967, 2708, 1],
|
||||
["SHILOH", 40000., 44894., 10699, 13047, 3],
|
||||
["SEVEN DAYS", 95000., 115000., 20614, 15849, 3],
|
||||
["SECOND BULL RUN", 54000., 63000., 10000, 14000, 2],
|
||||
["ANTIETAM", 40000., 50000., 10000, 12000, 3],
|
||||
["FREDERICKSBURG", 75000., 120000., 5377, 12653, 1],
|
||||
["MURFREESBORO", 38000., 45000., 11000, 12000, 1],
|
||||
["CHANCELLORSVILLE", 32000, 90000., 13000, 17197, 2],
|
||||
["VICKSBURG", 50000., 70000., 12000, 19000, 1],
|
||||
["GETTYSBURG", 72500., 85000., 20000, 23000, 3],
|
||||
["CHICKAMAUGA", 66000., 60000., 18000, 16000, 2],
|
||||
["CHATTANOOGA", 37000., 60000., 36700., 5800, 2],
|
||||
["SPOTSYLVANIA", 62000., 110000., 17723, 18000, 2],
|
||||
["ATLANTA", 65000., 100000., 8500, 3700, 1]]
|
||||
sa = {}
|
||||
da = {}
|
||||
fa = {}
|
||||
ha = {}
|
||||
ba = {}
|
||||
oa = {}
|
||||
print(tab(26) + "CIVIL WAR")
|
||||
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
# Original game design: Cram, Goodie, Hibbard Lexington H.S.
|
||||
# Modifications: G. Paul, R. Hess (Ties), 1973
|
||||
# Union info on likely confederate strategy
|
||||
sa[1] = 25
|
||||
sa[2] = 25
|
||||
sa[3] = 25
|
||||
sa[4] = 25
|
||||
d = -1 # number of players in the game
|
||||
print()
|
||||
while True:
|
||||
X = input("DO YOU WANT INSTRUCTIONS? ")
|
||||
if (X == "YES" or X == "NO"):
|
||||
break
|
||||
print("YES OR NO -- ")
|
||||
|
||||
if X == "YES":
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print("THIS IS A CIVIL WAR SIMULATION.")
|
||||
print("TO PLAY TYPE A RESPONSE WHEN THE COMPUTER ASKS.")
|
||||
print("REMEMBER THAT ALL FACTORS ARE INTERRELATED AND THAT YOUR")
|
||||
print("RESPONSES COULD CHANGE HISTORY. FACTS AND FIGURES USED ARE")
|
||||
print("BASED ON THE ACTUAL OCCURRENCE. MOST BATTLES TEND TO RESULT")
|
||||
print("AS THEY DID IN THE CIVIL WAR, BUT IT ALL DEPENDS ON YOU!!")
|
||||
print()
|
||||
print("THE OBJECT OF THE GAME IS TO WIN AS MANY BATTLES AS ")
|
||||
print("POSSIBLE.")
|
||||
print()
|
||||
print("YOUR CHOICES FOR DEFENSIVE STRATEGY ARE:")
|
||||
print(" (1) ARTILLERY ATTACK")
|
||||
print(" (2) FORTIFICATION AGAINST FRONTAL ATTACK")
|
||||
print(" (3) FORTIFICATION AGAINST FLANKING MANEUVERS")
|
||||
print(" (4) FALLING BACK")
|
||||
print(" YOUR CHOICES FOR OFFENSIVE STRATEGY ARE:")
|
||||
print(" (1) ARTILLERY ATTACK")
|
||||
print(" (2) FRONTAL ATTACK")
|
||||
print(" (3) FLANKING MANEUVERS")
|
||||
print(" (4) ENCIRCLEMENT")
|
||||
print("YOU MAY SURRENDER BY TYPING A '5' FOR YOUR STRATEGY.")
|
||||
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print("ARE THERE TWO GENERALS PRESENT ", end="")
|
||||
while True:
|
||||
bs = input("(ANSWER YES OR NO) ")
|
||||
if bs == "YES":
|
||||
d = 2
|
||||
break
|
||||
elif bs == "NO":
|
||||
d = 1
|
||||
print()
|
||||
print("YOU ARE THE CONFEDERACY. GOOD LUCK!")
|
||||
print()
|
||||
break
|
||||
|
||||
print("SELECT A BATTLE BY TYPING A NUMBER FROM 1 TO 14 ON")
|
||||
print("REQUEST. TYPE ANY OTHER NUMBER TO END THE SIMULATION.")
|
||||
print("BUT '0' BRINGS BACK EXACT PREVIOUS BATTLE SITUATION")
|
||||
print("ALLOWING YOU TO REPLAY IT")
|
||||
print()
|
||||
print("NOTE: A NEGATIVE FOOD$ ENTRY CAUSES THE PROGRAM TO ")
|
||||
print("USE THE ENTRIES FROM THE PREVIOUS BATTLE")
|
||||
print()
|
||||
print("AFTER REQUESTING A BATTLE, DO YOU WISH ", end="")
|
||||
print("BATTLE DESCRIPTIONS ", end="")
|
||||
while True:
|
||||
xs = input("(ANSWER YES OR NO) ")
|
||||
if xs == "YES" or xs == "NO":
|
||||
break
|
||||
l = 0
|
||||
w = 0
|
||||
r1 = 0
|
||||
q1 = 0
|
||||
m3 = 0
|
||||
m4 = 0
|
||||
p1 = 0
|
||||
p2 = 0
|
||||
t1 = 0
|
||||
t2 = 0
|
||||
for i in range(1, 3):
|
||||
da[i] = 0
|
||||
fa[i] = 0
|
||||
ha[i] = 0
|
||||
ba[i] = 0
|
||||
oa[i] = 0
|
||||
r2 = 0
|
||||
q2 = 0
|
||||
c6 = 0
|
||||
f = 0
|
||||
w0 = 0
|
||||
y = 0
|
||||
y2 = 0
|
||||
u = 0
|
||||
u2 = 0
|
||||
while True:
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
a = int(input("WHICH BATTLE DO YOU WISH TO SIMULATE? "))
|
||||
if a < 1 or a > 14:
|
||||
break
|
||||
if a != 0 or r == 0:
|
||||
cs = historical_data[a][0]
|
||||
m1 = historical_data[a][1]
|
||||
m2 = historical_data[a][2]
|
||||
c1 = historical_data[a][3]
|
||||
c2 = historical_data[a][4]
|
||||
m = historical_data[a][5]
|
||||
u = 0
|
||||
# Inflation calc
|
||||
i1 = 10 + (l - w) * 2
|
||||
i2 = 10 + (w - l) * 2
|
||||
# Money available
|
||||
da[1] = 100 * math.floor((m1 * (100 - i1) / 2000)
|
||||
* (1 + (r1 - q1) / (r1 + 1)) + 0.5)
|
||||
da[2] = 100 * math.floor(m2 * (100 - i2) / 2000 + 0.5)
|
||||
if bs == "YES":
|
||||
da[2] = 100 * math.floor((m2 * (100 - i2) / 2000)
|
||||
* (1 + (r2 - q2) / (r2 + 1)) + 0.5)
|
||||
# Men available
|
||||
m5 = math.floor(m1 * (1 + (p1 - t1) / (m3 + 1)))
|
||||
m6 = math.floor(m2 * (1 + (p2 - t2) / (m4 + 1)))
|
||||
f1 = 5 * m1 / 6
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print(f"THIS IS THE BATTLE OF {cs}")
|
||||
if xs != "NO":
|
||||
print("\n".join(battles[a-1]))
|
||||
|
||||
else:
|
||||
print(cs + " INSTANT REPLAY")
|
||||
|
||||
print()
|
||||
print(" \tCONFEDERACY\t UNION")
|
||||
print(f"MEN\t {m5}\t\t {m6}")
|
||||
print(f"MONEY\t ${da[1]}\t\t${da[2]}")
|
||||
print(f"INFLATION\t {i1 + 15}%\t {i2}%")
|
||||
print()
|
||||
# ONLY IN PRINTOUT IS CONFED INFLATION = I1 + 15 %
|
||||
# IF TWO GENERALS, INPUT CONFED, FIRST
|
||||
for i in range(1, d+1):
|
||||
if bs == "YES" and i == 1:
|
||||
print("CONFEDERATE GENERAL---", end="")
|
||||
print("HOW MUCH DO YOU WISH TO SPEND FOR")
|
||||
while True:
|
||||
f = int(input(" - FOOD...... ? "))
|
||||
if f < 0:
|
||||
if r1 == 0:
|
||||
print("NO PREVIOUS ENTRIES")
|
||||
continue
|
||||
print("ASSUME YOU WANT TO KEEP SAME ALLOCATIONS")
|
||||
print()
|
||||
break
|
||||
fa[i] = f
|
||||
while True:
|
||||
ha[i] = int(input(" - SALARIES.. ? "))
|
||||
if ha[i] >= 0:
|
||||
break
|
||||
print("NEGATIVE VALUES NOT ALLOWED.")
|
||||
while True:
|
||||
ba[i] = int(input(" - AMMUNITION ? "))
|
||||
if ba[i] >= 0:
|
||||
break
|
||||
print("NEGATIVE VALUES NOT ALLOWED.")
|
||||
print()
|
||||
if fa[i] + ha[i] + ba[i] > da[i]:
|
||||
print("THINK AGAIN! YOU HAVE ONLY $" + da[i])
|
||||
else:
|
||||
break
|
||||
|
||||
if bs != "YES" or i == 2:
|
||||
break
|
||||
print("UNION GENERAL---", end="")
|
||||
|
||||
for z in range(1, d+1):
|
||||
if bs == "YES":
|
||||
if z == 1:
|
||||
print("CONFEDERATE ", end="")
|
||||
else:
|
||||
print(" UNION ", end="")
|
||||
# Find morale
|
||||
o = ((2 * math.pow(fa[z], 2) +
|
||||
math.pow(ha[z], 2)) / math.pow(f1, 2) + 1)
|
||||
if o >= 10:
|
||||
print("MORALE IS HIGH")
|
||||
elif o >= 5:
|
||||
print("MORALE IS FAIR")
|
||||
else:
|
||||
print("MORALE IS POOR")
|
||||
if bs != "YES":
|
||||
break
|
||||
oa[z] = o
|
||||
|
||||
o2 = oa[2]
|
||||
o = oa[1]
|
||||
print("CONFEDERATE GENERAL---")
|
||||
# Actual off/def battle situation
|
||||
if m == 3:
|
||||
print("YOU ARE ON THE OFFENSIVE")
|
||||
elif m == 1:
|
||||
print("YOU ARE ON THE DEFENSIVE")
|
||||
else:
|
||||
print("BOTH SIDES ARE ON THE OFFENSIVE")
|
||||
|
||||
print()
|
||||
# Choose strategies
|
||||
if bs != "YES":
|
||||
while True:
|
||||
y = int(input("YOUR STRATEGY "))
|
||||
if (abs(y - 3) < 3):
|
||||
break
|
||||
print(f"STRATEGY {y} NOT ALLOWED.")
|
||||
if y == 5:
|
||||
print("THE CONFEDERACY HAS SURRENDERED.")
|
||||
break
|
||||
# Union strategy is computer chosen
|
||||
if a == 0:
|
||||
while True:
|
||||
y2 = int(input("UNION STRATEGY IS "))
|
||||
if y2 > 0 and y2 < 5:
|
||||
break
|
||||
print("ENTER 1, 2, 3, OR 4 (USUALLY PREVIOUS UNION STRATEGY)")
|
||||
else:
|
||||
s0 = 0
|
||||
r = math.random() * 100
|
||||
for i in range(1, 5):
|
||||
s0 += sa[i]
|
||||
# If actual strategy info is in program data statements
|
||||
# then r-100 is extra weight given to that strategy.
|
||||
if r < s0:
|
||||
break
|
||||
y2 = i
|
||||
print(y2)
|
||||
else:
|
||||
for i in range(1, 3):
|
||||
if (i == 1):
|
||||
print("CONFEDERATE STRATEGY ? ", end="")
|
||||
while True:
|
||||
y = int(input())
|
||||
if abs(y - 3) < 3:
|
||||
break
|
||||
print(f"STRATEGY {y} NOT ALLOWED.")
|
||||
print("YOUR STRATEGY ? ", end="")
|
||||
if (i == 2):
|
||||
y2 = y
|
||||
y = y1
|
||||
if (y2 != 5):
|
||||
break
|
||||
else:
|
||||
y1 = y
|
||||
print("UNION STRATEGY ? ", end="")
|
||||
# Simulated losses - North
|
||||
c6 = (2 * c2 / 5) * (1 + 1 / (2 * (abs(y2 - y) + 1)))
|
||||
c6 = c6 * (1.28 + (5 * m2 / 6) / (ba[2] + 1))
|
||||
c6 = math.floor(c6 * (1 + 1 / o2) + 0.5)
|
||||
# If loss > men present, rescale losses
|
||||
e2 = 100 / o2
|
||||
if math.floor(c6 + e2) >= m6:
|
||||
c6 = math.floor(13 * m6 / 20)
|
||||
e2 = 7 * c6 / 13
|
||||
u2 = 1
|
||||
# Calculate simulated losses
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print("\t\tCONFEDERACY\tUNION")
|
||||
c5 = (2 * c1 / 5) * (1 + 1 / (2 * (abs(y2 - y) + 1)))
|
||||
c5 = math.floor(c5 * (1 + 1 / o) * (1.28 + f1 / (ba[1] + 1)) + 0.5)
|
||||
e = 100 / o
|
||||
if c5 + 100 / o >= m1 * (1 + (p1 - t1) / (m3 + 1)):
|
||||
c5 = math.floor(13 * m1 / 20 * (1 + (p1 - t1) / (m3 + 1)))
|
||||
e = 7 * c5 / 13
|
||||
u = 1
|
||||
|
||||
if (d == 1):
|
||||
c6 = math.floor(17 * c2 * c1 / (c5 * 20))
|
||||
e2 = 5 * o
|
||||
|
||||
print("CASUALTIES\t" + str(c5) + "\t\t" + str(c6))
|
||||
print("DESERTIONS\t" + str(math.floor(e)) + "\t\t" + str(math.floor(e2)))
|
||||
print()
|
||||
if (bs == "YES"):
|
||||
print("COMPARED TO THE ACTUAL CASUALTIES AT " + str(cs))
|
||||
print("CONFEDERATE: " + str(math.floor(100 *
|
||||
(c5 / c1) + 0.5)) + "% OF THE ORIGINAL")
|
||||
print("UNION: " + str(math.floor(100 *
|
||||
(c6 / c2) + 0.5)) + "% OF THE ORIGINAL")
|
||||
|
||||
print()
|
||||
# Find who won
|
||||
if (u == 1 and u2 == 1 or (u != 1 and u2 != 1 and c5 + e == c6 + e2)):
|
||||
print("BATTLE OUTCOME UNRESOLVED")
|
||||
w0 += 1
|
||||
elif (u == 1 or (u != 1 and u2 != 1 and c5 + e > c6 + e2)):
|
||||
print("THE UNION WINS " + str(cs))
|
||||
if a != 0:
|
||||
l += 1
|
||||
else:
|
||||
print("THE CONFEDERACY WINS " + str(cs))
|
||||
if (a != 0):
|
||||
w += 1
|
||||
|
||||
# Lines 2530 to 2590 from original are unreachable.
|
||||
if (a != 0):
|
||||
t1 += c5 + e
|
||||
t2 += c6 + e2
|
||||
p1 += c1
|
||||
p2 += c2
|
||||
q1 += fa[1] + ha[1] + ba[1]
|
||||
q2 += fa[2] + ha[2] + ba[2]
|
||||
r1 += m1 * (100 - i1) / 20
|
||||
r2 += m2 * (100 - i2) / 20
|
||||
m3 += m1
|
||||
m4 += m2
|
||||
# Learn present strategy, start forgetting old ones
|
||||
# present startegy of south gains 3*s, others lose s
|
||||
# probability points, unless a strategy falls below 5 % .
|
||||
s = 3
|
||||
s0 = 0
|
||||
for i in range(1, 5):
|
||||
if (sa[i] <= 5):
|
||||
continue
|
||||
sa[i] -= 5
|
||||
s0 += s
|
||||
sa[y] += s0
|
||||
|
||||
u = 0
|
||||
u2 = 0
|
||||
print("---------------")
|
||||
continue
|
||||
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print(f"THE CONFEDERACY HAS WON {w} BATTLES AND LOST {l}")
|
||||
if (y == 5 or (y2 != 5 and w <= l)):
|
||||
print("THE UNION HAS WON THE WAR")
|
||||
else:
|
||||
print("THE CONFEDERACY HAS WON THE WAR")
|
||||
print()
|
||||
if (r1 > 0):
|
||||
print(f"FOR THE {w + l + w0} BATTLES FOUGHT (EXCLUDING RERUNS)")
|
||||
print(" \t \t ")
|
||||
print("CONFEDERACY\t UNION")
|
||||
print(f"HISTORICAL LOSSES\t{math.floor(p1 + 0.5)}\t{math.floor(p2 + 0.5)}")
|
||||
print(f"SIMULATED LOSSES\t{math.floor(t1 + 0.5)}\t{math.floor(t2 + 0.5)}")
|
||||
print()
|
||||
print(
|
||||
f" % OF ORIGINAL\t{math.floor(100 * (t1 / p1) + 0.5)}\t{math.floor(100 * (t2 / p2) + 0.5)}")
|
||||
if (bs != "YES"):
|
||||
print()
|
||||
print("UNION INTELLIGENCE SUGGEST THAT THE SOUTH USED")
|
||||
print("STRATEGIES 1, 2, 3, 4 IN THE FOLLOWING PERCENTAGES")
|
||||
print(f"{sa[1]} {sa[2]} {sa[3]} {sa[4]}")
|
||||
12
59_Lunar_LEM_Rocket/rust/README.md
Normal file
12
59_Lunar_LEM_Rocket/rust/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
README.md
|
||||
|
||||
Original source downloaded from Vintage Basic
|
||||
|
||||
This folder for chapter #59 contains three different games. Three folders here contain the three games:
|
||||
|
||||
- Rocket
|
||||
- LEM
|
||||
- lunar
|
||||
|
||||
Conversion to [Rust](https://www.rust-lang.org)
|
||||
|
||||
10
59_Lunar_LEM_Rocket/rust/rocket/Cargo.toml
Normal file
10
59_Lunar_LEM_Rocket/rust/rocket/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "rocket"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
text_io = "0.1.10"
|
||||
num-integer = "0.1"
|
||||
107
59_Lunar_LEM_Rocket/rust/rocket/src/main.rs
Normal file
107
59_Lunar_LEM_Rocket/rust/rocket/src/main.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
//Goal of this port is to keep Basic lang idioms where possible. Gotta have those single letter capital variables!
|
||||
use num_integer::{sqrt};
|
||||
use std::io::{self, Write};
|
||||
use text_io::{read, try_read};
|
||||
#[allow(non_snake_case)]
|
||||
fn main() {
|
||||
println!("{:>30}", "ROCKET");
|
||||
println!("{:>15}", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
println!();println!();println!();
|
||||
println!("LUNAR LANDING SIMULATION");
|
||||
println!("----- ------- ----------");println!();
|
||||
let A= input("DO YOU WANT INSTRUCTIONS (YES OR NO) ");
|
||||
if !(A=="NO") {
|
||||
println!();
|
||||
println!("YOU ARE LANDING ON THE MOON AND AND HAVE TAKEN OVER MANUAL");
|
||||
println!("CONTROL 1000 FEET ABOVE A GOOD LANDING SPOT. YOU HAVE A DOWN-");
|
||||
println!("WARD VELOCITY OF 50 FEET/SEC. 150 UNITS OF FUEL REMAIN.");
|
||||
println!();
|
||||
println!("HERE ARE THE RULES THAT GOVERN YOUR APOLLO SPACE-CRAFT:"); println!();
|
||||
println!("(1) AFTER EACH SECOND THE HEIGHT, VELOCITY, AND REMAINING FUEL");
|
||||
println!(" WILL BE REPORTED VIA DIGBY YOUR ON-BOARD COMPUTER.");
|
||||
println!("(2) AFTER THE REPORT A '?' WILL APPEAR. ENTER THE NUMBER");
|
||||
println!(" OF UNITS OF FUEL YOU WISH TO BURN DURING THE NEXT");
|
||||
println!(" SECOND. EACH UNIT OF FUEL WILL SLOW YOUR DESCENT BY");
|
||||
println!(" 1 FOOT/SEC.");
|
||||
println!("(3) THE MAXIMUM THRUST OF YOUR ENGINE IS 30 FEET/SEC/SEC");
|
||||
println!(" OR 30 UNITS OF FUEL PER SECOND.");
|
||||
println!("(4) WHEN YOU CONTACT THE LUNAR SURFACE. YOUR DESCENT ENGINE");
|
||||
println!(" WILL AUTOMATICALLY SHUT DOWN AND YOU WILL BE GIVEN A");
|
||||
println!(" REPORT OF YOUR LANDING SPEED AND REMAINING FUEL.");
|
||||
println!("(5) IF YOU RUN OUT OF FUEL THE '?' WILL NO LONGER APPEAR");
|
||||
println!(" BUT YOUR SECOND BY SECOND REPORT WILL CONTINUE UNTIL");
|
||||
println!(" YOU CONTACT THE LUNAR SURFACE.");println!();
|
||||
}
|
||||
loop {
|
||||
println!("BEGINNING LANDING PROCEDURE..........");println!();
|
||||
println!("G O O D L U C K ! ! !");
|
||||
println!();println!();
|
||||
println!("SEC FEET SPEED FUEL PLOT OF DISTANCE");
|
||||
println!();
|
||||
let mut T=0;let mut H:i32=1000;let mut V=50;let mut F=150;
|
||||
let D:i32; let mut V1:i32; let mut B:i32;
|
||||
'falling: loop {
|
||||
println!(" {:<4}{:<11}{:<10}{:<8}I{capsule:>high$}", T,H,V,F,high=(H/15) as usize,capsule="*");
|
||||
B = input_int("");
|
||||
if B<0 { B=0 }
|
||||
else { if B>30 { B=30 } }
|
||||
if B>F { B=F }
|
||||
'nofuel: loop {
|
||||
V1=V-B+5;
|
||||
F=F-B;
|
||||
H=H- (V+V1)/2;
|
||||
if H<=0 { break 'falling}
|
||||
T=T+1;
|
||||
V=V1;
|
||||
if F>0 { break 'nofuel }
|
||||
if B!=0 {
|
||||
println!("**** OUT OF FUEL ****");
|
||||
}
|
||||
println!(" {:<4}{:<11}{:<10}{:<8}I{capsule:>high$}", T,H,V,F,high=(H/12+29) as usize,capsule="*");
|
||||
B=0;
|
||||
}
|
||||
}
|
||||
H=H+ (V1+V)/2;
|
||||
if B==5 {
|
||||
D=H/V;
|
||||
} else {
|
||||
D=(-V+sqrt(V*V+H*(10-2*B)))/(5-B);
|
||||
V1=V+(5-B)*D;
|
||||
}
|
||||
println!("***** CONTACT *****");
|
||||
println!("TOUCHDOWN AT {} SECONDS.",T+D);
|
||||
println!("LANDING VELOCITY={} FEET/SEC.",V1);
|
||||
println!("{} UNITS OF FUEL REMAINING.", F);
|
||||
if V1==0 {
|
||||
println!("CONGRATULATIONS! A PERFECT LANDING!!");
|
||||
println!("YOUR LICENSE WILL BE RENEWED.......LATER.");
|
||||
}
|
||||
if V1.abs()>=2 {
|
||||
println!("***** SORRY, BUT YOU BLEW IT!!!!");
|
||||
println!("APPROPRIATE CONDOLENCES WILL BE SENT TO YOUR NEXT OF KIN.");
|
||||
}
|
||||
println!();println!();println!();
|
||||
let A = input("ANOTHER MISSION");
|
||||
if !(A=="YES") { break };
|
||||
}
|
||||
println!();println!( "CONTROL OUT.");println!();
|
||||
}
|
||||
|
||||
|
||||
fn input(prompt:&str) -> String {
|
||||
loop {
|
||||
print!("{} ? ",prompt);io::stdout().flush().unwrap();
|
||||
let innn:String=read!("{}\n");
|
||||
let out:String = innn.trim().to_string();
|
||||
if out!="" {return out}
|
||||
}
|
||||
}
|
||||
fn input_int(prompt:&str) -> i32 {
|
||||
loop {
|
||||
print!("{} ? ",prompt);io::stdout().flush().unwrap();
|
||||
match try_read!() {
|
||||
Ok(n) => return n,
|
||||
Err(_) => println!("Enter a number 0-30"),
|
||||
}
|
||||
}
|
||||
}
|
||||
9
61_Math_Dice/rust/Cargo.toml
Normal file
9
61_Math_Dice/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"
|
||||
5
61_Math_Dice/rust/README.md
Normal file
5
61_Math_Dice/rust/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
|
||||
|
||||
If you wish to give the user more than 2 attempts to get the number, change value assigned to the num_tries variable at the start of the main function
|
||||
134
61_Math_Dice/rust/src/main.rs
Normal file
134
61_Math_Dice/rust/src/main.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use rand::{Rng, prelude::{thread_rng}};
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
//DATA
|
||||
let num_tries:u8 = 2; //number of tries the player gets each round, must be at least 1
|
||||
let mut rng = thread_rng();
|
||||
let mut user_guess: u8;
|
||||
let mut dice_1:u8;
|
||||
let mut dice_2:u8;
|
||||
|
||||
//print welcome message
|
||||
welcome();
|
||||
|
||||
//game loop
|
||||
loop {
|
||||
//roll dice
|
||||
dice_1 = rng.gen_range(1..=6);
|
||||
dice_2 = rng.gen_range(1..=6);
|
||||
|
||||
//print dice
|
||||
print_dice(dice_1);
|
||||
println!(" +");
|
||||
print_dice(dice_2);
|
||||
println!(" =");
|
||||
|
||||
//get user guess, they have 2 tries
|
||||
for t in 0..num_tries {
|
||||
//get guess
|
||||
user_guess = get_number_from_user_input("", "That's not a valid number!", 1, 12);
|
||||
|
||||
//if they get it wrong
|
||||
if user_guess != (dice_1+dice_2) {
|
||||
//print different message depending on what try they're on
|
||||
if t < num_tries-1 { // user has tries left
|
||||
println!("NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER.");
|
||||
println!(" =");
|
||||
}
|
||||
else { //this is their last try
|
||||
println!("NO, THE ANSWER IS {}", dice_1+dice_2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
println!("RIGHT!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//play again
|
||||
println!("\nThe dice roll again....");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* prints the welcome message to the console
|
||||
*/
|
||||
fn welcome() {
|
||||
println!("
|
||||
MATH DICE
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
\n\n
|
||||
THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.
|
||||
WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION
|
||||
MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.
|
||||
TO CONCLUDE THE LESSON, PRESS Ctrl+C AS YOUR ANSWER.\n
|
||||
");
|
||||
}
|
||||
|
||||
/**
|
||||
* print the dice,
|
||||
*/
|
||||
fn print_dice(dice_value:u8) {
|
||||
//data
|
||||
|
||||
//top
|
||||
println!(" ----- ");
|
||||
//first layer
|
||||
match dice_value {
|
||||
4|5|6 => println!("| * * |"),
|
||||
2|3 => println!("| * |"),
|
||||
_=>println!("| |"),
|
||||
}
|
||||
|
||||
//second layer
|
||||
match dice_value {
|
||||
1|3|5 => println!("| * |"),
|
||||
2|4 => println!("| |"),
|
||||
_=>println!("| * * |"),
|
||||
}
|
||||
|
||||
//third layer
|
||||
match dice_value {
|
||||
4|5|6 => println!("| * * |"),
|
||||
2|3 => println!("| * |"),
|
||||
_=>println!("| |"),
|
||||
}
|
||||
|
||||
//bottom
|
||||
println!(" ----- ");
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a integer from user input
|
||||
*/
|
||||
fn get_number_from_user_input(prompt: &str, error_message: &str, min:u8, max:u8) -> u8 {
|
||||
//input loop
|
||||
return loop {
|
||||
let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
|
||||
|
||||
//print prompt
|
||||
println!("{}", prompt);
|
||||
//read user input from standard input, and store it to raw_input
|
||||
//raw_input.clear(); //clear input
|
||||
io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
|
||||
|
||||
//from input, try to read a number
|
||||
match raw_input.trim().parse::<u8>() {
|
||||
Ok(i) => {
|
||||
if i < min || i > max { //input out of desired range
|
||||
println!("{} ({}-{})", error_message, min,max);
|
||||
continue; // run the loop again
|
||||
}
|
||||
else {
|
||||
break i;// this escapes the loop, returning i
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("{} {}", error_message, e.to_string().to_uppercase());
|
||||
continue; // run the loop again
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user