Skip to content

网页 API 设计 #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"github.com/EndlessCheng/mahjong-helper/util"
"fmt"
"io"
"strings"
"github.com/fatih/color"
"github.com/EndlessCheng/mahjong-helper/util/model"
Expand Down Expand Up @@ -45,7 +46,7 @@ func humanHands(playerInfo *model.PlayerInfo) string {
return humanHands
}

func analysisPlayerWithRisk(playerInfo *model.PlayerInfo, mixedRiskTable riskTable) error {
func analysisPlayerWithRisk(writer io.Writer, playerInfo *model.PlayerInfo, mixedRiskTable riskTable) error {
// 手牌
humanTiles := humanHands(playerInfo)
fmt.Println(humanTiles)
Expand All @@ -61,7 +62,7 @@ func analysisPlayerWithRisk(playerInfo *model.PlayerInfo, mixedRiskTable riskTab
result13: result,
mixedRiskTable: mixedRiskTable,
}
r.printWaitsWithImproves13_oneRow()
r.printWaitsWithImproves13_oneRow(writer)
case 2:
// 分析手牌
shanten, results14, incShantenResults14 := util.CalculateShantenWithImproves14(playerInfo)
Expand All @@ -87,8 +88,8 @@ func analysisPlayerWithRisk(playerInfo *model.PlayerInfo, mixedRiskTable riskTab
// TODO: 接近流局时提示河底是哪家

// 何切分析结果
printResults14WithRisk(results14, mixedRiskTable)
printResults14WithRisk(incShantenResults14, mixedRiskTable)
printResults14WithRisk(writer, results14, mixedRiskTable)
printResults14WithRisk(writer, incShantenResults14, mixedRiskTable)
default:
err := fmt.Errorf("参数错误: %d 张牌", countOfTiles)
if debugMode {
Expand All @@ -107,7 +108,7 @@ func analysisPlayerWithRisk(playerInfo *model.PlayerInfo, mixedRiskTable riskTab
// isRedFive: 此舍牌是否为赤5
// allowChi: 是否能吃
// mixedRiskTable: 危险度表
func analysisMeld(playerInfo *model.PlayerInfo, targetTile34 int, isRedFive bool, allowChi bool, mixedRiskTable riskTable) error {
func analysisMeld(writer io.Writer, playerInfo *model.PlayerInfo, targetTile34 int, isRedFive bool, allowChi bool, mixedRiskTable riskTable) error {
if handsCount := util.CountOfTiles34(playerInfo.HandTiles34); handsCount%3 != 1 {
return fmt.Errorf("手牌错误:%d 张牌 %v", handsCount, playerInfo.HandTiles34)
}
Expand All @@ -132,7 +133,7 @@ func analysisMeld(playerInfo *model.PlayerInfo, targetTile34 int, isRedFive bool
result13: result,
mixedRiskTable: mixedRiskTable,
}
r.printWaitsWithImproves13_oneRow()
r.printWaitsWithImproves13_oneRow(writer)

// 提示信息
// TODO: 局收支相近时,提示:局收支相近,追求和率打xx,追求打点打xx
Expand All @@ -148,12 +149,12 @@ func analysisMeld(playerInfo *model.PlayerInfo, targetTile34 int, isRedFive bool
// TODO: 接近流局时提示河底是哪家

// 鸣牌何切分析结果
printResults14WithRisk(results14, mixedRiskTable)
printResults14WithRisk(incShantenResults14, mixedRiskTable)
printResults14WithRisk(writer, results14, mixedRiskTable)
printResults14WithRisk(writer, incShantenResults14, mixedRiskTable)
return nil
}

func analysisHumanTiles(humanTilesInfo *model.HumanTilesInfo) (playerInfo *model.PlayerInfo, err error) {
func analysisHumanTiles(writer io.Writer, humanTilesInfo *model.HumanTilesInfo) (playerInfo *model.PlayerInfo, err error) {
defer func() {
if er := recover(); er != nil {
err = er.(error)
Expand Down Expand Up @@ -231,13 +232,13 @@ func analysisHumanTiles(humanTilesInfo *model.HumanTilesInfo) (playerInfo *model
if er != nil {
return nil, er
}
if er := analysisMeld(playerInfo, targetTile34, isRedFive, true, nil); er != nil {
if er := analysisMeld(writer, playerInfo, targetTile34, isRedFive, true, nil); er != nil {
return nil, er
}
return
}

playerInfo.IsTsumo = humanTilesInfo.IsTsumo
err = analysisPlayerWithRisk(playerInfo, nil)
err = analysisPlayerWithRisk(writer, playerInfo, nil)
return
}
103 changes: 52 additions & 51 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math"
"sort"
"strings"
"io"
)

func printAccountInfo(accountID int) {
Expand Down Expand Up @@ -464,7 +465,7 @@ type analysisResult struct {

*/
// 打印何切分析结果(单行)
func (r *analysisResult) printWaitsWithImproves13_oneRow() {
func (r *analysisResult) printWaitsWithImproves13_oneRow(writer io.Writer) {
discardTile34 := r.discardTile34
openTiles34 := r.openTiles34
result13 := r.result13
Expand All @@ -474,19 +475,19 @@ func (r *analysisResult) printWaitsWithImproves13_oneRow() {
// 进张数
waitsCount := result13.Waits.AllCount()
c := getWaitsCountColor(shanten, float64(waitsCount))
color.New(c).Printf("%2d", waitsCount)
color.New(c).Fprintf(writer, "%2d", waitsCount)
// 改良进张均值
if len(result13.Improves) > 0 {
if r.highlightAvgImproveWaitsCount {
color.New(color.FgHiWhite).Printf("[%5.2f]", result13.AvgImproveWaitsCount)
color.New(color.FgHiWhite).Fprintf(writer, "[%5.2f]", result13.AvgImproveWaitsCount)
} else {
fmt.Printf("[%5.2f]", result13.AvgImproveWaitsCount)
fmt.Fprintf(writer, "[%5.2f]", result13.AvgImproveWaitsCount)
}
} else {
fmt.Print(strings.Repeat(" ", 7))
fmt.Fprint(writer, strings.Repeat(" ", 7))
}

fmt.Print(" ")
fmt.Fprint(writer, " ")

// 是否为3k+2张牌的何切分析
if discardTile34 != -1 {
Expand All @@ -496,14 +497,14 @@ func (r *analysisResult) printWaitsWithImproves13_oneRow() {
if openTiles34[0] == openTiles34[1] {
meldType = "碰"
}
color.New(color.FgHiWhite).Printf("%s%s", string([]rune(util.MahjongZH[openTiles34[0]])[:1]), util.MahjongZH[openTiles34[1]])
fmt.Printf("%s,", meldType)
color.New(color.FgHiWhite).Fprintf(writer, "%s%s", string([]rune(util.MahjongZH[openTiles34[0]])[:1]), util.MahjongZH[openTiles34[1]])
fmt.Fprintf(writer, "%s,", meldType)
}
// 舍牌
if r.isDiscardTileDora {
color.New(color.FgHiWhite).Print("ド")
color.New(color.FgHiWhite).Fprintf(writer, "ド")
} else {
fmt.Print("切")
fmt.Fprint(writer, "切")
}
tileZH := util.MahjongZH[discardTile34]
if discardTile34 >= 27 {
Expand All @@ -513,71 +514,71 @@ func (r *analysisResult) printWaitsWithImproves13_oneRow() {
// 若有实际危险度,则根据实际危险度来显示舍牌危险度
risk := r.mixedRiskTable[discardTile34]
if risk == 0 {
fmt.Print(tileZH)
fmt.Fprint(writer, tileZH)
} else {
color.New(getNumRiskColor(risk)).Print(tileZH)
color.New(getNumRiskColor(risk)).Fprint(writer, tileZH)
}
} else {
fmt.Print(tileZH)
fmt.Fprint(writer, tileZH)
}
}

fmt.Print(" => ")
fmt.Fprint(writer, " => ")

if shanten >= 1 {
// 前进后的进张数均值
incShanten := shanten - 1
c := getWaitsCountColor(incShanten, result13.AvgNextShantenWaitsCount)
color.New(c).Printf("%5.2f", result13.AvgNextShantenWaitsCount)
fmt.Printf("%s", util.NumberToChineseShanten(incShanten))
color.New(c).Fprintf(writer, "%5.2f", result13.AvgNextShantenWaitsCount)
fmt.Fprintf(writer, "%s", util.NumberToChineseShanten(incShanten))
if incShanten >= 1 {
//fmt.Printf("进张")
//fmt.Fprintf(writer, "进张")
} else { // incShanten == 0
fmt.Printf("数")
fmt.Fprintf(writer, "数")
//if showAgariAboveShanten1 {
// fmt.Printf("(%.2f%% 参考和率)", result13.AvgAgariRate)
// fmt.Fprintf(writer, "(%.2f%% 参考和率)", result13.AvgAgariRate)
//}
}
} else { // shanten == 0
// 前进后的和率
// 若振听或片听,则标红
if result13.FuritenRate == 1 || result13.IsPartWait {
color.New(color.FgHiRed).Printf("%5.2f%% 参考和率", result13.AvgAgariRate)
color.New(color.FgHiRed).Fprintf(writer, "%5.2f%% 参考和率", result13.AvgAgariRate)
} else {
fmt.Printf("%5.2f%% 参考和率", result13.AvgAgariRate)
fmt.Fprintf(writer, "%5.2f%% 参考和率", result13.AvgAgariRate)
}
}

// 手牌速度,用于快速过庄
if result13.MixedWaitsScore > 0 && shanten >= 1 && shanten <= 2 {
fmt.Print(" ")
fmt.Fprint(writer, " ")
if r.highlightMixedScore {
color.New(color.FgHiWhite).Printf("[%5.2f速度]", result13.MixedWaitsScore)
color.New(color.FgHiWhite).Fprintf(writer, "[%5.2f速度]", result13.MixedWaitsScore)
} else {
fmt.Printf("[%5.2f速度]", result13.MixedWaitsScore)
fmt.Fprintf(writer, "[%5.2f速度]", result13.MixedWaitsScore)
}
}

// 局收支
if showScore && result13.MixedRoundPoint != 0.0 {
fmt.Print(" ")
color.New(color.FgHiGreen).Printf("[局收支%4d]", int(math.Round(result13.MixedRoundPoint)))
fmt.Fprint(writer, " ")
color.New(color.FgHiGreen).Fprintf(writer, "[局收支%4d]", int(math.Round(result13.MixedRoundPoint)))
}

// (默听)荣和点数
if result13.DamaPoint > 0 {
fmt.Print(" ")
fmt.Fprint(writer, " ")
ronType := "荣和"
if !result13.IsNaki {
ronType = "默听"
}
color.New(color.FgHiGreen).Printf("[%s%d]", ronType, int(math.Round(result13.DamaPoint)))
color.New(color.FgHiGreen).Fprintf(writer, "[%s%d]", ronType, int(math.Round(result13.DamaPoint)))
}

// 立直点数,考虑了自摸、一发、里宝
if result13.RiichiPoint > 0 {
fmt.Print(" ")
color.New(color.FgHiGreen).Printf("[立直%d]", int(math.Round(result13.RiichiPoint)))
fmt.Fprint(writer, " ")
color.New(color.FgHiGreen).Fprintf(writer, "[立直%d]", int(math.Round(result13.RiichiPoint)))
}

if len(result13.YakuTypes) > 0 {
Expand All @@ -594,64 +595,64 @@ func (r *analysisResult) printWaitsWithImproves13_oneRow() {
}
if len(shownYakuTypes) > 0 {
sort.Ints(shownYakuTypes)
fmt.Print(" ")
color.New(color.FgHiGreen).Printf(util.YakuTypesToStr(shownYakuTypes))
fmt.Fprint(writer, " ")
color.New(color.FgHiGreen).Fprintf(writer, util.YakuTypesToStr(shownYakuTypes))
}
} else {
// debug
fmt.Print(" ")
color.New(color.FgHiGreen).Printf(util.YakuTypesWithDoraToStr(result13.YakuTypes, result13.DoraCount))
fmt.Fprint(writer, " ")
color.New(color.FgHiGreen).Fprintf(writer, util.YakuTypesWithDoraToStr(result13.YakuTypes, result13.DoraCount))
}
// 片听
if result13.IsPartWait {
fmt.Print(" ")
color.New(color.FgHiRed).Printf("[片听]")
fmt.Fprint(writer, " ")
color.New(color.FgHiRed).Fprintf(writer, "[片听]")
}
}
} else if result13.IsNaki && shanten >= 0 && shanten <= 2 {
// 鸣牌时的无役提示(从听牌到两向听)
fmt.Print(" ")
color.New(color.FgHiRed).Printf("[无役]")
fmt.Fprint(writer, " ")
color.New(color.FgHiRed).Fprintf(writer, "[无役]")
}

// 振听提示
if result13.FuritenRate > 0 {
fmt.Print(" ")
fmt.Fprint(writer, " ")
if result13.FuritenRate < 1 {
color.New(color.FgHiYellow).Printf("[可能振听]")
color.New(color.FgHiYellow).Fprintf(writer, "[可能振听]")
} else {
color.New(color.FgHiRed).Printf("[振听]")
color.New(color.FgHiRed).Fprintf(writer, "[振听]")
}
}

// 改良数
if showScore {
fmt.Print(" ")
fmt.Fprint(writer, " ")
if len(result13.Improves) > 0 {
fmt.Printf("[%2d改良]", len(result13.Improves))
fmt.Fprintf(writer, "[%2d改良]", len(result13.Improves))
} else {
fmt.Print(strings.Repeat(" ", 4))
fmt.Print(strings.Repeat(" ", 2)) // 全角空格
fmt.Fprint(writer, strings.Repeat(" ", 4))
fmt.Fprint(writer, strings.Repeat(" ", 2)) // 全角空格
}
}

// 进张类型
fmt.Print(" ")
fmt.Fprint(writer, " ")
waitTiles := result13.Waits.AvailableTiles()
fmt.Print(util.TilesToStrWithBracket(waitTiles))
fmt.Fprint(writer, util.TilesToStrWithBracket(waitTiles))

//

fmt.Println()
fmt.Fprintln(writer)

if showImproveDetail {
for tile, waits := range result13.Improves {
fmt.Printf("摸 %s 改良成 %s\n", util.Mahjong[tile], waits.String())
fmt.Fprintf(writer, "摸 %s 改良成 %s\n", util.Mahjong[tile], waits.String())
}
}
}

func printResults14WithRisk(results14 util.Hand14AnalysisResultList, mixedRiskTable riskTable) {
func printResults14WithRisk(writer io.Writer, results14 util.Hand14AnalysisResultList, mixedRiskTable riskTable) {
if len(results14) == 0 {
return
}
Expand Down Expand Up @@ -705,6 +706,6 @@ func printResults14WithRisk(results14 util.Hand14AnalysisResultList, mixedRiskTa
result.Result13.AvgImproveWaitsCount == maxAvgImproveWaitsCount,
result.Result13.MixedWaitsScore == maxMixedScore,
}
r.printWaitsWithImproves13_oneRow()
r.printWaitsWithImproves13_oneRow(writer)
}
}
18 changes: 15 additions & 3 deletions core.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"github.com/EndlessCheng/mahjong-helper/webapi"
"github.com/EndlessCheng/mahjong-helper/util"
"github.com/EndlessCheng/mahjong-helper/util/model"
"github.com/fatih/color"
Expand Down Expand Up @@ -167,6 +168,8 @@ func (p *playerInfo) doraNum(doraList []int) (doraCount int) {
//

type roundData struct {
ApiData webapi.ApiData

parser DataParser

gameMode gameMode
Expand Down Expand Up @@ -470,6 +473,14 @@ func (d *roundData) newModelPlayerInfo() *model.PlayerInfo {
}

func (d *roundData) analysis() error {
d.ApiData.Init()
writer := webapi.ApiDataConvertor{&d.ApiData}

defer func() {
d.ApiData.GetOutput()
d.ApiData.Counts = d.counts
}()

if !debugMode {
defer func() {
if err := recover(); err != nil {
Expand Down Expand Up @@ -582,7 +593,7 @@ func (d *roundData) analysis() error {
color.HiYellow("宝牌指示牌是 " + info)
fmt.Println()
// TODO: 显示地和概率
return analysisPlayerWithRisk(playerInfo, nil)
return analysisPlayerWithRisk(writer, playerInfo, nil)
case d.parser.IsOpen():
// 某家鸣牌(含暗杠、加杠)
who, meld, kanDoraIndicator := d.parser.ParseOpen()
Expand Down Expand Up @@ -768,7 +779,7 @@ func (d *roundData) analysis() error {

// 打印何切推荐
// TODO: 根据是否听牌/一向听、打点、巡目、和率等进行攻守判断
return analysisPlayerWithRisk(playerInfo, mixedRiskTable)
return analysisPlayerWithRisk(writer, playerInfo, mixedRiskTable)
case d.parser.IsDiscard():
who, discardTile, isRedFive, isTsumogiri, isReach, canBeMeld, kanDoraIndicator := d.parser.ParseDiscard()

Expand Down Expand Up @@ -854,6 +865,7 @@ func (d *roundData) analysis() error {
// 安全度分析
riskTables := d.analysisTilesRisk()
mixedRiskTable := riskTables.mixedRiskTable()
d.ApiData.RiskTable = mixedRiskTable

// 牌谱分析模式下,记录可能的鸣牌
if d.gameMode == gameModeRecordCache {
Expand Down Expand Up @@ -905,7 +917,7 @@ func (d *roundData) analysis() error {
// 为了方便解析牌谱,这里尽可能地解析副露
// TODO: 提醒: 消除海底/避免河底
allowChi := d.playerNumber != 3 && who == 3 && playerInfo.LeftDrawTilesCount > 0
return analysisMeld(playerInfo, discardTile, isRedFive, allowChi, mixedRiskTable)
return analysisMeld(writer, playerInfo, discardTile, isRedFive, allowChi, mixedRiskTable)
case d.parser.IsRoundWin():
// TODO: 解析天凤牌谱 - 注意 skipOutput

Expand Down
Loading