Java 枚举的按位运算;回复:国际象棋 EG
如果我在 C 中保留一个代表棋盘的数组,我可能会用大致如下所示的枚举项来填充它:
enum CHESSPIECE {
none = 0,
pawn, knight, bishop, rook, queen, king,
type_mask = 7,
white = 8,
white_pawn, white_knight, white_bishop, white_rook, white_queen, white_king,
black = 16,
black_pawn, black_kight, black_bishop, black_rook, black_queen, black_king,
color_mask = 24
} chessPiece;
因此允许如下逻辑:
if (board[i][j] & color_mask == currentColor)
{
impossibleMove = true; // or some-such
}
在 Java 中,我只是发现按位 &不支持枚举操作,而且,相当出色的 EnumSet 并不容易应用,因为一块不能是黑色和白色以及车和国王。
所以我的想法是这样的:
public enum ChessPieceId {
None (null, null),
Pawn (null, null),
Knight (null, null),
Bishop (null, null),
Rook (null, null),
Queen (null, null),
King (null, null),
Type_Mask (null, null),
White (null, null),
White_Pawn (ChessPieceId.White, ChessPieceId.Pawn),
White_Knight (ChessPieceId.White, ChessPieceId.Knight),
White_Bishop (ChessPieceId.White, ChessPieceId.Bishop),
White_Rook (ChessPieceId.White, ChessPieceId.Rook),
White_Queen (ChessPieceId.White, ChessPieceId.Queen),
White_King (ChessPieceId.White, ChessPieceId.King),
SkipA (null, null),
Black (null, null),
Black_Pawn (ChessPieceId.Black, ChessPieceId.Pawn),
Black_Knight (ChessPieceId.Black, ChessPieceId.Knight),
Black_Bishop (ChessPieceId.Black, ChessPieceId.Bishop),
Black_Rook (ChessPieceId.Black, ChessPieceId.Rook),
Black_Queen (ChessPieceId.Black, ChessPieceId.Queen),
Black_King (ChessPieceId.Black, ChessPieceId.King),
SkipB (null, null),
Color_Mask (null, null);
private final ChessPieceId color;
private final ChessPieceId type;
ChessPieceId(ChessPieceId pColor, ChessPieceId pType){
this.color = pColor;
this.type = pType;
}
ChessPieceId color() { return color; }
ChessPieceId type() { return type; }
// & operator should be built in. I considered an EnumSet but...
ChessPieceId and(ChessPieceId pSecond) {
switch(ChessPieceId.this.ordinal() & pSecond.ordinal()) {
case 0: //None.ordinal() etc. [if only Java were smarter]
return None;
case 1: return Pawn;
case 2: return Knight;
case 3: return Bishop;
case 4: return Rook;
case 5: return Queen;
case 6: return King;
case 7: return Type_Mask;
case 8: return White;
case 9: return White_Pawn;
case 10: return White_Knight;
case 11: return White_Rook;
case 12: return White_Bishop;
case 13: return White_Queen;
case 14: return White_King;
//case 15: return SkipA;
case 16: return Black;
case 17: return Black_Pawn;
case 18: return Black_Knight;
case 19: return Black_Rook;
case 20: return Black_Bishop;
case 21: return Black_Queen;
case 22: return Black_King;
//case 23: return SkipB;
case 24: return Color_Mask;
default:
return None;
}
}
}
显然我只需要其中之一(与操作或初始化值)。另外,如果我可以在它自己的定义中使用枚举类型,那肯定会很棒,但我不能。所以像这样的台词:
Bishop (null, ChessPieceId.Bishop),
...
White (ChessPieceId.White, null),
已经出局了。
我的问题是什么?有没有更好的方法我失踪了。我还可以将序数的 int 解析为枚举定义的值,从而避免整个 case 语句吗?
If I were keeping an array in C representing a chess board, I might fill it with enumed items that roughly look like:
enum CHESSPIECE {
none = 0,
pawn, knight, bishop, rook, queen, king,
type_mask = 7,
white = 8,
white_pawn, white_knight, white_bishop, white_rook, white_queen, white_king,
black = 16,
black_pawn, black_kight, black_bishop, black_rook, black_queen, black_king,
color_mask = 24
} chessPiece;
Thus allowing logic that looks like:
if (board[i][j] & color_mask == currentColor)
{
impossibleMove = true; // or some-such
}
In Java, I'd just find that bitwise & operations on enums are not supported, and that further, the fairly awesome EnumSet doesn't easily apply since a piece can't be black AND white AND a rook AND a king.
So what I'm thinking looks like:
public enum ChessPieceId {
None (null, null),
Pawn (null, null),
Knight (null, null),
Bishop (null, null),
Rook (null, null),
Queen (null, null),
King (null, null),
Type_Mask (null, null),
White (null, null),
White_Pawn (ChessPieceId.White, ChessPieceId.Pawn),
White_Knight (ChessPieceId.White, ChessPieceId.Knight),
White_Bishop (ChessPieceId.White, ChessPieceId.Bishop),
White_Rook (ChessPieceId.White, ChessPieceId.Rook),
White_Queen (ChessPieceId.White, ChessPieceId.Queen),
White_King (ChessPieceId.White, ChessPieceId.King),
SkipA (null, null),
Black (null, null),
Black_Pawn (ChessPieceId.Black, ChessPieceId.Pawn),
Black_Knight (ChessPieceId.Black, ChessPieceId.Knight),
Black_Bishop (ChessPieceId.Black, ChessPieceId.Bishop),
Black_Rook (ChessPieceId.Black, ChessPieceId.Rook),
Black_Queen (ChessPieceId.Black, ChessPieceId.Queen),
Black_King (ChessPieceId.Black, ChessPieceId.King),
SkipB (null, null),
Color_Mask (null, null);
private final ChessPieceId color;
private final ChessPieceId type;
ChessPieceId(ChessPieceId pColor, ChessPieceId pType){
this.color = pColor;
this.type = pType;
}
ChessPieceId color() { return color; }
ChessPieceId type() { return type; }
// & operator should be built in. I considered an EnumSet but...
ChessPieceId and(ChessPieceId pSecond) {
switch(ChessPieceId.this.ordinal() & pSecond.ordinal()) {
case 0: //None.ordinal() etc. [if only Java were smarter]
return None;
case 1: return Pawn;
case 2: return Knight;
case 3: return Bishop;
case 4: return Rook;
case 5: return Queen;
case 6: return King;
case 7: return Type_Mask;
case 8: return White;
case 9: return White_Pawn;
case 10: return White_Knight;
case 11: return White_Rook;
case 12: return White_Bishop;
case 13: return White_Queen;
case 14: return White_King;
//case 15: return SkipA;
case 16: return Black;
case 17: return Black_Pawn;
case 18: return Black_Knight;
case 19: return Black_Rook;
case 20: return Black_Bishop;
case 21: return Black_Queen;
case 22: return Black_King;
//case 23: return SkipB;
case 24: return Color_Mask;
default:
return None;
}
}
}
Clearly I only need one or the other (the and operation, or the initialized values). Also it sure would be great if I could use an enum type in it's own definition, but I can't. So lines like:
Bishop (null, ChessPieceId.Bishop),
...
White (ChessPieceId.White, null),
Are out.
What was my question? Is there a better way that I'm missing. Also can I parse an int of the ordinal into an enum defined value, avoiding that whole case statement?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 Sun 关于
Enum
的教程的Card
示例中,它们将一副纸牌表示为两个Enum
-Rank和<代码>西装。这与你的问题同构。请参阅 http://java.sun.com/j2se /1.5.0/docs/guide/language/enums.html。
请注意,您可以通过定义适当的构造函数来使用您喜欢的任何额外值来扩充
Enum
,因此您也不必绑定序数值。In the
Card
example in the Sun tutorial onEnum
s, they represent a deck of cards as twoEnum
s -Rank
andSuit
. This is isomorphic to your problem. See http://java.sun.com/j2se/1.5.0/docs/guide/language/enums.html.Note that you can augment an
Enum
with any extra values you like by defining an appropriate constructor, so you are not bound to ordinal values, either.为棋子定义一个类不是很有意义吗?
然后你可以在其中插入两个枚举。想象一下。
Wouldn't it make sense to define a class for the chess piece?
Then you can stick two enums in it. Imagine that.
为什么坚持使用 Enum?这显然不是合适的工具。
我将有一个 Token 接口,并在一个具有颜色和类型的类中实现它(类型和颜色是两个不同的枚举)。您甚至可以对不同类型的部件有多个实现,以便您可以将其移动行为直接放入类本身中......
Why do you insist on the usage of Enums? It's clearly not the proper tool.
I would have a Token interface, and have it implemented in a class with it's color and type (the type and color being two different enums). You may even have multiple implementations for the different type of pieces so that you can put right into the class itself it's movement behavior....
只是一个建模建议...
我可能会从具有抽象 canMoveTo() 方法的 ChessPiece 对象开始。它会被典当、主教、国王等所覆盖。白/黑是棋子的一个属性。
因此,从您的代码来看,它始终遵循相同的模式,piece.canMoveTo(x1,y1);或piece.move(x1,y1);
始终考虑要求您的对象做某事,而不是从外部(从另一个对象)对对象的属性进行操作。
至于枚举问题,我不会担心优化解决方案,直到您编写了一个出色的通用解决方案,并且您发现它不够快/小/酷,无法满足您的要求。事实可能是,使用枚举甚至不适合您的最终模型。我什至会暂时忽略按位运算。
编辑:
我听到了一些非常有力的论点,指出 Java 的 sub-int 会大大减慢一切速度。例如,使用字节数组或位域。如果你绝对需要它们来获得空间,那很好,但不要使用它们,因为你认为它们会让你的代码更快——它们只会搞乱缓存和优化例程。
例如,声明变量“byte b;”仍然占用 RAM 中的整个 int,但每次访问它时都会强制进行额外的屏蔽操作和额外的验证。我不确定同样的事情是否适用于位操作,但是对我来说,您尝试做的事情确实感觉像是过早优化——这是最大的编程罪恶之一。
Just a modeling suggestion...
I'd probably start out with a ChessPiece object that had an abstract canMoveTo() method. It would be overridden by pawn, bishop, king, ... The White/Black is an attribute of the piece.
So from your code, it always follows the same pattern, piece.canMoveTo(x1,y1); or piece.move(x1,y1);
Always think in terms of asking your object to do something--not operating on attributes of your object from outside (from another object).
As for the enum problem, I wouldn't worry about optimizing the solution until you had a great general purpose solution coded and you find that it isn't fast/small/cool enough to meet your requirements. It may turn out that using enums doesn't even fit into your final model. I'd even leave bitwise operations out for a bit.
EDIT:
I've heard some pretty strong arguments stating that going sub-int for Java slows everything down quite a bit. For instance, using byte arrays or bitfields. If you absolutely need them for space, fine, but don't use them because you think they are going to make your code faster--they just mess up caching and optimizing routines.
For instance, declaring a variable "byte b;" still takes up an entire int in ram, but forces an extra masking operation and additional validations every time you access it. I'm not sure if the same thing applies to bit operations, but what you are trying to do really feels like premature optimization to me--one of the biggest programming evils.