From 5bc113a5d0c17e825cfb54dd6bc985fa8595eb66 Mon Sep 17 00:00:00 2001 From: CoderSherlock Date: Tue, 10 Oct 2023 02:02:56 -0700 Subject: [PATCH] Initial commit --- .gitignore | 1 + bin/pattern-lookup/generatetable.go | 150 ++++++++++++++++ go.mod | 8 + go.sum | 4 + pkg/fan.go | 52 ++++++ pkg/fan_test.go | 38 ++++ pkg/name.go | 28 +++ pkg/tiles.go | 261 ++++++++++++++++++++++++++++ pkg/tiles_test.go | 95 ++++++++++ readme.md | 6 + test.json | 182 +++++++++++++++++++ 11 files changed, 825 insertions(+) create mode 100644 .gitignore create mode 100644 bin/pattern-lookup/generatetable.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/fan.go create mode 100644 pkg/fan_test.go create mode 100644 pkg/name.go create mode 100644 pkg/tiles.go create mode 100644 pkg/tiles_test.go create mode 100644 readme.md create mode 100644 test.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd12961 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bazel* diff --git a/bin/pattern-lookup/generatetable.go b/bin/pattern-lookup/generatetable.go new file mode 100644 index 0000000..69688d1 --- /dev/null +++ b/bin/pattern-lookup/generatetable.go @@ -0,0 +1,150 @@ +package main + +import ( + "fmt" + pkg "mjhelper/pkg" + + "github.com/mowshon/iterium" +) + +func allFans() []pkg.Fan { + testFans := []pkg.Fan{ + {Id: 1, Name: "大四喜", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{38, 49, 62, 63}, Value: 88}, + {Id: 2, Name: "大三元", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 88}, + {Id: 3, Name: "绿一色", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 88}, + {Id: 4, Name: "九莲宝灯", Pattern: pkg.PatternDivision{Ke: 2, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 88}, + {Id: 5, Name: "四杠", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 4}, ExcludeIds: []int{}, Value: 88}, + {Id: 6, Name: "连七对", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 7, Gang: 0}, ExcludeIds: []int{}, Value: 88}, + {Id: 7, Name: "十三幺", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 88}, + {Id: 8, Name: "清幺九", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 64}, + {Id: 9, Name: "小四喜", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 64}, + {Id: 10, Name: "小三元", Pattern: pkg.PatternDivision{Ke: 2, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 64}, + {Id: 11, Name: "字一色", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 64}, + {Id: 12, Name: "四暗刻", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 2, Gang: 0}, ExcludeIds: []int{}, Value: 64}, + {Id: 13, Name: "一色双龙会", Pattern: pkg.PatternDivision{Ke: 0, Shun: 4, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 64}, + {Id: 14, Name: "一色四同顺", Pattern: pkg.PatternDivision{Ke: 0, Shun: 4, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 48}, + {Id: 15, Name: "一色四节高", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 48}, + {Id: 16, Name: "一色四步高", Pattern: pkg.PatternDivision{Ke: 0, Shun: 4, Dui: 2, Gang: 0}, ExcludeIds: []int{}, Value: 32}, + {Id: 17, Name: "三杠", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 3}, ExcludeIds: []int{}, Value: 32}, + {Id: 18, Name: "混幺九", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 32}, + {Id: 19, Name: "七对", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 7, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 20, Name: "七星不靠", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 21, Name: "全双刻", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 2, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 22, Name: "清一色", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 23, Name: "一色三同顺", Pattern: pkg.PatternDivision{Ke: 0, Shun: 3, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 24, Name: "一色三节高", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 25, Name: "全大", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 26, Name: "全中", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 27, Name: "全小", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 24}, + {Id: 28, Name: "清龙", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 16}, + {Id: 29, Name: "三色双龙会", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 16}, + {Id: 30, Name: "一色三步高", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 16}, + {Id: 31, Name: "全带五", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 16}, + {Id: 32, Name: "三同刻", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 16}, + {Id: 33, Name: "三暗刻", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 16}, + {Id: 34, Name: "全不靠", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 12}, + {Id: 35, Name: "组合龙", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 12}, + {Id: 36, Name: "大于五", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 12}, + {Id: 37, Name: "小于五", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 12}, + {Id: 38, Name: "三风刻", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 12}, + {Id: 39, Name: "花龙", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 40, Name: "推不倒", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 41, Name: "三色三同顺", Pattern: pkg.PatternDivision{Ke: 0, Shun: 3, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 42, Name: "三色三节高", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 43, Name: "无番和", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 44, Name: "妙手回春", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 45, Name: "海底捞月", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + {Id: 46, Name: "杠上开花", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 1}, ExcludeIds: []int{}, Value: 8}, + {Id: 47, Name: "抢杠和", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 8}, + // 双暗杠 98版计6番,06版计8番 + {Id: 48, Name: "双暗杠", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 2}, ExcludeIds: []int{}, Value: 6}, + {Id: 49, Name: "碰碰和", Pattern: pkg.PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 50, Name: "混一色", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 51, Name: "三色三步高", Pattern: pkg.PatternDivision{Ke: 0, Shun: 3, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 52, Name: "三色三节高", Pattern: pkg.PatternDivision{Ke: 3, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 53, Name: "五门齐", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 54, Name: "全求人", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + // 明暗杠 06版计6番,存疑 + {Id: 55, Name: "明暗杠", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 56, Name: "双箭刻", Pattern: pkg.PatternDivision{Ke: 2, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 6}, + {Id: 57, Name: "全带幺", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 4}, + {Id: 58, Name: "不求人", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 4}, + {Id: 59, Name: "双明杠", Pattern: pkg.PatternDivision{Ke: 2, Shun: 0, Dui: 1, Gang: 2}, ExcludeIds: []int{}, Value: 4}, + {Id: 60, Name: "和绝张", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 4}, + {Id: 61, Name: "箭刻", Pattern: pkg.PatternDivision{Ke: 1, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 62, Name: "圈风刻", Pattern: pkg.PatternDivision{Ke: 1, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 63, Name: "门风刻", Pattern: pkg.PatternDivision{Ke: 1, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 64, Name: "门前清", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 65, Name: "平和", Pattern: pkg.PatternDivision{Ke: 0, Shun: 4, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 66, Name: "四归一", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 67, Name: "双同刻", Pattern: pkg.PatternDivision{Ke: 2, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 68, Name: "双暗刻", Pattern: pkg.PatternDivision{Ke: 2, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 69, Name: "暗杠", Pattern: pkg.PatternDivision{Ke: 1, Shun: 0, Dui: 1, Gang: 1}, ExcludeIds: []int{}, Value: 2}, + {Id: 70, Name: "断幺", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 2}, + {Id: 71, Name: "一般高", Pattern: pkg.PatternDivision{Ke: 0, Shun: 2, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 72, Name: "喜相逢", Pattern: pkg.PatternDivision{Ke: 0, Shun: 2, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 73, Name: "连六", Pattern: pkg.PatternDivision{Ke: 0, Shun: 2, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 74, Name: "老少副", Pattern: pkg.PatternDivision{Ke: 0, Shun: 2, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 75, Name: "幺九刻", Pattern: pkg.PatternDivision{Ke: 1, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 76, Name: "明杠", Pattern: pkg.PatternDivision{Ke: 1, Shun: 0, Dui: 0, Gang: 1}, ExcludeIds: []int{}, Value: 1}, + {Id: 77, Name: "缺一门", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 78, Name: "无字", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 79, Name: "边张", Pattern: pkg.PatternDivision{Ke: 0, Shun: 1, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 80, Name: "坎张", Pattern: pkg.PatternDivision{Ke: 0, Shun: 1, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 81, Name: "单钓将", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 82, Name: "自摸", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + {Id: 82, Name: "花牌", Pattern: pkg.PatternDivision{Ke: 0, Shun: 0, Dui: 0, Gang: 0}, ExcludeIds: []int{}, Value: 1}, + } + return testFans +} + +func AllFansToFile() { + allFans := allFans() + pkg.FansToFile(allFans, "test.json") +} + +func GenerateFan(fan pkg.Fan) { + +} + +func GenerateKe() { + +} + +func main() { + result := iterium.Combinations([]string{ + "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", + "1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T", + "1B", "2B", "3B", "4B", "5B", "6B", "7B", "8B", "9B", + "DO", "XI", "NA", "BE", "ZH", "FA", "BA", + "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", + "1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T", + "1B", "2B", "3B", "4B", "5B", "6B", "7B", "8B", "9B", + "DO", "XI", "NA", "BE", "ZH", "FA", "BA", + "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", + "1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T", + "1B", "2B", "3B", "4B", "5B", "6B", "7B", "8B", "9B", + "DO", "XI", "NA", "BE", "ZH", "FA", "BA", + "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", + "1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T", + "1B", "2B", "3B", "4B", "5B", "6B", "7B", "8B", "9B", + "DO", "XI", "NA", "BE", "ZH", "FA", "BA"}, 14) + fmt.Println(result.Count()) + result2_1 := iterium.CombinationsWithReplacement([]string{ + "1TS", "2TS", "3TS", "4TS", "5TS", "6TS", "7TS", + "1TK", "2TK", "3TK", "4TK", "5TK", "6TK", "7TK", "8TK", "9TK", + "1TS", "2TS", "3TS", "4TS", "5TS", "6TS", "7TS", + "1TK", "2TK", "3TK", "4TK", "5TK", "6TK", "7TK", "8TK", "9TK", + "1TS", "2TS", "3TS", "4TS", "5TS", "6TS", "7TS", + "1TK", "2TK", "3TK", "4TK", "5TK", "6TK", "7TK", "8TK", "9TK", + "DOK", "XIK", "NAK", "BEK", "ZHK", "FAK", "BAK"}, 4) + result2_2 := iterium.Combinations([]string{ + "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", + "1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T", + "1B", "2B", "3B", "4B", "5B", "6B", "7B", "8B", "9B", + "DO", "XI", "NA", "BE", "ZH", "FA", "BA"}, 1) + fmt.Println(result2_1.Count() * result2_2.Count()) + for i := range result2_1.Chan() { + fmt.Println(i) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..81ee6f1 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module mjhelper + +go 1.20 + +require ( + github.com/mowshon/iterium v1.0.0 // indirect + golang.org/x/exp v0.0.0-20230307190834-24139beb5833 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bdd5837 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/mowshon/iterium v1.0.0 h1:04pku9dTNnfVvshf+DQIV3E92T/EwmnXKtXMkmZ3+5Q= +github.com/mowshon/iterium v1.0.0/go.mod h1:Bnchn9HAYNQ/7MLUwKDyvjduSuVJQQwrNxmXJDqpXg4= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833 h1:SChBja7BCQewoTAU7IgvucQKMIXrEpFxNMs0spT3/5s= +golang.org/x/exp v0.0.0-20230307190834-24139beb5833/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= diff --git a/pkg/fan.go b/pkg/fan.go new file mode 100644 index 0000000..0b7ad8f --- /dev/null +++ b/pkg/fan.go @@ -0,0 +1,52 @@ +package pkg + +import ( + "encoding/json" + "log" + "os" +) + +type PatternDivision struct { + Ke int + Shun int + Dui int + Gang int +} +type Fan struct { + Id int + Name string + Pattern PatternDivision + ExcludeIds []int + Value int +} + +func FansEncoder(fans []Fan) ([]byte, error) { + return json.MarshalIndent(fans, "", " ") +} + +func FansDecoder(bytedata []byte) ([]Fan, error) { + var fans []Fan + err := json.Unmarshal(bytedata, &fans) + return fans, err +} + +func FansToFile(fans []Fan, filepath string) error { + jsonBytes, err := FansEncoder(fans) + if err != nil { + log.Fatalf("Can't encode fan xing: %v", err) + return err + } + err = os.WriteFile(filepath, jsonBytes, 0644) + if err != nil { + log.Fatalf("Failed to write fan xing into a file(%s): %v", filepath, err) + return err + } + return nil +} + +func DivisionHelperOnNumbers(tiles [9]int) { + +} +func HandPatternDivide() { + +} diff --git a/pkg/fan_test.go b/pkg/fan_test.go new file mode 100644 index 0000000..c6baee6 --- /dev/null +++ b/pkg/fan_test.go @@ -0,0 +1,38 @@ +package pkg + +import ( + "fmt" + "log" + "testing" +) + +func TestFanEncoder(t *testing.T) { + testFans := []Fan{ + {Id: 1, Name: "大四喜", Pattern: PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{12}, Value: 88}, + {Id: 2, Name: "七对", Pattern: PatternDivision{Ke: 0, Shun: 0, Dui: 7, Gang: 0}, ExcludeIds: []int{12}, Value: 24}, + } + data, err := FansEncoder(testFans) + if err != nil { + log.Fatalf("Can't encode fan xing: %v", err) + return + } + fmt.Println(data) +} + +func TestFanDecoder(t *testing.T) { + testFans := []Fan{ + {Id: 1, Name: "大四喜", Pattern: PatternDivision{Ke: 4, Shun: 0, Dui: 1, Gang: 0}, ExcludeIds: []int{12}, Value: 88}, + {Id: 2, Name: "七对", Pattern: PatternDivision{Ke: 0, Shun: 0, Dui: 7, Gang: 0}, ExcludeIds: []int{12}, Value: 24}, + } + jsonBytes, err := FansEncoder(testFans) + if err != nil { + log.Fatalf("Can't encode fan xing, cancel decoding test: %v", err) + return + } + fans, err := FansDecoder(jsonBytes) + if err != nil { + log.Fatalf("Failed decoding byte stream of fan xing: %v", err) + return + } + fmt.Println(fans) +} diff --git a/pkg/name.go b/pkg/name.go new file mode 100644 index 0000000..bc10df4 --- /dev/null +++ b/pkg/name.go @@ -0,0 +1,28 @@ +package pkg + +import "errors" + +var NumberToHanZiTable = []string{"一", "二", "三", "四", "五", "六", "七", "八", "九"} +var StyleToHanZiTable = []string{"序数牌", "风牌", "箭牌", "花牌"} +var XuStyleToHanZiTable = []string{"万", "条", "饼"} + +func NumberToHanzi(num uint) (string, error) { + if num >= uint(len(NumberToHanZiTable)) { + return "", errors.New("not a valid number") + } + return NumberToHanZiTable[num-1], nil +} + +func StyleToHanZi(num uint) (string, error) { + if num >= uint(len(StyleToHanZiTable)) { + return "", errors.New("not a valid number") + } + return StyleToHanZiTable[num], nil +} + +func XuStyleToHanZi(num uint) (string, error) { + if num >= uint(len(XuStyleToHanZiTable)) { + return "", errors.New("not a valid number") + } + return XuStyleToHanZiTable[num], nil +} diff --git a/pkg/tiles.go b/pkg/tiles.go new file mode 100644 index 0000000..f4c99b7 --- /dev/null +++ b/pkg/tiles.go @@ -0,0 +1,261 @@ +package pkg + +import ( + "errors" + "fmt" + "log" + "reflect" + "sort" +) + +const ( + // 未出场 + tl_notshow = 0 + // 本家状态 + tl_mhand = 1 + tl_mshow = 2 + tl_mdisc = 3 + // 下家状态 + tl_nhand = 4 + tl_nshow = 5 + tl_ndisc = 6 + // 对家状态 + tl_ohand = 7 + tl_oshow = 8 + tl_odisc = 9 + // 上家状态 + tl_phand = 10 + tl_pshow = 11 + tl_pdisc = 12 +) + +var tileTypes = [34]string{ + "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", + "1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T", + "1B", "2B", "3B", "4B", "5B", "6B", "7B", "8B", "9B", + "DO", "XI", "NA", "BE", "ZH", "FA", "BA"} +var tileHuaTypes = [8]string{"ME", "LA", "ZU", "JU", "CH", "XA", "QI", "DN"} + +type environment struct { + board [4][34]int +} + +func (e environment) Print() { + for i := 0; i < len(e.board)+1; i++ { + for j := 0; j < len(e.board[0]); j++ { + if i == 0 { + fmt.Printf("|% 3s", tileTypes[j]) + } else { + fmt.Printf("|% 3d", e.board[i-1][j]) + } + } + fmt.Printf("|\n") + } +} + +func (e environment) PatternNormalization() [3][34]int { + var ret [3][34]int + for i := 0; i < len(e.board[0]); i++ { + hand := 0 + disc := 0 + noshow := 0 + for j := 0; j < len(e.board); j++ { + switch e.board[j][i] { + case tl_mhand, tl_mshow: + hand += 1 + case tl_mdisc, tl_ndisc, tl_nshow, tl_odisc, tl_oshow, tl_pdisc, tl_pshow: + disc += 1 + case tl_notshow: + noshow += 1 + } + } + ret[0][i] = hand + ret[1][i] = disc + ret[2][i] = noshow + } + return ret +} + +func TileComparator(i, j string) (bool, error) { + if i == j { + return false, nil + } + ii, ij := -1, -1 + for k, v := range tileTypes { + if i == v { + ii = k + } + if j == v { + ij = k + } + } + if ii == -1 || ij == -1 { + return false, errors.New("can't find tile by name") + } + return ii < ij, nil +} + +func Sort(tiles []string) error { + sort.Slice(tiles, func(i, j int) bool { + res, err := TileComparator(tiles[i], tiles[j]) + if err != nil { + log.Fatalf("Sort tiles has error: %v", err) + } + return res + }) + return nil +} + +// Check +func CheckXushuHelper(tiles [9]int, index int, allCombs [][]int, combTable *[][][]int) { + if index >= 9 { + // Check invalid shun/ke/gang with more than 1 pair + gangAmount := 0 + shunKeAmount := 0 + pairAmount := 0 + for _, combs := range allCombs { + if len(combs) == 2 { + pairAmount++ + } + if len(combs) == 3 { + shunKeAmount++ + } + if len(combs) == 4 { + gangAmount++ + } + } + if pairAmount > 1 && (gangAmount > 0 || shunKeAmount > 0) { + return + } + *combTable = append(*combTable, allCombs) + return + } + if tiles[index] == 0 { + CheckXushuHelper(tiles, index+1, allCombs, combTable) + return + } + if tiles[index] == 4 { + tiles[index] = 0 + CheckXushuHelper(tiles, index+1, append(allCombs, []int{index, index, index, index}), combTable) + if index < 7 { + if tiles[index+1] > 0 && tiles[index+2] > 0 { + tiles[index] = 3 + tiles[index+1] -= 1 + tiles[index+2] -= 1 + CheckXushuHelper(tiles, index, append(allCombs, []int{index, index + 1, index + 2}), combTable) + tiles[index+1] += 1 + tiles[index+2] += 1 + } + } + tiles[index] = 2 + CheckXushuHelper(tiles, index, append(allCombs, []int{index, index}), combTable) + } else if tiles[index] == 3 { + tiles[index] = 0 + CheckXushuHelper(tiles, index+1, append(allCombs, []int{index, index, index}), combTable) + if index < 7 { + if tiles[index+1] > 0 && tiles[index+2] > 0 { + tiles[index] = 2 + tiles[index+1] -= 1 + tiles[index+2] -= 1 + CheckXushuHelper(tiles, index, append(allCombs, []int{index, index + 1, index + 2}), combTable) + tiles[index+1] += 1 + tiles[index+2] += 1 + } + } + } else if tiles[index] == 2 { + tiles[index] = 0 + CheckXushuHelper(tiles, index+1, append(allCombs, []int{index, index}), combTable) + if index < 7 { + if tiles[index+1] > 0 && tiles[index+2] > 0 { + tiles[index] = 1 + tiles[index+1] -= 1 + tiles[index+2] -= 1 + CheckXushuHelper(tiles, index, append(allCombs, []int{index, index + 1, index + 2}), combTable) + tiles[index+1] += 1 + tiles[index+2] += 1 + } + } + } else { + if index < 7 { + if tiles[index+1] > 0 && tiles[index+2] > 0 { + tiles[index] = 0 + tiles[index+1] -= 1 + tiles[index+2] -= 1 + CheckXushuHelper(tiles, index+1, append(allCombs, []int{index, index + 1, index + 2}), combTable) + } else { + return + } + } + } +} + +func CheckXushuTile(tiles []string) ([][][]string, error) { + limit := len(tiles) + if limit <= 0 { + return [][][]string{}, nil + } + var combTable [][][]int + if limit <= 3 { + var tileNumbers []int + for _, tile := range tiles { + tileNumbers = append(tileNumbers, int(tile[0])-48) + } + // bukao + isBukao := true + for _, num := range tileNumbers[1:] { + if ((num - tileNumbers[0]) != 3) && ((num - tileNumbers[0]) != 6) { + isBukao = false + } + } + // isbukao + shisanyao + if isBukao || reflect.DeepEqual(tileNumbers, []int{1, 9}) || reflect.DeepEqual(tileNumbers, []int{1, 1, 9}) || reflect.DeepEqual(tileNumbers, []int{1, 9, 9}) { + var tmpAllCombs [][]int + for _, tile := range tiles { + tmpAllCombs = append(tmpAllCombs, []int{int(tile[0]) - 49}) + } + combTable = append(combTable, tmpAllCombs) + } + } + var flatTileVector [9]int + for _, tile := range tiles { + flatTileVector[int(tile[0])-49] += 1 + } + CheckXushuHelper(flatTileVector, 0, [][]int{}, &combTable) + + if len(combTable) <= 0 { + return [][][]string{}, fmt.Errorf("tiles are not empty, but can't generate a valid pattern(%v)", tiles) + } + + strColor := tiles[0][1] + var readableResult [][][]string + for _, combs := range combTable { + readableCombs := [][]string{} + for _, comb := range combs { + readableComb := []string{} + for _, tile := range comb { + readableComb = append(readableComb, fmt.Sprintf("%d%c", tile+1, strColor)) + } + readableCombs = append(readableCombs, readableComb) + } + readableResult = append(readableResult, readableCombs) + } + return readableResult, nil +} + +func TilesDividedToPatterns(tiles []string) error { + Sort(tiles) + var wan, tiao, bing, zi []string + for _, tile := range tiles { + switch tile[1] { + case 'W': + wan = append(wan, tile) + case 'T': + tiao = append(tiao, tile) + case 'B': + bing = append(bing, tile) + default: + zi = append(zi, tile) + } + } + return nil +} diff --git a/pkg/tiles_test.go b/pkg/tiles_test.go new file mode 100644 index 0000000..06d8460 --- /dev/null +++ b/pkg/tiles_test.go @@ -0,0 +1,95 @@ +package pkg + +import ( + "fmt" + "log" + "reflect" + "testing" +) + +func TestPrint(t *testing.T) { + var env environment + env.Print() +} + +func TestPatternNormalization(t *testing.T) { + var env environment + handTiles := env.PatternNormalization() + fmt.Println(handTiles) +} + +func TestTileComparator(t *testing.T) { + type testcase struct { + Args [2]string + Expected bool + } + testcases := []testcase{ + {Args: [2]string{"1W", "6W"}, Expected: true}, + {Args: [2]string{"9W", "1B"}, Expected: true}, + {Args: [2]string{"4W", "BA"}, Expected: true}, + {Args: [2]string{"BE", "NA"}, Expected: false}, + } + for _, v := range testcases { + result, err := TileComparator(v.Args[0], v.Args[1]) + if err != nil { + log.Fatalf("Failed in comparing tiles: %v", err) + } + if result != v.Expected { + log.Fatalf("Failed in comparing tiles %s vs %s, expectd: %t, got %t", v.Args[0], v.Args[1], v.Expected, result) + } + } +} + +func TestSort(t *testing.T) { + type testcase struct { + Args []string + Expected []string + } + testcases := []testcase{ + {Args: []string{"7B", "4T", "2T", "3W", "5W", "4W", "BA", "DO"}, Expected: []string{"3W", "4W", "5W", "2T", "4T", "7B", "DO", "BA"}}, + {Args: []string{"8B", "8B", "7B", "6B", "5W", "4W", "4W", "4W"}, Expected: []string{"4W", "4W", "4W", "5W", "6B", "7B", "8B", "8B"}}, + {Args: []string{"BA", "BA", "BA", "BA", "XI", "XI", "XI", "NA"}, Expected: []string{"XI", "XI", "XI", "NA", "BA", "BA", "BA", "BA"}}, + } + for _, v := range testcases { + err := Sort(v.Args) + if err != nil { + log.Fatalf("Failed in sorting tiles: %v", err) + } + if !reflect.DeepEqual(v.Args, v.Expected) { + log.Fatalf("Failed in sorting tiles expectd: %v, got %v", v.Expected, v.Args) + } + } +} + +func TestCheckXushuTile(t *testing.T) { + type testcase struct { + Args []string + Expected [][][]string + } + testcases := []testcase{ + {Args: []string{}, Expected: [][][]string{}}, + {Args: []string{"4W", "4W"}, Expected: [][][]string{{{"4W", "4W"}}}}, + {Args: []string{"4W", "4W", "4W", "4W"}, Expected: [][][]string{{{"4W", "4W", "4W", "4W"}}, {{"4W", "4W"}, {"4W", "4W"}}}}, + {Args: []string{"2W", "3W", "3W", "4W", "4W", "4W", "5W", "5W", "6W"}, Expected: [][][]string{{{"2W", "3W", "4W"}, {"3W", "4W", "5W"}, {"4W", "5W", "6W"}}}}, + {Args: []string{"2W", "3W", "3W", "4W", "4W", "4W", "5W", "5W", "5W", "5W", "6W"}, Expected: [][][]string{{{"2W", "3W", "4W"}, {"3W", "4W", "5W"}, {"4W", "5W", "6W"}, {"5W", "5W"}}}}, + {Args: []string{"3W", "3W", "3W", "4W", "4W", "4W", "5W", "5W", "5W"}, Expected: [][][]string{{{"3W", "3W", "3W"}, {"4W", "4W", "4W"}, {"5W", "5W", "5W"}}, {{"3W", "4W", "5W"}, {"3W", "4W", "5W"}, {"3W", "4W", "5W"}}}}, + {Args: []string{"3W", "3W", "3W", "3W", "4W", "4W", "4W", "4W", "5W", "5W", "5W", "5W"}, Expected: [][][]string{{{"3W", "3W", "3W", "3W"}, {"4W", "4W", "4W", "4W"}, {"5W", "5W", "5W", "5W"}}, {{"3W", "4W", "5W"}, {"3W", "3W", "3W"}, {"4W", "4W", "4W"}, {"5W", "5W", "5W"}}, {{"3W", "4W", "5W"}, {"3W", "4W", "5W"}, {"3W", "4W", "5W"}, {"3W", "4W", "5W"}}, {{"3W", "3W"}, {"3W", "3W"}, {"4W", "4W"}, {"4W", "4W"}, {"5W", "5W"}, {"5W", "5W"}}}}, + {Args: []string{"1W", "4W", "7W"}, Expected: [][][]string{{{"1W"}, {"4W"}, {"7W"}}}}, + {Args: []string{"4W", "7W"}, Expected: [][][]string{{{"4W"}, {"7W"}}}}, + {Args: []string{"1W", "4W"}, Expected: [][][]string{{{"1W"}, {"4W"}}}}, + {Args: []string{"1W", "7W"}, Expected: [][][]string{{{"1W"}, {"7W"}}}}, + {Args: []string{"1W", "1W", "9W"}, Expected: [][][]string{{{"1W"}, {"1W"}, {"9W"}}}}, + {Args: []string{"1W", "9W", "9W"}, Expected: [][][]string{{{"1W"}, {"9W"}, {"9W"}}}}, + {Args: []string{"1W", "9W"}, Expected: [][][]string{{{"1W"}, {"9W"}}}}, + {Args: []string{"1W", "3W", "5W"}, Expected: [][][]string{}}, + } + for _, v := range testcases { + result, err := CheckXushuTile(v.Args) + if err != nil { + log.Printf("Failed in dividing tiles: %v", err) + } + if !reflect.DeepEqual(result, v.Expected) { + log.Fatalf("Failed in dividing tiles expectd: %v, got %v", v.Expected, result) + } + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..5a48a96 --- /dev/null +++ b/readme.md @@ -0,0 +1,6 @@ +# A Majiang helper + +## Dev diary + +S1: A helper to analysis the best way to achieve hu +S1.1: Without \ No newline at end of file diff --git a/test.json b/test.json new file mode 100644 index 0000000..07b439f --- /dev/null +++ b/test.json @@ -0,0 +1,182 @@ +[ + { + "Id": 1, + "Name": "大四喜", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 2, + "Name": "大三元", + "Pattern": { + "Ke": 3, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 3, + "Name": "绿一色", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 4, + "Name": "九莲宝灯", + "Pattern": { + "Ke": 2, + "Shun": 0, + "Dui": 0, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 5, + "Name": "四杠", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 1, + "Gang": 4 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 6, + "Name": "连七对", + "Pattern": { + "Ke": 0, + "Shun": 0, + "Dui": 7, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 7, + "Name": "十三幺", + "Pattern": { + "Ke": 0, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 8, + "Name": "清幺九", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 88 + }, + { + "Id": 9, + "Name": "小四喜", + "Pattern": { + "Ke": 3, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 64 + }, + { + "Id": 10, + "Name": "小三元", + "Pattern": { + "Ke": 2, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 64 + }, + { + "Id": 11, + "Name": "字一色", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 64 + }, + { + "Id": 12, + "Name": "四暗刻", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 2, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 64 + }, + { + "Id": 13, + "Name": "一色双龙会", + "Pattern": { + "Ke": 0, + "Shun": 4, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 64 + }, + { + "Id": 14, + "Name": "一色四同顺", + "Pattern": { + "Ke": 0, + "Shun": 4, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 48 + }, + { + "Id": 15, + "Name": "一色四节高", + "Pattern": { + "Ke": 4, + "Shun": 0, + "Dui": 1, + "Gang": 0 + }, + "ExcludeIds": [], + "Value": 48 + } +] \ No newline at end of file