Refactorings and bug fixes
Protocol was also updated to match the output from the backend. Fixed "id" not returning as an integer from the backend.
This commit is contained in:
parent
8782a731bc
commit
0d9e35dd28
4 changed files with 99 additions and 35 deletions
|
@ -1,5 +1,5 @@
|
||||||
use crate::constants::{COLS, ROWS};
|
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::sync::mpsc::{Receiver, SyncSender};
|
||||||
use std::{fmt, thread};
|
use std::{fmt, thread};
|
||||||
|
@ -64,14 +64,18 @@ impl From<i64> for DBGameState {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum DBPlaceStatus {
|
pub enum DBPlaceStatus {
|
||||||
Accepted,
|
Accepted,
|
||||||
GameEnded,
|
GameEndedDraw,
|
||||||
|
GameEndedCyanWon,
|
||||||
|
GameEndedMagentaWon,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DBPlaceStatus {
|
impl fmt::Display for DBPlaceStatus {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
DBPlaceStatus::Accepted => write!(f, "accepted"),
|
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))
|
Ok((DBGameState::InternalError, None))
|
||||||
} else if cyan_opt.is_none() || magenta_opt.is_none() {
|
} else if cyan_opt.is_none() || magenta_opt.is_none() {
|
||||||
// One player disconnected
|
// One player disconnected
|
||||||
let player_remove_result = self.disconnect_player(Some(conn), player_id);
|
self.disconnect_player(Some(conn), player_id).ok();
|
||||||
if player_remove_result.is_err() {
|
// Remove the game(s) with disconnected players
|
||||||
// Failed to disconnect remaining player
|
if self.clear_empty_games(Some(conn)).is_err() {
|
||||||
Ok((DBGameState::InternalError, None))
|
Ok((DBGameState::InternalError, None))
|
||||||
|
} else if status == 2 || status == 3 {
|
||||||
|
Ok((DBGameState::from(status), Some(board)))
|
||||||
} else {
|
} else {
|
||||||
// Remove the game(s) with disconnected players
|
Ok((DBGameState::OpponentDisconnected, Some(board)))
|
||||||
if self.clear_empty_games(Some(conn)).is_err() {
|
|
||||||
Ok((DBGameState::InternalError, None))
|
|
||||||
} else {
|
|
||||||
Ok((DBGameState::OpponentDisconnected, Some(board)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Game in progress, or other state depending on "status"
|
// 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 (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) {
|
if let (Some(cyan_id), Some(_magenta_id)) = (cyan_id_opt, magenta_id_opt) {
|
||||||
Ok(Ok((cyan_id == player_id, status, board)))
|
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 {
|
} else {
|
||||||
Ok(Err(DBPlaceError::OpponentDisconnected))
|
Ok(Err(DBPlaceError::OpponentDisconnected))
|
||||||
}
|
}
|
||||||
|
@ -654,9 +659,8 @@ impl DBHandler {
|
||||||
|
|
||||||
// if opponent has disconnected, disconnect the remaining player as well
|
// if opponent has disconnected, disconnect the remaining player as well
|
||||||
if let Err(DBPlaceError::OpponentDisconnected) = query_result {
|
if let Err(DBPlaceError::OpponentDisconnected) = query_result {
|
||||||
if self.disconnect_player(Some(conn), player_id).is_err()
|
self.disconnect_player(Some(conn), player_id).ok();
|
||||||
|| self.clear_empty_games(Some(conn)).is_err()
|
if self.clear_empty_games(Some(conn)).is_err() {
|
||||||
{
|
|
||||||
return Err(DBPlaceError::InternalError);
|
return Err(DBPlaceError::InternalError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -676,9 +680,29 @@ impl DBHandler {
|
||||||
return Err(DBPlaceError::NotYourTurn);
|
return Err(DBPlaceError::NotYourTurn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2 | 3 | 4 => {
|
2 => {
|
||||||
// game over, cyan won, or magenta won, or draw
|
// game over, cyan won
|
||||||
return Ok((DBPlaceStatus::GameEnded, Some(board_string)));
|
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
|
// 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
|
// 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 {
|
if let Err(_e) = update_result {
|
||||||
return Err(DBPlaceError::InternalError);
|
return Err(DBPlaceError::InternalError);
|
||||||
} else if let Ok(count) = update_result {
|
} 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();
|
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 {
|
} else {
|
||||||
Ok((DBPlaceStatus::Accepted, Some(board_string)))
|
Ok((DBPlaceStatus::Accepted, Some(board_string)))
|
||||||
}
|
}
|
||||||
|
@ -758,5 +808,3 @@ pub fn start_db_handler_thread(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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 Ok((pid, is_cyan_opt)) = player_rx.recv_timeout(DB_REQUEST_TIMEOUT) {
|
||||||
if let Some(is_cyan) = is_cyan_opt {
|
if let Some(is_cyan) = is_cyan_opt {
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{{\"type\":\"pairing_response\", \"id\": \"{}\", \"status\": \"paired\", \"color\": \"{}\"}}",
|
"{{\"type\":\"pairing_response\", \"id\": {}, \"status\": \"paired\", \"color\": \"{}\"}}",
|
||||||
pid,
|
pid,
|
||||||
if is_cyan { "cyan" } else { "magenta" }
|
if is_cyan { "cyan" } else { "magenta" }
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{{\"type\":\"pairing_response\", \"id\": \"{}\", \"status\": \"waiting\"}}",
|
"{{\"type\":\"pairing_response\", \"id\": {}, \"status\": \"waiting\"}}",
|
||||||
pid
|
pid
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,8 +104,10 @@ then the back-end will respond with "too\_many\_players".
|
||||||
{
|
{
|
||||||
"type": "place_token",
|
"type": "place_token",
|
||||||
"status": "not_paired_yet", // or "accepted", "illegal",
|
"status": "not_paired_yet", // or "accepted", "illegal",
|
||||||
// "not_your_turn", "game_ended",
|
// "not_your_turn", "game_ended_draw",
|
||||||
// "unknown_id"
|
// "game_ended_cyan_won",
|
||||||
|
// "game_ended_magenta_won", "unknown_id"
|
||||||
|
"board": "abcdefg..." // see protocol 5 response for details
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::ai::AIDifficulty;
|
use crate::ai::AIDifficulty;
|
||||||
use crate::game_logic::{WinType, check_win_draw};
|
use crate::constants::{COLS, ROWS};
|
||||||
use crate::constants::{ROWS, COLS};
|
use crate::game_logic::{check_win_draw, WinType};
|
||||||
|
|
||||||
use std::collections::hash_set::HashSet;
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
use std::collections::hash_set::HashSet;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -324,14 +324,15 @@ pub fn board_from_string(board_string: String) -> BoardType {
|
||||||
board
|
board
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the board as a String, and true if the game has ended
|
/// Returns the board as a String, and None if game has not ended, Empty if game
|
||||||
pub fn string_from_board(board: BoardType, placed: usize) -> (String, bool) {
|
/// 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);
|
let mut board_string = String::with_capacity(56);
|
||||||
|
|
||||||
// check for winning pieces
|
// check for winning pieces
|
||||||
let mut win_set: HashSet<usize> = HashSet::new();
|
let mut win_set: HashSet<usize> = HashSet::new();
|
||||||
let win_opt = check_win_draw(&board);
|
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 {
|
match win_type {
|
||||||
WinType::Horizontal(pos) => {
|
WinType::Horizontal(pos) => {
|
||||||
for i in pos..(pos + 4) {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue