;================================================================
; mahjong/mahjong_hand 麻将游戏 手牌算法相关
; handle_mentu.erb 处理面子
;================================================================


;;;取出指定的一组顺子
;;;- (ref)str count_map_str 手牌数量映射数组
;;;- int shuntsu_index 指定的顺子下标
;;;@return 1成功 0失败
@mj_remove_shuntsu(count_map_str, shuntsu_index)
#FUNCTION
    #DIMS REF count_map_str
    #DIM shuntsu_index
    #DIM DYNAMIC count_map, 34
    #DIM DYNAMIC i
    FOR i, 0, VARSIZE("count_map")
        count_map:i = TOINT(SUBSTRINGU(count_map_str, i, 1))
    NEXT
    IF count_map:shuntsu_index > 0 && count_map:(shuntsu_index + 1) > 0 && count_map:(shuntsu_index + 2) > 0
        count_map:shuntsu_index -= 1
        count_map:(shuntsu_index + 1) -= 1
        count_map:(shuntsu_index + 2) -= 1
        count_map_str '= STRJOIN(count_map, "")
        RETURNF 1
    ENDIF
    RETURNF 0


;;;取出指定的一组刻子
;;;- (ref)str count_map_str 手牌数量映射数组
;;;- int koutsu_index 指定的刻子下标
;;;@return 1成功 0失败
@mj_remove_koutsu(count_map_str, koutsu_index)
#FUNCTION
    #DIMS REF count_map_str
    #DIM  koutsu_index
    #DIM DYNAMIC tile_count
    tile_count = TOINT(SUBSTRINGU(count_map_str, koutsu_index, 1))
    IF tile_count >= 3
        ; TODO 好像不兼容最后一位(7z)?
        ; koutsu_index + 1 也许会越界
        count_map_str '= SUBSTRINGU(count_map_str, 0, koutsu_index) + TOSTR(tile_count - 3) + SUBSTRINGU(count_map_str, koutsu_index + 1)
        RETURNF 1
    ENDIF
    RETURNF 0


;;;取出指定的面子组合
;;;- (ref)str count_map_str 手牌数量映射数组
;;;- str mentu_list_str 指定的面子组合列表
;;;@return 1成功 0失败
@mj_remove_mentu(count_map_str, mentu_list_str)
#FUNCTIONS
    #DIMS count_map_str
    #DIMS mentu_list_str
    #DIM DYNAMIC count_map, 34
    #DIM DYNAMIC i
    #DIM DYNAMIC index
    FOR index, 0, VARSIZE("count_map")
        count_map:index = TOINT(SUBSTRINGU(count_map_str, index, 1))
    NEXT
    FOR i, 0, STRLENSU(mentu_list_str) / 3
        index = TOINT(SUBSTRINGU(mentu_list_str, i * 3 + 1, 2))
        SELECTCASE SUBSTRINGU(mentu_list_str, i * 3, 1)
            CASE "s"
                SIF MIN(count_map:index, count_map:(index + 1), count_map:(index + 2)) < 1
                    THROW 取出顺子 %SUBSTRINGU(mentu_list_str, i * 3, 3)% 失败
                count_map:index -= 1
                count_map:(index + 1) -= 1
                count_map:(index + 2) -= 1
            CASE "k"
                SIF count_map:index < 3
                    THROW 取出刻子 %SUBSTRINGU(mentu_list_str, i * 3, 3)% 失败
                count_map:index -= 3
            CASEELSE
                THROW 输入的面子格式错误 %SUBSTRINGU(mentu_list_str, i * 3, 3)%
        ENDSELECT
    NEXT
    count_map_str '= STRJOIN(count_map, "")
    RETURNF count_map_str


;;;取出所有顺子
;;;- (ref)str count_map_str 手牌数量映射数组
;;;@return 取出的顺子
@mj_clear_shuntsu(count_map_str)
#FUNCTIONS
    #DIMS REF count_map_str
    #DIM DYNAMIC count_map, 34
    #DIM DYNAMIC i
    #DIM DYNAMIC j
    #DIM DYNAMIC k
    #DIM DYNAMIC index
    #DIMS DYNAMIC shuntsu
    FOR i, 0, VARSIZE("count_map")
        count_map:i = TOINT(SUBSTRINGU(count_map_str, i, 1))
    NEXT
    FOR i, 0, 3
        FOR j, 0, 7
            index = i * 9 + j
            FOR k, 0, 4
                IF count_map:index > 0 && count_map:(index + 1) > 0 && count_map:(index + 2) > 0
                    count_map:index -= 1
                    count_map:(index + 1) -= 1
                    count_map:(index + 2) -= 1
                    shuntsu += (shuntsu == "" ? "" # ",") + "s" + TOSTR(index, "D2")
                ENDIF
            NEXT
        NEXT
    NEXT
    count_map_str '= STRJOIN(count_map, "")
    RETURNF shuntsu


;;;取出所有刻子
;;;- (ref)str count_map_str 手牌数量映射数组
;;;@return 取出的刻子
@mj_clear_koutsu(count_map_str)
#FUNCTIONS
    #DIMS REF count_map_str
    #DIM DYNAMIC count_map, 34
    #DIM DYNAMIC i
    #DIMS DYNAMIC koutsu
    FOR i, 0, VARSIZE("count_map")
        count_map:i = TOINT(SUBSTRINGU(count_map_str, i, 1))
    NEXT
    FOR i, 0, VARSIZE("count_map")
        IF count_map:i >= 3
            count_map:i -= 3
            koutsu += (koutsu == "" ? "" # ",") + "k" + TOSTR(i, "D2")
        ENDIF
    NEXT
    count_map_str '= STRJOIN(count_map, "")
    RETURNF koutsu


;;;获取每一个可能的顺子
;;;- str count_map_str 手牌数量映射数组
;;;@return 可能的顺子列表(字符串形式)
@mj_get_every_shuntsu(count_map_str)
#FUNCTIONS
    #DIMS count_map_str
    #DIM DYNAMIC count_map, 34
    #DIM DYNAMIC i
    #DIM DYNAMIC j
    #DIM DYNAMIC index
    #DIMS DYNAMIC shuntsu
    FOR i, 0, VARSIZE("count_map")
        count_map:i = TOINT(SUBSTRINGU(count_map_str, i, 1))
    NEXT
    FOR i, 0, 3
        FOR j, 0, 7
            index = i * 9 + j
            SIF count_map:index > 0 && count_map:(index + 1) > 0 && count_map:(index + 2) > 0
                shuntsu += (shuntsu == "" ? "" # ",") + "s" + TOSTR(index, "D2")
        NEXT
    NEXT
    RETURNF shuntsu


;;;获取每一个可能的刻子
;;;- str count_map_str 手牌数量映射数组
;;;@return 可能的刻子列表(字符串形式)
@mj_get_every_koutsu(count_map_str)
#FUNCTIONS
    #DIMS count_map_str
    #DIM DYNAMIC count_map, 34
    #DIM DYNAMIC i
    #DIMS DYNAMIC koutsu
    FOR i, 0, VARSIZE("count_map")
        count_map:i = TOINT(SUBSTRINGU(count_map_str, i, 1))
    NEXT
    FOR i, 0, VARSIZE("count_map")
        SIF count_map:i >= 3
            koutsu += (koutsu == "" ? "" # ",") + "k" + TOSTR(i, "D2")
    NEXT
    RETURNF koutsu


;;;获取每一个可能的面子
;;;- str count_map_str 手牌数量映射数组
;;;@return 可能的面子数组(字符串形式)
@mj_get_every_mentu(count_map_str)
#FUNCTIONS
    #DIMS count_map_str
    #DIMS DYNAMIC mentu_list_str
    mentu_list_str '= mj_get_every_shuntsu(count_map_str)
    RESULTS '= mj_get_every_koutsu(count_map_str)
    SIF RESULTS != ""
        mentu_list_str += (mentu_list_str == "" ? "" # ",") + RESULTS
    RETURNF mentu_list_str


;;;DFS 可能的面子组合
;;;- int depth 当前深度
;;;@return 返回值没有意义
@mj_dfs_mentu(depth)
#FUNCTION
    #DIM DYNAMIC depth  ; 必须重置
    #DIM DYNAMIC skip_count
    #DIMS DYNAMIC hand_status
    #DIM DYNAMIC remove_success
    #DIM DYNAMIC target_index
    #DIM DYNAMIC i
    #DIM DYNAMIC j
    MJ_DFS_TIMES += 1
    ; DEBUGPRINTFORML 查找面子组合方式 DFS 第{MJ_DFS_TIMES, 2}次递归 (当前深度{depth}/{MJ_DFS_MAX_DEPTH}) {str_array_len(MJ_DFS_WALK_PATH)}
    IF str_array_len(MJ_DFS_WALK_PATH) >= MJ_DFS_MAX_DEPTH  ; 达到最大深度
        IF mj_save_dfs_result()  ; 试图保存当前路径
            ; DEBUGPRINTFORML 保存当前路径成功 新的面子组合=%STRJOIN(MJ_DFS_RESULT, ", ", 0, str_array_len(MJ_DFS_RESULT))%
        ENDIF
        RETURNF 1  ; 直接终止本层探索
    ENDIF
    FOR i, 0, str_array_len(MJ_DFS_TREE_NODE)
        FOR j, 0, str_array_len(MJ_DFS_STATUS_STACK) - depth - 1
            SIF str_array_pop(MJ_DFS_STATUS_STACK) == ""
                THROW 舍弃上一轮在本层的探索 回退到当前层数的手牌状态时失败 {str_array_len(MJ_DFS_STATUS_STACK)}
        NEXT
        hand_status '= MJ_DFS_STATUS_STACK:depth  ; 初始化当前层数的手牌状态
        FOR j, 0, str_array_len(MJ_DFS_WALK_PATH) - depth
            SIF str_array_pop(MJ_DFS_WALK_PATH) == ""
                THROW 舍弃上一轮在本层的探索 回退到当前层数的节点路径时失败 {str_array_len(MJ_DFS_STATUS_STACK)}
        NEXT
        target_index = TOINT(SUBSTRINGU(MJ_DFS_TREE_NODE:i, 1))  ; 获取标记的下标
        IF SUBSTRINGU(MJ_DFS_TREE_NODE:i, 0, 1) == "s"  ; 尝试去掉指定的顺子
            remove_success = mj_remove_shuntsu(hand_status, target_index)
        ELSEIF SUBSTRINGU(MJ_DFS_TREE_NODE:i, 0, 1) == "k"  ; 尝试去掉指定的刻子
            remove_success = mj_remove_koutsu(hand_status, target_index)
        ENDIF
        ; DEBUGPRINTFORML 第 {i} 次循环 尝试处理 %MJ_DFS_TREE_NODE:i% %remove_success ? "成功" # "失败"%
        IF remove_success
            SIF str_array_push(MJ_DFS_WALK_PATH, MJ_DFS_TREE_NODE:i) <= 0
                THROW 保存更新后的手牌状态失败 %hand_status%
        ELSE
            skip_count += 1
            SIF skip_count >= str_array_len(MJ_DFS_TREE_NODE)  ; 本层所有分支都被剪掉了
                RESULT = mj_save_dfs_result()  ; 试图保存当前路径
            CONTINUE  ; 剪掉在此基础上的分支
        ENDIF
        SIF str_array_push(MJ_DFS_STATUS_STACK, hand_status) <= 0
            THROW 保存更新后的手牌状态失败 %hand_status% %STRJOIN(MJ_DFS_STATUS_STACK, "|", 0, str_array_len(MJ_DFS_STATUS_STACK))%
        RESULT = mj_dfs_mentu(depth + 1)  ; 进入下一层递归
    NEXT
    RETURNF 0


;;;把手牌拆分为面子
;;;- str count_map_str 手牌数量映射数组
;;;@return 1成功 0失败
@mj_get_mentu(count_map_str)
#FUNCTION
    #DIMS DYNAMIC count_map_str
    #DIM DYNAMIC i
    #DIM DYNAMIC j
    #DIM DYNAMIC k
    #DIM DYNAMIC meld_max_count
    #DIM DYNAMIC meld_count
    #DIM DYNAMIC count
    #DIMS DYNAMIC mentu_temp, 4
    VARSET MJ_DFS_STATUS_STACK, ""
    MJ_DFS_STATUS_STACK:0 '= count_map_str
    VARSET MJ_DFS_TREE_NODE, ""
    SPLIT mj_get_every_mentu(count_map_str), ",", MJ_DFS_TREE_NODE
    MJ_DFS_MAX_DEPTH = mj_count_hand_from(count_map_str) / 3
    VARSET MJ_DFS_WALK_PATH, ""
    VARSET MJ_DFS_RESULT, ""
    MJ_DFS_TIMES = 0
    RESULT = mj_dfs_mentu(0)
    ; 尝试向听倒退(为了更广的进张)
    ; 比如     234 6  进张只有 6 (3枚)
    ; 可以拆成 23 46  进张变成 1/4/5 (11枚)
    FOR i, 0, str_array_len(MJ_DFS_RESULT)
        meld_count = STRLENSU(MJ_DFS_RESULT:i) / 3
        SIF meld_count > meld_max_count
            meld_max_count = meld_count
    NEXT
    FOR i, 0, str_array_len(MJ_DFS_RESULT)
        meld_count = STRLENSU(MJ_DFS_RESULT:i) / 3
        IF meld_count == meld_max_count
            FOR j, 0, meld_count
                VARSET mentu_temp, ""
                FOR k, 0, meld_count
                    mentu_temp:k '= SUBSTRINGU(MJ_DFS_RESULT:i, k * 3, 3)
                NEXT
                ; 遍历无序组合 C(max_got_depth, max_got_depth - 1)
                mentu_temp:j '= ""
                VARSET MJ_DFS_WALK_PATH, ""
                ARRAYCOPY "mentu_temp", "MJ_DFS_WALK_PATH"
                RESULT = mj_save_dfs_result()  ; 试图保存当前路径
            NEXT
        ENDIF
    NEXT
    RETURNF str_array_len(MJ_DFS_RESULT) > 0


;;;复位移除的顺子
;;;- (ref)str count_map_str 手牌数量映射数组
;;;- int shuntsu_index 顺子的位置下标
;;;@return 1成功 0失败
; @mj_restore_shuntsu(count_map_str, shuntsu_index)
; #FUNCTION
;     #DIMS REF count_map_str
;     #DIM shuntsu_index
;     #DIM DYNAMIC i
;     FOR i, 0, 3
;         SIF SUBSTRINGU(count_map_str, shuntsu_index + i, 1) == "4"
;             RETURNF 0
;     NEXT
;     count_map_str '= SUBSTRINGU(count_map_str, 0, shuntsu_index)
;     FOR i, 0, 3
;         count_map_str += TOSTR(TOINT(SUBSTRINGU(count_map_str, shuntsu_index + i, 1)) + 1)
;     NEXT
;     count_map_str += SUBSTRINGU(count_map_str, shuntsu_index + 3)
;     RETURNF 1


;;;复位移除的刻子
;;;- (ref)str count_map_str 手牌数量映射数组
;;;- int koutsu_index 刻子的位置下标
;;;@return 1成功 0失败
; @mj_restore_koutsu(count_map_str, koutsu_index)
; #FUNCTION
;     #DIMS REF count_map_str
;     #DIM koutsu_index
;     #DIM DYNAMIC tile_count
;     tile_count = TOINT(SUBSTRINGU(count_map_str, koutsu_index, 1))
;     SIF tile_count > 1
;         RETURNF 0
;     count_map_str '= SUBSTRINGU(count_map_str, 0, koutsu_index) + TOSTR(tile_count + 3) + SUBSTRINGU(count_map_str, koutsu_index + 1)
;     RETURNF 1
