2022-04-06 11:49:54 +00:00
//Four Line Dropper Frontend/Backend - A webapp that allows one to play a game of Four Line Dropper
//Copyright (C) 2022 Stephen Seo
//
//This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
2022-03-10 07:17:16 +00:00
use crate ::ai ::AIDifficulty ;
2022-04-01 08:17:42 +00:00
use crate ::constants ::{ COLS , ROWS } ;
use crate ::game_logic ::{ check_win_draw , WinType } ;
2022-03-31 12:02:52 +00:00
2022-04-06 04:39:12 +00:00
use serde ::{ Deserialize , Serialize } ;
2022-04-27 03:47:45 +00:00
use std ::cell ::{ Cell , RefCell } ;
2022-04-01 08:17:42 +00:00
use std ::collections ::hash_set ::HashSet ;
2022-03-02 08:51:14 +00:00
use std ::fmt ::Display ;
2022-03-02 06:18:10 +00:00
use std ::rc ::Rc ;
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-27 03:47:45 +00:00
#[ derive(Clone, Debug, PartialEq, Eq) ]
2022-03-04 07:22:30 +00:00
pub enum GameState {
MainMenu ,
2022-03-10 07:17:16 +00:00
SinglePlayer ( Turn , AIDifficulty ) ,
2022-03-04 07:22:30 +00:00
LocalMultiplayer ,
2022-04-04 09:25:17 +00:00
NetworkedMultiplayer {
paired : bool ,
current_side : Option < Turn > ,
current_turn : Turn ,
2022-04-27 03:47:45 +00:00
phrase : Option < String > ,
2022-04-04 09:25:17 +00:00
} ,
2022-03-09 08:29:53 +00:00
PostGameResults ( BoardState ) ,
2022-03-04 07:22:30 +00:00
}
2022-04-04 09:25:17 +00:00
impl GameState {
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-27 03:47:45 +00:00
pub fn is_networked_multiplayer ( & self ) -> bool {
2022-04-04 09:25:17 +00:00
matches! (
2022-04-27 03:47:45 +00:00
* self ,
2022-04-04 09:25:17 +00:00
GameState ::NetworkedMultiplayer {
2022-04-08 02:01:33 +00:00
paired : _ ,
current_side : _ ,
2022-04-27 03:47:45 +00:00
current_turn : _ ,
phrase : _ ,
2022-04-04 09:25:17 +00:00
}
)
}
2022-04-06 09:43:17 +00:00
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
pub fn set_networked_paired ( & mut self ) {
if let GameState ::NetworkedMultiplayer {
ref mut paired ,
current_side : _ ,
current_turn : _ ,
2022-04-27 03:47:45 +00:00
phrase : _ ,
2022-04-06 09:43:17 +00:00
} = self
{
* paired = true ;
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
pub fn get_networked_current_side ( & self ) -> Option < Turn > {
if let GameState ::NetworkedMultiplayer {
2022-04-08 02:01:33 +00:00
paired : _ ,
2022-04-06 09:43:17 +00:00
current_side ,
2022-04-08 02:01:33 +00:00
current_turn : _ ,
2022-04-27 03:47:45 +00:00
phrase : _ ,
2022-04-06 09:43:17 +00:00
} = * self
{
current_side
} else {
None
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
pub fn set_networked_current_side ( & mut self , side : Option < Turn > ) {
if let GameState ::NetworkedMultiplayer {
2022-04-08 02:01:33 +00:00
paired : _ ,
2022-04-06 09:43:17 +00:00
ref mut current_side ,
2022-04-08 02:01:33 +00:00
current_turn : _ ,
2022-04-27 03:47:45 +00:00
phrase : _ ,
2022-04-06 09:43:17 +00:00
} = self
{
* current_side = side ;
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
pub fn get_current_turn ( & self ) -> Turn {
if let GameState ::SinglePlayer ( turn , _ ) = * self {
turn
} else if let GameState ::NetworkedMultiplayer {
paired : _ ,
current_side : _ ,
current_turn ,
2022-04-27 03:47:45 +00:00
phrase : _ ,
2022-04-06 09:43:17 +00:00
} = * self
{
current_turn
} else {
Turn ::CyanPlayer
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
pub fn set_networked_current_turn ( & mut self , turn : Turn ) {
if let GameState ::NetworkedMultiplayer {
paired : _ ,
current_side : _ ,
ref mut current_turn ,
2022-04-27 03:47:45 +00:00
phrase : _ ,
2022-04-06 09:43:17 +00:00
} = self
{
* current_turn = turn ;
}
}
2022-04-27 03:47:45 +00:00
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-27 03:47:45 +00:00
pub fn get_phrase ( & self ) -> Option < String > {
if let GameState ::NetworkedMultiplayer {
paired : _ ,
current_side : _ ,
current_turn : _ ,
phrase ,
} = self
{
phrase . clone ( )
} else {
None
}
}
2022-04-29 10:21:59 +00:00
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-29 10:21:59 +00:00
pub fn get_singleplayer_current_side ( & self ) -> Option < Turn > {
if let GameState ::SinglePlayer ( turn , _ ) = * self {
Some ( turn )
} else {
None
}
}
2022-04-04 09:25:17 +00:00
}
2022-03-04 07:22:30 +00:00
impl Default for GameState {
fn default ( ) -> Self {
GameState ::MainMenu
}
}
2022-03-07 04:14:57 +00:00
impl From < MainMenuMessage > for GameState {
fn from ( msg : MainMenuMessage ) -> Self {
match msg {
2022-03-10 07:17:16 +00:00
MainMenuMessage ::SinglePlayer ( t , ai ) = > GameState ::SinglePlayer ( t , ai ) ,
2022-03-07 04:14:57 +00:00
MainMenuMessage ::LocalMultiplayer = > GameState ::LocalMultiplayer ,
2022-04-27 03:47:45 +00:00
MainMenuMessage ::NetworkedMultiplayer ( phrase ) = > GameState ::NetworkedMultiplayer {
2022-04-04 09:25:17 +00:00
paired : false ,
current_side : None ,
current_turn : Turn ::CyanPlayer ,
2022-04-27 03:47:45 +00:00
phrase ,
2022-04-04 09:25:17 +00:00
} ,
2022-03-07 04:14:57 +00:00
}
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-02 06:18:10 +00:00
#[ derive(Copy, Clone, Debug, PartialEq, Eq) ]
pub enum BoardState {
Empty ,
Cyan ,
Magenta ,
2022-03-09 09:10:13 +00:00
CyanWin ,
MagentaWin ,
2022-03-02 06:18:10 +00:00
}
impl Default for BoardState {
fn default ( ) -> Self {
Self ::Empty
}
}
2022-03-02 08:51:14 +00:00
impl Display for BoardState {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
match * self {
BoardState ::Empty = > f . write_str ( " open " ) ,
BoardState ::Cyan = > f . write_str ( " cyan " ) ,
2022-03-09 09:10:13 +00:00
BoardState ::CyanWin = > f . write_str ( " cyan win " ) ,
2022-03-02 08:51:14 +00:00
BoardState ::Magenta = > f . write_str ( " magenta " ) ,
2022-03-09 09:10:13 +00:00
BoardState ::MagentaWin = > f . write_str ( " magenta win " ) ,
2022-03-02 08:51:14 +00:00
}
}
}
2022-03-03 09:01:46 +00:00
impl From < Turn > for BoardState {
fn from ( t : Turn ) -> Self {
match t {
Turn ::CyanPlayer = > BoardState ::Cyan ,
Turn ::MagentaPlayer = > BoardState ::Magenta ,
}
}
}
impl BoardState {
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-03 09:01:46 +00:00
pub fn is_empty ( & self ) -> bool {
* self = = BoardState ::Empty
}
2022-03-09 09:10:13 +00:00
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-10 06:43:03 +00:00
pub fn is_win ( self ) -> bool {
match self {
BoardState ::Empty | BoardState ::Cyan | BoardState ::Magenta = > false ,
BoardState ::CyanWin | BoardState ::MagentaWin = > true ,
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-09 09:14:01 +00:00
pub fn into_win ( self ) -> Self {
match self {
2022-03-09 09:10:13 +00:00
BoardState ::Empty = > BoardState ::Empty ,
BoardState ::Cyan | BoardState ::CyanWin = > BoardState ::CyanWin ,
BoardState ::Magenta | BoardState ::MagentaWin = > BoardState ::MagentaWin ,
}
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code, clippy::wrong_self_convention) ]
pub fn from_win ( self ) -> Self {
match self {
2022-03-09 09:10:13 +00:00
BoardState ::Empty = > BoardState ::Empty ,
BoardState ::Cyan | BoardState ::CyanWin = > BoardState ::Cyan ,
2022-03-09 10:29:00 +00:00
BoardState ::Magenta | BoardState ::MagentaWin = > BoardState ::Magenta ,
2022-03-09 09:10:13 +00:00
}
}
2022-03-03 09:01:46 +00:00
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-02 06:18:10 +00:00
#[ derive(Copy, Clone, Debug, PartialEq, Eq) ]
pub enum Turn {
CyanPlayer ,
MagentaPlayer ,
}
2022-03-02 08:51:14 +00:00
impl Display for Turn {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
match * self {
Turn ::CyanPlayer = > f . write_str ( " CyanPlayer " ) ,
Turn ::MagentaPlayer = > f . write_str ( " MagentaPlayer " ) ,
}
}
}
2022-03-03 09:01:46 +00:00
impl From < BoardState > for Turn {
fn from ( board_state : BoardState ) -> Self {
match board_state {
2022-03-09 09:10:13 +00:00
BoardState ::Empty | BoardState ::Cyan | BoardState ::CyanWin = > Turn ::CyanPlayer ,
BoardState ::Magenta | BoardState ::MagentaWin = > Turn ::MagentaPlayer ,
2022-03-03 09:01:46 +00:00
}
}
}
2022-03-03 08:36:51 +00:00
impl Turn {
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-03 08:36:51 +00:00
pub fn get_color ( & self ) -> & str {
match * self {
Turn ::CyanPlayer = > " cyan " ,
Turn ::MagentaPlayer = > " magenta " ,
}
}
2022-03-03 09:01:46 +00:00
pub fn get_opposite ( & self ) -> Self {
match * self {
Turn ::CyanPlayer = > Turn ::MagentaPlayer ,
Turn ::MagentaPlayer = > Turn ::CyanPlayer ,
}
}
2022-03-03 08:36:51 +00:00
}
2022-03-07 04:12:05 +00:00
pub type BoardType = [ Rc < Cell < BoardState > > ; 56 ] ;
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-09 08:29:53 +00:00
pub fn new_empty_board ( ) -> BoardType {
[
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
Rc ::new ( Cell ::new ( BoardState ::default ( ) ) ) ,
]
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-10 07:39:26 +00:00
pub fn board_deep_clone ( board : & BoardType ) -> BoardType {
let cloned_board = new_empty_board ( ) ;
for i in 0 .. board . len ( ) {
cloned_board [ i ] . replace ( board [ i ] . get ( ) ) ;
}
cloned_board
}
2022-03-10 06:43:03 +00:00
pub type PlacedType = [ Rc < Cell < bool > > ; 56 ] ;
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-10 06:43:03 +00:00
pub fn new_placed ( ) -> PlacedType {
[
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
Rc ::new ( Cell ::new ( false ) ) ,
]
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-02 06:18:10 +00:00
#[ derive(Clone, Debug, PartialEq) ]
pub struct SharedState {
2022-03-07 04:12:05 +00:00
pub board : BoardType ,
2022-04-27 03:47:45 +00:00
pub game_state : Rc < RefCell < GameState > > ,
2022-03-02 08:51:14 +00:00
pub turn : Rc < Cell < Turn > > ,
2022-03-10 06:43:03 +00:00
pub placed : PlacedType ,
2022-03-02 06:18:10 +00:00
}
impl Default for SharedState {
fn default ( ) -> Self {
Self {
// cannot use [<type>; 56] because Rc does not impl Copy
2022-03-09 08:29:53 +00:00
board : new_empty_board ( ) ,
2022-04-27 03:47:45 +00:00
game_state : Rc ::new ( RefCell ::new ( GameState ::default ( ) ) ) ,
2022-03-02 08:51:14 +00:00
turn : Rc ::new ( Cell ::new ( Turn ::CyanPlayer ) ) ,
2022-03-10 06:43:03 +00:00
placed : new_placed ( ) ,
2022-03-02 06:18:10 +00:00
}
}
}
2022-03-29 05:41:04 +00:00
// This enum moved from yew_components module so that this module would have no
// dependencies on the yew_components module
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-27 03:47:45 +00:00
#[ derive(Clone, Debug, PartialEq, Eq) ]
2022-03-29 05:41:04 +00:00
pub enum MainMenuMessage {
SinglePlayer ( Turn , AIDifficulty ) ,
LocalMultiplayer ,
2022-04-27 03:47:45 +00:00
NetworkedMultiplayer ( Option < String > ) ,
2022-03-29 05:41:04 +00:00
}
2022-03-31 12:02:52 +00:00
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-31 12:02:52 +00:00
pub fn new_string_board ( ) -> String {
let mut board = String ::with_capacity ( 56 ) ;
for _i in 0 .. 56 {
board . push ( 'a' ) ;
}
board
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-03-31 12:02:52 +00:00
pub fn board_from_string ( board_string : String ) -> BoardType {
let board = new_empty_board ( ) ;
for ( idx , c ) in board_string . chars ( ) . enumerate ( ) {
match c {
'a' = > board [ idx ] . replace ( BoardState ::Empty ) ,
2022-04-06 09:43:17 +00:00
'b' | 'f' = > board [ idx ] . replace ( BoardState ::Cyan ) ,
2022-04-06 11:09:22 +00:00
'd' | 'h' = > board [ idx ] . replace ( BoardState ::CyanWin ) ,
2022-04-06 09:43:17 +00:00
'c' | 'g' = > board [ idx ] . replace ( BoardState ::Magenta ) ,
2022-04-06 11:09:22 +00:00
'e' | 'i' = > board [ idx ] . replace ( BoardState ::MagentaWin ) ,
2022-03-31 12:02:52 +00:00
_ = > BoardState ::Empty ,
} ;
}
board
}
2022-04-01 08:17:42 +00:00
/// Returns the board as a String, and None if game has not ended, Empty if game
/// ended in a draw, or a player if that player has won
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-29 06:21:22 +00:00
pub fn string_from_board ( board : & BoardType , placed : usize ) -> ( String , Option < BoardState > ) {
2022-03-31 12:02:52 +00:00
let mut board_string = String ::with_capacity ( 56 ) ;
// check for winning pieces
let mut win_set : HashSet < usize > = HashSet ::new ( ) ;
2022-04-29 06:21:22 +00:00
let win_opt = check_win_draw ( board ) ;
2022-04-01 08:17:42 +00:00
if let Some ( ( _board_state , win_type ) ) = win_opt {
2022-03-31 12:02:52 +00:00
match win_type {
WinType ::Horizontal ( pos ) = > {
for i in pos .. ( pos + 4 ) {
win_set . insert ( i ) ;
}
}
WinType ::Vertical ( pos ) = > {
for i in 0 .. 4 {
win_set . insert ( pos + i * COLS as usize ) ;
}
}
WinType ::DiagonalUp ( pos ) = > {
for i in 0 .. 4 {
win_set . insert ( pos + i - i * COLS as usize ) ;
}
}
WinType ::DiagonalDown ( pos ) = > {
for i in 0 .. 4 {
win_set . insert ( pos + i + i * COLS as usize ) ;
}
}
WinType ::None = > ( ) ,
}
}
// set values to String
let mut is_full = true ;
for ( idx , board_state ) in board . iter ( ) . enumerate ( ) . take ( ( COLS * ROWS ) as usize ) {
board_string . push ( match board_state . get ( ) {
BoardState ::Empty = > {
is_full = false ;
'a'
}
BoardState ::Cyan | BoardState ::CyanWin = > {
if win_set . contains ( & idx ) {
2022-04-06 11:09:22 +00:00
if idx = = placed {
'h'
} else {
'd'
}
2022-03-31 12:02:52 +00:00
} else if idx = = placed {
'f'
} else {
'b'
}
}
BoardState ::Magenta | BoardState ::MagentaWin = > {
if win_set . contains ( & idx ) {
2022-04-06 11:09:22 +00:00
if idx = = placed {
'i'
} else {
'e'
}
2022-03-31 12:02:52 +00:00
} else if idx = = placed {
'g'
} else {
'c'
}
}
} ) ;
}
2022-04-01 08:17:42 +00:00
if is_full & & win_set . is_empty ( ) {
( board_string , Some ( BoardState ::Empty ) )
} else if ! win_set . is_empty ( ) {
2022-04-28 13:12:36 +00:00
let winning_char : char =
board_string . chars ( ) . collect ::< Vec < char > > ( ) [ * win_set . iter ( ) . next ( ) . unwrap ( ) ] ;
2022-04-01 08:17:42 +00:00
(
board_string . clone ( ) ,
2022-04-28 13:12:36 +00:00
if winning_char = = 'd' | | winning_char = = 'h' {
2022-04-01 08:17:42 +00:00
Some ( BoardState ::CyanWin )
} else {
Some ( BoardState ::MagentaWin )
} ,
)
} else {
( board_string , None )
}
2022-03-31 12:02:52 +00:00
}
2022-04-04 09:25:17 +00:00
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 04:39:12 +00:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct PairingRequestResponse {
pub r#type : String ,
pub id : u32 ,
pub status : String ,
pub color : Option < String > ,
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct PairingStatusResponse {
pub r#type : String ,
pub status : String ,
pub color : Option < String > ,
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct GameStateResponse {
pub r#type : String ,
pub status : String ,
pub board : Option < String > ,
2022-04-29 08:24:42 +00:00
pub peer_emote : Option < String > ,
2022-04-30 07:27:43 +00:00
pub updated_time : Option < String > ,
2022-04-06 09:43:17 +00:00
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct PlaceTokenResponse {
pub r#type : String ,
pub status : String ,
pub board : String ,
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-29 09:30:41 +00:00
#[ derive(Debug, Serialize, Deserialize) ]
pub struct SendEmoteRequestResponse {
pub r#type : String ,
pub status : String ,
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
#[ derive(Copy, Clone, Debug, PartialEq, Eq) ]
pub enum NetworkedGameState {
CyanTurn ,
MagentaTurn ,
CyanWon ,
MagentaWon ,
Draw ,
Disconnected ,
InternalError ,
NotPaired ,
UnknownID ,
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-06 09:43:17 +00:00
#[ derive(Copy, Clone, Debug, PartialEq, Eq) ]
pub enum PlacedEnum {
Accepted ,
Illegal ,
NotYourTurn ,
Other ( NetworkedGameState ) ,
}
2022-05-03 04:17:54 +00:00
#[ allow(dead_code) ]
2022-04-29 08:24:42 +00:00
#[ derive(Copy, Clone, PartialEq, Eq, Debug) ]
pub enum EmoteEnum {
Smile ,
Neutral ,
Frown ,
Think ,
}
impl Display for EmoteEnum {
fn fmt ( & self , f : & mut std ::fmt ::Formatter < '_ > ) -> std ::fmt ::Result {
match * self {
EmoteEnum ::Smile = > f . write_str ( " smile " ) ,
EmoteEnum ::Neutral = > f . write_str ( " neutral " ) ,
EmoteEnum ::Frown = > f . write_str ( " frown " ) ,
EmoteEnum ::Think = > f . write_str ( " think " ) ,
}
}
}
impl TryFrom < & str > for EmoteEnum {
type Error = ( ) ;
fn try_from ( value : & str ) -> Result < Self , Self ::Error > {
match value . to_lowercase ( ) . as_str ( ) {
" smile " = > Ok ( Self ::Smile ) ,
" neutral " = > Ok ( Self ::Neutral ) ,
" frown " = > Ok ( Self ::Frown ) ,
" think " = > Ok ( Self ::Think ) ,
_ = > Err ( ( ) ) ,
}
}
}
impl From < EmoteEnum > for String {
fn from ( e : EmoteEnum ) -> Self {
match e {
EmoteEnum ::Smile = > " smile " . into ( ) ,
EmoteEnum ::Neutral = > " neutral " . into ( ) ,
EmoteEnum ::Frown = > " frown " . into ( ) ,
EmoteEnum ::Think = > " think " . into ( ) ,
}
}
}
2022-04-29 09:30:41 +00:00
impl EmoteEnum {
pub fn get_unicode ( & self ) -> char {
match * self {
EmoteEnum ::Smile = > '🙂' ,
EmoteEnum ::Neutral = > '😐' ,
EmoteEnum ::Frown = > '🙁' ,
EmoteEnum ::Think = > '🤔' ,
}
}
}
2022-04-04 09:25:17 +00:00
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn test_is_networked_multiplayer_enum ( ) {
let state = GameState ::MainMenu ;
assert! ( ! state . is_networked_multiplayer ( ) ) ;
let state = GameState ::LocalMultiplayer ;
assert! ( ! state . is_networked_multiplayer ( ) ) ;
let state = GameState ::NetworkedMultiplayer {
paired : false ,
current_side : None ,
current_turn : Turn ::CyanPlayer ,
2022-04-27 03:47:45 +00:00
phrase : None ,
2022-04-04 09:25:17 +00:00
} ;
assert! ( state . is_networked_multiplayer ( ) ) ;
let state = GameState ::NetworkedMultiplayer {
paired : true ,
current_side : Some ( Turn ::CyanPlayer ) ,
current_turn : Turn ::MagentaPlayer ,
2022-04-27 03:47:45 +00:00
phrase : None ,
2022-04-04 09:25:17 +00:00
} ;
2022-04-27 03:47:45 +00:00
assert! ( state . is_networked_multiplayer ( ) ) ;
2022-04-04 09:25:17 +00:00
}
2022-04-28 13:12:36 +00:00
#[ test ]
fn test_board_string ( ) {
let board = new_empty_board ( ) ;
board [ 49 ] . set ( BoardState ::Cyan ) ;
board [ 51 ] . set ( BoardState ::Cyan ) ;
board [ 52 ] . set ( BoardState ::Cyan ) ;
board [ 53 ] . set ( BoardState ::Cyan ) ;
board [ 54 ] . set ( BoardState ::Cyan ) ;
board [ 55 ] . set ( BoardState ::Magenta ) ;
2022-04-29 08:24:42 +00:00
let ( board_string , state_opt ) = string_from_board ( & board , 51 ) ;
2022-04-28 13:12:36 +00:00
let board_chars : Vec < char > = board_string . chars ( ) . collect ( ) ;
assert_eq! ( board_chars [ 49 ] , 'b' ) ;
assert_eq! ( board_chars [ 50 ] , 'a' ) ;
assert_eq! ( board_chars [ 51 ] , 'h' ) ;
assert_eq! ( board_chars [ 52 ] , 'd' ) ;
assert_eq! ( board_chars [ 53 ] , 'd' ) ;
assert_eq! ( board_chars [ 54 ] , 'd' ) ;
assert_eq! ( board_chars [ 55 ] , 'c' ) ;
assert_eq! ( state_opt , Some ( BoardState ::CyanWin ) ) ;
board [ 49 ] . set ( BoardState ::Magenta ) ;
board [ 51 ] . set ( BoardState ::Magenta ) ;
board [ 52 ] . set ( BoardState ::Magenta ) ;
board [ 53 ] . set ( BoardState ::Magenta ) ;
board [ 54 ] . set ( BoardState ::Magenta ) ;
board [ 55 ] . set ( BoardState ::Cyan ) ;
2022-04-29 08:24:42 +00:00
let ( board_string , state_opt ) = string_from_board ( & board , 51 ) ;
2022-04-28 13:12:36 +00:00
let board_chars : Vec < char > = board_string . chars ( ) . collect ( ) ;
assert_eq! ( board_chars [ 49 ] , 'c' ) ;
assert_eq! ( board_chars [ 50 ] , 'a' ) ;
assert_eq! ( board_chars [ 51 ] , 'i' ) ;
assert_eq! ( board_chars [ 52 ] , 'e' ) ;
assert_eq! ( board_chars [ 53 ] , 'e' ) ;
assert_eq! ( board_chars [ 54 ] , 'e' ) ;
assert_eq! ( board_chars [ 55 ] , 'b' ) ;
assert_eq! ( state_opt , Some ( BoardState ::MagentaWin ) ) ;
}
2022-04-04 09:25:17 +00:00
}