Skip to content

Commit

Permalink
实现切换座位 - 牌谱
Browse files Browse the repository at this point in the history
  • Loading branch information
EndlessCheng committed Jun 16, 2019
1 parent 46c9b96 commit 5c8ee98
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 50 deletions.
38 changes: 33 additions & 5 deletions analysis_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ func (rc *roundAnalysisCache) print() {
if c.isRiichiWhenDiscard {
suffix = "[立直]"
} else if c.selfDiscardTile == -1 && i == len(rc.cache)-1 {
suffix = "[自摸]"
//suffix = "[自摸]"
// TODO: 流局
}
printTileInfo(c.selfDiscardTile, c.selfDiscardTileRisk, suffix)
}
Expand Down Expand Up @@ -194,7 +195,7 @@ func (rc *roundAnalysisCache) addPossibleChiPonKan(attackTile int, attackTileRis
//

type gameAnalysisCache struct {
// roundNumber ben 巡目
// 局数 本场数
wholeGameCache [][]*roundAnalysisCache

majsoulRecordUUID string
Expand All @@ -205,7 +206,7 @@ type gameAnalysisCache struct {
func newGameAnalysisCache(majsoulRecordUUID string, selfSeat int) *gameAnalysisCache {
cache := make([][]*roundAnalysisCache, 3*4) // 最多到西四
for i := range cache {
cache[i] = make([]*roundAnalysisCache, 20)
cache[i] = make([]*roundAnalysisCache, 100) // 最多连庄
}
return &gameAnalysisCache{
wholeGameCache: cache,
Expand All @@ -214,7 +215,30 @@ func newGameAnalysisCache(majsoulRecordUUID string, selfSeat int) *gameAnalysisC
}
}

var globalAnalysisCache *gameAnalysisCache
//

// TODO: 重构成 struct
var (
_analysisCacheList = make([]*gameAnalysisCache, 4)
_currentSeat int
)

func resetAnalysisCache() {
_analysisCacheList = make([]*gameAnalysisCache, 4)
}

func setAnalysisCache(analysisCache *gameAnalysisCache) {
_analysisCacheList[analysisCache.selfSeat] = analysisCache
_currentSeat = analysisCache.selfSeat
}

func getAnalysisCache(seat int) *gameAnalysisCache {
return _analysisCacheList[seat]
}

func getCurrentAnalysisCache() *gameAnalysisCache {
return getAnalysisCache(_currentSeat)
}

func (c *gameAnalysisCache) runMajsoulRecordAnalysisTask(actions majsoulRoundActions) error {
// 从第一个 action 中取出局和场
Expand All @@ -234,14 +258,17 @@ func (c *gameAnalysisCache) runMajsoulRecordAnalysisTask(actions majsoulRoundAct
}
c.wholeGameCache[roundNumber][ben] = roundCache
} else if roundCache.isStart {
if debugMode {
fmt.Println("无需重复计算")
}
return nil
}

// 遍历自家舍牌,找到舍牌前的操作
// 若为摸牌操作,计算出此时的 AI 进攻舍牌和防守舍牌
// 若为鸣牌操作,计算出此时的 AI 进攻舍牌(无进攻舍牌则设为 -1),防守舍牌设为 -1
// TODO: 玩家跳过,但是 AI 觉得应鸣牌?
majsoulRoundData := &majsoulRoundData{selfSeat: c.selfSeat}
majsoulRoundData := &majsoulRoundData{selfSeat: c.selfSeat} // 注意这里是用的一个新的 majsoulRoundData 去计算的,不会有数据冲突
majsoulRoundData.roundData = newGame(majsoulRoundData)
majsoulRoundData.roundData.gameMode = gameModeRecordCache
majsoulRoundData.skipOutput = true
Expand All @@ -250,6 +277,7 @@ func (c *gameAnalysisCache) runMajsoulRecordAnalysisTask(actions majsoulRoundAct
if debugMode {
fmt.Println("用户退出该牌谱")
}
// 提前退出,减少不必要的计算
return nil
}
if debugMode {
Expand Down
24 changes: 14 additions & 10 deletions core.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,15 @@ func (d *roundData) analysis() error {
return nil
}

if debugMode {
fmt.Println("当前座位为", d.parser.GetSelfSeat())
}

var currentRoundCache *roundAnalysisCache
if analysisCache := getAnalysisCache(d.parser.GetSelfSeat()); analysisCache != nil {
currentRoundCache = analysisCache.wholeGameCache[d.roundNumber][d.benNumber]
}

switch {
case d.parser.IsInit():
// round 开始/重连
Expand Down Expand Up @@ -440,6 +449,11 @@ func (d *roundData) analysis() error {
panic("not impl!")
}

// 由于 reset 了,重新获取 currentRoundCache
if analysisCache := getAnalysisCache(d.parser.GetSelfSeat()); analysisCache != nil {
currentRoundCache = analysisCache.wholeGameCache[d.roundNumber][d.benNumber]
}

d.doraIndicators = []int{doraIndicator}
d.descLeftCounts(doraIndicator)
for _, tile := range hands {
Expand All @@ -452,7 +466,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录舍牌推荐
if d.gameMode == gameModeRecordCache && len(hands) == 14 {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.addAIDiscardTileWhenDrawTile(simpleBestDiscardTile(playerInfo), -1, 0, 0)
}

Expand All @@ -462,7 +475,6 @@ func (d *roundData) analysis() error {

// 牌谱模式下,打印舍牌推荐
if d.gameMode == gameModeRecord {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.print()
}

Expand Down Expand Up @@ -510,7 +522,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录加杠操作
if d.gameMode == gameModeRecordCache {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.addKan(meldType)
}
}
Expand Down Expand Up @@ -555,7 +566,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录暗杠操作
if d.gameMode == gameModeRecordCache {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.addKan(meldType)
}
} else {
Expand All @@ -570,7 +580,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录吃碰明杠操作
if d.gameMode == gameModeRecordCache {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.addChiPonKan(meldType)
}
}
Expand Down Expand Up @@ -634,7 +643,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录舍牌推荐
if d.gameMode == gameModeRecordCache {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
bestAttackDiscardTile := simpleBestDiscardTile(playerInfo)
bestDefenceDiscardTile := mixedRiskTable.getBestDefenceTile(playerInfo.HandTiles34)
bestAttackDiscardTileRisk, bestDefenceDiscardTileRisk := 0.0, 0.0
Expand All @@ -657,7 +665,6 @@ func (d *roundData) analysis() error {

// 牌谱模式下,打印舍牌推荐
if d.gameMode == gameModeRecord {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.print()
}

Expand Down Expand Up @@ -702,7 +709,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录自家舍牌
if d.gameMode == gameModeRecordCache {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.addSelfDiscardTile(discardTile, mixedRiskTable[discardTile], isReach)
}

Expand Down Expand Up @@ -760,7 +766,6 @@ func (d *roundData) analysis() error {

// 牌谱分析模式下,记录可能的鸣牌
if d.gameMode == gameModeRecordCache {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
allowChi := who == 3
_, results14, incShantenResults14 := util.CalculateMeld(playerInfo, discardTile, isRedFive, allowChi)
bestAttackDiscardTile := -1
Expand Down Expand Up @@ -800,7 +805,6 @@ func (d *roundData) analysis() error {

// 牌谱模式下,打印舍牌推荐
if d.gameMode == gameModeRecord {
currentRoundCache := globalAnalysisCache.wholeGameCache[d.roundNumber][d.benNumber]
currentRoundCache.print()
}

Expand Down
1 change: 0 additions & 1 deletion majsoul_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,3 @@ func parseMajsoulRecordAction(actions []*majsoulRecordAction) (roundActionsList
}
return
}

89 changes: 55 additions & 34 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,18 @@ func (h *mjHandler) runAnalysisMajsoulMessageTask() {
color.HiGreen("收到 %2d 个雀魂牌谱(已收集 %d 个),请在网页上点击「查看」", len(d.RecordBaseInfoList), len(h.majsoulRecordMap))
case d.SharedRecordBaseInfo != nil:
// 处理分享的牌谱基本信息
// FIXME: 观看自己的牌谱也会有 d.SharedRecordBaseInfo
record := d.SharedRecordBaseInfo
h.majsoulRecordMap[record.UUID] = record
if err := h._loadMajsoulRecordBaseInfo(record.UUID); err != nil {
h.logError(err)
break
}
case d.CurrentRecordUUID != "":
// 载入某个牌谱
resetAnalysisCache()
h.majsoulCurrentRecordActionsList = nil

if err := h._loadMajsoulRecordBaseInfo(d.CurrentRecordUUID); err != nil {
// 看的是分享的牌谱(先收到 CurrentRecordUUID 和 AccountID,然后收到 SharedRecordBaseInfo)
// 记录主视角 ID
Expand All @@ -220,6 +225,11 @@ func (h *mjHandler) runAnalysisMajsoulMessageTask() {
gameConf.currentActiveMajsoulAccountID = d.AccountID
}
case len(d.RecordActions) > 0:
if h.majsoulCurrentRecordActionsList != nil {
// TODO: 网页发送更恰当的信息?
break
}

if h.majsoulCurrentRecordUUID == "" {
h.logError(fmt.Errorf("错误:程序未收到所观看的雀魂牌谱的 UUID"))
break
Expand All @@ -237,21 +247,16 @@ func (h *mjHandler) runAnalysisMajsoulMessageTask() {
break
}

// 获取并设置第一局的庄家(相对于主视角的位置)
firstRoundDealer, err := baseInfo.getFistRoundDealer(selfAccountID)
if err != nil {
h.logError(err)
break
}
h.majsoulRoundData.reset(0, 0, firstRoundDealer)
h.majsoulRoundData.newGame()
h.majsoulRoundData.gameMode = gameModeRecord

// 设置主视角初始座位
h.majsoulRoundData.selfSeat, err = baseInfo.getSelfSeat(selfAccountID)
// 获取并设置主视角初始座位
selfSeat, err := baseInfo.getSelfSeat(selfAccountID)
if err != nil {
h.logError(err)
break
}
h.majsoulRoundData.selfSeat = selfSeat

// 准备分析……
majsoulCurrentRecordActions, err := parseMajsoulRecordAction(d.RecordActions)
Expand All @@ -263,11 +268,12 @@ func (h *mjHandler) runAnalysisMajsoulMessageTask() {
h.majsoulCurrentRoundIndex = 0
h.majsoulCurrentActionIndex = 0

actions := h.majsoulCurrentRecordActionsList[0]
actions := h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex]

// 创建分析任务
globalAnalysisCache = newGameAnalysisCache(h.majsoulCurrentRecordUUID, h.majsoulRoundData.selfSeat)
go globalAnalysisCache.runMajsoulRecordAnalysisTask(actions)
analysisCache := newGameAnalysisCache(h.majsoulCurrentRecordUUID, selfSeat)
setAnalysisCache(analysisCache)
go analysisCache.runMajsoulRecordAnalysisTask(actions)

// 分析第一局的起始信息
data := actions[0].Action
Expand Down Expand Up @@ -297,26 +303,39 @@ func (h *mjHandler) runAnalysisMajsoulMessageTask() {
case d.ChangeSeatTo != nil:
// 切换座位
changeSeatTo := *(d.ChangeSeatTo)
h.majsoulRoundData.selfSeat = changeSeatTo
if debugMode {
fmt.Println("座位已切换至", changeSeatTo)
}

var actions majsoulRoundActions
if h.majsoulRoundData.gameMode == gameModeLive { // 观战
h.majsoulRoundData.selfSeat = changeSeatTo
actions := h.majsoulCurrentRoundActions
if len(actions) == 0 {
break
}
if len(actions) == 1 {
h._analysisMajsoulRoundData(actions[0].Action, "")
break
}
h.majsoulRoundData.skipOutput = true
// 留最后两个刷新,这样确保会刷新界面?
for _, action := range actions[:len(actions)-2] {
h._analysisMajsoulRoundData(action.Action, "")
}
h.majsoulRoundData.skipOutput = false
h._analysisMajsoulRoundData(actions[len(actions)-2].Action, "")
h._analysisMajsoulRoundData(actions[len(actions)-1].Action, "")
actions = h.majsoulCurrentRoundActions
} else { // 牌谱
fullActions := h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex]
actions = fullActions[:h.majsoulCurrentActionIndex+1]
analysisCache := getAnalysisCache(changeSeatTo)
if analysisCache == nil {
analysisCache = newGameAnalysisCache(h.majsoulCurrentRecordUUID, changeSeatTo)
}
setAnalysisCache(analysisCache)
// 创建分析任务
go analysisCache.runMajsoulRecordAnalysisTask(fullActions)
}

if len(actions) == 0 {
break
}

fastRecordEnd := util.MaxInt(0, len(actions)-3)
h.majsoulRoundData.skipOutput = true
// 留最后三个刷新,这样确保会刷新界面
for _, action := range actions[:fastRecordEnd] {
h._analysisMajsoulRoundData(action.Action, "")
}
h.majsoulRoundData.skipOutput = false
for _, action := range actions[fastRecordEnd:] {
h._analysisMajsoulRoundData(action.Action, "")
}
default:
// 其他:AI 分析
Expand Down Expand Up @@ -376,6 +395,8 @@ func (h *mjHandler) _onRecordClick(clickAction string, clickActionIndex int, fas
fmt.Println("[_onRecordClick] 收到", clickAction, clickActionIndex, fastRecordTo)
}

analysisCache := getCurrentAnalysisCache()

switch clickAction {
case "nextStep", "update":
newActionIndex := h.majsoulCurrentActionIndex + 1
Expand All @@ -386,19 +407,19 @@ func (h *mjHandler) _onRecordClick(clickAction string, clickActionIndex int, fas
case "nextRound":
h.majsoulCurrentRoundIndex = (h.majsoulCurrentRoundIndex + 1) % len(h.majsoulCurrentRecordActionsList)
h.majsoulCurrentActionIndex = 0
go globalAnalysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
go analysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
case "preRound":
h.majsoulCurrentRoundIndex = (h.majsoulCurrentRoundIndex - 1 + len(h.majsoulCurrentRecordActionsList)) % len(h.majsoulCurrentRecordActionsList)
h.majsoulCurrentActionIndex = 0
go globalAnalysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
go analysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
case "jumpRound":
h.majsoulCurrentRoundIndex = clickActionIndex % len(h.majsoulCurrentRecordActionsList)
h.majsoulCurrentActionIndex = 0
go globalAnalysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
go analysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
case "nextXun", "preXun", "jumpXun", "preStep", "jumpToLastRoundXun":
if clickAction == "jumpToLastRoundXun" {
h.majsoulCurrentRoundIndex = (h.majsoulCurrentRoundIndex - 1 + len(h.majsoulCurrentRecordActionsList)) % len(h.majsoulCurrentRecordActionsList)
go globalAnalysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
go analysisCache.runMajsoulRecordAnalysisTask(h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex])
}

h.majsoulRoundData.skipOutput = true
Expand Down Expand Up @@ -442,7 +463,7 @@ func (h *mjHandler) _onRecordClick(clickAction string, clickActionIndex int, fas
time.Sleep(time.Second)

actions := h.majsoulCurrentRecordActionsList[h.majsoulCurrentRoundIndex]
go globalAnalysisCache.runMajsoulRecordAnalysisTask(actions)
go analysisCache.runMajsoulRecordAnalysisTask(actions)
// 分析下一局的起始信息
data := actions[h.majsoulCurrentActionIndex].Action
h._analysisMajsoulRoundData(data, "")
Expand Down

0 comments on commit 5c8ee98

Please sign in to comment.