Merge branch 'coding-horror:main' into main

This commit is contained in:
Ahmad Masykur
2022-03-04 16:33:57 +07:00
committed by GitHub
10 changed files with 1364 additions and 0 deletions

View 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"

View 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)

View 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");
}

View 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]}")

View 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)

View 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"

View 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"),
}
}
}

View 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"

View 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

View 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
}
};
};
}