]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
Refactorings and bug fixes
authorStephen Seo <seo.disparate@gmail.com>
Fri, 1 Apr 2022 08:17:42 +0000 (17:17 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 1 Apr 2022 08:17:42 +0000 (17:17 +0900)
Protocol was also updated to match the output from the backend.
Fixed "id" not returning as an integer from the backend.

back_end/src/db_handler.rs
back_end/src/json_handlers.rs
backend_protocol_specification.md
front_end/src/state.rs

index 08b6bb029427918892050bac3265ae62fc3edcb5..cdc6d9e96f102c34c0d9db5a3451a4999a0d8884 100644 (file)
@@ -1,5 +1,5 @@
 use crate::constants::{COLS, ROWS};
-use crate::state::{BoardState, new_string_board, board_from_string, string_from_board};
+use crate::state::{board_from_string, new_string_board, string_from_board, BoardState};
 
 use std::sync::mpsc::{Receiver, SyncSender};
 use std::{fmt, thread};
@@ -64,14 +64,18 @@ impl From<i64> for DBGameState {
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum DBPlaceStatus {
     Accepted,
-    GameEnded,
+    GameEndedDraw,
+    GameEndedCyanWon,
+    GameEndedMagentaWon,
 }
 
 impl fmt::Display for DBPlaceStatus {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
             DBPlaceStatus::Accepted => write!(f, "accepted"),
-            DBPlaceStatus::GameEnded => write!(f, "game_ended"),
+            DBPlaceStatus::GameEndedDraw => write!(f, "game_ended_draw"),
+            DBPlaceStatus::GameEndedCyanWon => write!(f, "game_ended_cyan_won"),
+            DBPlaceStatus::GameEndedMagentaWon => write!(f, "game_ended_magenta_won"),
         }
     }
 }
@@ -512,17 +516,14 @@ impl DBHandler {
                 Ok((DBGameState::InternalError, None))
             } else if cyan_opt.is_none() || magenta_opt.is_none() {
                 // One player disconnected
-                let player_remove_result = self.disconnect_player(Some(conn), player_id);
-                if player_remove_result.is_err() {
-                    // Failed to disconnect remaining player
+                self.disconnect_player(Some(conn), player_id).ok();
+                // Remove the game(s) with disconnected players
+                if self.clear_empty_games(Some(conn)).is_err() {
                     Ok((DBGameState::InternalError, None))
+                } else if status == 2 || status == 3 {
+                    Ok((DBGameState::from(status), Some(board)))
                 } else {
-                    // Remove the game(s) with disconnected players
-                    if self.clear_empty_games(Some(conn)).is_err() {
-                        Ok((DBGameState::InternalError, None))
-                    } else {
-                        Ok((DBGameState::OpponentDisconnected, Some(board)))
-                    }
+                    Ok((DBGameState::OpponentDisconnected, Some(board)))
                 }
             } else {
                 // Game in progress, or other state depending on "status"
@@ -637,6 +638,10 @@ impl DBHandler {
                 if let (Ok(cyan_id_opt), Ok(magenta_id_opt)) = (cyan_id_result, magenta_id_result) {
                     if let (Some(cyan_id), Some(_magenta_id)) = (cyan_id_opt, magenta_id_opt) {
                         Ok(Ok((cyan_id == player_id, status, board)))
+                    } else if (2..=4).contains(&status) {
+                        // game has ended, don't return error
+                        // first result will be safely ignored
+                        Ok(Ok((false, status, board)))
                     } else {
                         Ok(Err(DBPlaceError::OpponentDisconnected))
                     }
@@ -654,9 +659,8 @@ impl DBHandler {
 
         // if opponent has disconnected, disconnect the remaining player as well
         if let Err(DBPlaceError::OpponentDisconnected) = query_result {
-            if self.disconnect_player(Some(conn), player_id).is_err()
-                || self.clear_empty_games(Some(conn)).is_err()
-            {
+            self.disconnect_player(Some(conn), player_id).ok();
+            if self.clear_empty_games(Some(conn)).is_err() {
                 return Err(DBPlaceError::InternalError);
             }
         }
@@ -676,9 +680,29 @@ impl DBHandler {
                     return Err(DBPlaceError::NotYourTurn);
                 }
             }
-            2 | 3 | 4 => {
-                // game over, cyan won, or magenta won, or draw
-                return Ok((DBPlaceStatus::GameEnded, Some(board_string)));
+            2 => {
+                // game over, cyan won
+                self.disconnect_player(Some(conn), player_id).ok();
+                if self.clear_empty_games(Some(conn)).is_err() {
+                    return Err(DBPlaceError::InternalError);
+                }
+                return Ok((DBPlaceStatus::GameEndedCyanWon, Some(board_string)));
+            }
+            3 => {
+                // game over, magenta won
+                self.disconnect_player(Some(conn), player_id).ok();
+                if self.clear_empty_games(Some(conn)).is_err() {
+                    return Err(DBPlaceError::InternalError);
+                }
+                return Ok((DBPlaceStatus::GameEndedMagentaWon, Some(board_string)));
+            }
+            4 => {
+                // game over, draw
+                self.disconnect_player(Some(conn), player_id).ok();
+                if self.clear_empty_games(Some(conn)).is_err() {
+                    return Err(DBPlaceError::InternalError);
+                }
+                return Ok((DBPlaceStatus::GameEndedDraw, Some(board_string)));
             }
             _ => (),
         }
@@ -710,10 +734,28 @@ impl DBHandler {
         }
 
         // board back to string
-        let (board_string, ended) = string_from_board(board, final_pos);
+        let (board_string, ended_state_opt) = string_from_board(board, final_pos);
 
         // update DB
-        let update_result = conn.execute("UPDATE games SET status = ?, board = ? FROM players WHERE players.game_id = games.id AND players.id = ?;" , params![if status == 0 { 1u8 } else { 0u8 }, board_string, player_id]);
+        let update_result = if ended_state_opt.is_none() {
+            conn.execute(
+                "UPDATE games SET status = ?, board = ? FROM players WHERE players.game_id = games.id AND players.id = ?;",
+                params![if status == 0 { 1u8 }
+                            else { 0u8 },
+                        board_string,
+                        player_id]
+            )
+        } else {
+            conn.execute(
+                "UPDATE games SET status = ?, board = ? FROM players WHERE players.game_id = games.id AND players.id = ?;",
+                params![if ended_state_opt.unwrap() == BoardState::Empty { 4u8 }
+                            else if ended_state_opt.unwrap() == BoardState::CyanWin { 2u8 }
+                            else { 3u8 },
+                        board_string,
+                        player_id]
+            )
+        };
+
         if let Err(_e) = update_result {
             return Err(DBPlaceError::InternalError);
         } else if let Ok(count) = update_result {
@@ -722,9 +764,17 @@ impl DBHandler {
             }
         }
 
-        if ended {
+        if let Some(ended_state) = ended_state_opt {
             self.disconnect_player(Some(conn), player_id).ok();
-            Ok((DBPlaceStatus::GameEnded, Some(board_string)))
+            Ok((
+                match ended_state {
+                    BoardState::Empty => DBPlaceStatus::GameEndedDraw,
+                    BoardState::Cyan | BoardState::Magenta => unreachable!(),
+                    BoardState::CyanWin => DBPlaceStatus::GameEndedCyanWon,
+                    BoardState::MagentaWin => DBPlaceStatus::GameEndedMagentaWon,
+                },
+                Some(board_string),
+            ))
         } else {
             Ok((DBPlaceStatus::Accepted, Some(board_string)))
         }
@@ -758,5 +808,3 @@ pub fn start_db_handler_thread(
         }
     });
 }
-
-
index 03fb98e7daf07193497c12ff117aee450e74e44e..badeb72b69fb704f0fc7c47a1bc969f1005bdbb9 100644 (file)
@@ -36,13 +36,13 @@ fn handle_pairing_request(tx: SyncSender<DBHandlerRequest>) -> Result<String, St
     if let Ok((pid, is_cyan_opt)) = player_rx.recv_timeout(DB_REQUEST_TIMEOUT) {
         if let Some(is_cyan) = is_cyan_opt {
             Ok(format!(
-                "{{\"type\":\"pairing_response\", \"id\": \"{}\", \"status\": \"paired\", \"color\": \"{}\"}}",
+                "{{\"type\":\"pairing_response\", \"id\": {}, \"status\": \"paired\", \"color\": \"{}\"}}",
                 pid,
                 if is_cyan { "cyan" } else { "magenta" }
             ))
         } else {
             Ok(format!(
-                "{{\"type\":\"pairing_response\", \"id\": \"{}\", \"status\": \"waiting\"}}",
+                "{{\"type\":\"pairing_response\", \"id\": {}, \"status\": \"waiting\"}}",
                 pid
             ))
         }
index 59b9bfb9277f905773acbeca198aa51eb08051cb..88c0a06093a3c760ced07a0a9e5b8388d03c5873 100644 (file)
@@ -104,8 +104,10 @@ then the back-end will respond with "too\_many\_players".
     {
         "type": "place_token",
         "status": "not_paired_yet", // or "accepted", "illegal",
-                                    // "not_your_turn", "game_ended",
-                                    // "unknown_id"
+                                    // "not_your_turn", "game_ended_draw",
+                                    // "game_ended_cyan_won",
+                                    // "game_ended_magenta_won", "unknown_id"
+        "board": "abcdefg..."       // see protocol 5 response for details
     }   
 ```
 
index a98e5b308fe898f13e09582761ffa8e3fb03ba69..acaa2fba68a4def645f9ee5ede064b6971f3d952 100644 (file)
@@ -1,9 +1,9 @@
 use crate::ai::AIDifficulty;
-use crate::game_logic::{WinType, check_win_draw};
-use crate::constants::{ROWS, COLS};
+use crate::constants::{COLS, ROWS};
+use crate::game_logic::{check_win_draw, WinType};
 
-use std::collections::hash_set::HashSet;
 use std::cell::Cell;
+use std::collections::hash_set::HashSet;
 use std::fmt::Display;
 use std::rc::Rc;
 
@@ -324,14 +324,15 @@ pub fn board_from_string(board_string: String) -> BoardType {
     board
 }
 
-/// Returns the board as a String, and true if the game has ended
-pub fn string_from_board(board: BoardType, placed: usize) -> (String, bool) {
+/// 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
+pub fn string_from_board(board: BoardType, placed: usize) -> (String, Option<BoardState>) {
     let mut board_string = String::with_capacity(56);
 
     // check for winning pieces
     let mut win_set: HashSet<usize> = HashSet::new();
     let win_opt = check_win_draw(&board);
-    if let Some((board_state, win_type)) = win_opt {
+    if let Some((_board_state, win_type)) = win_opt {
         match win_type {
             WinType::Horizontal(pos) => {
                 for i in pos..(pos + 4) {
@@ -386,5 +387,18 @@ pub fn string_from_board(board: BoardType, placed: usize) -> (String, bool) {
         });
     }
 
-    (board_string, is_full || !win_set.is_empty())
+    if is_full && win_set.is_empty() {
+        (board_string, Some(BoardState::Empty))
+    } else if !win_set.is_empty() {
+        (
+            board_string.clone(),
+            if board_string.chars().collect::<Vec<char>>()[*win_set.iter().next().unwrap()] == 'd' {
+                Some(BoardState::CyanWin)
+            } else {
+                Some(BoardState::MagentaWin)
+            },
+        )
+    } else {
+        (board_string, None)
+    }
 }