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 {
|
button.magenta {
|
||||||
background: #F0F;
|
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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
use crate::constants::{COLS, ROWS};
|
use crate::constants::{COLS, ROWS};
|
||||||
use crate::state::{BoardState, BoardType};
|
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
|
/// 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;
|
let mut has_empty_slot = false;
|
||||||
for slot in board {
|
for slot in board {
|
||||||
match slot.get() {
|
match slot.get() {
|
||||||
|
@ -10,28 +19,32 @@ pub fn check_win_draw(board: &BoardType) -> Option<BoardState> {
|
||||||
has_empty_slot = true;
|
has_empty_slot = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
BoardState::Cyan | BoardState::Magenta => (),
|
BoardState::Cyan
|
||||||
|
| BoardState::CyanWin
|
||||||
|
| BoardState::Magenta
|
||||||
|
| BoardState::MagentaWin => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_empty_slot {
|
if !has_empty_slot {
|
||||||
return Some(BoardState::Empty);
|
return Some((BoardState::Empty, WinType::None));
|
||||||
}
|
}
|
||||||
|
|
||||||
let check_result = |state| -> Option<BoardState> {
|
let check_result = |state| -> Option<BoardState> {
|
||||||
match state {
|
match state {
|
||||||
BoardState::Empty => None,
|
BoardState::Empty => None,
|
||||||
BoardState::Cyan => Some(BoardState::Cyan),
|
BoardState::Cyan | BoardState::CyanWin => Some(BoardState::Cyan),
|
||||||
BoardState::Magenta => Some(BoardState::Magenta),
|
BoardState::Magenta | BoardState::MagentaWin => Some(BoardState::Magenta),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// check horizontals
|
// check horizontals
|
||||||
for y in 0..(ROWS as usize) {
|
for y in 0..(ROWS as usize) {
|
||||||
for x in 0..((COLS - 3) 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() {
|
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
|
// check verticals
|
||||||
for y in 0..((ROWS - 3) as usize) {
|
for y in 0..((ROWS - 3) as usize) {
|
||||||
for x in 0..(COLS 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() {
|
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
|
// check up diagonals
|
||||||
for y in 3..(ROWS as usize) {
|
for y in 3..(ROWS as usize) {
|
||||||
for x in 0..((COLS - 3) 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() {
|
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
|
// check down diagonals
|
||||||
for y in 0..((ROWS - 3) as usize) {
|
for y in 0..((ROWS - 3) as usize) {
|
||||||
for x in 0..((COLS - 3) as usize) {
|
for x in 0..((COLS - 3) as usize) {
|
||||||
let result = check_result(has_right_down_diagonal_at_idx(
|
let idx = x + y * (COLS as usize);
|
||||||
x + y * (COLS as usize),
|
let result = check_result(has_right_down_diagonal_at_idx(idx, board));
|
||||||
board,
|
|
||||||
));
|
|
||||||
if result.is_some() {
|
if result.is_some() {
|
||||||
return result;
|
return Some((result.unwrap(), WinType::DiagonalDown(idx)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,3 +52,13 @@ pub fn append_to_info_text(
|
||||||
|
|
||||||
Ok(())
|
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,
|
Empty,
|
||||||
Cyan,
|
Cyan,
|
||||||
Magenta,
|
Magenta,
|
||||||
|
CyanWin,
|
||||||
|
MagentaWin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BoardState {
|
impl Default for BoardState {
|
||||||
|
@ -46,7 +48,9 @@ impl Display for BoardState {
|
||||||
match *self {
|
match *self {
|
||||||
BoardState::Empty => f.write_str("open"),
|
BoardState::Empty => f.write_str("open"),
|
||||||
BoardState::Cyan => f.write_str("cyan"),
|
BoardState::Cyan => f.write_str("cyan"),
|
||||||
|
BoardState::CyanWin => f.write_str("cyan win"),
|
||||||
BoardState::Magenta => f.write_str("magenta"),
|
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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
*self == BoardState::Empty
|
*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)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -84,8 +104,8 @@ impl Display for Turn {
|
||||||
impl From<BoardState> for Turn {
|
impl From<BoardState> for Turn {
|
||||||
fn from(board_state: BoardState) -> Self {
|
fn from(board_state: BoardState) -> Self {
|
||||||
match board_state {
|
match board_state {
|
||||||
BoardState::Empty | BoardState::Cyan => Turn::CyanPlayer,
|
BoardState::Empty | BoardState::Cyan | BoardState::CyanWin => Turn::CyanPlayer,
|
||||||
BoardState::Magenta => Turn::MagentaPlayer,
|
BoardState::Magenta | BoardState::MagentaWin => Turn::MagentaPlayer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS};
|
use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS};
|
||||||
use crate::game_logic::check_win_draw;
|
use crate::game_logic::{check_win_draw, WinType};
|
||||||
use crate::html_helper::{append_to_info_text, get_window_document};
|
use crate::html_helper::{append_to_info_text, element_append_class, get_window_document};
|
||||||
use crate::state::{BoardState, GameState, SharedState, Turn};
|
use crate::state::{BoardState, GameState, SharedState, Turn};
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -281,7 +281,7 @@ impl Component for Wrapper {
|
||||||
|
|
||||||
// check for win
|
// check for win
|
||||||
let check_win_draw_opt = check_win_draw(&shared.board);
|
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 {
|
if endgame_state == BoardState::Empty {
|
||||||
// draw
|
// draw
|
||||||
let text_append_result = append_to_info_text(
|
let text_append_result = append_to_info_text(
|
||||||
|
@ -307,6 +307,175 @@ impl Component for Wrapper {
|
||||||
if let Err(e) = text_append_result {
|
if let Err(e) = text_append_result {
|
||||||
log::warn!("ERROR: text append to info_text0 failed: {}", e);
|
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 =
|
let text_append_result =
|
||||||
|
|
Loading…
Reference in a new issue