Impl buttons indicator that won the game
This commit is contained in:
parent
b902b1c7b4
commit
76e6d3be52
5 changed files with 247 additions and 21 deletions
|
@ -143,6 +143,20 @@
|
|||
button.magenta {
|
||||
background: #F0F;
|
||||
}
|
||||
button.win {
|
||||
animation-duration: 400ms;
|
||||
animation-name: blink;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
@keyframes blink {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</html>
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
use crate::constants::{COLS, ROWS};
|
||||
use crate::state::{BoardState, BoardType};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum WinType {
|
||||
Horizontal(usize),
|
||||
Vertical(usize),
|
||||
DiagonalUp(usize),
|
||||
DiagonalDown(usize),
|
||||
None,
|
||||
}
|
||||
|
||||
/// Returns a BoardState if win/draw, None if game is still going
|
||||
pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
||||
pub fn check_win_draw(board: &BoardType) -> Option<(BoardState, WinType)> {
|
||||
let mut has_empty_slot = false;
|
||||
for slot in board {
|
||||
match slot.get() {
|
||||
|
@ -10,28 +19,32 @@ pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
|||
has_empty_slot = true;
|
||||
break;
|
||||
}
|
||||
BoardState::Cyan | BoardState::Magenta => (),
|
||||
BoardState::Cyan
|
||||
| BoardState::CyanWin
|
||||
| BoardState::Magenta
|
||||
| BoardState::MagentaWin => (),
|
||||
}
|
||||
}
|
||||
|
||||
if !has_empty_slot {
|
||||
return Some(BoardState::Empty);
|
||||
return Some((BoardState::Empty, WinType::None));
|
||||
}
|
||||
|
||||
let check_result = |state| -> Option<BoardState> {
|
||||
match state {
|
||||
BoardState::Empty => None,
|
||||
BoardState::Cyan => Some(BoardState::Cyan),
|
||||
BoardState::Magenta => Some(BoardState::Magenta),
|
||||
BoardState::Cyan | BoardState::CyanWin => Some(BoardState::Cyan),
|
||||
BoardState::Magenta | BoardState::MagentaWin => Some(BoardState::Magenta),
|
||||
}
|
||||
};
|
||||
|
||||
// check horizontals
|
||||
for y in 0..(ROWS as usize) {
|
||||
for x in 0..((COLS - 3) as usize) {
|
||||
let result = check_result(has_right_horizontal_at_idx(x + y * (COLS as usize), board));
|
||||
let idx = x + y * (COLS as usize);
|
||||
let result = check_result(has_right_horizontal_at_idx(idx, board));
|
||||
if result.is_some() {
|
||||
return result;
|
||||
return Some((result.unwrap(), WinType::Horizontal(idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,9 +52,10 @@ pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
|||
// check verticals
|
||||
for y in 0..((ROWS - 3) as usize) {
|
||||
for x in 0..(COLS as usize) {
|
||||
let result = check_result(has_down_vertical_at_idx(x + y * (COLS as usize), board));
|
||||
let idx = x + y * (COLS as usize);
|
||||
let result = check_result(has_down_vertical_at_idx(idx, board));
|
||||
if result.is_some() {
|
||||
return result;
|
||||
return Some((result.unwrap(), WinType::Vertical(idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,9 +63,10 @@ pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
|||
// check up diagonals
|
||||
for y in 3..(ROWS as usize) {
|
||||
for x in 0..((COLS - 3) as usize) {
|
||||
let result = check_result(has_right_up_diagonal_at_idx(x + y * (COLS as usize), board));
|
||||
let idx = x + y * (COLS as usize);
|
||||
let result = check_result(has_right_up_diagonal_at_idx(idx, board));
|
||||
if result.is_some() {
|
||||
return result;
|
||||
return Some((result.unwrap(), WinType::DiagonalUp(idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,12 +74,10 @@ pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
|||
// check down diagonals
|
||||
for y in 0..((ROWS - 3) as usize) {
|
||||
for x in 0..((COLS - 3) as usize) {
|
||||
let result = check_result(has_right_down_diagonal_at_idx(
|
||||
x + y * (COLS as usize),
|
||||
board,
|
||||
));
|
||||
let idx = x + y * (COLS as usize);
|
||||
let result = check_result(has_right_down_diagonal_at_idx(idx, board));
|
||||
if result.is_some() {
|
||||
return result;
|
||||
return Some((result.unwrap(), WinType::DiagonalDown(idx)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,3 +52,13 @@ pub fn append_to_info_text(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn element_append_class(document: &Document, id: &str, class: &str) -> Result<(), String> {
|
||||
let element = document
|
||||
.get_element_by_id(id)
|
||||
.ok_or_else(|| format!("Failed to get element with id \"{}\"", id))?;
|
||||
let new_class = format!("{} {}", element.class_name(), class);
|
||||
element.set_class_name(&new_class);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ pub enum BoardState {
|
|||
Empty,
|
||||
Cyan,
|
||||
Magenta,
|
||||
CyanWin,
|
||||
MagentaWin,
|
||||
}
|
||||
|
||||
impl Default for BoardState {
|
||||
|
@ -46,7 +48,9 @@ impl Display for BoardState {
|
|||
match *self {
|
||||
BoardState::Empty => f.write_str("open"),
|
||||
BoardState::Cyan => f.write_str("cyan"),
|
||||
BoardState::CyanWin => f.write_str("cyan win"),
|
||||
BoardState::Magenta => f.write_str("magenta"),
|
||||
BoardState::MagentaWin => f.write_str("magenta win"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +68,22 @@ impl BoardState {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
*self == BoardState::Empty
|
||||
}
|
||||
|
||||
pub fn into_win(&self) -> Self {
|
||||
match *self {
|
||||
BoardState::Empty => BoardState::Empty,
|
||||
BoardState::Cyan | BoardState::CyanWin => BoardState::CyanWin,
|
||||
BoardState::Magenta | BoardState::MagentaWin => BoardState::MagentaWin,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_win(&self) -> Self {
|
||||
match *self {
|
||||
BoardState::Empty => BoardState::Empty,
|
||||
BoardState::Cyan | BoardState::CyanWin => BoardState::Cyan,
|
||||
BoardState::Magenta | BoardState::MagentaWin => BoardState::MagentaWin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -84,8 +104,8 @@ impl Display for Turn {
|
|||
impl From<BoardState> for Turn {
|
||||
fn from(board_state: BoardState) -> Self {
|
||||
match board_state {
|
||||
BoardState::Empty | BoardState::Cyan => Turn::CyanPlayer,
|
||||
BoardState::Magenta => Turn::MagentaPlayer,
|
||||
BoardState::Empty | BoardState::Cyan | BoardState::CyanWin => Turn::CyanPlayer,
|
||||
BoardState::Magenta | BoardState::MagentaWin => Turn::MagentaPlayer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS};
|
||||
use crate::game_logic::check_win_draw;
|
||||
use crate::html_helper::{append_to_info_text, get_window_document};
|
||||
use crate::game_logic::{check_win_draw, WinType};
|
||||
use crate::html_helper::{append_to_info_text, element_append_class, get_window_document};
|
||||
use crate::state::{BoardState, GameState, SharedState, Turn};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
@ -281,7 +281,7 @@ impl Component for Wrapper {
|
|||
|
||||
// check for win
|
||||
let check_win_draw_opt = check_win_draw(&shared.board);
|
||||
if let Some(endgame_state) = check_win_draw_opt {
|
||||
if let Some((endgame_state, win_type)) = check_win_draw_opt {
|
||||
if endgame_state == BoardState::Empty {
|
||||
// draw
|
||||
let text_append_result = append_to_info_text(
|
||||
|
@ -307,6 +307,175 @@ impl Component for Wrapper {
|
|||
if let Err(e) = text_append_result {
|
||||
log::warn!("ERROR: text append to info_text0 failed: {}", e);
|
||||
}
|
||||
|
||||
match win_type {
|
||||
WinType::Horizontal(idx) => {
|
||||
let append_result =
|
||||
element_append_class(&document, &format!("slot{}", idx), "win");
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 1),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 2),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 3),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
|
||||
shared.board[idx].replace(shared.board[idx].get().into_win());
|
||||
shared.board[idx + 1]
|
||||
.replace(shared.board[idx + 1].get().into_win());
|
||||
shared.board[idx + 2]
|
||||
.replace(shared.board[idx + 2].get().into_win());
|
||||
shared.board[idx + 3]
|
||||
.replace(shared.board[idx + 3].get().into_win());
|
||||
}
|
||||
WinType::Vertical(idx) => {
|
||||
let append_result =
|
||||
element_append_class(&document, &format!("slot{}", idx), "win");
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 1 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 2 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 3 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
|
||||
shared.board[idx].replace(shared.board[idx].get().into_win());
|
||||
shared.board[idx + 1 * (COLS as usize)].replace(
|
||||
shared.board[idx + 1 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
shared.board[idx + 2 * (COLS as usize)].replace(
|
||||
shared.board[idx + 2 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
shared.board[idx + 3 * (COLS as usize)].replace(
|
||||
shared.board[idx + 3 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
}
|
||||
WinType::DiagonalUp(idx) => {
|
||||
let append_result =
|
||||
element_append_class(&document, &format!("slot{}", idx), "win");
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 1 - 1 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 2 - 2 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 3 - 3 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
|
||||
shared.board[idx].replace(shared.board[idx].get().into_win());
|
||||
shared.board[idx + 1 - 1 * (COLS as usize)].replace(
|
||||
shared.board[idx + 1 - 1 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
shared.board[idx + 2 - 2 * (COLS as usize)].replace(
|
||||
shared.board[idx + 2 - 2 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
shared.board[idx + 3 - 3 * (COLS as usize)].replace(
|
||||
shared.board[idx + 3 - 3 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
}
|
||||
WinType::DiagonalDown(idx) => {
|
||||
let append_result =
|
||||
element_append_class(&document, &format!("slot{}", idx), "win");
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 1 + 1 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 2 + 2 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
let append_result = element_append_class(
|
||||
&document,
|
||||
&format!("slot{}", idx + 3 + 3 * (COLS as usize)),
|
||||
"win",
|
||||
);
|
||||
if let Err(e) = append_result {
|
||||
log::warn!("ERROR: element_append_class failed: {}", e);
|
||||
}
|
||||
|
||||
shared.board[idx].replace(shared.board[idx].get().into_win());
|
||||
shared.board[idx + 1 + 1 * (COLS as usize)].replace(
|
||||
shared.board[idx + 1 + 1 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
shared.board[idx + 2 + 2 * (COLS as usize)].replace(
|
||||
shared.board[idx + 2 + 2 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
shared.board[idx + 3 + 3 * (COLS as usize)].replace(
|
||||
shared.board[idx + 3 + 3 * (COLS as usize)].get().into_win(),
|
||||
);
|
||||
}
|
||||
WinType::None => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
let text_append_result =
|
||||
|
|
Loading…
Reference in a new issue