所以,我正在try 制作一个国际象棋引擎,以弥补我的不足,我通常是一名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中,像这样的东西将是应用更高级的枚举的一个很好的例子,所以如下所示:

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 struct 中感觉就像是这种 struct 试图实现的对立面.

我想我可以有一个带有Vec<PieceType>Board struct ,它有一个类似于GetMovesFor(PieceType)的方法,里面有一个switch ?然而,在这一点上,感觉一个方法将远远不止一个工作,它将拥有每一部分的实现细节,而不是像我用C#构建它的方式那样只有一部分的实现细节.

另一种 Select 是,我可以走一条非常类似于我在C#中采用的路由,只给我们一个Piece特征,在每种类型上实现它,并给我的董事会打Vec<Piece>分.不过,在这一点上,我觉得我只是在try 模仿多态,而不是以质朴的方式行事.

所以我觉得我要么误解了这些扩展枚举的目标是什么,而这实际上并不是如何使用它们.或者,我遗漏了一些实现细节.

可以解决这两个问题的答案将是非常棒的!

推荐答案

我认为最简单可能的模型通常是最好的,那就是一件东西有一种 colored颜色 和一种类型,这两种 colored颜色 都可以表示为没有有效负载的枚举.我认为,当您的枚举并不真正需要有效负载时,您却试图给它们一个有效负载,这让您有点迷失方向.

pub struct Piece {
    pub kind: PieceKind,
    pub color: PieceColor,
}

pub enum PieceKind {
    Pawn,
    Knight,
    Bishop,
    Rook,
    Queen,
    King,
}

pub enum PieceColor {
    White,
    Black,
}

一块棋盘将只是一个由Piece个数字组成的8x8数组,外加一些额外的信息,以确定投掷和过路人移动的合法性:

pub struct Board {
    pub pieces: [[Option<Piece>; 8]; 8],

    pub white_can_castle_a: bool,
    pub white_can_castle_h: bool,
    pub black_can_castle_a: bool,
    pub black_can_castle_h: bool,

    // Some if the last piece moved was a pawn that was moved two spaces;
    // contains the coordinates to check for a valid en passant capture.
    pub last_double_pawn_move: Option<(usize, usize)>,
}

有了这个模型,你就有了制作任何棋子的有效动作列表所需的所有信息.

注意,棋子在这里没有位置数据;棋子的位置由它在棋盘中的位置确定.在我看来,你希望"获得可能的移动"是一个主要存在于piece上的操作是不正确的.您需要关于整个董事会的信息来确定这一点,这在您的模型中通过让此方法接受Board而显示出来.我会将其建模为Board上的一个方法,它接受x,y坐标对.

// This type could use more members to be able to annotate special
// moves like castling.
struct Move {
    from: (usize, usize),
    to: (usize, usize),
}

impl Board {
    pub fn get_legal_moves_for(&self, piece: (usize, usize)) -> Vec<Move> {
        todo!()
    }
}

我认为你困惑的部分原因是,你试图用特征来设计模型,使其在每一种类型的作品上都是多态的.虽然基于特征的多态很好,但在这种模型中,它需要动态分派和堆分配(在Java中只需要happens),而Rust可以使用更有效的内存布局.基于match的多态也没有错--事实上,这正是Either类型实现特征的方式!

Rust相关问答推荐

为什么单元类型(空元组)实现了`Extend`trait?

如何从铁 rust 中呼唤_mm_256_mul_ph?

Rust:跨多个线程使用hashmap Arc和rwlock

"value is never read警告似乎不正确.我应该忽略它吗?

为什么铁 rust S似乎有内在的易变性?

如何定义实现同名但返回类型不同的 struct 的函数

如何实现Deref;多次;?

将特征与具有生命周期的关联类型一起使用时的生命周期方差问题

是否可以在不直接重复的情况下为许多特定类型实现一个函数?

在使用粗粒度锁访问的数据 struct 中使用 RefCell 是否安全?

为什么需要静态生命周期以及在处理 Rust 迭代器时如何缩小它?

在 Rust 中查找向量中 dyn struct 的索引

返回优化后的标题:返回异步块的闭包的类型擦除

go 重并堆积MPSC通道消息

为什么在 macOS / iOS 上切换 WiFi 网络时 reqwest 响应会挂起?

在 RefCell 上borrow

为什么 Rust 编译器在移动不可变值时执行复制?

BigUint 二进制补码

如何重写这个通用参数?

您不能borrow 对只读值的可变引用