简介
上一次我们谈到了Lotería有多棒,我们定义了游戏的具体规则。接下来并不令人惊讶:我们将翻译这些规则。我们将把它们转化为机器可以理解的东西。
让我们来看看每一条规则,并将其 "翻译 "成技术术语。
有一副有54张牌的牌。
这副牌用一个由54张Card的数组表示。
有N个玩家,每个玩家都有一个Tabla(棋盘)。
我们有N个Player,每个都有一个Board ,玩家不能选择这些板,它们是随机分配的。
Tablas总是有一个随机创建的4x4网格的卡片。
在游戏过程中,给定的Tablas是唯一的。
Board在游戏过程中,Tablas是随机的,每个游戏环节都是唯一的,这意味着没有玩家会在已经创建的任何其他棋盘上使用相同的数值排列。
有一个Cantor(播音员),负责从牌组中随机选择卡片,玩家必须听从宣布的卡片并在他们的棋盘上标记。
这两个要求非常明确,但是我们仍然需要更多的时间来提供一个更好的技术实现。让我们把这些移到停车场。
赢家由第一个喊出**"LOTERÍA!"并且(更重要的是**)在水平、垂直或对角线上有四张连续标记牌的玩家决定。
我们需要一种方法让玩家喊出他们赢了,显然,我们必须验证结果是否真的符合规则。
迭代#1
最初的迭代将考虑以下三个,八个要求。
- 有一副有54张牌的牌。
- 塔布拉斯总是有一个随机创建的4x4网格的牌,并且
- 赢家在水平、垂直或对角线上有四张连续的标记牌。
有一副有54张牌的牌组
定义一个Card 类型应该可以明确我们的目标,那么我们需要一个长度为54的Cards数组,或者更好的是我们可以定义一个新的类型Deck 来明确表示。
type (
// Card defines the card, which is part of the board and also announced by
// the caller.
Card uint64
// Deck defines the type containing all the 54 Cards.
Deck [54]Card
)
为什么卡uint64 ?因为这个类型有64位,在实践中最多可以代表64张不同的卡片,所以通过使用这个类型,我们可以对每一位进行标记,以独立表示每一位的具体卡片;我们只需要54位,但我们最多支持64位。既然如此......为什么是uint64 而不是int64 ?我个人喜欢用非负数工作(在这个特殊情况下,这一点并不重要)。
定义具体的卡片是一个重复性的工作,有点像。
const (
RoosterCard Card = iota
DevilCard
LadyCard
DandyCard
// all other cards ...
)
Tablas总是有一个随机创建的4x4网格的卡片
Boards有16个Cards,把它们定义为一个具体的类型是个好办法,因为在我们的游戏中,我们将与之进行大量的互动,所以这很有意义。
type (
// Board defines a "tabla", which is 4x4 grid of 16 Cards.
Board struct {
cards map[Card]index
}
)
注意有一个
index类型,我将在下面描述。
那么,随机创建的需求呢?对于这一点,最好的办法是创建一个具体的 "构造函数",像这样。
// NewRandomBoard returns a board with random Cards.
func NewRandomBoard() Board {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
cards := map[Card]index{}
for len(cards) < 16 {
v := r.Intn(53)
if _, ok := cards[Card(v)]; !ok {
cards[Card(v)] = index(1) << uint16(len(cards))
}
}
return Board{cards: cards}
}
上面的那个函数随机化了16个0到53之间的数字,并将它们分配给内部地图。在这个函数中,有一件非常重要的事情需要注意,那就是地图中index 的值,它代表了这张牌在(将要添加的)marked 属性中的位置。请继续阅读。
赢家有四张连续标记的牌
这意味着我们要求在我们的Board ,有一种方法来标记牌。
// Mark marks off the card on the board.
func (b *Board) Mark(c Card) error {
index, ok := b.cards[c]
if !ok {
return ErrCardNotOnBoard
}
b.marked |= index
return nil
}
上面的方法引入了上面提到的字段 marked上面提到过,这个属性是用来记录被标记的牌的,使用的是位标志。这个相同的字段,marked ,也被用来确定Board 是否是一个 赢家:
// Winner indicates whether the marked cards win the game.
func (b *Board) Winner() bool {
for _, pattern := range defaultWinningPatterns {
if (uint16(b.marked) & uint16(pattern)) == uint16(pattern) {
b.WinningPattern = pattern
return true
}
}
return false
}
其中这些defaultWinningPatterns ,是我们事先计算好的预定义的赢家模式。
看起来我们取得了重大进展!!!。让我们暂时休息一下,消化一下一切,明天再来。