How to handle multiple user inputs into a match? - rust

I'm new to programming and I got stucked while trying to get user's input in a match expression. I read the documentation but I didn't find the answer to my problem.
The idea is simple : the user chooses a game mode (Human Vs Human or Human Vs Machine) then the program asks for the user's name. I don't get any error but the program lets me enter only the second name (player_2) but not the first one (player_1). It's like if the program is not stoping for me to enter the first player name.
Here is the code I wrote :
use std::io;
use text_io::read;
fn player_input() -> String {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("Wrong input");
return input;
}
fn main() {
println!("GAME MODE");
println!("1. Human Vs Human");
println!("2. Human Vs Machine");
let game_mode : String = read!();
match game_mode.as_str() {
"1" => {
println!("Player 1 : \n");
let player_1 = player_input();
println!("Player 2 : ");
let player_2 = player_input();
println!("{} - {}", player_1, player_2);
},
"2" => {
println!("Player 1 : ");
let player_1 = player_input();
let player_2 = "Skynet";
println!("{} - {}", player_1, player_2);
},
_ => println!("Wrong input"),
};
}
And here is the output I get :
GAME MODE
1. Human Vs Human
2. Human Vs Machine
1
Player 1 :
Player 2 :
George
- George
I don't know what am I doing wrong because if I test the same code but without the match expression the program actually stops and lets me enter the first name (player_1) before asking me for the second one (player_2). Maybe match is not the way to do it and I should rather use an if/else ?
Thank you for your help

This is definitley not a problem with match. Your problem seems to occur because the read! macro doesn't remove the whitespace that causes it to end (in this case, \n). Then, the first call to player_input reads the \n and immediatley stops. Only then does the second call to player_input actually pick up any input. You can fix this in two different ways:
-You can not use text_io and simply rely on the built in std::io, which does remove the \n that caused its call to end
-You can change the read! call to also pick up the \n like so: read!("{}\n")

Related

Rust ncurses: garbled Chinese characters and setlocale doesn't work

I am trying to print Chinese character in a ncurses screen
log::debug!("row str {}", row_str);
ncurses::addstr(&row_str);
variable row_str is displayed well in as a parameter of log::debug , but get garbled by using ncurses::addstr, like this:
中彖~G潛®弾U // it should be "中文目录"
i've tried to fix it by the following 3 methods, but no one works.
// method 1
gettextrs::setlocale(gettextrs::LocaleCategory::LcAll, "");
// method 2
use libc::setlocale;
unsafe {
setlocale(0, "".as_bytes().as_ptr() as *const i8);
}
//method 3
ncurses::setlocale(ncurses::constants::LcCategory::all, "")

What are some good ways to implement a read line or a sleep timer in Rust

I've finished my CLI but it exits too quickly for people to use it, Anyone know how would I go towards implementing code in my main.rs without breaking the compiler lol.
I was thinking of maybe a for loop that prints , read and execute and then start again. Or maybe a read line function so it stays up long enough to output the display.
Where would you guys implement that ? Thanks!
use structopt::StructOpt;
mod cli;
mod task;
use cli::{Action::*, CommandLineArgs};
use task::Task;
fn main() {
// Get the command-line arguments.
let CommandLineArgs {
action,
todo_file,
} = CommandLineArgs::from_args();
// Unpack the todo file.
let todo_file = todo_file.expect("Failed to find todo file");
// Perform the action.
match action {
Add { text } => task::add_task(todo_file,
Task::new(text)),
List => task::list_tasks(todo_file),
Done { position } =>
task::complete_task(todo_file, position),
}
.expect("Failed to perform action")
}
From the example, it seems you're getting the arguments from the command line. If you instead wanted the program to wait for a user to enter some text, interpret that text as a command and run it, and then wait for input again, until the user exits the program, then you'll probably want https://doc.rust-lang.org/std/io/struct.Stdin.html or possibly a higher level crate such as https://docs.rs/rustyline/8.0.0/rustyline/
If you were using stdin directly, you can call io::stdin().read_line() which will wait until the user has entered a line of text and pressed enter before the function will return. You can then parse that string to get the action to perform. The input/parsing/action code can be surrounded in a loop {} and one of the actions can be an exit command that will exit the loop.

Rust: macro to keep same frame rate does not work

To keep a certain frame rate, i've been using std::thread::sleep() to wait until enough time has passed. Calculating how much it sleeps messes up the code a bit, so I tried making a macro for it. This is it now, but it does not work:
macro_rules! fps30 {
($($body: expr;);*) => {
let time = std::time::Instant::now()
$($body)*
let time_elapsed = time.elapsed().as_micros();
if FRAME_TIME_30FPS_IN_MICROS > time_elapsed {
let time_to_sleep = FRAME_TIME_30FPS_IN_MICROS - time_elapsed;
std::thread::sleep(std::time::Duration::from_micros(time_to_sleep as u64));
}
};
}
And I want to use it like this:
loop {
fps30!{
// do everything I want to do in the loop
}
}
When I don't implement it as a macro (by pasting the code in the loop directly) it works, and retains a nice 29 frames per second (I guess not 30 because of the small overhead the sleep calculations give). The error it gives during compilation states: no rules expected the token 'p', where p is an object/struct instance I use in the macro.
Any suggestions?
The problem is with the handling of the ; in:
$($($body: expr;);*)
When you want to accept a ; separated list of expressions you should either write $($($body: expr;)*) or $($($body: expr);*). The former means a list of ;-terminated expressions, while the latter is a list of ;-separated expressions.
The difference is subtle but important. If you add both then you would need to write two ;; to separate every expression.
It would be better if you accepted a list of ; terminated expressions, so do:
$($($body: expr;)*)
Then you have a couple of errors in the body of the macro, also related to ;. You are missing the ; before the expansion of the $body:
let time = std::time::Instant::now();
And you are missing the ; in the expansion of $body itself, because the ; is not part of the captured expr:
$($body;)*
With these changed it works, except if you try:
fps30!{
if a {
}
if a {
}
}
Because there is no ; after the if expressions!!! You could try switching to:
$($($body: expr);*)
But it will not work either, now because there is no ; between the expressiones!
You could accept a single $body: block but then you will be required to write an additional couple of {}. Not ideal...
If you really want to accept any kind of block of code, I recommend to accept a list of token trees (tt). And when expanding them, enclose them in a {}, in case it does not end with a ;.
macro_rules! fps30 {
($($body: tt)*) => {
let time = std::time::Instant::now();
{ $($body)* }
let time_elapsed = time.elapsed().as_micros();
//...
};
}
Now your macro will accept any kind of syntax, and will expand it dumbly inside macro.
You could even add the possibility of $body having a type and a value, and make fps30 evaluate to that value`:
let res = { $($body)* };
//...
res
As an additional bonus, if you write a syntax error, it will fail when compiling the code, not when expanding the macro, which is much easier to debug.

First note played in AKSequencer is off

I am using AKSequencer to create a sequence of notes that are played by an AKMidiSampler. My problem is, at higher tempos the first note always plays with a little delay, no matter what i do.
I tried prerolling the sequence but it won't help. Substituting the AKMidiSampler with an AKSampler or a AKSamplePlayer (and using a callback track to play them) hasn't helped either, though it made me think that the problem probably resides in the sequencer or in the way I create the notes.
Here's an example of what I'm doing (I tried to make it as simple as I could):
import UIKit
import AudioKit
class ViewController: UIViewController {
let sequencer = AKSequencer()
let sampler = AKMIDISampler()
let callbackInst = AKCallbackInstrument()
var metronomeTrack : AKMusicTrack?
var callbackTrack : AKMusicTrack?
let numberOfBeats = 8
let tempo = 280.0
var startTime : TimeInterval = 0
override func viewDidLoad() {
super.viewDidLoad()
print("Begin setup.")
// Load .wav sample in AKMidiSampler
do {
try sampler.loadWav("tick")
} catch {
print("sampler.loadWav() failed")
}
// Create tracks for the sequencer and set midi outputs
metronomeTrack = sequencer.newTrack("metronomeTrack")
callbackTrack = sequencer.newTrack("callbackTrack")
metronomeTrack?.setMIDIOutput(sampler.midiIn)
callbackTrack?.setMIDIOutput(callbackInst.midiIn)
// Setup and start AudioKit
AudioKit.output = sampler
do {
try AudioKit.start()
} catch {
print("AudioKit.start() failed")
}
// Set sequencer tempo
sequencer.setTempo(tempo)
// Create the notes
var midiSequenceIndex = 0
for i in 0 ..< numberOfBeats {
// Add notes to tracks
metronomeTrack?.add(noteNumber: 60, velocity: 100, position: AKDuration(beats: Double(midiSequenceIndex)), duration: AKDuration(beats: 0.5))
callbackTrack?.add(noteNumber: MIDINoteNumber(midiSequenceIndex), velocity: 100, position: AKDuration(beats: Double(midiSequenceIndex)), duration: AKDuration(beats: 0.5))
print("Adding beat number \(i+1) at position: \(midiSequenceIndex)")
midiSequenceIndex += 1
}
// Set the callback
callbackInst.callback = {status, noteNumber, velocity in
if status == .noteOn {
let currentTime = Date().timeIntervalSinceReferenceDate
let noteDelay = currentTime - ( self.startTime + ( 60.0 / self.tempo ) * Double(noteNumber) )
print("Beat number: \(noteNumber) delay: \(noteDelay)")
} else if ( noteNumber == midiSequenceIndex - 1 ) && ( status == .noteOff) {
print("Sequence ended.\n")
self.toggleMetronomePlayback()
} else {return}
}
// Preroll the sequencer
sequencer.preroll()
print("Setup ended.\n")
}
#IBAction func playButtonPressed(_ sender: UIButton) {
toggleMetronomePlayback()
}
func toggleMetronomePlayback() {
if sequencer.isPlaying == false {
print("Playback started.")
startTime = Date().timeIntervalSinceReferenceDate
sequencer.play()
} else {
sequencer.stop()
sequencer.rewind()
}
}
}
Could anyone help? Thank you.
As Aure commented, the start up latency is a known problem. Even with preroll, there is still noticeable latency, especially at higher tempos.
But if you are using a looping sequence, I found that you can sometimes mitigate how noticeable the latency is by setting the 'starting point' of the sequence to a position after the final MIDI event, but within the loop length. If you can find a good position, you can get the latency effects out of the way before it loops back to your content.
Make sure to call setTime() before you need it (e.g., after stopping the sequence, not when you are ready to play) because the setTime()call itself can introduce about 200ms of wonkiness.
Edit:
As an afterthought, you could do the same thing on a non-looping sequence by enabling looping and using an arbitrarily long sequence length. If you needed playback to stop at the end of the MIDI content, you could do this with an AKCallbackInstrument triggered by an MIDI event placed just after the final note.
After a bit of testing I actually found out that it is not the first note that plays off but the subsequent notes that play in advance. Moreover, the amount of notes that play exactly on time when starting the sequencer depends on the set tempo.
The funny thing is that if the tempo is < 400 there will be one note played on time and the others in advance, if it is 400 <= bpm < 800 there will be two notes played correctly and the others in advance and so on, for every 400 bpm increment you get one more note played correctly.
So... since the notes are played in advance and not late, the solution that solved it for me is:
1) Use a sampler that is not connected directly to a track's midi output but has its .play() method called inside a callback.
2) Keep track of when the sequencer gets started
3) At every callback calculate when the note should play in relation to the start time and store what time it actually is, so you can then calculate the offset.
4) use the computed offset to dispatch_async after the offset your .play() method.
And that's it, I tested this on multiple devices and now all the notes play perfectly on time.
I had the same issue, preroll didn't help, but I have managed to solve it with a dedicated sampler for the first notes.
I used a delay on the other sampler, about 0.06 of a second, works like a charm.
Kind of a silly solution but it did the job and I could go on with the project :)
//This is for fixing AK bug that plays the first playback not in delay
let fixDelay = AKDelay()
fixDelay.dryWetMix = 1
fixDelay.feedback = 0
fixDelay.lowPassCutoff = 22000
fixDelay.time = 0.06
fixDelay.start()
let preDelayMixer = AKMixer()
let preFirstMixer = AKMixer()
[playbackSampler,vocalSampler] >>> preDelayMixer >>> fixDelay
[firstNoteVocalSampler, firstRoundPlaybackSampler] >>> preFirstMixer
[fixDelay,preFirstMixer] >>> endMixer

Difference between String and StaticString

I was browsing the docs, and I found StaticString. It states:
An simple string designed to represent text that is "knowable at compile-time".
I originally thought that String has the same behaviour as NSString, which is known at compile time, but it looks like that I was wrong. So my question is when should we use StaticString instead of a String, and is the only difference is that StaticString is known at compile-time?
One thing I found is
var a: String = "asdf" //"asdf"
var b: StaticString = "adsf" //{(Opaque Value), (Opaque Value), (Opaque Value)}
sizeofValue(a) //24
sizeofValue(b) //17
So it looks like StaticString has a little bit less memory footprint.
It appears that StaticString can hold string literals. You can't assign a variable of type String to it, and it can't be mutated (with +=, for example).
"Knowable at compile time" doesn't mean that the value held by the variable will be determined at compile time, just that any value assigned to it is known at compile time.
Consider this example which does work:
var str: StaticString
for _ in 1...10 {
switch arc4random_uniform(3) {
case 0: str = "zero"
case 1: str = "one"
case 2: str = "two"
default: str = "default"
}
print(str)
}
Any time you can give Swift more information about how a variable is to be used, it can optimize the code using it. By restricting a variable to StaticString, Swift knows the variable won't be mutated so it might be able to store it more efficiently, or access the individual characters more efficiently.
In fact, StaticString could be implemented with just an address pointer and a length. The address it points to is just the place in the static code where the string is defined. A StaticString doesn't need to be reference counted since it doesn't (need to) exist in the heap. It is neither allocated nor deallocated, so no reference count is needed.
"Knowable at compile time" is pretty strict. Even this doesn't work:
let str: StaticString = "hello " + "world"
which fails with error:
error: 'String' is not convertible to 'StaticString'
StaticString is knowable at compile time. This can lead to optimizations. Example:
EDIT: This part doesn't work, see edit below
Suppose you have a function that calculates an Int for some String values for some constants that you define at compile time.
let someString = "Test"
let otherString = "Hello there"
func numberForString(string: String) -> Int {
return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}
let some = numberForString(someString)
let other = numberForString(otherString)
Like this, the function would be executed with "Test" and "Hello there" when it really gets called in the program, when the app starts for example. Definitely at runtime. However if you change your function to take a StaticString
func numberForString(string: StaticString) -> Int {
return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}
the compiler knows that the passed in StaticString is knowable at compile time, so guess what it does? It runs the function right at compile time (How awesome is that!). I once read an article about that, the author inspected the generated assembly and he actually found the already computed numbers.
As you can see this can be useful in some cases like the one mentioned, to not decrease runtime performance for stuff that can be done at compile time.
EDIT: Dániel Nagy and me had a conversation. The above example of mine doesn't work because the function stringValue of StaticString can't be known at compile time (because it returns a String). Here is a better example:
func countStatic(string: StaticString) -> Int {
return string.byteSize // Breakpoint here
}
func count(string: String) -> Int {
return string.characters.count // Breakpoint here
}
let staticString : StaticString = "static string"
let string : String = "string"
print(countStatic(staticString))
print(count(string))
In a release build only the second breakpoint gets triggered whereas if you change the first function to
func countStatic(string: StaticString) -> Int {
return string.stringValue.characters.count // Breakpoint here
}
both breakpoints get triggered.
Apparently there are some methods which can be done at compile time while other can't. I wonder how the compiler figures this out actually.

Resources