reformat only

This commit is contained in:
David Lotts
2022-03-13 04:20:26 -04:00
parent b396077651
commit 7bddf191a3

View File

@@ -1,97 +1,131 @@
use std::{io::{self, Write}};
use nanorand::{tls::TlsWyRand, Rng}; use nanorand::{tls::TlsWyRand, Rng};
use std::io::{self, Write};
use text_io::{read, try_read}; use text_io::{read, try_read};
/// Play Nim on the command line! /// Play Nim on the command line!
fn main() { fn main() {
let mut rng = nanorand::tls_rng(); let mut rng = nanorand::tls_rng();
println!("{:>37}","NIM"); //100 println!("{:>37}", "NIM"); //100
println!("{:>15}{}","","CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); //110 println!("{:>15}{}", "", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); //110
println!();println!();println!(); //120 println!();
let mut piles = [0.0;100]; let mut b_piles = [[0.0;100];11]; let mut ix_do= [0usize;3]; //210 println!();
println!("THIS IS THE GAME OF NIM."); //220 println!(); //120
//230 let mut piles = [0.0; 100];
loop{ let mut b_piles = [[0.0; 100]; 11];
let instuct_me=input("DO YOU WANT INSTRUCTIONS"); //240 let mut ix_do = [0usize; 3]; //210
if instuct_me=="NO" || instuct_me=="no" { break } //440 //260 println!("THIS IS THE GAME OF NIM."); //220
if instuct_me=="YES" || instuct_me=="yes" { instructions(); break }//310 //280 //230
println!("PLEASE ANSWER YES OR NO"); //290 loop {
} //300 let instuct_me = input("DO YOU WANT INSTRUCTIONS"); //240
if instuct_me == "NO" || instuct_me == "no" {
break;
} //440 //260
if instuct_me == "YES" || instuct_me == "yes" {
instructions();
break;
} //310 //280
println!("PLEASE ANSWER YES OR NO"); //290
} //300
'play_again: loop { 'play_again: loop {
println!(); //440 println!(); //440
let winner_take_last:bool = let winner_take_last: bool = loop {
loop { let choice = input_int("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST"); //460
let choice= input_int("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST"); //460 if (1..=2).contains(&choice) {
if (1..=2).contains(&choice) { break choice==1 } //490 //470 break choice == 1;
} //490 //470
}; };
let np= let np = loop {
loop { //490 //490
let choice = input_int("ENTER NUMBER OF PILES"); //500 let choice = input_int("ENTER NUMBER OF PILES"); //500
if choice<=100 && choice>=1 { break choice }//490 //510 if choice <= 100 && choice >= 1 {
//490 //520 break choice;
//490 //530 } //490 //510
//490 //520
//490 //530
}; };
println!("ENTER PILE SIZES"); //540 println!("ENTER PILE SIZES"); //540
for ix in 0..np as usize { //550 for ix in 0..np as usize {
piles[ix] = //550
loop { piles[ix] = loop {
let choice = input_int(&(ix+1).to_string()); //570 let choice = input_int(&(ix + 1).to_string()); //570
if choice<=2000 && choice>=1 && choice>=1 { break choice as f64 }//560 //580 if choice <= 2000 && choice >= 1 && choice >= 1 {
//560 //600 break choice as f64;
} //560 //580
//560 //600
} }
} //610 } //610
let human_first = let human_first = loop {
loop {//620 //620
let choice = input("DO YOU WANT TO MOVE FIRST"); //630 let choice = input("DO YOU WANT TO MOVE FIRST"); //630
let choice = choice.to_lowercase(); let choice = choice.to_lowercase();
if choice=="yes" { break true } //1450 //650 if choice == "yes" {
if choice=="no" { break false }//700 //670 break true;
println!("PLEASE ANSWER YES OR NO."); //680 } //1450 //650
}; //690 if choice == "no" {
break false;
} //700 //670
println!("PLEASE ANSWER YES OR NO."); //680
}; //690
let mut winner = WinState::GameOn; let mut winner = WinState::GameOn;
if human_first {winner = human_turn(winner_take_last, np, &mut piles);}; if human_first {
winner = human_turn(winner_take_last, np, &mut piles);
};
//### main game loop //### main game loop
if winner.is_game_on() { if winner.is_game_on() {
winner = winner = loop {
loop {
// break on winner returning from here: // break on winner returning from here:
let win = machines_turn(&mut rng, winner_take_last, np, &mut piles, &mut ix_do, &mut b_piles); let win = machines_turn(
if ! win.is_game_on() { &mut rng,
winner_take_last,
np,
&mut piles,
&mut ix_do,
&mut b_piles,
);
if !win.is_game_on() {
break win; break win;
}; //starts at 700 }; //starts at 700
println!("PILE SIZE"); //1380 println!("PILE SIZE"); //1380
for ix in 0..np as usize { //1390 for ix in 0..np as usize {
println!("{} {}",ix+1,piles[ix]); //1400 //1390
}// NEXT ix //1410 println!("{} {}", ix + 1, piles[ix]); //1400
} // NEXT ix //1410
// break on winner returning from here: // break on winner returning from here:
let win = human_turn(winner_take_last, np, &mut piles); let win = human_turn(winner_take_last, np, &mut piles);
if ! win.is_game_on() { if !win.is_game_on() {
break win; break win;
}; };
// GOTO 700 //1560 // GOTO 700 //1560
}; };
} }
println!("MACHINE {}", if winner==WinState::ComputerWins{"WINS"}else{"LOSES"}); println!(
"MACHINE {}",
if winner == WinState::ComputerWins {
"WINS"
} else {
"LOSES"
}
);
loop { loop {
// Game over //1640 // Game over //1640
let choice = input("do you want to play another game"); //1650 let choice = input("do you want to play another game"); //1650
match choice.to_ascii_lowercase().as_str() { match choice.to_ascii_lowercase().as_str() {
"yes" => { break }, //1720 //1660 "yes" => break, //1720 //1660
"no" => { break 'play_again },//1730 //1680 "no" => break 'play_again, //1730 //1680
_ => println!("PLEASE. YES OR NO."), //1700 _ => println!("PLEASE. YES OR NO."), //1700
} // GOTO 1650 //1710 } // GOTO 1650 //1710
} // GOTO 440 //1720 } // GOTO 440 //1720
} // END //1730 } // END //1730
} }
#[derive(PartialEq)] #[derive(PartialEq)]
enum WinState { enum WinState {
GameOn, GameOn,
ComputerWins, ComputerWins,
HumanWins HumanWins,
} }
impl WinState { impl WinState {
@@ -103,151 +137,212 @@ impl WinState {
} }
} }
/// Computer's turn /// Computer's turn
fn machines_turn(rng:&mut TlsWyRand, winner_take_last: bool, np: i32, piles: &mut [f64; 100], ix_do: &mut [usize; 3], b_piles: &mut [[f64;100];11] ) -> WinState { fn machines_turn(
if ! winner_take_last { //940 //700 rng: &mut TlsWyRand,
winner_take_last: bool,
np: i32,
piles: &mut [f64; 100],
ix_do: &mut [usize; 3],
b_piles: &mut [[f64; 100]; 11],
) -> WinState {
if !winner_take_last {
//940 //700
//### Loser takes last, check for winner //### Loser takes last, check for winner
let mut count=0; //710 let mut count = 0; //710
'wayout: loop { 'wayout: loop {
'outer: loop { 'outer: loop {
for ix in 0..np as usize { //720 for ix in 0..np as usize {
if piles[ix]==0.0 { continue } //730 //720
count+=1; //740 if piles[ix] == 0.0 {
if count==3 { break 'outer }//840 //750 continue;
ix_do[count]=ix; //760 } //730
} //770 count += 1; //740
// exactly two piles remain if count == 3 {
if count==2 { break 'outer;
} //840 //750
ix_do[count] = ix; //760
} //770
// exactly two piles remain
if count == 2 {
// println!("Only two piles remain : unused0={} pile1={} pile2={}",ix_do[0]+1,ix_do[1]+1,ix_do[2]+1); //diagnostic // println!("Only two piles remain : unused0={} pile1={} pile2={}",ix_do[0]+1,ix_do[1]+1,ix_do[2]+1); //diagnostic
if piles[ix_do[1]]==1.0 || piles[ix_do[2]]==1.0 { //920 if piles[ix_do[1]] == 1.0 || piles[ix_do[2]] == 1.0 {
return WinState::ComputerWins; //920
}//820 //930 return WinState::ComputerWins;
break 'wayout; } //820 //930
} //920 //780 break 'wayout;
// exactly one pile remains, loser takes last, and before machine's turn } //920 //780
// println!("Only one pile remains : pile={}",ix_do[1]); //diagnostic // exactly one pile remains, loser takes last, and before machine's turn
assert!(piles[ix_do[1]] > 0.0 ); // println!("Only one pile remains : pile={}",ix_do[1]); //diagnostic
if piles[ix_do[1]] == 1.0 { //820 //790 assert!(piles[ix_do[1]] > 0.0);
return WinState::HumanWins; //800 if piles[ix_do[1]] == 1.0 {
// GOTO 1640 //810 //820 //790
return WinState::HumanWins; //800
// GOTO 1640 //810
} else { } else {
return WinState::ComputerWins; //820 return WinState::ComputerWins; //820
// GOTO 1640 //830 // GOTO 1640 //830
} }
} }
count=0 ; //840 count = 0; //840
let mut is_all_ones=true; let mut is_all_ones = true;
for ix in 0..np as usize {// FOR ix=1 TO N //850 for ix in 0..np as usize {
if piles[ix]>1.0 { is_all_ones=false; break }//940 //860 // FOR ix=1 TO N //850
if piles[ix]!=0.0 { //890 //870 if piles[ix] > 1.0 {
count=count+1 ; //880 is_all_ones = false;
break;
} //940 //860
if piles[ix] != 0.0 {
//890 //870
count = count + 1; //880
} }
}// NEXT ix //890 } // NEXT ix //890
if is_all_ones && count%2 != 0 { //800 //900 if is_all_ones && count % 2 != 0 {
return WinState::HumanWins; //800 //900
return WinState::HumanWins;
} }
break; // GOTO 940 //910 break; // GOTO 940 //910
} }
} }
//### winner take last (or first?) -- check for winner //### winner take last (or first?) -- check for winner
for ix in 0..np as usize { //940 for ix in 0..np as usize {
let mut sticks=piles[ix]; //950 //940
for jx in 0..=10 { //960 let mut sticks = piles[ix]; //950
let half=sticks/2.0; //970 for jx in 0..=10 {
b_piles[ix][jx]=2.0*(half-half.trunc()) ; //980 //960
sticks=half.trunc(); //990 let half = sticks / 2.0; //970
}// NEXT J //1000 b_piles[ix][jx] = 2.0 * (half - half.trunc()); //980
}// NEXT I //1010 sticks = half.trunc(); //990
} // NEXT J //1000
} // NEXT I //1010
let mut is_odd = false; let mut is_odd = false;
let mut ix_max_pile = usize::MAX; // make sure this fails if ever used. let mut ix_max_pile = usize::MAX; // make sure this fails if ever used.
for jx in (0..=10).rev() { //1020 for jx in (0..=10).rev() {
let mut count=0; //1030 //1020
let mut highest=0.0; //1040 let mut count = 0; //1030
for ix in 0..np as usize { //1050 let mut highest = 0.0; //1040
if b_piles[ix][jx]==0.0 { continue }; //1110 //1060 for ix in 0..np as usize {
count=count+1; //1070 //1050
if piles[ix]<=highest as f64 { continue }; //1110 //1080 if b_piles[ix][jx] == 0.0 {
highest = piles[ix]; //1090 continue;
ix_max_pile=ix; //1100 }; //1110 //1060
count = count + 1; //1070
if piles[ix] <= highest as f64 {
continue;
}; //1110 //1080
highest = piles[ix]; //1090
ix_max_pile = ix; //1100
} //NEXT I //1110 } //NEXT I //1110
// println!("if none are odd, use random: count={} odd={}", count,count%2!=0); //diagnostic // println!("if none are odd, use random: count={} odd={}", count,count%2!=0); //diagnostic
if count%2!=0 { is_odd=true ; break } // C/2<>INT(C/2) //1190 //1120 if count % 2 != 0 {
}// NEXT J //1130 is_odd = true;
if ! is_odd { break;
} // C/2<>INT(C/2) //1190 //1120
} // NEXT J //1130
if !is_odd {
let mut ix_random; let mut ix_random;
loop { loop {
ix_random= rng.generate_range(0..np as usize); //(N*RND(1)+1).trunc(); //1140 ix_random = rng.generate_range(0..np as usize); //(N*RND(1)+1).trunc(); //1140
if piles[ix_random] != 0.0 { break } //1140 //1150 if piles[ix_random] != 0.0 {
break;
} //1140 //1150
} }
let remove_random= rng.generate_range(1..=piles[ix_random] as i32); // INT(A[E]*RND(1)+1) //1160 let remove_random = rng.generate_range(1..=piles[ix_random] as i32); // INT(A[E]*RND(1)+1) //1160
piles[ix_random] = piles[ix_random] - remove_random as f64; //1170 piles[ix_random] = piles[ix_random] - remove_random as f64; //1170
// println!("I choose random: pile={} removed={}",ix_random+1,remove_random) //diagnostic // println!("I choose random: pile={} removed={}",ix_random+1,remove_random) //diagnostic
// GOTO 1380 //1180 // GOTO 1380 //1180
} else { } else {
// println!("max pile: pile={}, was={} setting to 0. Expect add back.",ix_max_pile+1,piles[ix_max_pile]); //diagnostic // println!("max pile: pile={}, was={} setting to 0. Expect add back.",ix_max_pile+1,piles[ix_max_pile]); //diagnostic
piles[ix_max_pile] = 0.0; //1190 piles[ix_max_pile] = 0.0; //1190
for jx in 0..=10 { //1200 for jx in 0..=10 {
b_piles[ix_max_pile][jx]=0.0; //1210 //1200
let mut countum=0; //1220 b_piles[ix_max_pile][jx] = 0.0; //1210
for ix in 0..np as usize { //1230 let mut countum = 0; //1220
if b_piles[ix][jx]==0.0 { continue }//1260 //1240 for ix in 0..np as usize {
countum+=1; // count non-empty //1250 //1230
}// NEXT ix //1260 if b_piles[ix][jx] == 0.0 {
piles[ix_max_pile] = piles[ix_max_pile] + ((countum%2)*2usize.pow(jx as u32)) as f64; //1270 continue;
} //1260 //1240
countum += 1; // count non-empty //1250
} // NEXT ix //1260
piles[ix_max_pile] =
piles[ix_max_pile] + ((countum % 2) * 2usize.pow(jx as u32)) as f64;
//1270
// println!("I choose max pile : pile={}, add back=odd?2^{}:0={}",ix_max_pile+1,jx,((countum%2)*2usize.pow(jx as u32))); //diagnostic // println!("I choose max pile : pile={}, add back=odd?2^{}:0={}",ix_max_pile+1,jx,((countum%2)*2usize.pow(jx as u32))); //diagnostic
}// NEXT J //1280 } // NEXT J //1280
'done: loop { 'done: loop {
if ! winner_take_last { //1380 //1290 if !winner_take_last {
let mut counter=0; //1300 //1380 //1290
for ix in 0..np as usize { //1310 let mut counter = 0; //1300
if piles[ix]>1.0 { break 'done } //1380 //1320 for ix in 0..np as usize {
if piles[ix]!=0.0 { //1350 //1330 //1310
counter+=1; //1340 if piles[ix] > 1.0 {
break 'done;
} //1380 //1320
if piles[ix] != 0.0 {
//1350 //1330
counter += 1; //1340
} }
}// NEXT ix //1350 } // NEXT ix //1350
// done if C is odd // done if C is odd
if counter%2!=0 { break } //1380 //1360 if counter % 2 != 0 {
// println!("max pile if even: 1 - pile : pile={}, before 1-p = {}",ix_max_pile+1,piles[ix_max_pile]); //diagnostic break;
piles[ix_max_pile] = 1.0 - piles[ix_max_pile]; //1370 } //1380 //1360
// println!("max pile if even: 1 - pile : pile={}, before 1-p = {}",ix_max_pile+1,piles[ix_max_pile]); //diagnostic
piles[ix_max_pile] = 1.0 - piles[ix_max_pile]; //1370
} }
break; break;
} }
} }
return WinState::GameOn; //1380 is after this return WinState::GameOn; //1380 is after this
} }
/// Human decide what you want to do and see if there is a winner /// Human decide what you want to do and see if there is a winner
fn human_turn(winner_take_last: bool, np: i32, piles: &mut [f64; 100], ) -> WinState { fn human_turn(winner_take_last: bool, np: i32, piles: &mut [f64; 100]) -> WinState {
if winner_take_last { //1450 //1420 if winner_take_last {
let is_all_empty=one_if_all_zero(np, &piles);// GOSUB 1570 //1430 //1450 //1420
if is_all_empty==1 { return WinState::ComputerWins }//820 //1440 //### machine wins let is_all_empty = one_if_all_zero(np, &piles); // GOSUB 1570 //1430
} if is_all_empty == 1 {
return WinState::ComputerWins;
} //820 //1440 //### machine wins
}
//### many things go here //### many things go here
loop {//1450 loop {
let (pile_choice, remove_choice) = input_2int("YOUR MOVE - PILE, NUMBER TO BE REMOVED"); //1460 //1450
if pile_choice>np || pile_choice<1 { continue } //1450 //1480 let (pile_choice, remove_choice) = input_2int("YOUR MOVE - PILE, NUMBER TO BE REMOVED"); //1460
let ix_choice= (pile_choice - 1) as usize; if pile_choice > np || pile_choice < 1 {
if remove_choice<1 || remove_choice as f64 > piles[ix_choice] { continue }; //1450 //1500 continue;
// remove the humans choice: } //1450 //1480
piles[ix_choice] = piles[ix_choice]-remove_choice as f64 ; //1530 let ix_choice = (pile_choice - 1) as usize;
if remove_choice < 1 || remove_choice as f64 > piles[ix_choice] {
continue;
}; //1450 //1500
// remove the humans choice:
piles[ix_choice] = piles[ix_choice] - remove_choice as f64; //1530
break; break;
} }
if one_if_all_zero(np, &piles)==1 // GOSUB 1570 //1540 if one_if_all_zero(np, &piles) == 1
{ return WinState::HumanWins }//800 //1550 //### machine loses! // GOSUB 1570 //1540
{
return WinState::HumanWins;
} //800 //1550 //### machine loses!
return WinState::GameOn; return WinState::GameOn;
} }
/// returns 0 if all A are 0, otherwise 1 /// returns 0 if all A are 0, otherwise 1
fn one_if_all_zero(np:i32, piles: &[f64; 100]) -> i32 { // sets Z to the return value fn one_if_all_zero(np: i32, piles: &[f64; 100]) -> i32 {
// sets Z to the return value
//1570 //1570
for ix in 0..np as usize { //1580 for ix in 0..np as usize {
if piles[ix]!=0.0 {return 0} //1610 //1590 //1580
//1600 if piles[ix] != 0.0 {
return 0;
} //1610 //1590
//1600
} //1610 } //1610
return 1; //1620 return 1; //1620
} //1630 } //1630
fn instructions() { fn instructions() {
@@ -264,34 +359,43 @@ fn instructions() {
println!("THE MACHINE WILL SHOW ITS MOVE BY LISTING EACH PILE AND THE"); println!("THE MACHINE WILL SHOW ITS MOVE BY LISTING EACH PILE AND THE");
println!("NUMBER OF OBJECTS REMAINING IN THE PILES AFTER EACH OF ITS"); println!("NUMBER OF OBJECTS REMAINING IN THE PILES AFTER EACH OF ITS");
println!("MOVES."); println!("MOVES.");
} }
/// print the prompt, wait for a number and newline. Loop if invalid. /// print the prompt, wait for a number and newline. Loop if invalid.
fn input(prompt:&str) -> String { fn input(prompt: &str) -> String {
loop { loop {
print!("{} ? ",prompt);io::stdout().flush().unwrap(); print!("{} ? ", prompt);
io::stdout().flush().unwrap();
// TODO: asks twice on win10, not linux. \r vs \n? // TODO: asks twice on win10, not linux. \r vs \n?
let innn:String=read!("{}\n"); let innn: String = read!("{}\n");
let out:String = innn.trim().to_string(); let out: String = innn.trim().to_string();
if out!="" {return out} if out != "" {
return out;
}
} }
} }
fn input_int(prompt:&str) -> i32 { fn input_int(prompt: &str) -> i32 {
loop { loop {
print!("{} ? ",prompt);io::stdout().flush().unwrap(); print!("{} ? ", prompt);
io::stdout().flush().unwrap();
match try_read!() { match try_read!() {
Ok(n) => return n, Ok(n) => return n,
Err(_) => {}, Err(_) => {}
} }
} }
} }
fn input_2int(prompt:&str) -> (i32,i32) { fn input_2int(prompt: &str) -> (i32, i32) {
loop { loop {
let inp = input(prompt); let inp = input(prompt);
let nums:Vec<i32> = inp.split(",").filter_map(|c| c.parse::<i32>().ok()).collect(); let nums: Vec<i32> = inp
if nums.len()!=2 {println!("Enter two numbers like: 9,9",);continue} .split(",")
return (nums[0], nums[1]) .filter_map(|c| c.parse::<i32>().ok())
.collect();
if nums.len() != 2 {
println!("Enter two numbers like: 9,9",);
continue;
}
return (nums[0], nums[1]);
} }
} }