Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-09-09 02:22:35 +03:00
parent e980f7a46e
commit 7b7b7cb866
3 changed files with 2294 additions and 2 deletions

2109
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,3 +4,9 @@ version = "0.1.0"
edition = "2024"
[dependencies]
chrono = "0.4.42"
crossterm = "0.29.0"
reqwest = { version = "0.12.23", features = ["json", "blocking", "rustls-tls"] }
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.143"
tokio = { version = "1.47.1", features = ["full"] }

View File

@@ -1,3 +1,180 @@
fn main() {
println!("Hello, world!");
use std::io::stdout;
use chrono::Local;
use crossterm::{
cursor::{MoveTo, SetCursorStyle},
event::{self, Event, KeyCode},
execute,
style::{Color, ResetColor, SetForegroundColor},
terminal::{self, Clear, ClearType},
};
use reqwest::header::{HeaderMap, HeaderValue};
use serde::Deserialize;
#[derive(Deserialize)]
struct WordleAPIResponse {
word: String,
}
async fn fetch_todays_word() -> Option<String> {
let today = Local::now().date_naive();
let formatted = today.format("%Y-%m-%d").to_string();
let url = format!(
"https://wordle-api3.p.rapidapi.com/getwordfor/{}",
formatted
);
let mut headers = HeaderMap::new();
headers.insert(
"x-rapidapi-host",
HeaderValue::from_static("wordle-api3.p.rapidapi.com"),
);
headers.insert(
"x-rapidapi-key",
HeaderValue::from_static("a8b1e12b84msh561bcaca54d5e16p10166djsn45cb233e0888"),
);
let client = reqwest::Client::new();
let resp = client
.get(url)
.headers(headers)
.send()
.await
.ok()?
.json::<WordleAPIResponse>()
.await
.ok()?;
Some(resp.word)
}
#[derive(Clone, PartialEq)]
enum GameState {
Playing,
Win,
Lose,
}
struct Game {
word: String,
guesses: [String; 6],
guess_count: u64,
state: GameState,
}
impl Game {
pub fn from(word: String) -> Self {
Self {
word,
guesses: std::array::from_fn(|_| String::from(" ")),
guess_count: 0,
state: GameState::Playing,
}
}
pub fn print_board(&self) {
execute!(stdout(), Clear(ClearType::All), MoveTo(0, 0)).unwrap();
print!("┏━━━━━┓\r\n");
for (ri, row) in self.guesses.iter().enumerate() {
print!("");
for (i, ch) in row.chars().enumerate() {
if ri != self.guess_count as usize {
if self.word.chars().nth(i).unwrap() == ch {
execute!(stdout(), SetForegroundColor(Color::Green)).unwrap();
} else if self.word.contains(ch) && ch != ' ' {
execute!(stdout(), SetForegroundColor(Color::Yellow)).unwrap();
}
}
print!("{ch}");
execute!(stdout(), ResetColor).unwrap();
}
print!("\r\n");
}
print!("┗━━━━━┛\r\n");
let row = &self.guesses[self.guess_count as usize];
let caret_idx = row.find(' ').unwrap_or(4) as u16; // 0..4
execute!(stdout(), MoveTo(1 + caret_idx, 1 + self.guess_count as u16)).unwrap();
}
pub fn process_input(&mut self, ch: char) {
let row_i = self.guess_count as usize;
let s = &self.guesses[row_i];
let mut chars: Vec<char> = s.chars().collect();
let pos = chars.iter().position(|&c| c == ' ').unwrap_or(chars.len());
match ch {
'\n' => {
if pos == chars.len() {
if *s == self.word {
self.state = GameState::Win;
}
if row_i == self.guesses.len() - 1 {
if self.state == GameState::Playing {
self.state = GameState::Lose;
}
} else {
self.guess_count += 1;
}
}
}
' ' => {
if pos > 0 {
let idx = if pos == chars.len() { pos - 1 } else { pos - 1 };
chars[idx] = ' ';
self.guesses[row_i] = chars.into_iter().collect();
}
}
c if c.is_ascii_alphabetic() && pos < chars.len() => {
chars[pos] = c.to_ascii_lowercase();
self.guesses[row_i] = chars.into_iter().collect();
}
_ => {}
}
}
pub fn get_state(&self) -> GameState {
self.state.clone()
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let todays_word = fetch_todays_word()
.await
.expect("Failed to fetch today's word.");
println!("Today's word is {}", todays_word);
execute!(stdout(), SetCursorStyle::SteadyBlock)?;
terminal::enable_raw_mode()?;
let mut game = Game::from(todays_word);
loop {
game.print_board();
if let Event::Key(key_event) = event::read()? {
match key_event.code {
KeyCode::Char(c) => game.process_input(c),
KeyCode::Enter => game.process_input('\n'),
KeyCode::Backspace => game.process_input(' '),
KeyCode::Esc => break,
_ => {}
}
}
if game.get_state() != GameState::Playing {
break;
}
}
game.print_board();
execute!(stdout(), MoveTo(0, 8))?;
terminal::disable_raw_mode()?;
match game.get_state() {
GameState::Win => println!("You win!"),
GameState::Lose => println!("You lose!"),
_ => {}
}
Ok(())
}