Compare commits
4 Commits
d521d69eac
...
8d02ea1b6a
Author | SHA1 | Date |
---|---|---|
kirbylife | 8d02ea1b6a | |
kirbylife | f8108ed7b7 | |
kirbylife | d64996d5dd | |
kirbylife | 8420721145 |
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2023 Kirbylife <hola@kirbylife.dev>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,23 @@
|
||||||
|
## Worsdle
|
||||||
|
|
||||||
|
A simple [Wordle](https://es.wikipedia.org/wiki/Wordle) clone written in Rust and Yem with spanish words.
|
||||||
|
|
||||||
|
### Dependencies to build
|
||||||
|
|
||||||
|
- [Trunk](https://trunkrs.dev/)
|
||||||
|
|
||||||
|
### How to build to production
|
||||||
|
|
||||||
|
1. `cargo install --locked trunk`
|
||||||
|
2. `git clone https://git.kirbylife.dev/kirbylife/worsdle`
|
||||||
|
3. `cd worsdle`
|
||||||
|
4. `trunk build --release`
|
||||||
|
5. The build should be in the `dist/` folder.
|
||||||
|
|
||||||
|
### Run a dev server
|
||||||
|
|
||||||
|
1. `trunk serve`
|
||||||
|
|
||||||
|
### Contribute
|
||||||
|
|
||||||
|
If you want to add a new word to the list, you can pass it to me through any social network.
|
|
@ -2,12 +2,13 @@
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||||
<title>Yew App</title>
|
<title>Yew App</title>
|
||||||
<link rel="stylesheet" href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css">
|
<link href="https://maxst.icons8.com/vue-static/landings/line-awesome/line-awesome/1.3.0/css/line-awesome.min.css"
|
||||||
<link data-trunk rel="css" href="static/css/styles.css" />
|
rel="stylesheet">
|
||||||
<link data-trunk rel="css" href="static/css/normalize.css" />
|
<link data-trunk href="static/css/styles.css" rel="css"/>
|
||||||
|
<link data-trunk href="static/css/normalize.css" rel="css"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Hola mundo</h1>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::consts::{Key, KeyboardKeyType, VirtualKey};
|
use crate::consts::{Key, KeyboardKeyType, VirtualKey, Status};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
|
@ -21,7 +21,19 @@ pub fn Keyboard(props: &KeyboardProps) -> Html {
|
||||||
<div class="keyboard"> {
|
<div class="keyboard"> {
|
||||||
for (props.keyboard).iter().map(|vk: &VirtualKey| {
|
for (props.keyboard).iter().map(|vk: &VirtualKey| {
|
||||||
match vk.key {
|
match vk.key {
|
||||||
KeyboardKeyType::CharKey(c) => html! { <button onclick={ handle_click(Key::CharKey(c)) }> { c } </button> },
|
KeyboardKeyType::CharKey(c) => html! {
|
||||||
|
<button
|
||||||
|
class={
|
||||||
|
match vk.status {
|
||||||
|
Some(Status::Found) => "correct",
|
||||||
|
Some(Status::NotFound) => "missed",
|
||||||
|
Some(Status::Almost) => "almost",
|
||||||
|
None => ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onclick={ handle_click(Key::CharKey(c)) }
|
||||||
|
> { c } </button>
|
||||||
|
},
|
||||||
KeyboardKeyType::Backspace => html! { <button onclick={ handle_click(Key::Backspace) } class="key-big"><i class="las la-undo"></i></button> },
|
KeyboardKeyType::Backspace => html! { <button onclick={ handle_click(Key::Backspace) } class="key-big"><i class="las la-undo"></i></button> },
|
||||||
KeyboardKeyType::Enter => html! { <button onclick={ handle_click(Key::Enter) } class="key-big"> { "Enviar" } </button> }
|
KeyboardKeyType::Enter => html! { <button onclick={ handle_click(Key::Enter) } class="key-big"> { "Enviar" } </button> }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::consts::GameResult;
|
use crate::consts::{GameResult, MAX_ATTEMPTS};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use crate::consts::{Attempts, Status};
|
use crate::consts::{Attempts, Status};
|
||||||
|
|
||||||
|
@ -14,20 +14,26 @@ pub fn ResultBoard(props: &ResultBoardProps) -> Html {
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class="result-board"> {
|
<div class="result-board">
|
||||||
|
<p>
|
||||||
|
{ format!("Palabra encontrada en {}/{} intentos", attempts.fields.len(), MAX_ATTEMPTS) }
|
||||||
|
</p><br/>
|
||||||
|
<p> {
|
||||||
for attempts.fields.iter().map(|attempt| {
|
for attempts.fields.iter().map(|attempt| {
|
||||||
html_nested! {
|
html_nested! {
|
||||||
<p class={ "result-board" }> {
|
<> {
|
||||||
for attempt.iter().map(| att| {
|
for attempt.iter().map(| att| {
|
||||||
match att.status {
|
match att.status {
|
||||||
Status::Found => html_nested! { <> { "🟩" } </> },
|
Status::Found => html_nested! { <> { "🟩" } </> },
|
||||||
Status::Almost => html_nested! { <span>{ "🟨" }</span> },
|
Status::Almost => html_nested! { <>{ "🟨" }</> },
|
||||||
Status::NotFound => html_nested! { <span>{ "⬜" }</span> }
|
Status::NotFound => html_nested! { <>{ "⬛" }</> }
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} <br/></>
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} </p>
|
} </p>
|
||||||
}
|
</div>
|
||||||
})
|
|
||||||
} </div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,15 @@ pub enum KeyboardKeyType {
|
||||||
CharKey(char),
|
CharKey(char),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl KeyboardKeyType {
|
||||||
|
pub fn cmp_char(&self, char_to_compare: &char) -> bool {
|
||||||
|
match self {
|
||||||
|
KeyboardKeyType::CharKey(ch) => ch == char_to_compare,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum Status {
|
pub enum Status {
|
||||||
NotFound,
|
NotFound,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::consts::{AttemptField, Attempts, GameResult, Key, VirtualKey, MAX_ATTEMPTS};
|
use crate::consts::{AttemptField, Attempts, GameResult, Key, VirtualKey, MAX_ATTEMPTS, Status};
|
||||||
use crate::services::words::{get_word_of_the_day, WORDS};
|
use crate::services::words::{get_word_of_the_day, WORDS};
|
||||||
use crate::utils::{evaluate_status, new_empty_virtual_keyboard};
|
use crate::utils::{evaluate_status, new_empty_virtual_keyboard};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -64,8 +64,22 @@ impl UseBoardHandle {
|
||||||
char_field: att,
|
char_field: att,
|
||||||
status,
|
status,
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect::<Vec<AttemptField>>();
|
||||||
new_attempts.fields.push(new_attempt_row);
|
new_attempts.fields.push(new_attempt_row.clone());
|
||||||
|
|
||||||
|
let mut new_keyboard = (*self.virtual_keyboard).clone();
|
||||||
|
for key in new_keyboard.iter_mut() {
|
||||||
|
for attempt_field in new_attempt_row.iter() {
|
||||||
|
if key.key.cmp_char(&attempt_field.char_field) {
|
||||||
|
if key.status.is_none() {
|
||||||
|
key.status = Some(attempt_field.status.clone());
|
||||||
|
} else if key.status == Some(Status::Almost) && attempt_field.status == Status::Found {
|
||||||
|
key.status = Some(Status::Found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.virtual_keyboard.set(new_keyboard);
|
||||||
|
|
||||||
self.attempts.set(new_attempts);
|
self.attempts.set(new_attempts);
|
||||||
*self.attempt_index.borrow_mut() += 1;
|
*self.attempt_index.borrow_mut() += 1;
|
||||||
|
@ -88,8 +102,7 @@ pub fn use_board() -> UseBoardHandle {
|
||||||
let current_attempt = use_state(|| "".to_string());
|
let current_attempt = use_state(|| "".to_string());
|
||||||
let attempts: UseStateHandle<Attempts> = use_state(|| Attempts::new());
|
let attempts: UseStateHandle<Attempts> = use_state(|| Attempts::new());
|
||||||
let attempt_index = use_mut_ref(|| 0usize);
|
let attempt_index = use_mut_ref(|| 0usize);
|
||||||
// let answer = use_memo(|_| get_word_of_the_day(), None::<()>);
|
let answer = use_memo(|_| get_word_of_the_day(), None::<()>);
|
||||||
let answer = use_memo(|_| "ABEJA".to_owned(), None::<()>);
|
|
||||||
let virtual_keyboard = use_state(|| new_empty_virtual_keyboard().into());
|
let virtual_keyboard = use_state(|| new_empty_virtual_keyboard().into());
|
||||||
let result = use_state(|| None::<GameResult>);
|
let result = use_state(|| None::<GameResult>);
|
||||||
|
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -40,7 +40,10 @@ fn App() -> Html {
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
|
<header>
|
||||||
<h1>{ "Worsdle" }</h1>
|
<h1>{ "Worsdle" }</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
<Board
|
<Board
|
||||||
attempts={ (*board.attempts).clone() }
|
attempts={ (*board.attempts).clone() }
|
||||||
attempt_index={ *board.attempt_index.borrow() }
|
attempt_index={ *board.attempt_index.borrow() }
|
||||||
|
@ -57,6 +60,16 @@ fn App() -> Html {
|
||||||
}
|
}
|
||||||
} </span>
|
} </span>
|
||||||
</div>
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<small> {
|
||||||
|
html_nested! {
|
||||||
|
<>
|
||||||
|
{ "Hecho por " } <a href={ "https://kirbylife.dev" }> { "kirbylife" } </a>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
} </small>
|
||||||
|
</footer>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,34 +4,40 @@ body > * {
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 99dvh;
|
min-height: 99dvh;
|
||||||
margin-left: 150px;
|
display: grid;
|
||||||
margin-right: 150px;
|
grid-template-rows: auto 1fr auto;
|
||||||
display: flex;
|
justify-items: center;
|
||||||
align-items: center;
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
display: flex !important;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
.board {
|
||||||
width: 25%;
|
|
||||||
height: 25%;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
width: min-content;
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||||
justify-items: center;
|
justify-items: center;
|
||||||
justify-self: auto;
|
gap: 5px;
|
||||||
gap: 10px;
|
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.board > p {
|
.board > p {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 70px;
|
width: 50px;
|
||||||
height: 70px;
|
aspect-ratio: 1/1;
|
||||||
border: 3px solid #444444;
|
border: 3px solid #444444;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 40px;
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.correct {
|
.correct {
|
||||||
|
@ -69,18 +75,15 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-board {
|
.result-board {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
flex-direction: column;
|
||||||
width: fit-content;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-board > span {
|
.result-board > p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboard {
|
.keyboard {
|
||||||
|
@ -90,6 +93,8 @@ body {
|
||||||
|
|
||||||
.keyboard > button {
|
.keyboard > button {
|
||||||
grid-column: span 2;
|
grid-column: span 2;
|
||||||
|
width: auto;
|
||||||
|
height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.keyboard > button.key-big {
|
.keyboard > button.key-big {
|
||||||
|
|
Loading…
Reference in New Issue