Initial commit

This commit is contained in:
2023-10-10 02:02:56 -07:00
commit 5bc113a5d0
11 changed files with 825 additions and 0 deletions
+52
View File
@@ -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() {
}
+38
View File
@@ -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)
}
+28
View File
@@ -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
}
+261
View File
@@ -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
}
+95
View File
@@ -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)
}
}
}