# -*- coding: utf-8 -*-
"""
邪魔付き重力付き三目並べ（プログラム）
"""
# これは，難易度３を達成するプログラムのソースコードです．


import random


BOARD_SIZE_H = 8  # 盤面横サイズ
BOARD_SIZE_V = 8  # 盤面縦サイズ
WINCON_LEN   = 3  # 勝利条件：三目
STONE_STR = ["[ ]", " X ", " O ", " # ", "\x1b[4;101m<X>\x1b[0m", "\x1b[4;101m(O)\x1b[0m"]  # 石の文字
# 盤面を初期化した状態でグローバル変数として生成
board   = [[0 for j in range(BOARD_SIZE_H)] for i in range(BOARD_SIZE_V)]
TICKS_H = "\x1b[4;104m" + "".join([f"{i+1:2d} " for i in range(BOARD_SIZE_H)])  + "\x1b[0m"  # 横軸目盛

# 盤面の表示
def print_board():
    # print("\x1b[2J", end="")  # 画面のクリア
    print(TICKS_H)  # 横軸目盛
    j = 1
    for board_raw in board:
        # １行分の盤面の要素をそれぞれ文字列化
        str_raw = [STONE_STR[i] for i in board_raw]
        # 縦軸目盛とともに１行分の盤面を表示
        print("".join(str_raw))
        j += 1
    print(TICKS_H)  # 横軸目盛


# 位置が盤面の外かどうかを判定
def is_out_of_board(num_v, num_h):
    return not ((0 <= num_v < BOARD_SIZE_V) and (0 <= num_h < BOARD_SIZE_H))


# 盤面に空きがないか確認
def is_board_full():
    for j in range(BOARD_SIZE_H):
        # 重力付きなので上辺だけ確認すればよい
        if board[0][j] == 0:
            return False
    return True


# 一番下まで落ちたところに石を置く
def put_stone(num_side, num_h):
    num_v = BOARD_SIZE_V - 1
    if is_out_of_board(0, num_h) or (board[0][num_h] != 0):
        return
    while board[num_v][num_h] != 0:
        num_v -= 1
    board[num_v][num_h] = num_side
    return num_v


# 前に置かれた石の近くに邪魔な石を置く
def obstacle(num_h):
    dh = random.randrange(3) - 1
    put_stone(3, num_h + dh)


# 縦横斜めのいずれかの方向に指定の石が並んでいるか確認
# 再帰処理を利用する場合のコード
def check_line(num_side, num_v, num_h, dv, dh):
    # 石の種類が異なったら探索から復帰（０個目）
    # そうでなければ次を探索し，その結果に１を加算
    if is_out_of_board(num_v, num_h) or (board[num_v][num_h] != num_side):
        return 0
    else:
        return check_line(num_side, num_v+dv, num_h+dh, dv, dh) + 1


# 特定の方向に並んでいる指定の石を強調
# 再帰処理を利用する場合のコード
def empha_line(num_side, num_v, num_h, dv, dh):
    # 石の種類が異なったら探索から復帰
    # そうでなければ強調して次を探索
    if is_out_of_board(num_v, num_h) or ((board[num_v][num_h] != num_side) and (board[num_v][num_h] != num_side+3)):
        return
    else:
        board[num_v][num_h] = num_side + 3
        empha_line(num_side, num_v+dv, num_h+dh, dv, dh)


def main():
    num_side = 1      # どちらの手番か
    num_move = 1      # 何手目か
    flag_win = False  # 勝敗フラグ
    flag_man = [True, True]  # 人間フラグ
    num_h = -1

    num_h = random.randrange(BOARD_SIZE_H)
    put_stone(3, num_h)
    print_board()
    # 盤面に空きがあれば継続
    while not is_board_full():
        # 手番の出力
        print(STONE_STR[num_side] + "の番です")
        if flag_man[num_side-1]:
            try:
                # 位置の入力　listのインデックスと対応させるため -1
                num_h = int(input(f"どこに置きますか？（{num_move}手目）：")) - 1
            except ValueError:
                print("整数で入力してください")
                continue
            print()  # 改行のみ

            # 位置が 0 の場合は終了
            if num_h == -1:
                break
            elif num_h < -1:
                flag_man[num_side-1] = False
            else:
                # 位置が盤面の外の場合や上限に達していた場合はやり直し
                if is_out_of_board(0, num_h) or (board[0][num_h] != 0):
                    print("そこには置けません")
                    continue
        
        if not flag_man[num_side-1]:
            num_h = random.randrange(BOARD_SIZE_H)
            while is_out_of_board(0, num_h) or (board[0][num_h] != 0):
               num_h = random.randrange(BOARD_SIZE_H)
            print(f"コンピュータが {num_h} に置きました\n")

        num_v = put_stone(num_side, num_h)
        obstacle(num_h)

        # 各方向について同じ種類の石がどれだけ並んでいるかチェック
        flag_win = False
        for dv in range(-1, 2):
            for dh in range(2):
                # 置いたところと対向は省略（左上，上，右，右下を確認）
                if (dv <= 0) and (dh <= 0):
                    continue

                # 自分＋指示した方向＋その対向　で数え上げて合計
                num_stone = 1 + check_line(num_side, num_v+dv, num_h+dh, dv, dh) \
                            + check_line(num_side, num_v-dv, num_h-dh, -dv, -dh)
                
                # 指定の数以上なら勝ち
                if  num_stone >= WINCON_LEN:
                    empha_line(num_side, num_v, num_h,  dv,  dh)
                    empha_line(num_side, num_v, num_h, -dv, -dh)
                    flag_win = True

        # 盤面を表示
        print_board()
        # 勝敗がついた場合（石を置いた側が勝ち）は終了
        if flag_win:
            print(STONE_STR[num_side] + "の勝ちです")
            break
        # 攻守交替
        num_side = (num_side % 2) + 1
        num_move += 1

    else:
        print("引き分けです")
    
    input("\nEnterを押してプログラムを完全終了")


if __name__ == "__main__":
    main()
