mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-11 17:42:26 +08:00
init commit
This commit is contained in:
196
common/utils/alg/bfs_pathfinding.go
Normal file
196
common/utils/alg/bfs_pathfinding.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package alg
|
||||
|
||||
const (
|
||||
NODE_NONE = iota
|
||||
NODE_START
|
||||
NODE_END
|
||||
NODE_BLOCK
|
||||
)
|
||||
|
||||
type PathNode struct {
|
||||
x int16
|
||||
y int16
|
||||
z int16
|
||||
visit bool
|
||||
state int
|
||||
parent *PathNode
|
||||
}
|
||||
|
||||
type BFS struct {
|
||||
gMap map[int16]map[int16]map[int16]*PathNode
|
||||
startPathNode *PathNode
|
||||
endPathNode *PathNode
|
||||
}
|
||||
|
||||
func NewBFS() (r *BFS) {
|
||||
r = new(BFS)
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *BFS) InitMap(terrain map[MeshMapPos]bool, start MeshMapPos, end MeshMapPos, extR int16) {
|
||||
xLen := end.X - start.X
|
||||
yLen := end.Y - start.Y
|
||||
zLen := end.Z - start.Z
|
||||
dx := int16(1)
|
||||
dy := int16(1)
|
||||
dz := int16(1)
|
||||
if xLen < 0 {
|
||||
dx = -1
|
||||
xLen *= -1
|
||||
}
|
||||
if yLen < 0 {
|
||||
dy = -1
|
||||
yLen *= -1
|
||||
}
|
||||
if zLen < 0 {
|
||||
dz = -1
|
||||
zLen *= -1
|
||||
}
|
||||
b.gMap = make(map[int16]map[int16]map[int16]*PathNode)
|
||||
for x := start.X - extR*dx; x != end.X+extR*dx; x += dx {
|
||||
b.gMap[x] = make(map[int16]map[int16]*PathNode)
|
||||
for y := start.Y - extR*dy; y != end.Y+extR*dy; y += dy {
|
||||
b.gMap[x][y] = make(map[int16]*PathNode)
|
||||
for z := start.Z - extR*dz; z != end.Z+extR*dz; z += dz {
|
||||
state := -1
|
||||
if x == start.X && y == start.Y && z == start.Z {
|
||||
state = NODE_START
|
||||
} else if x == end.X && y == end.Y && z == end.Z {
|
||||
state = NODE_END
|
||||
} else {
|
||||
_, exist := terrain[MeshMapPos{
|
||||
X: x,
|
||||
Y: y,
|
||||
Z: z,
|
||||
}]
|
||||
if exist {
|
||||
state = NODE_NONE
|
||||
} else {
|
||||
state = NODE_BLOCK
|
||||
}
|
||||
}
|
||||
node := &PathNode{
|
||||
x: x,
|
||||
y: y,
|
||||
z: z,
|
||||
visit: false,
|
||||
state: state,
|
||||
parent: nil,
|
||||
}
|
||||
b.gMap[x][y][z] = node
|
||||
if node.state == NODE_START {
|
||||
b.startPathNode = node
|
||||
} else if node.state == NODE_END {
|
||||
b.endPathNode = node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BFS) GetNeighbor(node *PathNode) []*PathNode {
|
||||
neighborList := make([]*PathNode, 0)
|
||||
dir := [][3]int16{
|
||||
//
|
||||
{1, 0, 0},
|
||||
{-1, 0, 0},
|
||||
{0, 1, 0},
|
||||
{0, -1, 0},
|
||||
{0, 0, 1},
|
||||
{0, 0, -1},
|
||||
//
|
||||
{1, 1, 0},
|
||||
{-1, 1, 0},
|
||||
{-1, -1, 0},
|
||||
{1, -1, 0},
|
||||
//
|
||||
{1, 0, 1},
|
||||
{-1, 0, 1},
|
||||
{-1, 0, -1},
|
||||
{1, 0, -1},
|
||||
//
|
||||
{0, 1, 1},
|
||||
{0, -1, 1},
|
||||
{0, -1, -1},
|
||||
{0, 1, -1},
|
||||
//
|
||||
{1, 1, 1},
|
||||
{1, 1, -1},
|
||||
{1, -1, 1},
|
||||
{1, -1, -1},
|
||||
{-1, 1, 1},
|
||||
{-1, 1, -1},
|
||||
{-1, -1, 1},
|
||||
{-1, -1, -1},
|
||||
}
|
||||
for _, v := range dir {
|
||||
x := node.x + v[0]
|
||||
y := node.y + v[1]
|
||||
z := node.z + v[2]
|
||||
if _, exist := b.gMap[x]; !exist {
|
||||
continue
|
||||
}
|
||||
if _, exist := b.gMap[x][y]; !exist {
|
||||
continue
|
||||
}
|
||||
if _, exist := b.gMap[x][y][z]; !exist {
|
||||
continue
|
||||
}
|
||||
neighborNode := b.gMap[x][y][z]
|
||||
neighborList = append(neighborList, neighborNode)
|
||||
}
|
||||
return neighborList
|
||||
}
|
||||
|
||||
func (b *BFS) GetPath() []*PathNode {
|
||||
path := make([]*PathNode, 0)
|
||||
if b.endPathNode.parent == nil {
|
||||
return nil
|
||||
}
|
||||
node := b.endPathNode
|
||||
for {
|
||||
if node == nil {
|
||||
break
|
||||
}
|
||||
path = append(path, node)
|
||||
node = node.parent
|
||||
}
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (b *BFS) Pathfinding() []MeshMapPos {
|
||||
queue := NewALQueue[*PathNode]()
|
||||
b.startPathNode.visit = true
|
||||
queue.EnQueue(b.startPathNode)
|
||||
for queue.Len() > 0 {
|
||||
head := queue.DeQueue()
|
||||
neighborList := b.GetNeighbor(head)
|
||||
for _, neighbor := range neighborList {
|
||||
if !neighbor.visit && neighbor.state != NODE_BLOCK {
|
||||
neighbor.visit = true
|
||||
neighbor.parent = head
|
||||
queue.EnQueue(neighbor)
|
||||
if neighbor.state == NODE_END {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
path := b.GetPath()
|
||||
if path == nil {
|
||||
return nil
|
||||
}
|
||||
pathVectorList := make([]MeshMapPos, 0)
|
||||
for i := len(path) - 1; i >= 0; i-- {
|
||||
node := path[i]
|
||||
pathVectorList = append(pathVectorList, MeshMapPos{
|
||||
X: node.x,
|
||||
Y: node.y,
|
||||
Z: node.z,
|
||||
})
|
||||
}
|
||||
return pathVectorList
|
||||
}
|
||||
7
common/utils/alg/common_pathfinding.go
Normal file
7
common/utils/alg/common_pathfinding.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package alg
|
||||
|
||||
type MeshMapPos struct {
|
||||
X int16
|
||||
Y int16
|
||||
Z int16
|
||||
}
|
||||
127
common/utils/alg/queue.go
Normal file
127
common/utils/alg/queue.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package alg
|
||||
|
||||
type LinkList struct {
|
||||
value any
|
||||
frontNode *LinkList
|
||||
nextNode *LinkList
|
||||
}
|
||||
|
||||
// LinkListQueue 无界队列 每个元素可存储不同数据结构
|
||||
type LLQueue struct {
|
||||
headPtr *LinkList
|
||||
tailPtr *LinkList
|
||||
len uint64
|
||||
}
|
||||
|
||||
func NewLLQueue() *LLQueue {
|
||||
return &LLQueue{
|
||||
headPtr: nil,
|
||||
tailPtr: nil,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *LLQueue) Len() uint64 {
|
||||
return q.len
|
||||
}
|
||||
|
||||
func (q *LLQueue) EnQueue(value any) {
|
||||
if q.headPtr == nil || q.tailPtr == nil {
|
||||
q.headPtr = new(LinkList)
|
||||
q.tailPtr = q.headPtr
|
||||
} else {
|
||||
q.tailPtr.nextNode = new(LinkList)
|
||||
q.tailPtr.nextNode.frontNode = q.tailPtr
|
||||
q.tailPtr = q.tailPtr.nextNode
|
||||
}
|
||||
q.tailPtr.value = value
|
||||
q.len++
|
||||
}
|
||||
|
||||
func (q *LLQueue) DeQueue() any {
|
||||
if q.Len() == 0 || q.headPtr == nil {
|
||||
return nil
|
||||
}
|
||||
ret := q.headPtr.value
|
||||
q.len--
|
||||
q.headPtr = q.headPtr.nextNode
|
||||
return ret
|
||||
}
|
||||
|
||||
// ArrayListQueue 无界队列 泛型
|
||||
type ALQueue[T any] struct {
|
||||
array []T
|
||||
}
|
||||
|
||||
func NewALQueue[T any]() *ALQueue[T] {
|
||||
return &ALQueue[T]{
|
||||
array: make([]T, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (q *ALQueue[T]) Len() uint64 {
|
||||
return uint64(len(q.array))
|
||||
}
|
||||
|
||||
func (q *ALQueue[T]) EnQueue(value T) {
|
||||
q.array = append(q.array, value)
|
||||
}
|
||||
|
||||
func (q *ALQueue[T]) DeQueue() T {
|
||||
if q.Len() == 0 {
|
||||
var null T
|
||||
return null
|
||||
}
|
||||
ret := q.array[0]
|
||||
q.array = q.array[1:]
|
||||
return ret
|
||||
}
|
||||
|
||||
// RingArrayQueue 有界队列 性能最好
|
||||
type RAQueue[T any] struct {
|
||||
ringArray []T
|
||||
ringArrayLen uint64
|
||||
headPtr uint64
|
||||
tailPtr uint64
|
||||
len uint64
|
||||
}
|
||||
|
||||
func NewRAQueue[T any](size uint64) *RAQueue[T] {
|
||||
return &RAQueue[T]{
|
||||
ringArray: make([]T, size),
|
||||
ringArrayLen: size,
|
||||
headPtr: 0,
|
||||
tailPtr: 0,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (q *RAQueue[T]) Len() uint64 {
|
||||
return q.len
|
||||
}
|
||||
|
||||
func (q *RAQueue[T]) EnQueue(value T) {
|
||||
if q.len >= q.ringArrayLen {
|
||||
return
|
||||
}
|
||||
q.ringArray[q.tailPtr] = value
|
||||
q.tailPtr++
|
||||
if q.tailPtr >= q.ringArrayLen {
|
||||
q.tailPtr = 0
|
||||
}
|
||||
q.len++
|
||||
}
|
||||
|
||||
func (q *RAQueue[T]) DeQueue() T {
|
||||
if q.Len() == 0 {
|
||||
var null T
|
||||
return null
|
||||
}
|
||||
ret := q.ringArray[q.headPtr]
|
||||
q.headPtr++
|
||||
if q.headPtr >= q.ringArrayLen {
|
||||
q.headPtr = 0
|
||||
}
|
||||
q.len--
|
||||
return ret
|
||||
}
|
||||
92
common/utils/alg/queue_test.go
Normal file
92
common/utils/alg/queue_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package alg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLLQueue(t *testing.T) {
|
||||
queue := NewLLQueue()
|
||||
queue.EnQueue(float32(100.123))
|
||||
queue.EnQueue(uint8(66))
|
||||
queue.EnQueue("aaa")
|
||||
queue.EnQueue(int64(-123456789))
|
||||
queue.EnQueue(true)
|
||||
queue.EnQueue(5)
|
||||
for queue.Len() > 0 {
|
||||
value := queue.DeQueue()
|
||||
fmt.Println(value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestALQueue(t *testing.T) {
|
||||
queue := NewALQueue[uint8]()
|
||||
queue.EnQueue(1)
|
||||
queue.EnQueue(2)
|
||||
queue.EnQueue(8)
|
||||
queue.EnQueue(9)
|
||||
for queue.Len() > 0 {
|
||||
value := queue.DeQueue()
|
||||
fmt.Println(value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRAQueue(t *testing.T) {
|
||||
queue := NewRAQueue[uint8](1000)
|
||||
queue.EnQueue(1)
|
||||
queue.EnQueue(2)
|
||||
queue.EnQueue(8)
|
||||
queue.EnQueue(9)
|
||||
for queue.Len() > 0 {
|
||||
value := queue.DeQueue()
|
||||
fmt.Println(value)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLLQueue(b *testing.B) {
|
||||
data := ""
|
||||
for i := 0; i < 1024; i++ {
|
||||
data += "X"
|
||||
}
|
||||
queue := NewLLQueue()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
queue.EnQueue(&data)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
queue.DeQueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkALQueue(b *testing.B) {
|
||||
data := ""
|
||||
for i := 0; i < 1024; i++ {
|
||||
data += "X"
|
||||
}
|
||||
queue := NewALQueue[*string]()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
queue.EnQueue(&data)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
queue.DeQueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRAQueue(b *testing.B) {
|
||||
data := ""
|
||||
for i := 0; i < 1024; i++ {
|
||||
data += "X"
|
||||
}
|
||||
queue := NewRAQueue[*string](1000)
|
||||
for i := 0; i < b.N; i++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
queue.EnQueue(&data)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
queue.DeQueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
89
common/utils/alg/snowflake.go
Normal file
89
common/utils/alg/snowflake.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package alg
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 雪花算法的基本实现
|
||||
|
||||
// snowflake ID 是一个64位的int数据 由四部分组成
|
||||
// A-B-C-D
|
||||
// A 1位 最高位不使用
|
||||
// B 41位 时间戳
|
||||
// C 10位 节点ID
|
||||
// D 12位 毫秒内序列号
|
||||
// 时间戳 节点ID 毫秒内序列号 位数可按需调整
|
||||
|
||||
const (
|
||||
workerBits uint8 = 10 // 节点ID位数 2^10=1024
|
||||
numberBits uint8 = 12 // 毫秒内序列号位数 2^12=4096
|
||||
workerMax int64 = -1 ^ (-1 << workerBits) // 节点ID最大值
|
||||
numberMax int64 = -1 ^ (-1 << numberBits) // 毫秒内序列号最大值
|
||||
timeShift = workerBits + numberBits // 时间戳向左偏移量
|
||||
workerShift = numberBits // 节点ID向左偏移量
|
||||
/*
|
||||
* 原始算法使用41位字节作为时间戳数值
|
||||
* 大约68年也就是2038年就会用完
|
||||
* 这里做个偏移以增加可用时间
|
||||
* !!!这个一旦定义且开始生成ID后千万不要改了!!!
|
||||
* 不然可能会生成相同的ID
|
||||
*/
|
||||
epoch int64 = 1657148827000 // 2022-07-07 07:07:07
|
||||
)
|
||||
|
||||
type SnowflakeWorker struct {
|
||||
lock sync.Mutex // 互斥锁
|
||||
timestamp int64 // 记录时间戳
|
||||
workerId int64 // 节点ID
|
||||
number int64 // 当前毫秒内已经生成的ID序列号 从0开始累加
|
||||
}
|
||||
|
||||
func NewSnowflakeWorker(workerId int64) *SnowflakeWorker {
|
||||
if workerId < 0 || workerId > workerMax {
|
||||
// worker id error
|
||||
return nil
|
||||
}
|
||||
worker := &SnowflakeWorker{
|
||||
timestamp: 0,
|
||||
workerId: workerId,
|
||||
number: 0,
|
||||
}
|
||||
return worker
|
||||
}
|
||||
|
||||
func (s *SnowflakeWorker) GenId() int64 {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
// 当前毫秒时间戳
|
||||
now := time.Now().UnixNano() / 1e6
|
||||
if s.timestamp > now {
|
||||
// 发生了时钟回拨
|
||||
if s.timestamp-now > 1000 {
|
||||
// 时钟回拨太严重
|
||||
return -1
|
||||
}
|
||||
for now <= s.timestamp {
|
||||
// 自旋等待当前时间超过上一次ID生成的时间
|
||||
now = time.Now().UnixNano() / 1e6
|
||||
}
|
||||
}
|
||||
if s.timestamp == now {
|
||||
s.number++
|
||||
if s.number > numberMax {
|
||||
// 当前毫秒内生成ID数量超过限制
|
||||
for now <= s.timestamp {
|
||||
// 自旋等待
|
||||
now = time.Now().UnixNano() / 1e6
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.timestamp < now {
|
||||
// 新的毫秒到来重置序列号和时间戳
|
||||
s.number = 0
|
||||
s.timestamp = now
|
||||
}
|
||||
// 生成ID
|
||||
id := (now-epoch)<<timeShift | (s.workerId << workerShift) | (s.number)
|
||||
return id
|
||||
}
|
||||
50
common/utils/alg/snowflake_test.go
Normal file
50
common/utils/alg/snowflake_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package alg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type UniqueID interface {
|
||||
~int64 | ~string
|
||||
}
|
||||
|
||||
func idDupCheck[T UniqueID](genIdFunc func() T) {
|
||||
var wg sync.WaitGroup
|
||||
totalIdList := make(map[int]*[]T)
|
||||
for i := 0; i < 1000; i++ {
|
||||
wg.Add(1)
|
||||
_idList := make([]T, 0)
|
||||
_idListPtr := &_idList
|
||||
totalIdList[i] = _idListPtr
|
||||
go func(idListPtr *[]T) {
|
||||
defer wg.Done()
|
||||
for ii := 0; ii < 10000; ii++ {
|
||||
id := genIdFunc()
|
||||
*idListPtr = append(*idListPtr, id)
|
||||
}
|
||||
}(_idListPtr)
|
||||
}
|
||||
wg.Wait()
|
||||
dupCheck := make(map[T]bool)
|
||||
for gid, idListPtr := range totalIdList {
|
||||
for _, id := range *idListPtr {
|
||||
value, exist := dupCheck[id]
|
||||
if exist && value == true {
|
||||
fmt.Printf("find dup id, gid: %v, id: %v\n", gid, id)
|
||||
} else {
|
||||
dupCheck[id] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("check finish\n")
|
||||
}
|
||||
|
||||
func TestSnowflakeGenId(t *testing.T) {
|
||||
snowflake := NewSnowflakeWorker(1)
|
||||
if snowflake == nil {
|
||||
panic("create snowflake worker error")
|
||||
}
|
||||
idDupCheck(snowflake.GenId)
|
||||
}
|
||||
Reference in New Issue
Block a user