package entity import ( "mongo.games.com/game/gamesrv/slotspkg/internal/exported/excel2go/structs" "mongo.games.com/game/gamesrv/slotspkg/internal/generic/errors" "mongo.games.com/game/gamesrv/slotspkg/internal/generic/key" "mongo.games.com/game/gamesrv/slotspkg/internal/module/shared" "mongo.games.com/goserver/core/logger" "strconv" ) const ( // WalkRootFirst walks root first, recurses from root WalkRootFirst int64 = 1 // WalkRootLast walks root last, recurses from farest leaf WalkRootLast int64 = 2 ) // GetNode finds node by nodeID func (e *Entity) GetNode(nodeID int64) *shared.Node { for _, node := range e.NodeTree.Nodes { if node.ID == nodeID { return node } } panic(errors.NodeNotFound.ErrorWith(strconv.FormatInt(nodeID, 10))) } // IsEmpty returns if the NodeTree is empty func (e *Entity) IsEmpty() bool { return len(e.NodeTree.Nodes) == 0 } // IsClosing returns if node is in `NodeTree.Closing` slice func (e *Entity) IsClosing(node *shared.Node) bool { for _, closed := range e.NodeTree.Closing { if node.ID == closed { return true } } return false } // CursorNode returns the `Cursor` node in `NodeTree` func (e *Entity) CursorNode() *shared.Node { return e.GetNode(e.NodeTree.Cursor) } // NextNode returns the `Next` node in `NodeTree` func (e *Entity) NextNode() *shared.Node { return e.GetNode(e.NodeTree.Next) } // RootNode returns the `Root` node in `NodeTree` func (e *Entity) RootNode() *shared.Node { return e.GetNode(e.NodeTree.Root) } // ParentNode returns the `Parent` node of `Cursor` node in `NodeTree` func (e *Entity) ParentNode() *shared.Node { return e.GetNode(e.GetNode(e.NodeTree.Cursor).Parent) } // AncestorNode returns the `Ancestor` Node of `Cursor` Node in `NodeTree` func (e *Entity) AncestorNode() *shared.Node { switch e.CursorNode().Type { case key.BaseSpin, key.FreeSpin, key.Root: return e.RootNode() default: return e.recursiveGetAncestor(e.CursorNode()) } } func (e *Entity) recursiveGetAncestor(node *shared.Node) *shared.Node { node = e.GetNode(node.GetParent()) nodeType := node.GetType() switch nodeType { case key.BaseSpin, key.FreeSpin, key.Root: return node default: return e.recursiveGetAncestor(node) } } // ClosingNodes returns the `Closing` node in `NodeTree` func (e *Entity) ClosingNodes() []*shared.Node { closingNodes := make([]*shared.Node, len(e.NodeTree.Closing)) for i, closing := range e.NodeTree.Closing { closingNodes[i] = e.GetNode(closing) } return closingNodes } // InitRootNode add root node to node tree func (e *Entity) InitRootNode() int64 { ID := e.NodeTree.Incr e.NodeTree.Cursor = ID e.NodeTree.Next = ID e.NodeTree.Root = ID e.NodeTree.Nodes = append(e.NodeTree.Nodes, &shared.Node{ ID: ID, Parent: ID, Children: make([]int64, 0), Type: key.Root, Formations: make([]*shared.Formation, 0), Features: make([]*shared.Feature, 0), }) return ID } func (e *Entity) InitAct() { e.NodeTree.Act = &shared.Act{ BetSizeIndex: 0, BetLevelIndex: 0, BetLineIndex: 0, Ratio: 1, } FirstBet := e.Shell.DataSet.GetMachineSheet(e.Theme, "Bet", "FirstBet", 0) if FirstBet != nil { FirstBetMap := FirstBet.(map[int64]*structs.FirstBet) e.NodeTree.Act.BetSizeIndex = FirstBetMap[1].BetSizeIndex e.NodeTree.Act.BetLevelIndex = FirstBetMap[1].BetLevelIndex } } func (e *Entity) UpdateAct(act *shared.Act) { e.NodeTree.Act = act } func (e *Entity) addChildNodeBySide(parent int64, typ string, progress int64, rightSide bool) *shared.Node { if progress == 0 { panic(errors.NewNodeZeroProgress.Error()) } e.NodeTree.Incr++ nodeID := e.NodeTree.Incr parentNode := e.GetNode(parent) if rightSide { parentNode.Children = append(parentNode.Children, nodeID) } else { parentNode.Children = append([]int64{nodeID}, parentNode.Children...) } formations := make([]*shared.Formation, 0) spinType := e.MachineDesc.GetSpinType(typ) formationSeqDescs := e.NodeDesc.GetFormationSeqDescs(typ) for _, desc := range formationSeqDescs { formation := &shared.Formation{ SpinType: desc.SpinType, ID: desc.ID, NodeType: desc.NodeType, SeqID: desc.SeqID, NodeID: nodeID, } formations = append(formations, formation) } node := &shared.Node{ ID: nodeID, Parent: parent, Children: make([]int64, 0), Type: typ, SpinType: spinType, Formations: formations, Features: make([]*shared.Feature, 0), Bet: parentNode.Bet, ProgressMax: progress, NeedPrepare: false, Prepared: false, } e.NodeTree.Nodes = append(e.NodeTree.Nodes, node) return node } // AddChildNode add new node to node tree func (e *Entity) AddChildNode(parent int64, typ string, progress int64) *shared.Node { return e.addChildNodeBySide(parent, typ, progress, true) } // AddChildNode add new node to node tree func (e *Entity) AddChildNodeAtLeft(parent int64, typ string, progress int64) *shared.Node { return e.addChildNodeBySide(parent, typ, progress, false) } // UpdateNode2NewType update cursor to new node func (e *Entity) UpdateNode2NewType(nodeId int64, typ string, progress int64) *shared.Node { node := e.GetNode(nodeId) if node == nil { logger.Logger.Errorf("UpdateNode2NewType err: node == nil nodeid=(%v)", nodeId) return nil } node.Type = typ node.SpinType = e.MachineDesc.GetSpinType(typ) node.ProgressMax = progress node.NoBase = true e.NextNodeDesc = e.GetNodeDesc(node) e.PrepareNextOriginFormations() formations := make([]*shared.Formation, 0) formationSeqDescs := e.NodeDesc.GetFormationSeqDescs(typ) for _, desc := range formationSeqDescs { formation := &shared.Formation{ SpinType: desc.SpinType, ID: desc.ID, NodeType: desc.NodeType, SeqID: desc.SeqID, NodeID: node.ID, } formations = append(formations, formation) } node.Formations = formations return node } // PruneNode prunes a node from tree func (e *Entity) PruneNode(node *shared.Node) { for _, child := range node.Children { childNode := e.GetNode(child) e.PruneNode(childNode) } parentNode := e.GetNode(node.Parent) for index, child := range parentNode.Children { if child == node.ID { parentNode.Children = append(parentNode.Children[:index], parentNode.Children[index+1:]...) break } } for index, n := range e.NodeTree.Nodes { if n.ID == node.ID { e.NodeTree.Nodes = append(e.NodeTree.Nodes[:index], e.NodeTree.Nodes[index+1:]...) break } } } // Walk walks all nodes in node tree // return true means stopping right now; false means continuing func (e *Entity) Walk(f func(node *shared.Node) bool) bool { for _, node := range e.NodeTree.Nodes { if f(node) { return true } } return false } // WalkTree walks nodes from root node func (e *Entity) WalkTree(direction int64, f func(node *shared.Node) bool) bool { return e.WalkTreeNode(e.RootNode(), direction, f) } // WalkTreeNode walks nodes from parent node // return true means stopping right now; false means continuing func (e *Entity) WalkTreeNode( node *shared.Node, direction int64, f func(node *shared.Node) bool, ) bool { switch direction { case WalkRootFirst: if f(node) { return true } for _, child := range node.Children { childNode := e.GetNode(child) if e.WalkTreeNode(childNode, direction, f) { return true } } case WalkRootLast: for _, child := range node.Children { childNode := e.GetNode(child) if e.WalkTreeNode(childNode, direction, f) { return true } } if f(node) { return true } } return false } // GetChildrenNodes gets children nodes of one nodes func (e *Entity) GetChildrenNodes(node *shared.Node) []*shared.Node { nodes := make([]*shared.Node, 0) for _, childNodeID := range node.Children { nodes = append(nodes, e.GetNode(childNodeID)) } return nodes } // MustNext must return a node // add a new node if all nodes is accomplished func (e *Entity) MustNext() { if next := e.Next(); next != nil { if !e.CheatNextNodeOnMustNext() { e.NodeTree.Next = next.ID } return } e.AddChildNode(e.NodeTree.Root, key.BaseSpin, 1) if next := e.Next(); next != nil { e.NodeTree.Next = next.ID return } panic(errors.NextNodeNotFound.Error()) } // Next finds Next node in `NodeTree` func (e *Entity) Next() *shared.Node { node := e.CursorNode() e.NodeTree.Closing = make([]int64, 0) if next := e.nextChildren(node); next != nil { return next } if next := e.nextSelf(node); next != nil { return next } if next := e.nextParent(node); next != nil { return next } return nil } // nextChildren finds node in children that is not accomplished func (e *Entity) nextChildren(node *shared.Node) (next *shared.Node) { for _, child := range node.Children { childNode := e.GetNode(child) if e.IsClosing(childNode) { // Forbid repeated `next`. continue } if next := e.nextChildren(childNode); next != nil { return next } if next := e.nextSelf(childNode); next != nil { return next } } return nil } // nextSelf finds node in self node that is not accomplished func (e *Entity) nextSelf(node *shared.Node) (next *shared.Node) { if e.IsClosing(node) { // Forbid repeated `next`. return nil } if e.IsFinished(node) { e.NodeTree.Closing = append(e.NodeTree.Closing, node.ID) return nil } return node } // nextParent recursively next every node below this node, // including all descendents. func (e *Entity) nextParent(node *shared.Node) (next *shared.Node) { if node.ID == 0 || node.Type == key.Root { return nil } nodeParent := e.GetNode(node.Parent) if nodeParent == nil { panic(errors.NodeNotFound.Error()) } if next := e.nextChildren(nodeParent); next != nil { return next } if next := e.nextSelf(nodeParent); next != nil { return next } if next := e.nextParent(nodeParent); next != nil { return next } return nil } // MoveNext move `Cursor` to `Next` when receive a request func (e *Entity) MoveNext() { e.PruneNodeFeatures(e.CursorNode()) if !e.CheatNextNodeOnMoveNext() { e.NodeTree.Cursor = e.NodeTree.Next } e.PruneNodeFeatures(e.CursorNode()) for _, closingNode := range e.ClosingNodes() { e.PruneNode(closingNode) } } func (e *Entity) RecheckClosing() { closing := make([]int64, len(e.NodeTree.Closing)) copy(closing, e.NodeTree.Closing) e.NodeTree.Closing = make([]int64, 0) for _, nodeID := range closing { node := e.GetNode(nodeID) if next := e.nextChildren(node); next != nil { continue } if next := e.nextSelf(node); next != nil { continue } } } func (e *Entity) CheatNextNodeOnMoveNext() bool { v, ok := e.Data[key.MachineNextNodeID] if !ok { return false } nodeID := v.(int64) if nodeID == 0 { return false } e.NodeTree.Cursor = nodeID e.NodeTree.Next = nodeID e.RecheckClosing() delete(e.Data, key.MachineNextNodeID) return true } func (e *Entity) CheatNextNodeOnMustNext() bool { v, ok := e.Data[key.MachineNextNodeID] if !ok { return false } nodeID := v.(int64) if nodeID == 0 { return false } e.NodeTree.Next = nodeID delete(e.Data, key.MachineNextNodeID) return true } func (e *Entity) PrepareStep() { e.NodeTree.Step = e.NodeTree.Step + 1 }