Compare commits

..

10 Commits

Author SHA1 Message Date
thatscringebro
c93c5c3134 started making the game 2025-07-16 10:06:01 -04:00
thatscringebro
7417a1b6b2 changed to egui + made the taquin_lib
Signed-off-by: thatscringebro <thatscringebro@tutanota.com>
2025-07-11 15:30:03 -04:00
thatscringebro
3e9f790d82 début TP2 2025-04-16 21:29:49 -04:00
thatscringebro
22149290da finished cli ui, end of TP 2025-03-26 10:45:20 -04:00
thatscringebro
25dbbc4f4b fixed the bad stuff 2025-03-26 09:50:18 -04:00
thatscringebro
b4f6db070a added event on key press with crossterm 2025-03-22 23:36:28 -04:00
thatscringebro
87f8a916dc affichage
Signed-off-by: thatscringebro <thatscringebro@tutanota.com>
2025-03-14 14:10:16 -04:00
thatscringebro
a55a0f7960 Potentiel finish pour la classe 2025-03-12 15:46:36 -04:00
thatscringebro
4da9010a09 new stuff idk 2025-03-12 15:19:33 -04:00
thatscringebro
94220e85a3 Move to struct, cause it's what we do fr fr 2025-03-12 13:50:54 -04:00
8 changed files with 588 additions and 3 deletions

View File

@@ -6,3 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
crossterm = "0.28.1"
left-pad = "1.0.1"
rand = "0.9.0"

View File

@@ -1,8 +1,183 @@
mod models; mod models;
use crate::models::taquin::*; use crate::models::taquin::*;
use crossterm::event::{read, Event, KeyCode, KeyEvent};
use crossterm::terminal::enable_raw_mode;
use left_pad::leftpad;
use std::io::{self, Write};
fn main() { fn main() {
println!("Hello, world!"); let _ = enable_raw_mode();
let mut taquin_game: TaquinGame = TaquinGame::new();
let mut playing: bool = true;
move_to(); taquin_game.init_grid();
draw_game(&taquin_game);
while playing {
let mut message = "";
let mut user_cheated: bool = false;
let mut game_won: bool = false;
match read().unwrap() {
Event::Key(KeyEvent {
code: KeyCode::Char('q'),
..
}) => {
playing = false;
message = "Bye bye!";
}
Event::Key(KeyEvent {
code: KeyCode::Char('r'),
..
}) => {
taquin_game.init_grid();
user_cheated = false;
message = "New game started";
}
Event::Key(KeyEvent {
code: KeyCode::Char('s'),
..
}) => {
taquin_game.resolve();
user_cheated = true;
message = "Too hard?";
}
Event::Key(KeyEvent {
code: KeyCode::Up, ..
}) => {
if !taquin_game.move_to(Directions::Up) {
message = "Cannot move any more up";
}
}
Event::Key(KeyEvent {
code: KeyCode::Down,
..
}) => {
if !taquin_game.move_to(Directions::Down) {
message = "Cannot move any more down";
}
}
Event::Key(KeyEvent {
code: KeyCode::Left,
..
}) => {
if !taquin_game.move_to(Directions::Left) {
message = "Cannot move any more left";
}
}
Event::Key(KeyEvent {
code: KeyCode::Right,
..
}) => {
if !taquin_game.move_to(Directions::Right) {
message = "Cannot move any more right";
}
}
_ => {
message = "error";
}
}
if taquin_game.is_grid_done() {
if !user_cheated {
message = "Yahoo! Good job!";
}
game_won = true;
}
draw_game(&taquin_game);
print!("{}\r\n", message);
let _ = io::stdout().flush();
if game_won {
print!("Press 'R' to start a new game, 'Q' to quit.");
let _ = io::stdout().flush();
let mut good_choice = false;
while !good_choice {
match read().unwrap() {
Event::Key(KeyEvent {
code: KeyCode::Char('q'),
..
}) => {
playing = false;
good_choice = true;
}
Event::Key(KeyEvent {
code: KeyCode::Char('r'),
..
}) => {
taquin_game.init_grid();
good_choice = true;
}
_ => {}
}
}
}
}
}
fn draw_game(game: &TaquinGame) {
print!("{}[2J", 27 as char);
let grid = game.grid();
for i in 0..ROWS * 2 + 1 {
for j in 0..COLUMNS * 2 + 1 {
if i == 0 {
if j == 0 {
print!("");
} else if j == COLUMNS * 2 {
print!("");
} else if !is_even(j) {
print!("═══");
} else {
print!("");
}
} else if i == ROWS * 2 {
if j == 0 {
print!("");
} else if j == COLUMNS * 2 {
print!("");
} else if !is_even(j) {
print!("═══");
} else {
print!("");
}
} else if !is_even(i) {
if !is_even(j) {
print!("{}", leftpad(grid[i / 2][j / 2].to_string(), 3));
} else {
print!("");
}
} else {
if j == 0 {
print!("");
} else if j == COLUMNS * 2 {
print!("");
} else if !is_even(j) {
print!("═══");
} else {
print!("");
}
}
}
if i == 0 {
print!("Score: {}", game.score());
} else if i == 1 {
print!("High score: {}", game.high_score());
}
print!("\r\n");
}
print!("Arrows to move\r\n");
print!("Q: Quit\r\n");
print!("R: Restart\r\n");
print!("S: Solve\r\n");
let _ = io::stdout().flush();
}
fn is_even(num: usize) -> bool {
return num % 2 == 0;
} }

View File

@@ -1 +1,162 @@
pub fn move_to() {} use rand::Rng;
pub const COLUMNS: usize = 4;
pub const ROWS: usize = 4;
pub enum Directions {
Down,
Up,
Right,
Left,
}
pub struct TaquinGame {
empty_coord: [usize; 2],
grid: [[u8; COLUMNS]; ROWS],
rng: rand::rngs::ThreadRng,
score: usize,
high_score: usize,
}
impl TaquinGame {
pub fn new() -> Self {
return TaquinGame {
empty_coord: [0, 0],
grid: [[0; COLUMNS]; ROWS],
rng: rand::rng(),
score: 0,
high_score: 0,
};
}
pub fn score(&self) -> usize {
return self.score;
}
pub fn grid(&self) -> [[u8; COLUMNS]; ROWS] {
return self.grid;
}
pub fn high_score(&self) -> usize {
return self.high_score;
}
pub fn move_to(&mut self, direction: Directions) -> bool {
let (mut row, mut column) = (self.empty_coord[0], self.empty_coord[1]);
let mut valid_direction = false;
match direction {
Directions::Down => {
if row + 1 < ROWS {
row += 1;
valid_direction = true;
}
}
Directions::Up => {
if row > 0 {
row -= 1;
valid_direction = true;
}
}
Directions::Right => {
if column + 1 < COLUMNS {
column += 1;
valid_direction = true;
}
}
Directions::Left => {
if column > 0 {
column -= 1;
valid_direction = true;
}
}
}
if valid_direction {
self.exchange(self.empty_coord[1], self.empty_coord[0], column, row);
self.score += 1;
}
return valid_direction;
}
pub fn is_grid_done(&mut self) -> bool {
let mut error_found = false;
let mut value = 0;
for i in 0..ROWS {
if !error_found {
for j in 0..COLUMNS {
if !error_found {
if self.grid[i][j] != value {
error_found = true;
}
value += 1;
} else {
break;
}
}
} else {
break;
}
}
if !error_found {
if self.score > 0 && (self.high_score == 0 || self.score < self.high_score) {
self.high_score = self.score;
}
}
return !error_found;
}
pub fn init_grid(&mut self) {
let move_nmbr = self.rng.random_range(400..500);
let mut rnd_nmbr = [0u8, 0, 0, 0];
self.resolve();
for _ in 0..move_nmbr {
loop {
rnd_nmbr[3] = self.rng.random_range(0..4);
if rnd_nmbr[0] != rnd_nmbr[3] {
break;
}
}
rnd_nmbr = [rnd_nmbr[1], rnd_nmbr[2], rnd_nmbr[3], rnd_nmbr[3]];
self.move_to(match rnd_nmbr[3] {
0 => Directions::Up,
1 => Directions::Down,
2 => Directions::Left,
3 => Directions::Right,
_ => Directions::Up,
});
}
self.score = 0;
}
fn exchange(&mut self, ax: usize, ay: usize, bx: usize, by: usize) {
if ax < COLUMNS && ay < ROWS && bx < COLUMNS && by < ROWS {
let temp = self.grid[by][bx];
self.grid[by][bx] = self.grid[ay][ax];
self.grid[ay][ax] = temp;
self.empty_coord = [by, bx];
}
}
pub fn resolve(&mut self) {
let mut value = 0;
self.score = 0;
self.empty_coord = [0, 0];
for i in 0..ROWS {
for j in 0..COLUMNS {
self.grid[i][j] = value;
value += 1;
}
}
}
}

View File

@@ -0,0 +1,8 @@
[package]
name = "taquin_gui"
version = "0.1.0"
edition = "2024"
[dependencies]
eframe = "0.32.0"
taquin_lib = { path = "../taquin_lib" }

View File

@@ -0,0 +1,69 @@
use taquin_lib::{self, TaquinGame};
use eframe::egui;
fn main() -> eframe::Result {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
..Default::default()
};
eframe::run_native(
"Taquin Game",
options,
Box::new(|cc| Ok(Box::<TaquinGUI>::default())),
)
}
struct TaquinGUI {
first_frame: bool,
taquin_game: TaquinGame,
}
impl Default for TaquinGUI {
fn default() -> Self {
Self {
first_frame: true,
taquin_game: TaquinGame::new(),
}
}
}
impl eframe::App for TaquinGUI {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
if self.first_frame {
self.first_frame = false;
self.taquin_game.init_grid();
}
egui::CentralPanel::default().show(ctx, |ui| {
egui::Grid::new("taquin_grid").show(ui, |ui| {
for row in self.taquin_game.grid() {
for column in row {
let text = match column {
0 => "".to_string(),
_ => column.to_string(),
};
egui::Frame::default().show(ui, |ui| {
ui.label(egui::RichText::new(text).strong().size(20.0));
});
}
ui.end_row();
}
});
if ui.button("up").clicked() {
self.taquin_game.move_to(taquin_lib::Directions::Up);
};
if ui.button("down").clicked() {
self.taquin_game.move_to(taquin_lib::Directions::Down);
};
if ui.button("left").clicked() {
self.taquin_game.move_to(taquin_lib::Directions::Left);
};
if ui.button("right").clicked() {
self.taquin_game.move_to(taquin_lib::Directions::Right);
};
});
}
}

View File

@@ -0,0 +1,7 @@
[package]
name = "taquin_lib"
version = "0.1.0"
edition = "2024"
[dependencies]
rand = "0.9.1"

View File

@@ -0,0 +1,162 @@
use rand::Rng;
pub const COLUMNS: usize = 4;
pub const ROWS: usize = 4;
pub enum Directions {
Down,
Up,
Right,
Left,
}
pub struct TaquinGame {
empty_coord: [usize; 2],
grid: [[u8; COLUMNS]; ROWS],
rng: rand::rngs::ThreadRng,
score: usize,
high_score: usize,
}
impl TaquinGame {
pub fn new() -> Self {
return TaquinGame {
empty_coord: [0, 0],
grid: [[0; COLUMNS]; ROWS],
rng: rand::rng(),
score: 0,
high_score: 0,
};
}
pub fn score(&self) -> usize {
return self.score;
}
pub fn grid(&self) -> [[u8; COLUMNS]; ROWS] {
return self.grid;
}
pub fn high_score(&self) -> usize {
return self.high_score;
}
pub fn move_to(&mut self, direction: Directions) -> bool {
let (mut row, mut column) = (self.empty_coord[0], self.empty_coord[1]);
let mut valid_direction = false;
match direction {
Directions::Down => {
if row + 1 < ROWS {
row += 1;
valid_direction = true;
}
}
Directions::Up => {
if row > 0 {
row -= 1;
valid_direction = true;
}
}
Directions::Right => {
if column + 1 < COLUMNS {
column += 1;
valid_direction = true;
}
}
Directions::Left => {
if column > 0 {
column -= 1;
valid_direction = true;
}
}
}
if valid_direction {
self.exchange(self.empty_coord[1], self.empty_coord[0], column, row);
self.score += 1;
}
return valid_direction;
}
pub fn is_grid_done(&mut self) -> bool {
let mut error_found = false;
let mut value = 0;
for i in 0..ROWS {
if !error_found {
for j in 0..COLUMNS {
if !error_found {
if self.grid[i][j] != value {
error_found = true;
}
value += 1;
} else {
break;
}
}
} else {
break;
}
}
if !error_found {
if self.score > 0 && (self.high_score == 0 || self.score < self.high_score) {
self.high_score = self.score;
}
}
return !error_found;
}
pub fn init_grid(&mut self) {
let move_nmbr = self.rng.random_range(400..500);
let mut rnd_nmbr = [0u8, 0, 0, 0];
self.resolve();
for _ in 0..move_nmbr {
loop {
rnd_nmbr[3] = self.rng.random_range(0..4);
if rnd_nmbr[0] != rnd_nmbr[3] {
break;
}
}
rnd_nmbr = [rnd_nmbr[1], rnd_nmbr[2], rnd_nmbr[3], rnd_nmbr[3]];
self.move_to(match rnd_nmbr[3] {
0 => Directions::Up,
1 => Directions::Down,
2 => Directions::Left,
3 => Directions::Right,
_ => Directions::Up,
});
}
self.score = 0;
}
fn exchange(&mut self, ax: usize, ay: usize, bx: usize, by: usize) {
if ax < COLUMNS && ay < ROWS && bx < COLUMNS && by < ROWS {
let temp = self.grid[by][bx];
self.grid[by][bx] = self.grid[ay][ax];
self.grid[ay][ax] = temp;
self.empty_coord = [by, bx];
}
}
pub fn resolve(&mut self) {
let mut value = 0;
self.score = 0;
self.empty_coord = [0, 0];
for i in 0..ROWS {
for j in 0..COLUMNS {
self.grid[i][j] = value;
value += 1;
}
}
}
}