You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python多人Tic-Tac-Toe游戏故障:对手无法落子+标记显示错误

问题描述

我正在开发一款基于Python的多人Tic-Tac-Toe(井字棋)游戏,目前遇到两个问题:

  1. 回合无法传递到客户端(guest)侧;
  2. 棋盘上显示错误的标记位置。

主机侧操作及输出:

Enter row and column numbers to fix spot(row,column): 1,2
- X -
- - -
- - -

客户端侧输出:

X - -
- - -
- - -

代码说明:需将提供的源码复制生成两个文件,主机文件调用host_game函数,客户端文件调用connect_to_game函数。已尝试搜索资料、咨询AI,但未解决问题,现附上完整代码寻求帮助。

import socket
import threading


HOST = '192.168.1.115'
PORT = 9090


class TicTacToe:
    def __init__(self):
        self.you = 'O'
        self.opponet = 'X'
        self.turn = 'X'
        self.board = []
        self.game_over = False
        self.winner = None
        self.counter = 0
        self.create_board()

    def create_board(self):
        for i in range(3):
            row = []
            for j in range(3):
                row.append('-')
            self.board.append(row)

    def show_board(self):
        for row in self.board:
            for item in row:
                print(item, end=" ")
            print()

    def is_board_filled(self):
        for row in self.board:
            for item in row:
                if item == '-':
                    return False
        return True


    def fix_move(self, row, col, player):
        if self.game_over:
            return

        self.counter +=1
        self.board[row-1][col-1] = player
        self.show_board()

        if self.is_player_win(player):
            if self.winner == self.you:
                print('You win!')
                exit()
            if self.winner == self.opponet:
                print('You lose!')
                exit()
        else:
            if self.counter == 9:
                print('Its a draw!')
                exit()

                

    def valid_move(self, row, col):
        if self.board[row][col] == 'O' or self.board[row][col] == 'X':
            return False
        return True

    def swap_player_turn(self):
        self.turn =  self.you if self.turn == self.opponet else self.opponet

    def handle(self, client):
        while not self.game_over:
            if self.turn == self.you:            
                move = input("Enter row and column numbers to fix spot(row,column): ")
                place = move.split(',')
                if self.valid_move(int(place[0]), int(place[1])):
                    self.fix_move(int(place[0]), int(place[1]), self.you)
                    self.turn = self.opponet
                    client.send(move.encode('utf-8'))
                else:
                    print('Invalid Move!')
            else:
                data = client.recv(1024)
                if not data:
                    client.close()
                    break
                else:
                    place = data.decode('utf-8').split(',')
                    self.fix_move(int(place[0]), int(place[0]), self.opponet)
        client.close()

    def is_player_win(self, player):
        if self.board[0][0] == self.board[0][1] == self.board[0][2] == 'X':
            return  True
        if self.board[1][0] == self.board[1][1] == self.board[1][2] == 'X' or 'O':
            return  True
        if self.board[2][0] == self.board[2][1] == self.board[2][2] == 'X' or 'O':
            return  True
         # etc etc
        
    def host_game(self, host, port):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind((host, port))
        server.listen(1)

        client, addr = server.accept()
        self.you = 'X'
        self.opponet = 'O'
        threading.Thread(target=self.handle, args=(client,)).start()
        server.close()

    def connect_to_game(self, host, port):
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect((host, port))

        self.you = 'O'
        self.opponet = 'X'
        threading.Thread(target=self.handle, args=(client,)).start()


game = TicTacToe()
game.host_game(HOST, PORT)

问题分析与修复方案

1. 标记位置错误的原因及修复

客户端接收主机移动时,代码错误地将行号同时作为列号传入fix_move:

self.fix_move(int(place[0]), int(place[0]), self.opponet)

修复:将第二个参数改为int(place[1]),确保行和列参数正确分离。

2. 回合无法传递的核心问题及修复

问题点:

  • 主机手动修改回合状态,客户端处理完对方移动后未更新回合;
  • valid_move方法未将用户输入的1-based索引转换为棋盘的0-based索引,导致判断逻辑错误;
  • is_player_win方法硬编码判断'X',且逻辑错误(== 'X' or 'O'永远为True),未正确设置winner和game_over状态;
  • 回合切换逻辑分散,未统一管理。

修复步骤:

(1)修复valid_move方法

将输入的行/列转换为0-based索引,并增加合法性检查:

def valid_move(self, row, col):
    row_idx = row - 1
    col_idx = col - 1
    if 0 <= row_idx < 3 and 0 <= col_idx < 3:
        return self.board[row_idx][col_idx] == '-'
    return False
(2)修复is_player_win方法

正确判断当前玩家的获胜条件,并设置全局状态:

def is_player_win(self, player):
    # 检查行
    for row in self.board:
        if all(cell == player for cell in row):
            self.winner = player
            self.game_over = True
            return True
    # 检查列
    for col in range(3):
        if all(self.board[row][col] == player for row in range(3)):
            self.winner = player
            self.game_over = True
            return True
    # 检查对角线
    if self.board[0][0] == self.board[1][1] == self.board[2][2] == player:
        self.winner = player
        self.game_over = True
        return True
    if self.board[0][2] == self.board[1][1] == self.board[2][0] == player:
        self.winner = player
        self.game_over = True
        return True
    return False
(3)统一回合切换逻辑

修改fix_move方法,在移动处理完成后自动切换回合(游戏未结束时),并移除handle方法中手动修改回合的代码:

def fix_move(self, row, col, player):
    if self.game_over:
        return

    row_idx = row - 1
    col_idx = col - 1
    self.counter += 1
    self.board[row_idx][col_idx] = player
    self.show_board()

    if self.is_player_win(player):
        print('You win!' if self.winner == self.you else 'You lose!')
        self.game_over = True
        exit()
    elif self.counter == 9:
        print('Its a draw!')
        self.game_over = True
        exit()
    
    # 自动切换回合
    if not self.game_over:
        self.swap_player_turn()
(4)修复handle方法的接收逻辑

增加输入合法性检查,并移除手动修改回合的代码:

def handle(self, client):
    while not self.game_over:
        if self.turn == self.you:            
            move = input("Enter row and column numbers to fix spot(row,column): ")
            place = move.split(',')
            if len(place) != 2:
                print('Invalid input! Use format: row,column')
                continue
            try:
                row = int(place[0])
                col = int(place[1])
            except ValueError:
                print('Invalid input! Enter numbers only')
                continue
            if self.valid_move(row, col):
                self.fix_move(row, col, self.you)
                client.send(move.encode('utf-8'))
            else:
                print('Invalid Move!')
        else:
            print("Waiting for opponent's move...")
            data = client.recv(1024)
            if not data:
                print("Opponent disconnected!")
                client.close()
                self.game_over = True
                break
            else:
                place = data.decode('utf-8').split(',')
                if len(place) != 2:
                    print("Invalid move from opponent!")
                    continue
                try:
                    row = int(place[0])
                    col = int(place[1])
                except ValueError:
                    print("Invalid move from opponent!")
                    continue
                self.fix_move(row, col, self.opponet)
    client.close()

完整修复后的代码

主机文件(host.py)

import socket
import threading


HOST = '192.168.1.115'
PORT = 9090


class TicTacToe:
    def __init__(self):
        self.you = 'O'
        self.opponet = 'X'
        self.turn = 'X'
        self.board = []
        self.game_over = False
        self.winner = None
        self.counter = 0
        self.create_board()

    def create_board(self):
        for i in range(3):
            row = []
            for j in range(3):
                row.append('-')
            self.board.append(row)

    def show_board(self):
        for row in self.board:
            for item in row:
                print(item, end=" ")
            print()

    def valid_move(self, row, col):
        row_idx = row - 1
        col_idx = col - 1
        if 0 <= row_idx < 3 and 0 <= col_idx < 3:
            return self.board[row_idx][col_idx] == '-'
        return False

    def swap_player_turn(self):
        self.turn = self.you if self.turn == self.opponet else self.opponet

    def is_player_win(self, player):
        # 检查行
        for row in self.board:
            if all(cell == player for cell in row):
                self.winner = player
                self.game_over = True
                return True
        # 检查列
        for col in range(3):
            if all(self.board[row][col] == player for row in range(3)):
                self.winner = player
                self.game_over = True
                return True
        # 检查对角线
        if self.board[0][0] == self.board[1][1] == self.board[2][2] == player:
            self.winner = player
            self.game_over = True
            return True
        if self.board[0][2] == self.board[1][1] == self.board[2][0] == player:
            self.winner = player
            self.game_over = True
            return True
        return False

    def fix_move(self, row, col, player):
        if self.game_over:
            return

        row_idx = row - 1
        col_idx = col - 1
        self.counter += 1
        self.board[row_idx][col_idx] = player
        self.show_board()

        if self.is_player_win(player):
            print('You win!' if self.winner == self.you else 'You lose!')
            self.game_over = True
            exit()
        elif self.counter == 9:
            print('Its a draw!')
            self.game_over = True
            exit()
        
        if not self.game_over:
            self.swap_player_turn()

    def handle(self, client):
        while not self.game_over:
            if self.turn == self.you:            
                move = input("Enter row and column numbers to fix spot(row,column): ")
                place = move.split(',')
                if len(place) != 2:
                    print('Invalid input! Use format: row,column')
                    continue
                try:
                    row = int(place[0])
                    col = int(place[1])
                except ValueError:
                    print('Invalid input! Enter numbers only')
                    continue
                if self.valid_move(row, col):
                    self.fix_move(row, col, self.you)
                    client.send(move.encode('utf-8'))
                else:
                    print('Invalid Move!')
            else:
                print("Waiting for opponent's move...")
                data = client.recv(1024)
                if not data:
                    print("Opponent disconnected!")
                    client.close()
                    self.game_over = True
                    break
                else:
                    place = data.decode('utf-8').split(',')
                    if len(place) != 2:
                        print("Invalid move from opponent!")
                        continue
                    try:
                        row = int(place[0])
                        col = int(place[1])
                    except ValueError:
                        print("Invalid move from opponent!")
                        continue
                    self.fix_move(row, col, self.opponet)
        client.close()

    def host_game(self, host, port):
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind((host, port))
        server.listen(1)

        client, addr = server.accept()
        self.you = 'X'
        self.opponet = 'O'
        threading.Thread(target=self.handle, args=(client,)).start()
        server.close()


game = TicTacToe()
game.host_game(HOST, PORT)

客户端文件(client.py)

仅需将最后两行替换为:

game = TicTacToe()
game.connect_to_game(HOST, PORT)

内容的提问来源于stack exchange,提问作者Scalobe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 12:51:56