Refactor code, added new checkXu test cases(9 latterns)
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
package tiles
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
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"}
|
||||
|
||||
// Given two tiles, return if first tile is smaller the second tile, functions used as comparator for ordering
|
||||
// Default order is: 万 < 条 < 饼 < 字(东西南北中发白)
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package tiles
|
||||
|
||||
type tileStruct struct {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package tiles
|
||||
|
||||
import (
|
||||
"log"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTileComparator(t *testing.T) {
|
||||
type testcase struct {
|
||||
Args [2]string
|
||||
Want bool
|
||||
}
|
||||
testcases := []testcase{
|
||||
{Args: [2]string{"1W", "6W"}, Want: true},
|
||||
{Args: [2]string{"9W", "1B"}, Want: true},
|
||||
{Args: [2]string{"4W", "BA"}, Want: true},
|
||||
{Args: [2]string{"BE", "NA"}, Want: 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.Want {
|
||||
log.Fatalf("Failed in comparing tiles %s vs %s, expectd: %t, got %t", v.Args[0], v.Args[1], v.Want, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
type testcase struct {
|
||||
Args []string
|
||||
Want []string
|
||||
}
|
||||
testcases := []testcase{
|
||||
{Args: []string{"7B", "4T", "2T", "3W", "5W", "4W", "BA", "DO"}, Want: []string{"3W", "4W", "5W", "2T", "4T", "7B", "DO", "BA"}},
|
||||
{Args: []string{"8B", "8B", "7B", "6B", "5W", "4W", "4W", "4W"}, Want: []string{"4W", "4W", "4W", "5W", "6B", "7B", "8B", "8B"}},
|
||||
{Args: []string{"BA", "BA", "BA", "BA", "XI", "XI", "XI", "NA"}, Want: []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.Want) {
|
||||
log.Fatalf("Failed in sorting tiles expectd: %v, got %v", v.Want, v.Args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckXushuTile(t *testing.T) {
|
||||
type testcase struct {
|
||||
Name string
|
||||
Args []string
|
||||
Want [][][]string
|
||||
}
|
||||
testcases := []testcase{
|
||||
{Name: "空牌组", Args: []string{}, Want: [][][]string{}},
|
||||
{Name: "一对", Args: []string{"4W", "4W"}, Want: [][][]string{{{"4W", "4W"}}}},
|
||||
{Name: "四张齐", Args: []string{"4W", "4W", "4W", "4W"}, Want: [][][]string{{{"4W", "4W", "4W", "4W"}}, {{"4W", "4W"}, {"4W", "4W"}}}},
|
||||
{Name: "三个顺子", Args: []string{"2W", "3W", "3W", "4W", "4W", "4W", "5W", "5W", "6W"}, Want: [][][]string{{{"2W", "3W", "4W"}, {"3W", "4W", "5W"}, {"4W", "5W", "6W"}}}},
|
||||
{Name: "三个顺子+将牌", Args: []string{"2W", "3W", "3W", "4W", "4W", "4W", "5W", "5W", "5W", "5W", "6W"}, Want: [][][]string{{{"2W", "3W", "4W"}, {"3W", "4W", "5W"}, {"4W", "5W", "6W"}, {"5W", "5W"}}}},
|
||||
{Name: "三同顺或三同刻", Args: []string{"3W", "3W", "3W", "4W", "4W", "4W", "5W", "5W", "5W"}, Want: [][][]string{{{"3W", "3W", "3W"}, {"4W", "4W", "4W"}, {"5W", "5W", "5W"}}, {{"3W", "4W", "5W"}, {"3W", "4W", "5W"}, {"3W", "4W", "5W"}}}},
|
||||
{Name: "三步四张齐", Args: []string{"3W", "3W", "3W", "3W", "4W", "4W", "4W", "4W", "5W", "5W", "5W", "5W"}, Want: [][][]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"}}}},
|
||||
{Name: "三张不靠", Args: []string{"1W", "4W", "7W"}, Want: [][][]string{{{"1W"}, {"4W"}, {"7W"}}}},
|
||||
{Name: "两张不靠", Args: []string{"4W", "7W"}, Want: [][][]string{{{"4W"}, {"7W"}}}},
|
||||
{Name: "两张不靠", Args: []string{"1W", "4W"}, Want: [][][]string{{{"1W"}, {"4W"}}}},
|
||||
{Name: "两张不靠", Args: []string{"1W", "7W"}, Want: [][][]string{{{"1W"}, {"7W"}}}},
|
||||
{Name: "", Args: []string{"1W", "1W", "9W"}, Want: [][][]string{{{"1W"}, {"1W"}, {"9W"}}}},
|
||||
{Name: "", Args: []string{"1W", "9W", "9W"}, Want: [][][]string{{{"1W"}, {"9W"}, {"9W"}}}},
|
||||
{Name: "", Args: []string{"1W", "9W"}, Want: [][][]string{{{"1W"}, {"9W"}}}},
|
||||
{Name: "无组合", Args: []string{"1W", "3W", "5W"}, Want: [][][]string{}},
|
||||
{Name: "九连宝灯", Args: []string{"1W", "1W", "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", "9W", "9W", "1W"}, Want: [][][]string{{{"1W", "2W", "3W"}, {"1W", "1W", "1W"}, {"4W", "5W", "6W"}, {"7W", "8W", "9W"}, {"9W", "9W"}}}},
|
||||
{Name: "九连宝灯", Args: []string{"1W", "1W", "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W", "9W", "9W", "5W"}, Want: [][][]string{{{"1W", "1W", "1W"}, {"2W", "3W", "4W"}, {"5W", "5W"}, {"6W", "7W", "8W"}, {"9W", "9W", "9W"}}}},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
result, err := CheckXushuTile(tc.Args)
|
||||
if err != nil {
|
||||
log.Printf("Failed in dividing tiles: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, tc.Want) {
|
||||
log.Fatalf("TestCheckXushuTile(%v) = %v want %v", tc.Args, result, tc.Want)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user