2109
Cargo.lock
generated
Normal file
2109
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,3 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[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"] }
|
||||||
|
|||||||
181
src/main.rs
181
src/main.rs
@@ -1,3 +1,180 @@
|
|||||||
fn main() {
|
use std::io::stdout;
|
||||||
println!("Hello, world!");
|
|
||||||
|
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(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user