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

在Rust中实现国际象棋引擎:Trait与Enum的选型对比

用Rust实现国际象棋引擎:棋子走法的设计思路

背景:C#中的实现方式

作为C#开发者,你习惯用抽象类+派生类的多态结构实现棋子系统:

public abstract class Piece 
{
   public Coordinate Location;
   
   public abstract bool IsWhite {get;}
   public abstract string PieceCode {get;} // e.g. K for a king

   public abstract Move[] GetPossibleMoves(Board board);
}

public sealed class Pawn:Piece {//etc...}

棋盘通过存储所有棋子的数组处理逻辑。

Rust的初步尝试与困惑

你尝试用Rust的增强型Enum实现,但设计如下:

struct  Piece {
    position: Coordinate,
    isWhite: bool,
    charCode: char
}

enum PieceType {
    King(Piece),
    Queen(Piece),
    Bishop(Piece),
    Knight(Piece),
    Rook(Piece),
    Pawn(Piece)
}

但遇到了困惑:

  • 每个Piece实例不知道自身类型,把PieceType加入Piece结构体又违背设计初衷
  • 若在Board中用包含大量match分支的GetMovesFor方法处理所有棋子走法,会导致方法职责过重
  • 用Trait模仿C#多态又感觉不符合Rust风格

符合Rust风格的解决方案

Rust的Enum是代数数据类型,核心优势就是处理这种有限、明确的类型集合(国际象棋刚好是固定6种棋子),正确的设计应该让Enum直接代表棋子类型,每个变体携带该棋子的必要属性:

方案1:结构体式Enum变体

#[derive(Debug, Clone, Copy)]
// 假设Coordinate和Move已定义
struct Coordinate(i8, i8);
struct Move {
    from: Coordinate,
    to: Coordinate,
}

#[derive(Debug, Clone, Copy)]
enum Piece {
    King { position: Coordinate, is_white: bool },
    Queen { position: Coordinate, is_white: bool },
    Bishop { position: Coordinate, is_white: bool },
    Knight { position: Coordinate, is_white: bool },
    Rook { position: Coordinate, is_white: bool },
    Pawn { position: Coordinate, is_white: bool },
}

impl Piece {
    // 获取当前棋子的可行走法
    fn get_possible_moves(&self, board: &Board) -> Vec<Move> {
        match self {
            Piece::King { position, is_white } => {
                // 王的走法逻辑:可向8个方向走1格,需考虑边界和己方棋子
                let mut moves = Vec::new();
                for dx in [-1, 0, 1] {
                    for dy in [-1, 0, 1] {
                        if dx == 0 && dy == 0 { continue; }
                        let new_pos = Coordinate(position.0 + dx, position.1 + dy);
                        if board.is_valid_position(new_pos) && !board.has_own_piece(new_pos, *is_white) {
                            moves.push(Move { from: *position, to: new_pos });
                        }
                    }
                }
                moves
            }
            Piece::Pawn { position, is_white } => {
                // 兵的走法逻辑:白兵向上(y-1),黑兵向下(y+1),首次可走2格,吃子斜向等
                let mut moves = Vec::new();
                let direction = if *is_white { -1 } else { 1 };
                // 前进1格
                let forward = Coordinate(position.0, position.1 + direction);
                if board.is_valid_position(forward) && !board.has_piece(forward) {
                    moves.push(Move { from: *position, to: forward });
                    // 首次走可前进2格
                    if (*is_white && position.1 == 6) || (!*is_white && position.1 == 1) {
                        let forward_two = Coordinate(position.0, position.1 + 2 * direction);
                        if board.is_valid_position(forward_two) && !board.has_piece(forward_two) {
                            moves.push(Move { from: *position, to: forward_two });
                        }
                    }
                }
                // 斜向吃子
                for dx in [-1, 1] {
                    let capture = Coordinate(position.0 + dx, position.1 + direction);
                    if board.is_valid_position(capture) && board.has_enemy_piece(capture, *is_white) {
                        moves.push(Move { from: *position, to: capture });
                    }
                }
                moves
            }
            // 其他棋子(后、象、马、车)的走法逻辑同理实现
            Piece::Queen { position, is_white } => vec![],
            Piece::Bishop { position, is_white } => vec![],
            Piece::Knight { position, is_white } => vec![],
            Piece::Rook { position, is_white } => vec![],
        }
    }

    // 获取棋子的字符编码(如白王'K',黑王'k')
    fn piece_code(&self) -> char {
        match self {
            Piece::King { is_white, .. } => if *is_white { 'K' } else { 'k' },
            Piece::Queen { is_white, .. } => if *is_white { 'Q' } else { 'q' },
            Piece::Bishop { is_white, .. } => if *is_white { 'B' } else { 'b' },
            Piece::Knight { is_white, .. } => if *is_white { 'N' } else { 'n' },
            Piece::Rook { is_white, .. } => if *is_white { 'R' } else { 'r' },
            Piece::Pawn { is_white, .. } => if *is_white { 'P' } else { 'p' },
        }
    }

    fn is_white(&self) -> bool {
        match self {
            Piece::King { is_white, .. } => *is_white,
            Piece::Queen { is_white, .. } => *is_white,
            Piece::Bishop { is_white, .. } => *is_white,
            Piece::Knight { is_white, .. } => *is_white,
            Piece::Rook { is_white, .. } => *is_white,
            Piece::Pawn { is_white, .. } => *is_white,
        }
    }
}

// 假设Board结构体的简化定义
struct Board {
    pieces: Vec<Piece>,
}

impl Board {
    fn is_valid_position(&self, pos: Coordinate) -> bool {
        pos.0 >= 0 && pos.0 < 8 && pos.1 >= 0 && pos.1 < 8
    }

    fn has_piece(&self, pos: Coordinate) -> bool {
        self.pieces.iter().any(|p| match p {
            Piece::King { position, .. } => *position == pos,
            Piece::Queen { position, .. } => *position == pos,
            // 其他变体同理,或者可以给Piece实现position()方法简化
            _ => false,
        })
    }

    fn has_own_piece(&self, pos: Coordinate, is_white: bool) -> bool {
        self.pieces.iter().any(|p| match p {
            Piece::King { position, is_white: p_white, .. } => *position == pos && *p_white == is_white,
            // 其他变体同理
            _ => false,
        })
    }

    fn has_enemy_piece(&self, pos: Coordinate, is_white: bool) -> bool {
        self.pieces.iter().any(|p| match p {
            Piece::King { position, is_white: p_white, .. } => *position == pos && *p_white != is_white,
            // 其他变体同理
            _ => false,
        })
    }
}

方案2:提取公共属性到结构体

如果觉得每个变体重复写position和is_white太冗余,可以把这些公共属性抽成一个结构体,让Enum变体持有该结构体:

#[derive(Debug, Clone, Copy)]
struct PieceData {
    position: Coordinate,
    is_white: bool,
}

#[derive(Debug, Clone, Copy)]
enum Piece {
    King(PieceData),
    Queen(PieceData),
    Bishop(PieceData),
    Knight(PieceData),
    Rook(PieceData),
    Pawn(PieceData),
}

impl Piece {
    fn get_possible_moves(&self, board: &Board) -> Vec<Move> {
        match self {
            Piece::King(data) => {
                // 使用data.position和data.is_white实现走法逻辑
                vec![]
            }
            // 其他变体同理
            _ => vec![],
        }
    }
}

设计优势

  • 类型明确:Enum直接代表棋子类型,每个变体天然知道自己的类型,无需额外标记
  • 职责清晰:走法逻辑封装在Piece的get_possible_moves方法中,每个match分支只负责对应棋子的逻辑,不会让Board方法臃肿
  • 符合Rust风格:利用代数数据类型处理有限类型集合,比模仿C#多态更高效(无需虚函数调用,编译时就能确定分支)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:32:12