diff --git a/part_8/tic_tac_toe_v7/client/client.go b/part_8/tic_tac_toe_v7/client/client.go index 23cb4be..868460d 100644 --- a/part_8/tic_tac_toe_v7/client/client.go +++ b/part_8/tic_tac_toe_v7/client/client.go @@ -14,24 +14,15 @@ import ( // Объявление структуры клиента type Client struct { - // подключение к серверу - conn net.Conn - // игровое поле - board *b.Board - // фигура игрока - mySymbol b.BoardField - // фигура игрока, ход которой сейчас - currentPlayer b.BoardField - // никнейм игрока - playerName string - // имя комнаты - roomName string - // текущее состояние клиента - state State - // мьютекс для защиты доступа к данным - mutex sync.RWMutex - // время последнего сообщения - lastMsgTime time.Time + conn net.Conn // подключение к серверу + board *b.Board // игровое поле + mySymbol b.BoardField // фигура игрока + currentPlayer b.BoardField // фигура игрока, чей сейчас ход + playerName string // имя комнаты + roomName string // имя комнаты + state State // текущее состояние клиента + mutex sync.RWMutex // мьютекс для защиты доступа к данным + lastMsgTime time.Time // время последнего сообщения } // Констукторная функция для создания клиента @@ -51,7 +42,7 @@ func NewClient(addr string) (*Client, error) { }, nil } -// Устанавливаем никнейм игрока +// Устанавливаем никнейма игрока func (c *Client) setNickname(nickname string) { c.playerName = nickname } @@ -88,7 +79,7 @@ func (c *Client) Start() { // Запускаем горутину для чтения сообщений от сервера go c.readFromServer() // Запускаем меню клиента для взаимодействия с пользователем - c.menu() + c.handleUserFlow() } // Читаем сообщения от сервера diff --git a/part_8/tic_tac_toe_v7/client/menu.go b/part_8/tic_tac_toe_v7/client/menu.go index 5250d36..eda5067 100644 --- a/part_8/tic_tac_toe_v7/client/menu.go +++ b/part_8/tic_tac_toe_v7/client/menu.go @@ -12,8 +12,8 @@ import ( "time" ) -// Метод для взаимодействия с пользователем -func (c *Client) menu() { +// Метод управления всем пользовательским потоком +func (c *Client) handleUserFlow() { // Создаем буфер для чтения ввода reader := bufio.NewReader(os.Stdin) // Создаем энкодер для отправки сообщений diff --git a/part_8/tic_tac_toe_v7/client/server_message_handlers.go b/part_8/tic_tac_toe_v7/client/server_message_handlers.go index 2e6607c..d357f51 100644 --- a/part_8/tic_tac_toe_v7/client/server_message_handlers.go +++ b/part_8/tic_tac_toe_v7/client/server_message_handlers.go @@ -5,106 +5,20 @@ import ( "fmt" "log" - b "tic-tac-toe/board" // Added for g.GameMode + b "tic-tac-toe/board" "tic-tac-toe/network" ) -// Обрабатываем ответ на запрос на присоединение к комнате -func (c *Client) handleRoomJoinResponse(payload json.RawMessage) { - var res network.RoomJoinResponse // Десериализуем ответ +// Обрабатываем ответ сервера, содержащий никнейм игрока +func (c *Client) handleNickNameResponse(payload json.RawMessage) { + var res network.NickNameResponse // Десериализуем ответ if err := json.Unmarshal(payload, &res); err == nil { - c.mySymbol = res.PlayerSymbol // Устанавливаем фигуру игрока - c.roomName = res.RoomName // Устанавливаем имя комнаты - if res.Board.Size > 0 { // Проверяем размер поля - c.board = &res.Board // Устанавливаем игровое поле - fmt.Printf( - "\nSuccessfully joined room '%s' as %s.\n", - res.RoomName, res.PlayerSymbol, - ) - c.board.PrintBoard() - } else { - fmt.Printf( - "\nSuccessfully joined room '%s' as %s. "+ - "Waiting for game to start...\n", - res.RoomName, res.PlayerSymbol, - ) - } + fmt.Printf("\nWelcome, %s!\n> ", res.Nickname) + c.setNickname(res.Nickname) // Устанавливаем никнейм игрока + c.setState(mainMenu) // Переходим в главное меню } else { - log.Printf("Error unmarshalling RoomJoinResponse: %v", err) + log.Printf("Error unmarshalling NickNameResponse: %v", err) } - // Устанавливаем состояние клиента - c.setState(waitingOpponentInRoom) -} - -// Обрабатываем ответ на запрос на инициализацию игры -func (c *Client) handleInitGame(payload json.RawMessage) { - var res network.InitGameResponse // Десериализуем ответ - if err := json.Unmarshal(payload, &res); err == nil { - c.board = &res.Board // Устанавливаем игровое поле - c.currentPlayer = res.CurrentPlayer // Устанавливаем фигуру игрока - fmt.Println("\n--- Game Started ---") - c.board.PrintBoard() // Выводим игровое поле - c.printTurnInfo() // Выводим информацию о ходе игрока - // Устанавливаем состояние клиента - if res.CurrentPlayer == c.mySymbol { - c.setState(playerMove) - } else { - c.setState(opponentMove) - } - } else { - log.Printf("Error unmarshalling InitGameResponse: %v", err) - } -} - -// Обрабатываем сообщение об обновлении состояния игры -func (c *Client) handleUpdateState(payload json.RawMessage) { - var res network.GameStateUpdate // Десериализуем ответ - if err := json.Unmarshal(payload, &res); err == nil { - c.board = &res.Board // Устанавливаем игровое поле - c.currentPlayer = res.CurrentPlayer // Устанавливаем фигуру игрока - fmt.Println("\n--- Game State Update ---") - c.board.PrintBoard() // Выводим игровое поле - c.printTurnInfo() // Выводим информацию о ходе игрока - // Устанавливаем состояние клиента - if res.CurrentPlayer == c.mySymbol { - c.setState(playerMove) - } else { - c.setState(opponentMove) - } - } else { - log.Printf("Error unmarshalling GameStateUpdate: %v", err) - } -} - -// Обрабатываем сообщение об окончании игры -func (c *Client) handleEndGame(payload json.RawMessage) { - var res network.EndGameResponse // Десериализуем ответ - if err := json.Unmarshal(payload, &res); err == nil { - c.board = &res.Board // Устанавливаем игровое поле - fmt.Println("\n--- Game Over ---") - c.board.PrintBoard() // Выводим игровое поле - // Выводим информацию о победителе - if res.CurrentPlayer == b.Empty { - fmt.Println("It's a Draw!") - } else { - fmt.Printf("Player %s wins!\n", res.CurrentPlayer) - } - c.setState(endGame) // Устанавливаем состояние клиента - fmt.Print("> ") - } else { - log.Printf("Error unmarshalling EndGameResponse: %v", err) - } -} - -// Обрабатываем сообщение об ошибке -func (c *Client) handleError(payload json.RawMessage) { - var errPayload network.ErrorResponse // Десериализуем ответ - if err := json.Unmarshal(payload, &errPayload); err == nil { - fmt.Printf("\nServer Error: %s\n> ", errPayload.Message) - } else { - log.Printf("Error unmarshalling ErrorResponse: %v", err) - } - c.setState(mainMenu) } // Обрабатываем сообщение о списке комнат @@ -130,30 +44,33 @@ func (c *Client) handleRoomListResponse(payload json.RawMessage) { } else { log.Printf("Error unmarshalling RoomListResponse: %v", err) } - c.setState(mainMenu) + c.setState(mainMenu) // Переходим в главное меню } // Обрабатываем ответ на запрос на присоединение к комнате -func (c *Client) handleNickNameResponse(payload json.RawMessage) { - var res network.NickNameResponse // Десериализуем ответ +func (c *Client) handleRoomJoinResponse(payload json.RawMessage) { + var res network.RoomJoinResponse // Десериализуем ответ if err := json.Unmarshal(payload, &res); err == nil { - fmt.Printf("\nWelcome, %s!\n> ", res.Nickname) - c.setNickname(res.Nickname) // Устанавливаем никнейм игрока - c.setState(mainMenu) // Устанавливаем состояние клиента + c.mySymbol = res.PlayerSymbol // Устанавливаем фигуру игрока + c.roomName = res.RoomName // Устанавливаем имя комнаты + if res.Board.Size > 0 { // Проверяем размер поля + c.board = &res.Board // Устанавливаем игровое поле + fmt.Printf( + "\nSuccessfully joined room '%s' as %s.\n", + res.RoomName, res.PlayerSymbol, + ) + c.board.PrintBoard() + } else { + fmt.Printf( + "\nSuccessfully joined room '%s' as %s. "+ + "Waiting for game to start...\n", + res.RoomName, res.PlayerSymbol, + ) + } } else { - log.Printf("Error unmarshalling NickNameResponse: %v", err) + log.Printf("Error unmarshalling RoomJoinResponse: %v", err) } -} - -// Обрабатываем сообщение об отключении оппонента -func (c *Client) handleOpponentLeft(payload json.RawMessage) { - var res network.OpponentLeft // Десериализуем ответ - if err := json.Unmarshal(payload, &res); err == nil { - fmt.Printf("\nPlayer '%s' has left the game.\n> ", res.Nickname) - } else { - log.Printf("Error unmarshalling OpponentLeft: %v", err) - } - // Устанавливаем состояние клиента + // переходим в состояние ожидания присоединения оппонента к комнате c.setState(waitingOpponentInRoom) } @@ -175,10 +92,12 @@ func (c *Client) handleFinishedGamesResponse(payload json.RawMessage) { } } } else { - log.Printf("Error unmarshalling FinishedGamesResponse: %v", err) + log.Printf( + "Error unmarshalling FinishedGamesResponse: %v", + err, + ) } - // Устанавливаем состояние клиента - c.setState(mainMenu) + c.setState(mainMenu) // Переходим в главное меню } // Обрабатываем ответ на запрос на получение данных @@ -197,7 +116,101 @@ func (c *Client) handleFinishedGameResponse(payload json.RawMessage) { c.board.PrintBoard() fmt.Println() } else { - log.Printf("Error unmarshalling FinishedGameResponse: %v", err) + log.Printf( + "Error unmarshalling FinishedGameResponse: %v", + err, + ) } - c.setState(mainMenu) // Устанавливаем состояние клиента + c.setState(mainMenu) // Переходим в главное меню +} + +// Обрабатываем ответ на запрос на инициализацию игры +func (c *Client) handleInitGame(payload json.RawMessage) { + var res network.InitGameResponse // Десериализуем ответ + if err := json.Unmarshal(payload, &res); err == nil { + c.board = &res.Board // Устанавливаем игровое поле + c.currentPlayer = res.CurrentPlayer // Устанавливаем фигуру игрока + fmt.Println("\n--- Game Started ---") + c.board.PrintBoard() // Выводим игровое поле + c.printTurnInfo() // Выводим информацию о ходе игрока + if res.CurrentPlayer == c.mySymbol { + // Устанавливаем состояние, что ходит игрок + c.setState(playerMove) + } else { + // Устанавливаем состояние, что ходит оппонент + c.setState(opponentMove) + } + } else { + log.Printf( + "Error unmarshalling InitGameResponse: %v", + err, + ) + } +} + +// Обрабатываем сообщение об обновлении состояния игры +func (c *Client) handleUpdateState(payload json.RawMessage) { + var res network.GameStateUpdate // Десериализуем ответ + if err := json.Unmarshal(payload, &res); err == nil { + c.board = &res.Board // Устанавливаем игровое поле + c.currentPlayer = res.CurrentPlayer // Устанавливаем фигуру игрока + fmt.Println("\n--- Game State Update ---") + c.board.PrintBoard() // Выводим игровое поле + c.printTurnInfo() // Выводим информацию о ходе игрока + if res.CurrentPlayer == c.mySymbol { + // Устанавливаем состояние, что ходит игрок + c.setState(playerMove) + } else { + // Устанавливаем состояние, что ходит оппонент + c.setState(opponentMove) + } + } else { + log.Printf("Error unmarshalling GameStateUpdate: %v", err) + } +} + +// Обрабатываем сообщение об окончании игры +func (c *Client) handleEndGame(payload json.RawMessage) { + var res network.EndGameResponse // Десериализуем ответ + if err := json.Unmarshal(payload, &res); err == nil { + c.board = &res.Board // Устанавливаем игровое поле + fmt.Println("\n--- Game Over ---") + c.board.PrintBoard() // Выводим игровое поле + // Выводим информацию о победителе + if res.CurrentPlayer == b.Empty { + fmt.Println("It's a Draw!") + } else { + fmt.Printf("Player %s wins!\n", res.CurrentPlayer) + } + c.setState(endGame) /// Переходи состояние завершения игры + fmt.Print("> ") + } else { + log.Printf("Error unmarshalling EndGameResponse: %v", err) + } +} + +// Обрабатываем сообщение об ошибке +func (c *Client) handleError(payload json.RawMessage) { + var errPayload network.ErrorResponse // Десериализуем ответ + if err := json.Unmarshal(payload, &errPayload); err == nil { + fmt.Printf("\nServer Error: %s\n> ", errPayload.Message) + } else { + log.Printf("Error unmarshalling ErrorResponse: %v", err) + } + c.setState(mainMenu) // Переходим в главное меню +} + +// Обрабатываем сообщение об отключении оппонента +func (c *Client) handleOpponentLeft(payload json.RawMessage) { + var res network.OpponentLeft // Десериализуем ответ + if err := json.Unmarshal(payload, &res); err == nil { + fmt.Printf( + "\nPlayer '%s' has left the game.\n> ", + res.Nickname, + ) + } else { + log.Printf("Error unmarshalling OpponentLeft: %v", err) + } + // Переходим в состояние ожидания присоединения оппонента к комнате + c.setState(waitingOpponentInRoom) } diff --git a/part_8/tic_tac_toe_v7/client/utils.go b/part_8/tic_tac_toe_v7/client/utils.go index dc2c02e..56e4c14 100644 --- a/part_8/tic_tac_toe_v7/client/utils.go +++ b/part_8/tic_tac_toe_v7/client/utils.go @@ -36,7 +36,7 @@ func (c *Client) validateMove(row, col int) bool { ) return false } - // Преобразуем в 0-индексированный для доступа к полю + // Нормируем индекс для доступа к полю if c.board.Board[row-1][col-1] != b.Empty { fmt.Println("Invalid move. Cell is already occupied.") return false