云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第8篇

505 阅读5分钟

麻将牌 (Mahjong tiles) 抽象和编码实战

一句话描述麻将游戏业务:

  • 三人模式<三人两房>只用 子和 子两种花色共 72 张牌。
  • 四人模式<血战到底>使用 三种花色共 108 张牌。

系列文章

  1. Golang开发生产级麻将游戏服务器—第1篇
  2. Golang开发生产级麻将游戏服务器—第2篇
  3. Golang开发生产级麻将游戏服务器—第3篇
  4. Golang开发生产级麻将游戏服务器—第4篇
  5. Golang开发生产级麻将游戏服务器—第5篇
  6. Golang开发生产级麻将游戏服务器—第6篇
  7. Golang开发生产级麻将游戏服务器—第7篇

介绍

这将是一个完整的,完全践行 DevOps/GitOpsKubernetes 上云流程的 Golang 游戏服务器开发的系列教程。

这个系列教程是对开源项目 Nanoserver 的完整拆解,旨在帮助大家快速上手 Golang(游戏)服务器后端开发。通过实践去理解 Golang 开发的精髓 —— Share memory by communication(通过通信共享内存)

同时这个项目可能还会涉及到 Linux 性能调优(BPF 相关的工具)和系统保障(SRE)的相关的工作。

Step-By-Step 开发 Mahjong Server

  • 单体架构理解 Mahjong Server 业务 -> Nano Distributed Game Server(分布式) + 微服务 改造。
  • Demo:go-mahjong-server

牌(Tiles)抽象

定义 Tile struct

type Tile struct {
	Id    int
	Suit  int
	Rank  int
	Index int
}
  • Id108 张牌,用 0~107 标识每一张牌。
  • Suit:三种花色,用 0~2 标识每一种花色(0:,1:,2:)。
  • Rank9 种点数,用 1~9 标识(如:1条,9条,1筒,9筒,1万,9万等…)。
  • Index:索引(条:1~9,筒:11~19,万:21~29)。

通过一张表来理解

三种花色,每种类型牌的索引(Index):

条(1 ~ 9)筒(11 ~ 19)万(21 ~ 29)
1 1条11 1筒21 1万
2 2条12 2筒22 2万
3 3条13 3筒23 3万
4 4条14 4筒24 4万
5 5条15 5筒25 5万
6 6条16 6筒26 6万
7 7条17 7筒27 7万
8 8条18 8筒28 8万
9 9条19 9筒29 9万

所有牌(这里是 108 张)的 ID 编号:

条(0 ~ 35)筒(36 ~ 71)万(72 ~ 107)
0 1条36 1筒72 1万
1 1条37 1筒73 1万
2 1条38 1筒74 1万
3 1条39 1筒75 1万
4 2条40 2筒76 2万
5 2条41 2筒77 2万
6 2条42 2筒78 2万
7 2条43 2筒79 2万
8 3条44 3筒80 3万
9 3条45 3筒81 3万
10 3条46 3筒82 3万
11 3条47 3筒83 3万
12 4条48 4筒84 4万
13 4条49 4筒85 4万
14 4条50 4筒86 4万
15 4条51 4筒87 4万
16 5条52 5筒88 5万
17 5条53 5筒89 5万
18 5条54 5筒90 5万
19 5条55 5筒91 5万
20 6条56 6筒92 6万
21 6条57 6筒93 6万
22 6条58 6筒94 6万
23 6条59 6筒95 6万
24 7条60 7筒96 7万
25 7条61 7筒97 7万
26 7条62 7筒98 7万
27 7条63 7筒99 7万
28 8条64 8筒100 8万
29 8条65 8筒101 8万
30 8条66 8筒102 8万
31 8条67 8筒103 8万
32 9条68 9筒104 9万
33 9条69 9筒105 9万
34 9条70 9筒106 9万
35 9条71 9筒107 9万

编码实战

通过 ID 获取 Tile

一个算术问题,没啥好说的。

  • 三种花色,每种有 9 类不同的牌,每类又有 4 张相同的牌。

internal/game/tile.go

func TileFromID(id int) *Tile {
	if id < 0 {
		panic("illegal tile id")
	}
	var (
		tmp = id / 4
		h   = tmp / 9
		v   = tmp%9 + 1
		i   = h*10 + v
	)
	return &Tile{Suit: h, Rank: v, Index: i, Id: id}
}

编写单元测试函数

TileFromID 函数上右击选择 Generate Unit Tests For Function,我们编写它的单元测试函数。

internal/game/tile_test.go

func TestTileFromID(t *testing.T) {
	type args struct {
		id int
	}
	tests := []struct {
		name string
		args args
		want *Tile
	}{
    // 定义一堆用例
		{"1条", args{id: 0}, &Tile{Suit: 0, Rank: 1, Index: 1, Id: 0}},
		{"1条", args{id: 1}, &Tile{Suit: 0, Rank: 1, Index: 1, Id: 1}},
		{"1条", args{id: 2}, &Tile{Suit: 0, Rank: 1, Index: 1, Id: 2}},
		{"1条", args{id: 3}, &Tile{Suit: 0, Rank: 1, Index: 1, Id: 3}},
		{"9条", args{id: 35}, &Tile{Suit: 0, Rank: 9, Index: 9, Id: 35}},
		{"1筒", args{id: 36}, &Tile{Suit: 1, Rank: 1, Index: 11, Id: 36}},
		{"1筒", args{id: 37}, &Tile{Suit: 1, Rank: 1, Index: 11, Id: 37}},
		{"1筒", args{id: 38}, &Tile{Suit: 1, Rank: 1, Index: 11, Id: 38}},
		{"1筒", args{id: 39}, &Tile{Suit: 1, Rank: 1, Index: 11, Id: 39}},
		{"9筒", args{id: 71}, &Tile{Suit: 1, Rank: 9, Index: 19, Id: 71}},
		{"1万", args{id: 72}, &Tile{Suit: 2, Rank: 1, Index: 21, Id: 72}},
		{"1万", args{id: 73}, &Tile{Suit: 2, Rank: 1, Index: 21, Id: 73}},
		{"1万", args{id: 74}, &Tile{Suit: 2, Rank: 1, Index: 21, Id: 74}},
		{"1万", args{id: 75}, &Tile{Suit: 2, Rank: 1, Index: 21, Id: 75}},
		{"9万", args{id: 107}, &Tile{Suit: 2, Rank: 9, Index: 29, Id: 107}},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := TileFromID(tt.args.id); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("TileFromID() = %v, want %v", got, tt.want)
			}
		})
	}
}

执行:

cd internal/game/mahjong 
go test -v tile_test.go tile.go mahjong.go

我是为少
微信:uuhells123
公众号:黑客下午茶
加我微信(互相学习交流),关注公众号(获取更多学习资料~)