Flipping bits game: Difference between revisions
Content added Content deleted
Not a robot (talk | contribs) (Add FOCAL) |
(Add Rust implementation) |
||
Line 5,435: | Line 5,435: | ||
2 0 1 1 |
2 0 1 1 |
||
3 0 1 0 |
3 0 1 0 |
||
</pre> |
|||
=={{header|Rust}}== |
|||
<lang rust> |
|||
// For random generation |
|||
extern crate rand; |
|||
// For fmt::Display |
|||
use std::fmt; |
|||
// For I/O (stdin, stdout, etc) |
|||
use std::io::prelude::*; |
|||
use rand::Rng; |
|||
/// A simple struct for a board |
|||
struct Board { |
|||
/// The cells of the board |
|||
cells: Vec<bool>, |
|||
/// The size of the board |
|||
size: usize, |
|||
} |
|||
// Functions for the Board struct |
|||
impl Board { |
|||
/// Generate a new, empty board, of size >= 1 |
|||
/// |
|||
/// Returns a Board in the "off" state, where all cells are 0. |
|||
/// If a size of 0 is given, a Board of size 1 will be created instead. |
|||
/// A mutable board is required for using Board::fliprow and Board::flipcol functions. |
|||
/// |
|||
/// ``` |
|||
/// let mut board: Board = Board::new(3); |
|||
/// ``` |
|||
fn new(size: usize) -> Board { |
|||
// Ensure we make a board with a non-zero size |
|||
if size > 0 { |
|||
Board { |
|||
cells: vec![false; size * size], |
|||
size, |
|||
} |
|||
} else { |
|||
Board::new(1) |
|||
} |
|||
} |
|||
/// Flip the specified row |
|||
/// |
|||
/// Returns true if the row is within the size, false otherwise. |
|||
/// |
|||
/// ``` |
|||
/// let mut board: Board = Board::new(3); |
|||
/// board.fliprow(1); |
|||
/// ``` |
|||
fn fliprow(&mut self, row: usize) -> bool { |
|||
// Check constraints |
|||
if row > self.size { |
|||
return false; |
|||
} |
|||
// Starting position in the vector |
|||
let start = row * self.size; |
|||
// Loop through the vector row |
|||
for i in start..start + self.size { |
|||
self.cells[i] = !self.cells[i]; |
|||
} |
|||
true |
|||
} |
|||
/// Flip the specified column |
|||
/// |
|||
/// Returns true if the column is within the size, false otherwise. |
|||
/// |
|||
/// ``` |
|||
/// let mut board: Board = Board::new(3); |
|||
/// board.flipcol(0); |
|||
/// ``` |
|||
fn flipcol(&mut self, col: usize) -> bool { |
|||
// Check constraints |
|||
if col > self.size { |
|||
return false; |
|||
} |
|||
// Loop through the vector column |
|||
for i in 0..self.size { |
|||
self.cells[col + i * self.size] = !self.cells[col + i * self.size]; |
|||
} |
|||
true |
|||
} |
|||
/// Generate a random board |
|||
/// |
|||
/// Returns a Board in a random state. |
|||
/// If a size of 0 is given, a Board of size 1 will be created instead. |
|||
/// |
|||
/// ``` |
|||
/// let target: Board = Board::random(3); |
|||
/// ``` |
|||
fn random<R: Rng>(rng: &mut R, size: usize) -> Board { |
|||
// Ensure we make a board with a non-zero size |
|||
if size == 0 { |
|||
return Board::random(rng, 1); |
|||
} |
|||
// Make a vector of the board size with random bits |
|||
let cells = (0..size * size) |
|||
.map(|_| rng.gen::<bool>()) |
|||
.collect::<Vec<_>>(); |
|||
// Return the random board |
|||
Board { cells, size } |
|||
} |
|||
} |
|||
impl PartialEq for Board { |
|||
fn eq(&self, rhs: &Board) -> bool { |
|||
self.cells == rhs.cells |
|||
} |
|||
} |
|||
// Implement the Display format, used with `print!("{}", &board);` |
|||
impl fmt::Display for Board { |
|||
// Example output: |
|||
// 0 1 2 |
|||
// 0 0 1 0 |
|||
// 1 1 0 0 |
|||
// 2 0 1 1 |
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
|||
// Get the string width of the size of the board |
|||
let width = (self.size - 1).to_string().len(); |
|||
// Write the initial spaces (upper left) |
|||
write!(f, "{space: >0$}", width, space = " ")?; |
|||
// Write the column numbers |
|||
for i in 0..self.size { |
|||
write!(f, " {offset:>0$}", width, offset = i)?; |
|||
} |
|||
// Newline for rows |
|||
writeln!(f)?; |
|||
// Loop through the rows |
|||
for row in 0..self.size { |
|||
// Write the row number |
|||
write!(f, "{row:>0$}", width, row = row)?; |
|||
// Loop through the columns |
|||
for col in 0..self.size { |
|||
// Get the value of the cell as 1 or 0 |
|||
let p = self.cells[row * self.size + col] as usize; |
|||
// Write the column value |
|||
write!(f, " {col:>0$}", width, col = p)?; |
|||
} |
|||
// Newline for next row |
|||
writeln!(f)?; |
|||
} |
|||
// Return Formatter result |
|||
Ok(()) |
|||
} |
|||
} |
|||
fn main() { |
|||
let mut rng = rand::thread_rng(); |
|||
// The board size |
|||
let size: usize = 3; |
|||
// The target board |
|||
let target: Board = Board::random(&mut rng, size); |
|||
// The user board |
|||
let mut board: Board = Board::new(size); |
|||
// How many moves taken |
|||
let mut moves: u32 = 0; |
|||
// Loop until win or quit |
|||
'mainloop: loop { |
|||
// User input |
|||
let mut input: String; |
|||
// Write the boards |
|||
println!("Target:\n{}\nBoard:\n{}", &target, &board); |
|||
// User input loop |
|||
'userinput: loop { |
|||
// Prompt |
|||
print!("\nFlip? [q|[r|c]#] "); |
|||
// Flush stdout to write the previous print, if we can't then exit |
|||
match std::io::stdout().flush() { |
|||
Ok(_) => {} |
|||
Err(e) => { |
|||
println!("Error: cannot flush stdout: {}", e); |
|||
break 'mainloop; |
|||
} |
|||
}; |
|||
// Reset input for each loop |
|||
input = String::new(); |
|||
// Read user input |
|||
match std::io::stdin().read_line(&mut input) { |
|||
Ok(_) => { |
|||
input = input.trim().to_string(); |
|||
// Get the first character |
|||
let rc: char = match input.chars().next() { |
|||
Some(c) => c, |
|||
None => { |
|||
println!("Error: No input"); |
|||
continue 'userinput; |
|||
} |
|||
}; |
|||
// Make sure input is r, c, or q |
|||
if rc != 'r' && rc != 'c' && rc != 'q' { |
|||
println!("Error: '{}': Must use 'r'ow or 'c'olumn or 'q'uit", rc); |
|||
continue 'userinput; |
|||
} |
|||
// If input is q, exit game |
|||
if rc == 'q' { |
|||
println!("Thanks for playing!"); |
|||
break 'mainloop; |
|||
} |
|||
// If input is r or c, get the number after |
|||
let n: usize = match input[1..].to_string().parse() { |
|||
Ok(x) => { |
|||
// If we're within bounds, return the parsed number |
|||
if x < size { |
|||
x |
|||
} else { |
|||
println!( |
|||
"Error: Must specify a row or column within size({})", |
|||
size |
|||
); |
|||
continue 'userinput; |
|||
} |
|||
} |
|||
Err(_) => { |
|||
println!( |
|||
"Error: '{}': Unable to parse row or column number", |
|||
input[1..].to_string() |
|||
); |
|||
continue 'userinput; |
|||
} |
|||
}; |
|||
// Flip the row or column specified |
|||
match rc { |
|||
'r' => board.fliprow(n), |
|||
'c' => board.flipcol(n), |
|||
_ => { |
|||
// We want to panic here because should NEVER |
|||
// have anything other than 'r' or 'c' here |
|||
panic!("How did you end up here?"); |
|||
} |
|||
}; |
|||
// Increment moves |
|||
moves += 1; |
|||
println!("Moves taken: {}", moves); |
|||
break 'userinput; |
|||
} |
|||
Err(e) => { |
|||
println!("Error reading input: {}", e); |
|||
break 'mainloop; |
|||
} |
|||
} |
|||
} // 'userinput |
|||
if board == target { |
|||
println!("You win!"); |
|||
break; |
|||
} |
|||
} // 'mainloop |
|||
} |
|||
</lang> |
|||
{{out}} |
|||
<pre> |
|||
Target: |
|||
0 1 2 |
|||
0 1 0 0 |
|||
1 0 0 0 |
|||
2 0 1 1 |
|||
Board: |
|||
0 1 2 |
|||
0 0 0 0 |
|||
1 0 0 0 |
|||
2 0 0 0 |
|||
Flip? [q|[r|c]#] r1 |
|||
Moves taken: 1 |
|||
Target: |
|||
0 1 2 |
|||
0 1 0 0 |
|||
1 0 0 0 |
|||
2 0 1 1 |
|||
Board: |
|||
0 1 2 |
|||
0 0 0 0 |
|||
1 1 1 1 |
|||
2 0 0 0 |
|||
</pre> |
</pre> |
||