;================================================================
; mahjong/mahjong_hand 麻将游戏 手牌算法相关
; calculate_shanten.erb 向听数计算
;================================================================


;;;尝试保存当前面子路径
;;;@return 1成功 0失败
@mj_save_dfs_result()
#FUNCTION
    #DIMS temp, 4
    #DIMS DYNAMIC walk_path
    ARRAYCOPY "MJ_DFS_WALK_PATH", "temp"
    ARRAYSORT temp
    walk_path '= STRJOIN(temp, "")
    IF walk_path != "" && FINDELEMENT(MJ_DFS_RESULT, walk_path, 0, , 1) == -1
        SIF str_array_push(MJ_DFS_RESULT, walk_path) <= 0
            THROW 确定没有重复 添加到结果时失败
        RETURNF 1
    ENDIF
    RETURNF 0


;;;尝试保存当前搭子和对子路径
;;;@return 1成功 0失败
@mj_save_dfs_result_2()
#FUNCTION
    #DIMS temp, 4
    #DIMS DYNAMIC walk_path
    ARRAYCOPY "MJ_DFS_WALK_PATH", "temp"
    ARRAYSORT temp
    walk_path '= STRJOIN(temp, "")
    IF walk_path != "" && FINDELEMENT(MJ_DFS_RESULT_2, walk_path, 0, , 1) == -1
        SIF str_array_push(MJ_DFS_RESULT_2, walk_path) <= 0
            THROW 确定没有重复 添加到结果时失败
        RETURNF 1
    ENDIF
    RETURNF 0


;;;根据提供的参数计算一般型的向听数
;;;- int melds_count 面子数量(顺子/刻子)
;;;- int tatsu_and_toitsu_count 搭子(11/101)和对子(2)数量
;;;- int jantou_flag 雀头标记
;;;@return 一般型的向听数
@mj_count_shanten_for_regular(melds_count, tatsu_and_toitsu_count, jantou_flag)
#FUNCTION
    #DIM melds_count
    #DIM tatsu_and_toitsu_count
    #DIM jantou_flag
    #DIM DYNAMIC overload
    ; 算法来自 https://www.bilibili.com/read/cv10974292/
    overload = MAX(melds_count + tatsu_and_toitsu_count - 5, 0)
    SIF melds_count + tatsu_and_toitsu_count <= 4
        jantou_flag = 1
    RETURNF 9 - (melds_count * 2) - tatsu_and_toitsu_count + overload - jantou_flag


;;;计算一般型的向听数
;;;- str hand_code 手牌代码
;;;@return 一般型的向听数
@mj_calculate_shanten_for_regular(hand_code)
#FUNCTION
    #DIMS hand_code
    #DIMS DYNAMIC hand_map
    #DIMS DYNAMIC hand_map_without_mentu
    #DIMS DYNAMIC hand_map_without_tatsu_and_toitsu
    #DIM DYNAMIC i
    #DIM DYNAMIC j
    #DIM DYNAMIC melds_count
    #DIM DYNAMIC tatsu_and_toitsu_count
    #DIM DYNAMIC jantou_flag
    #DIM DYNAMIC new_shanten
    #DIM DYNAMIC min_shanten
    hand_map '= mj_map_hand_to_count(hand_code)

    ; TODO 临时 兼容副露
    RESULTS '= hand_map
    hand_map '= SUBSTRINGU(hand_map, 0, 33) + TOSTR(TOINT(SUBSTRINGU(hand_map, 33)) + ((14 - mj_count_hand_from(hand_map)) / 3) * 3)

    min_shanten = 8
    IF mj_get_mentu(hand_map)
        ; PRINTFORML 面子可能的组合=%STRJOIN(MJ_DFS_RESULT, "|", 0, str_array_len(MJ_DFS_RESULT))%
        FOR i, 0, str_array_len(MJ_DFS_RESULT)
            melds_count = STRLENSU(MJ_DFS_RESULT:i) / 3
            ; PRINTFORML %"", 2%当前测试面子组合:{i} = %MJ_DFS_RESULT:i% ({melds_count})
            ; PRINTFORML %"", 4%去掉面子前 %hand_map% ({mj_count_hand_from(hand_map)})
            hand_map_without_mentu '= mj_remove_mentu(hand_map, MJ_DFS_RESULT:i)
            ; PRINTFORML %"", 4%去掉面子后 %hand_map_without_mentu% ({mj_count_hand_from(hand_map_without_mentu)})
            IF mj_get_tatsu_and_toitsu(hand_map_without_mentu)
                ; PRINTFORM %"", 4%搭子/对子的可能性=%STRJOIN(MJ_DFS_TREE_NODE, ",", 0, str_array_len(MJ_DFS_TREE_NODE))%
                ; PRINTFORML (最大深度={mj_count_hand_from(hand_map_without_mentu)}/2={MJ_DFS_MAX_DEPTH})
                ; PRINTFORML %"", 4%搭子可能的组合=%STRJOIN(MJ_DFS_RESULT_2, ",", 0, str_array_len(MJ_DFS_RESULT_2))%
                FOR j, 0, str_array_len(MJ_DFS_RESULT_2)
                    tatsu_and_toitsu_count = STRLENSU(MJ_DFS_RESULT_2:j) / 3
                    new_shanten = mj_count_shanten_for_regular(melds_count, tatsu_and_toitsu_count, mj_count_toitsu(hand_map) > 1)
                    ; PRINTFORM %"", 6%当前测试面子组合:{j} = %MJ_DFS_RESULT_2:j% ({tatsu_and_toitsu_count}) 向听数
                    ; PRINTFORML ({melds_count}, {tatsu_and_toitsu_count}, {mj_count_toitsu(hand_map) > 1}) = {new_shanten}
                    SIF new_shanten < min_shanten
                        min_shanten = new_shanten
                    ; TODO 进一步分析有效牌 面子=%MJ_DFS_RESULT:i% 搭子/对子=%MJ_DFS_RESULT_2:j%
                NEXT
            ELSE
                new_shanten = mj_count_shanten_for_regular(melds_count, 0, mj_count_toitsu(hand_map) > 1)
                SIF new_shanten < min_shanten
                    min_shanten = new_shanten
            ENDIF
        NEXT
    ELSE
        hand_map_without_mentu '= hand_map
        IF mj_get_tatsu_and_toitsu(hand_map_without_mentu)
            FOR j, 0, str_array_len(MJ_DFS_RESULT_2)
                tatsu_and_toitsu_count = STRLENSU(MJ_DFS_RESULT_2:j) / 3
                new_shanten = mj_count_shanten_for_regular(melds_count, tatsu_and_toitsu_count, mj_count_toitsu(hand_map) > 1)
                SIF new_shanten < min_shanten
                    min_shanten = new_shanten
            NEXT
        ELSE
            new_shanten = mj_count_shanten_for_regular(melds_count, 0, mj_count_toitsu(hand_map) > 1)
            SIF new_shanten < min_shanten
                min_shanten = new_shanten
        ENDIF
    ENDIF
    RETURNF min_shanten


;;;计算七对子的向听数
;;;- str hand_code 手牌代码
;;;@return 七对子的向听数
@mj_calculate_shanten_for_chiitoitsu(hand_code)
#FUNCTION
    #DIMS hand_code
    RETURNF 6 - mj_count_toitsu(hand_code)


;;;计算国士无双的向听数
;;;- str hand_code 手牌代码
;;;@return 国士无双的向听数
@mj_calculate_shanten_for_kokushi(hand_code)
#FUNCTION
    #DIMS hand_code
    #DIM CONST YaochuuIndex, 13 = 0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33
    #DIM DYNAMIC i
    #DIMS DYNAMIC count_map_str
    #DIM DYNAMIC jantou_completed
    count_map_str '= mj_map_hand_to_count(hand_code)
    FOR i, 0, VARSIZE("YaochuuIndex")
        IF TOINT(SUBSTRINGU(count_map_str, i, 1)) >= 2
            jantou_completed = 1
            BREAK
        ENDIF
    NEXT
    RETURNF 13 - mj_count_yaochuu(hand_code) - jantou_completed


;;;计算向听数
;;;- str hand_code 手牌代码
;;;@return 最小的向听数
@mj_count_shanten(hand_code)
#FUNCTION
    #DIMS hand_code
    #DIM DYNAMIC shanten_count, 3
    hand_code '= mj_format_hand_code(hand_code)
    SIF STRLENSU(hand_code) % 6 != 2
        THROW 手牌(%hand_code%)数量({STRLENSU(hand_code) / 2})错误
    shanten_count:0 = mj_calculate_shanten_for_regular(hand_code)
    shanten_count:1 = mj_calculate_shanten_for_chiitoitsu(hand_code)
    shanten_count:2 = mj_calculate_shanten_for_kokushi(hand_code)
    RETURNF MINARRAY(shanten_count)
