Skip to content
def flip_Board(B):
    for i in range(4):
        for j in range(8):
            (B[i][j],B[7-i][7-j])=(B[7-i][7-j],B[i][j])
    return B
####################################################################
def flip_pieces(P):
    flipped=[]
    for piece in P:
        flipped.append((7-piece[0], 7-piece[1]))
    return flipped
####################################################################
def open_cells(B,i,j):
    moves={}
    if i!=0 and B[i-1][j]==0:
        moves['Up']=(i-1, j)
    if j!=7 and B[i][j+1]==0:
        moves['Right']=(i, j+1)
    if j!=0 and B[i][j-1]==0:
        moves['Left']=(i, j-1)
    return moves
####################################################################
def dama_moves(B,i,j):
    moves={}
    if i!=0:
        for k in range(i-1,-1,-1):
            if B[k][j]==0:
                if 'Up' not in moves:
                    moves['Up']=[(k,j)]
                else:
                    moves['Up'].append((k,j))
            else:
                break
    if j!=7:
        for k in range(j+1,8):
            if B[i][k]==0:
                if 'Right' not in moves:
                    moves['Right']=[(i,k)]
                else:
                    moves['Right'].append((i,k))
            else:
                break
    if j!=0:
        for k in range(j-1,-1,-1):
            if B[i][k]==0:
                if 'Left' not in moves:
                    moves['Left']=[(i,k)]
                else:
                    moves['Left'].append((i,k))
            else:
                break
    if i!=7:
        for k in range(i+1,8):
            if B[k][j]==0:
                if 'Down' not in moves:
                    moves['Down']=[(k,j)]
                else:
                    moves['Down'].append((k,j))
            else:
                break
    return moves
####################################################################
def choose_land(B, cells, opp):
    Land=[]
    Dir={}
    comp={}
    for cell in cells:
        comp[cell]=[]
        Dir[cell]=0
        i=cell[0] ; j=cell[1]
        moves=dama_moves(B, i, j)
        if 'Up' not in moves:
            moves['Up']=[(i,j)]
        k=moves['Up'][-1][0]
        if k>1 and B[k-1][j] in opp and B[k-2][j]==0:
            comp[cell].append('Up')
            Dir[cell]+=1
        if 'Right' not in moves:
            moves['Right']=[(i,j)]
        k=moves['Right'][-1][1]
        if k<6 and B[i][k+1] in opp and B[i][k+2]==0:
            comp[cell].append('Right')
            Dir[cell]+=1
        if 'Left' not in moves:
            moves['Left']=[(i,j)]
        k=moves['Left'][-1][1]
        if k>1 and B[i][k-1] in opp and B[i][k-2]==0:
            comp[cell].append('Left')
            Dir[cell]+=1
        if 'Down' not in moves:
            moves['Down']=[(i,j)]
        k=moves['Down'][-1][0]
        if k<6 and B[k+1][j] in opp and B[k+2][j]==0:
            comp[cell].append('Down')
            Dir[cell]+=1
    longest=0
    for cell in Dir:
        if Dir[cell]>longest:
            longest=Dir[cell]
            Land=[cell]
        elif Dir[cell]==longest:
            Land.append(cell)
    take=[]
    if longest==1:
        Land=[Land[0]]
    elif longest==2:
        L=[['Up','Down'],['Down','Up'],['Right','Left'],['Left','Right']]
        allow=True
        for cell in comp:
            if comp[cell] not in L:
                if Dir[cell]==2:
                    take.append(cell)
            else:
                take=[cell]
                break
        Land=take
    return Land
####################################################################
def mandatory_dama(Board, D, opp):
    def dama_kills(B, i, j, opp, I, J):
        from copy import deepcopy
        A = deepcopy(B)
        A[i][j] = 0
        options = {}
        o = 1

        def track(M, i, j, opp, o, I, J):
            Next = {}
            oUp = {}
            oRight = {}
            oLeft = {}
            oDown = {}
            Up = Right = Down = Left = False
            moves = dama_moves(M, i, j)

            ## Check Up
            if 'Up' not in moves:
                moves['Up'] = [(i, j)]
            k = moves['Up'][-1][0]
            if k > 1 and M[k - 1][j] in opp and M[k - 2][j] == 0:
                choices = []
                Up = True
                for h in range(k - 2, -1, -1):
                    if M[h][j] == 0:
                        choices.append((h, j))
                    else:
                        break
                lands = choose_land(M, choices, opp)
                UBoard = deepcopy(M)
                UBoard[k - 1][j] = 0
                Next['Up'] = [lands[0]]
                oUp[lands[0]] = o
                if 'option' + str(o) not in options:
                    options['option' + str(o)] = [lands[0]]
                else:
                    options['option' + str(o)].append(lands[0])
                n=len(options)
                for g in range(1, len(lands)):
                    Next['Up'].append(lands[g])
                    oUp[lands[g]] = n + g
                    options['option' + str(n + g)] = options['option' + str(o)][:-1]
                    options['option' + str(n + g)].append(lands[g])

            ## Check Right
            if 'Right' not in moves:
                moves['Right'] = [(i, j)]
            k = moves['Right'][-1][1]
            if k < 6 and M[i][k + 1] in opp and M[i][k + 2] == 0:
                choices = []
                Right = True
                for h in range(k + 2, 8):
                    if M[i][h] == 0:
                        choices.append((i, h))
                    else:
                        break
                lands = choose_land(M, choices, opp)
                RBoard = deepcopy(M)
                RBoard[i][k + 1] = 0
                Next['Right'] = [lands[0]]
                if Up:
                    n = len(options)
                    options['option' + str(n + 1)] = options['option' + str(o)][:-1]
                    options['option' + str(n + 1)].append(lands[0])
                    oRight[lands[0]] = n + 1
                    for g in range(1, len(lands)):
                        Next['Right'].append(lands[g])
                        oRight[lands[g]] = n + 1 + g
                        options['option' + str(n + 1 + g)]= options['option' + str(n + 1)][:-1]
                        options['option' + str(n + 1 + g)].append(lands[g])
                elif 'option' + str(o) not in options:
                    options['option' + str(o)] = [lands[0]]
                    oRight[lands[0]] = o
                    n=len(options)
                    for g in range(1, len(lands)):
                        Next['Right'].append(lands[g])
                        oRight[lands[g]] = n + g
                        options['option' + str(n + g)] = options['option' + str(o)][:-1]
                        options['option' + str(n + g)].append(lands[g])
                else:
                    options['option' + str(o)].append(lands[0])
                    oRight[lands[0]] = o
                    n=len(options)
                    for g in range(1, len(lands)):
                        Next['Right'].append(lands[g])
                        oRight[lands[g]] = n + g
                        options['option' + str(n + g)] = options['option' + str(o)][:-1]
                        options['option' + str(n + g)].append(lands[g])
            ## Check Down
            if 'Down' not in moves:
                moves['Down'] = [(i, j)]
            k = moves['Down'][-1][0]
            if k < 6 and M[k + 1][j] in opp and M[k + 2][j] == 0:
                choices = []
                Down = True
                for h in range(k + 2, 8):
                    if M[h][j] == 0:
                        choices.append((h, j))
                    else:
                        break
                lands = choose_land(M, choices, opp)
                DBoard = deepcopy(M)
                DBoard[k + 1][j] = 0
                Next['Down'] = [lands[0]]
                if Up or Right:
                    n = len(options)
                    options['option' + str(n + 1)] = options['option' + str(o)][:-1]
                    options['option' + str(n + 1)].append(lands[0])
                    oDown[lands[0]] = n + 1
                    for g in range(1, len(lands)):
                        Next['Down'].append(lands[g])
                        oDown[lands[g]] = n + 1 + g
                        options['option' + str(n + 1 + g)] = options['option' + str(n + 1)][:-1]
                        options['option' + str(n + 1 + g)].append(lands[g])
                elif 'option' + str(o) not in options:
                    options['option' + str(o)] = [lands[0]]
                    oDown[lands[0]] = o
                    n=len(options)
                    for g in range(1, len(lands)):
                        Next['Down'].append(lands[g])
                        oDown[lands[g]] = n + g
                        options['option' + str(n + g)] = options['option' + str(o)][:-1]
                        options['option' + str(n + g)].append(lands[g])
                else:
                    options['option' + str(o)].append(lands[0])
                    oDown[lands[0]] = o
                    n=len(options)
                    for g in range(1, len(lands)):
                        Next['Down'].append(lands[g])
                        oDown[lands[g]] = n + g
                        options['option' + str(n + g)] = options['option' + str(o)][:-1]
                        options['option' + str(n + g)].append(lands[g])

            ## Check Left
            if 'Left' not in moves:
                moves['Left'] = [(i, j)]
            k = moves['Left'][-1][1]
            if k > 1 and M[i][k - 1] in opp and M[i][k - 2] == 0:
                choices = []
                Left = True
                for h in range(k - 2, -1, -1):
                    if M[i][h] == 0:
                        choices.append((i, h))
                    else:
                        break
                lands = choose_land(M, choices, opp)
                LBoard = deepcopy(M)
                LBoard[i][k - 1] = 0
                Next['Left'] = [lands[0]]
                if Up or Right or Down:
                    n = len(options)
                    options['option' + str(n + 1)] = options['option' + str(o)][:-1]
                    options['option' + str(n + 1)].append(lands[0])
                    oLeft[lands[0]] = n + 1
                    for g in range(1, len(lands)):
                        Next['Left'].append(lands[g])
                        oLeft[lands[g]] = n + 1 + g
                        options['option' + str(n + 1 + g)] = options['option' + str(n+1)][:-1]
                        options['option' + str(n + 1 + g)].append(lands[g])
                elif 'option' + str(o) not in options:
                    options['option' + str(o)] = [lands[0]]
                    oLeft[lands[0]] = o
                    n=len(options)
                    for g in range(1, len(lands)):
                        Next['Left'].append(lands[g])
                        oLeft[lands[g]] = n + g
                        options['option' + str(n + g)] = options['option' + str(o)][:-1]
                        options['option' + str(n + g)].append(lands[g])
                else:
                    options['option' + str(o)].append(lands[0])
                    oLeft[lands[0]] = o
                    n=len(options)
                    for g in range(1, len(lands)):
                        Next['Left'].append(lands[g])
                        oLeft[lands[g]] = n + g
                        options['option' + str(n + g)] = options['option' + str(o)][:-1]
                        options['option' + str(n + g)].append(lands[g])

            ## Add all possible final lands
            if not (Up or Right or Down or Left):
                if len(options) != 0 and len(options['option' + str(o)]) == 1:
                    if j == J:
                        if i > I:
                            n = len(options)
                            m = 1
                            for h in range(i + 1, 8):
                                if M[h][j] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((h, j))
                                    m += 1
                                else:
                                    break
                        else:
                            n = len(options)
                            m = 1
                            for h in range(i - 1, -1, -1):
                                if M[h][j] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((h, j))
                                    m += 1
                                else:
                                    break
                    elif i == I:
                        if j > J:
                            n = len(options)
                            m = 1
                            for h in range(j + 1, 8, ):  ##
                                if M[i][h] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((i, h))
                                    m += 1
                                else:
                                    break
                        else:
                            n = len(options)
                            m = 1
                            for h in range(j - 1, -1, -1):
                                if M[i][h] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((i, h))
                                    m += 1
                                else:
                                    break
                elif len(options) != 0 and len(options['option' + str(o)]) > 1:
                    if j == options['option' + str(o)][-2][1]:
                        if i > options['option' + str(o)][-2][0]:
                            n = len(options)
                            m = 1
                            for h in range(i + 1, 8):
                                if M[h][j] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((h, j))
                                    m += 1
                                else:
                                    break
                        else:
                            n = len(options)
                            m = 1
                            for h in range(i - 1, -1, -1):
                                if M[h][j] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((h, j))
                                    m += 1
                                else:
                                    break
                    elif i == options['option' + str(o)][-2][0]:
                        if j > options['option' + str(o)][-2][1]:
                            n = len(options)
                            m = 1
                            for h in range(j + 1, 8, ):  ##
                                if M[i][h] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((i, h))
                                    m += 1
                                else:
                                    break
                        else:
                            n = len(options)
                            m = 1
                            for h in range(j - 1, -1, -1):
                                if M[i][h] == 0:
                                    options['option' + str(n + m)] = options['option' + str(o)][:-1]
                                    options['option' + str(n + m)].append((i, h))
                                    m += 1
                                else:
                                    break

            ## Start with next moves
            for Dir in Next:
                for cell in Next[Dir]:
                    if Dir == 'Up':
                        track(UBoard, cell[0], cell[1], opp, oUp[cell], I, J)
                    elif Dir == 'Right':
                        track(RBoard, cell[0], cell[1], opp, oRight[cell], I, J)
                    elif Dir == 'Down':
                        track(DBoard, cell[0], cell[1], opp, oDown[cell], I, J)
                    elif Dir == 'Left':
                        track(LBoard, cell[0], cell[1], opp, oLeft[cell], I, J)

        track(A, i, j, opp, o, I, J)
        Max = 0
        longest = {}
        for op in options:
            if len(options[op]) > Max:
                Max = len(options[op])
                longest = {op: options[op]}
            elif len(options[op]) == Max:
                longest[op] = options[op]
        return (longest, Max)

    Moves = {}
    Max = 0
    for dama in D:
        (Op, Len) = dama_kills(Board, dama[0], dama[1], opp, dama[0], dama[1])
        if Len > Max:
            Max = Len
            Moves = {}
            Moves[(dama[0], dama[1])] = Op
        elif Len == Max:
            Moves[(dama[0], dama[1])] = Op

    def order_dict(Dict):
        Options = {}
        n = 1
        for op in Dict:
            Options['option' + str(n)] = Dict[op]
            n += 1
        return Options

    for dama in Moves:
        Moves[dama] = order_dict(Moves[dama])
    return (Moves, Max)
####################################################################
def mantadory_normal(Board,P,opp):
    def kill_cells(B,i,j,opp):
        from copy import deepcopy
        A=deepcopy(B)
        options={}
        o=1
        def track(M,i,j,opp,o):
            next=[]
            O=[]
            Up=Right=False
            if i>1 and M[i-1][j] in opp and M[i-2][j]==0:
                if 'option'+str(o) not in options:
                    options['option'+str(o)]=[(i-2, j)]
                else:
                    options['option'+str(o)].append( (i-2, j) )
                if len(options['option'+str(o)])==1:
                    options['option'+str(o)]=[['start:',(i, j)]]+options['option'+str(o)]
                M[i][j]=5
                Up=True
                next.append( (i-2, j) )
                O.append(o)
            if j<6 and M[i][j+1] in opp and M[i][j+2]==0:
                if Up:
                    n=len(options)
                    options['option'+str(n+1)]=options['option'+str(o)][:-1]
                    options['option'+str(n+1)].append((i, j+2))
                    O.append(n+1)
                    if len(options['option'+str(n+1)])==1:
                        options['option'+str(n+1)]=[['start:',(i, j)]]+options['option'+str(n+1)]
                elif 'option'+str(o) not in options:
                    options['option'+str(o)]=[(i, j+2)]
                    O.append(o)
                    if len(options['option'+str(o)])==1:
                        options['option'+str(o)]=[['start:',(i, j)]]+options['option'+str(o)]
                else:
                    options['option'+str(o)].append( (i, j+2) )
                    O.append(o)
                    if len(options['option'+str(o)])==1:
                        options['option'+str(o)]=[['start:',(i, j)]]+options['option'+str(o)]
                M[i][j]=5
                Right=True
                next.append( (i, j+2) )
            if j>1 and M[i][j-1] in opp and M[i][j-2]==0:
                if Up or Right:
                    n=len(options)
                    options['option'+str(n+1)]=options['option'+str(o)][:-1]
                    options['option'+str(n+1)].append( (i, j-2) )
                    O.append(n+1)
                    if len(options['option'+str(n+1)])==1:
                        options['option'+str(n+1)]=[['start:',(i, j)]]+options['option'+str(n+1)]
                elif 'option'+str(o) not in options:
                    options['option'+str(o)]=[(i, j-2)]
                    O.append(o)
                    if len(options['option'+str(o)])==1:
                        options['option'+str(o)]=[['start:',(i, j)]]+options['option'+str(o)]
                else:
                    options['option'+str(o)].append( (i, j-2) )
                    O.append(O)
                    if len(options['option'+str(o)])==1:
                        options['option'+str(o)]=[['start:',(i, j)]]+options['option'+str(o)]
                M[i][j]=5
                next.append( (i, j-2) )
            if len(next)!=0:
                for i in range(len(next)):
                    A=deepcopy(M)
                    track(A, next[i][0], next[i][1], opp, O[i])
        track(A,i,j,opp,o)
        Max=0
        longest={}
        for op in options:
            if len(options[op])>Max:
                Max=len(options[op])
                longest={op:options[op]}
            elif len(options[op])==Max:
                longest[op]=options[op]
        return (longest, Max)
    Moves=[]
    Max=0
    for p in P:
        Filter= (p[0]>1 and Board[p[0]-1][p[1]] in opp) or \
                (p[1]<6 and Board[p[0]][p[1]+1] in opp) or \
                (p[1]>1 and Board[p[0]][p[1]-1] in opp) 
        if Filter:
                (Op, Len)=kill_cells(Board, p[0], p[1], opp)
                if Len>Max:
                    Max=Len
                    Moves=[Op]
                elif Len==Max:
                    Moves.append(Op)
    Options={}
    n=1
    for d in Moves:
        for op in d:
            Options['option'+str(n)]=d[op]
            n+=1
    return (Options, Max)
####################################################################
def choose_piece(player,piece_type,P,D,to=False):
    while True:
        n=[str(i) for i in range(0,8)]
        if to:
            choose = input(player + ', choose a cell to move to: ')
        else:
            choose=input(player+', choose your '+piece_type+' to move: ')
        if len(choose)==2:
            print('Please leave a space between the vertical and horizontal indices.')
        elif len(choose)!=3:
            print('Bad input!')
        elif choose[1]!=' ':
            print('Please leave a space between the vertical and horizontal indices.')
        elif choose[0] not in n and choose[2] not in n:
            print('The vertical and horizontal indexes are out of range!')
        elif choose[0] not in n:
            print('The vertical index is out of range!')
        elif choose[2] not in n:
            print('The horizontal index is out of range!')
        elif not to and (int(choose[0]),int(choose[2])) not in P and (int(choose[0]),int(choose[2])) not in D:
            print("You don't have a piece at this location!")
        elif not to and piece_type=='dama' and (int(choose[0]),int(choose[2])) not in D:
            print('Please choose a dama, not a normal pawn.')
        else:
            break
    choose=(int(choose[0]),int(choose[2]))
    return choose
####################################################################
def mandatory_moves(Board,P,D,Popp,Dopp,me,opp,player):
    ####################################
    def kills_normal(Board, path, opp):
        path[0] = path[0][1]
        dead_Dama = []
        dead_Pawn = []
        for c in range(len(path) - 1):
            i1 = path[c][0];
            i2 = path[c + 1][0]
            j1 = path[c][1];
            j2 = path[c + 1][1]
            if i1 == i2:
                if Board[i1][(j1 + j2) // 2] == opp[0]:
                    dead_Pawn.append((i1, (j1 + j2) // 2))
                else:
                    dead_Dama.append((i1, (j1 + j2) // 2))
                Board[i1][(j1 + j2) // 2] = 0
            else:
                if Board[(i1 + i2) // 2][j1] == opp[0]:
                    dead_Pawn.append(((i1 + i2) // 2, j1))
                else:
                    dead_Dama.append(((i1 + i2) // 2, j1))
                Board[(i1 + i2) // 2][j1] = 0
        return (Board, dead_Dama, dead_Pawn)
    def kills_dama(Board,path,opp):
        dead_Dama=[]
        dead_Pawn=[]
        for c in range(len(path)-1):
            i1=path[c][0] ; i2=path[c+1][0]
            j1=path[c][1] ; j2=path[c+1][1]
            if i1==i2:
                if j1>j2:
                    for h in range(j1-1,j2,-1):
                        if Board[i1][h] in opp:
                            if Board[i1][h]==opp[0]:
                                dead_Pawn.append((i1,h))
                            else:
                                dead_Dama.append((i1,h))
                            Board[i1][h]=0
                            break
                else:
                    for h in range(j1+1,j2):
                        if Board[i1][h] in opp:
                            if Board[i1][h]==opp[0]:
                                dead_Pawn.append((i1,h))
                            else:
                                dead_Dama.append((i1,h))
                            Board[i1][h]=0
                            break
            else:
                if i1>i2:
                    for v in range(i1-1,i2,-1):
                        if Board[v][j1] in opp:
                            if Board[v][j1]==opp[0]:
                                dead_Pawn.append((v,j1))
                            else:
                                dead_Dama.append((v,j1))
                            Board[v][j1]=0
                            break
                        
                else:
                    for v in range(i1+1,i2):
                        if Board[v][j1] in opp:
                            if Board[v][j1]==opp[0]:
                                dead_Pawn.append((v,j1))
                            else:
                                dead_Dama.append((v,j1))
                            Board[v][j1]=0
                            break
        return (Board,dead_Dama,dead_Pawn)
    #############################
    (MovesD,MaxD)=mandatory_dama(Board, D, opp)
    (MovesN,MaxN)=mantadory_normal(Board, P, opp)
    MaxN-=1
    if MaxD!=0 or MaxN!=-1:
        print('You have mandatory moves.')
        ## If dama moves are longer.
        if MaxD>MaxN:
            if len(MovesD)==1:
                for dama in MovesD:
                    if len(MovesD[dama])==1:
                        print('The dama at location',dama,'has to move in:')
                        print(MovesD[dama]['option1'])
                        i=MovesD[dama]['option1'][-1][0]
                        j=MovesD[dama]['option1'][-1][1]
                        Board[dama[0]][dama[1]]=0
                        D.remove((dama[0],dama[1]))
                        D.append((i,j))
                        Board[i][j]=me[1]
                        path=[dama]+MovesD[dama]['option1']
                        (Board,dead_Dama,dead_Pawn)=kills_dama(Board, path, opp)
                        cont=input('Please type "ok" to continue: ')
                        while cont not in ('ok','Ok','OK'):
                            cont=input('Please type "ok" to continue: ')
                    else:
                        print('The dama at location',dama,'can move in :')
                        for op in MovesD[dama]:
                            print(op+':',MovesD[dama][op])
                        Op=input('Please choose an option: ')
                        while Op not in MovesD[dama]:
                            print('Invalid option!')
                            Op=input('Please choose a valid option: ')
                        i=MovesD[dama][Op][-1][0]
                        j=MovesD[dama][Op][-1][1]
                        Board[dama[0]][dama[1]]=0
                        D.remove((dama[0],dama[1]))
                        D.append((i,j))
                        Board[i][j]=me[1]
                        path=[dama]+MovesD[dama][Op]
                        (Board,dead_Dama,dead_Pawn)=kills_dama(Board, path, opp)
            else:
                for dama in MovesD:
                    print('The dama at location',dama,'can move in :')
                    for op in MovesD[dama]:
                        print(op+':',MovesD[dama][op])
                ch_dama=choose_piece(player, 'dama')
                while ch_dama not in MovesD:
                    print('You have no dama at this location!')
                    ch_dama=choose_piece(player, 'dama')
                Op=input('Please choose an option: ')
                while Op not in MovesD[ch_dama]:
                    print('Invalid option!')
                    Op=input('Please choose a valid option: ')
                i=MovesD[ch_dama][Op][-1][0]
                j=MovesD[ch_dama][Op][-1][1]
                Board[ch_dama[0]][ch_dama[1]]=0
                D.remove((ch_dama[0],ch_dama[1]))
                D.append((i,j))
                Board[i][j]=me[1]
                path=[dama]+MovesD[ch_dama][Op]
                (Board,dead_Dama,dead_Pawn)=kills_dama(Board, path, opp)
        ## if normal pawns moves are longer. 
        elif MaxN>MaxD:
            if len(MovesN)==1:
                loc=MovesN['option1'][0][1]
                print('The pawn at location',loc,'has to move in:')
                print(MovesN['option1'])
                Board[loc[0]][loc[1]]=0
                i=MovesN['option1'][-1][0]
                j=MovesN['option1'][-1][1]
                P.remove(loc)
                if i==0:
                    Board[i][j]=me[1] ; D.append((i,j))
                else:
                    Board[i][j]=me[0] ; P.append((i,j))
                path=MovesN['option1']
                (Board,dead_Dama,dead_Pawn)=kills_normal(Board, path, opp)
                cont=input('Please type "ok" to continue: ')
                while cont not in ('ok','Ok','OK'):
                    cont=input('Please type "ok" to continue: ')
            else:
                for op in MovesN:
                    print(op+':',MovesN[op])
                Op=input('Please choose an option: ')
                while Op not in MovesN:
                    print('Invalid option!')
                    Op=input('Please choose a valid option: ')
                pawn=MovesN[Op][0][1]
                Board[pawn[0]][pawn[1]]=0
                P.remove((pawn[0],pawn[1]))
                i=MovesN[Op][-1][0]
                j=MovesN[Op][-1][1]
                if i==0:
                    Board[i][j]=me[1] ; D.append((i,j))
                else:
                    Board[i][j]=me[0] ; P.append((i,j))
                path=MovesN[Op]
                (Board,dead_Dama,dead_Pawn)=kills_normal(Board, path, opp)
        ## If pawns and damas moves are similar.
        else:
            for dama in MovesD:
                print('The dama at location',dama,'can move in :')
                for op in MovesD[dama]:
                    print(op+':',MovesD[dama][op])
            print('In addition, you got options for normal pawns:')
            for i in MovesN:
                print(i+':',MovesN[i])
            Type=input('Do you want to move a dama or a pawn: ')
            while Type not in ('dama','dama','pawn','Pawn'):
                print('Invalid input!')
                Type=input('Do you want to move a dama or a pawn: ')
            if Type in ('dama','dama'):
                if len(MovesD)==1:
                    for dama in MovesD:
                        if len(MovesD[dama])==1:
                            i=MovesD[dama]['option1'][-1][0]
                            j=MovesD[dama]['option1'][-1][1]
                            Board[dama[0]][dama[1]]=0
                            D.remove((dama[0],dama[1]))
                            D.append((i,j))
                            Board[i][j]=me[1]
                            path=[dama]+MovesD[dama]['option1']
                            (Board,dead_Dama,dead_Pawn)=kills_dama(Board, path, opp)
                            cont=input('Please type "ok" to continue: ')
                            while cont not in ('ok','Ok','OK'):
                                cont=input('Please type "ok" to continue: ')
                        else:
                            Op=input('Please choose an option: ')
                            while Op not in MovesD[dama]:
                                print('Invalid option!')
                                Op=input('Please choose a valid option: ')
                            i=MovesD[dama][Op][-1][0]
                            j=MovesD[dama][Op][-1][1]
                            Board[dama[0]][dama[1]]=0
                            D.remove((dama[0], dama[1]))
                            D.append((i, j))
                            Board[i][j]=me[1]
                            path=[dama]+MovesD[dama][Op]
                            (Board,dead_Dama,dead_Pawn)=kills_dama(Board, path, opp)
                else:
                    ch_dama=choose_piece(player, 'dama')
                    while ch_dama not in MovesD:
                        print('You have no dama at this location!')
                        ch_dama=choose_piece(player, 'dama')
                    Op=input('Please choose an option: ')
                    while Op not in MovesD[ch_dama]:
                        print('Invalid option!')
                        Op=input('Please choose a valid option: ')
                    i=MovesD[ch_dama][Op][-1][0]
                    j=MovesD[ch_dama][Op][-1][1]
                    Board[ch_dama[0]][ch_dama[1]]=0
                    D.remove((ch_dama[0], ch_dama[1]))
                    D.append((i, j))
                    Board[i][j]=me[1]
                    path=[dama]+MovesD[ch_dama][Op]
                    (Board,dead_Dama,dead_Pawn)=kills_dama(Board, path, opp)
            else:
                if len(MovesN)==1:
                    loc=MovesN['option1'][0][1]
                    Board[loc[0]][loc[i]]=0
                    i=MovesN['option1'][-1][0]
                    j=MovesN['option1'][-1][1]
                    P.remove(loc)
                    if i==0:
                        Board[i][j]=me[1] ; D.append((i,j))
                    else:
                        Board[i][j] = me[0] ; P.append((i,j))
                    path=MovesN['option1']
                    (Board,dead_Dama,dead_Pawn)=kills_normal(Board, path, opp)
                    cont=input('Please type "ok" to continue: ')
                    while cont not in ('ok','Ok','OK'):
                        cont=input('Please type "ok" to continue: ')
                else:
                    Op=input('Please choose an option: ')
                    while Op not in MovesN:
                        print('Invalid option!')
                        Op=input('Please choose a valid option: ')
                    pawn=MovesN[Op][0][1]
                    Board[pawn[0]][pawn[1]]=0
                    i=MovesN[Op][-1][0]
                    j=MovesN[Op][-1][1]
                    P.remove((pawn[0],pawn[1]))
                    if i==0:
                        Board[i][j]=me[1] ; D.append((i,j))
                    else:
                        Board[i][j]=me[0] ; P.append((i,j))
                    path=MovesN[Op]
                    (Board,dead_Dama,dead_Pawn)=kills_normal(Board, path, opp)
        # Remove dead pawns and damas from their lists.
        for dama in dead_Dama:
            Dopp.remove(dama)
        for pawn in dead_Pawn:
            Popp.remove(pawn)
        n=len(dead_Dama) ; m=len(dead_Pawn)
        if n==1 and m==0:
            print('You removed one piece from your opponent and it was a Dama. Great!')
        elif n>1 and m==0:
            print('You removed',n,'pieces from your opponent and all were Dama. Fantastic!')
        elif n==0 and m==1:
            print('You removed one pawn from your opponent. Nice!')
        elif n==0 and m>1:
            print('You removed',m,'pawns from your opponent. Very well!')
        else:
            print('You removed',n+m,'pieces from your opponent, including',n,'Dama. Superb!')
    return Board            
####################################################################
def show_Board(board):
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.patches import PathPatch
    from matplotlib.path import Path
    def convert_value(value):
        if value == "d1": return 1
        elif value == "d2":return 2
        else: return int(value)
    np_board = np.array(board)
    np_board_numeric = np.vectorize(convert_value)(np_board)
    fig, ax = plt.subplots()
    cmap = plt.cm.colors.ListedColormap(['white', 'blue', 'red'])
    norm = plt.cm.colors.BoundaryNorm([0, 1, 2, 3], cmap.N)
    ax.imshow(np_board_numeric, cmap=cmap, interpolation='nearest', norm=norm)
    for i in range(len(np_board)):
        for j in range(len(np_board[i])):
            if np_board_numeric[i][j] != 0:  # Only display the number if it's not equal to 0
                ax.text(j, i, np_board_numeric[i][j], ha='center', va='center', color='black')
            rect = plt.Rectangle((j - 0.5, i - 0.5), 1, 1, linewidth=1, edgecolor='black', facecolor='none')
            ax.add_patch(rect)
            if board[i][j] == "d1" or board[i][j] == "d2":
                diamond_path = Path([
                    [j - 0.5, i], [j, i - 0.5], [j + 0.5, i], [j, i + 0.5], [j - 0.5, i]])
                diamond_patch = PathPatch(diamond_path, edgecolor='gold', facecolor='none')
                ax.add_patch(diamond_patch)
                ax.text(j, i, np_board_numeric[i][j], ha='center', va='center', color='gold')
    ax.set_xticks([])
    ax.set_yticks([])
    for i, row_label in enumerate(np.arange(len(np_board))):
        ax.text(-0.6, i, f"{row_label}", va='center', ha='right', fontsize=10)
    for j, col_label in enumerate(np.arange(len(np_board[0]))):
        ax.text(j, -0.6, f"{col_label}", va='bottom', ha='center', fontsize=10)
    plt.show()
####################################################################
def play(board,P,D,Popp,Dopp,me,opp,player):
    print('\n')
    print('This is you turn',player+'.')
    back=False
    show_Board(board)
    from copy import deepcopy
    Compare=deepcopy(board)
    board=mandatory_moves(board,P,D,Popp,Dopp,me,opp,player)
    if Compare==board:
        playing=True
        while playing:
            if back: show_Board(board)
            choose=choose_piece(player, 'piece',P,D)
            i= choose[0] ; j= choose[1]
            if choose in P:
                moves = open_cells(board, i, j)
                if len(moves)==0:
                    print("You can't move this pawn!")
                elif len(moves)==1:
                    for Dir in moves:
                        if Dir == 'Up':
                            print('This pawn can only move "Up".')
                            con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            while con not in ('ok', 'Ok', 'OK', 'no', 'No', 'NO'):
                                con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            if con in ('ok', 'Ok', 'OK'):
                                board[i][j] = 0 ; P.remove((i,j))
                                v=moves['Up'][0] ; h=moves['Up'][1]
                                if v==0:
                                    board[v][h] = me[1] ; D.append((v, h))
                                else:
                                    board[v][h] = me[0] ; P.append((v,h))
                                playing=False
                            else: back=True
                        elif Dir in ('Right', 'Left'):
                            print('This pawn can only move to the "' + Dir + '".')
                            con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            while con not in ('ok', 'Ok', 'OK', 'no', 'No', 'NO'):
                                con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            if con in ('ok', 'Ok', 'OK'):
                                board[i][j] = 0 ; P.remove((i,j))
                                v=moves[Dir][0] ; h=moves[Dir][1]
                                board[v][h] = me[0] ; P.append((v,h))
                                playing=False
                            else: back=True
                else:
                    Dirs = []
                    for Dir in moves:
                        Dirs.append(Dir)
                    s = 'This pawn can move '
                    for y in range(len(Dirs) - 1):
                        if Dirs[y] == 'Up':
                            s += '"Up", '
                        else:
                            s += 'to the "' + Dirs[y] + '", '
                    if Dirs[-1] == 'Up':
                        s += 'and "Up".'
                    else:
                        s += 'and to the "' + Dirs[-1] + '".'
                    print(s)
                    con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                    while con not in ('ok', 'Ok', 'OK', 'no', 'No', 'NO'):
                        con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                    if con in ('ok', 'Ok', 'OK'):
                        Dir = input('Please choose a direction: ')
                        if Dir in ('right', 'left', 'up'):
                            Dir = Dir.capitalize()
                        while Dir not in moves:
                            print('Bad input!')
                            Dir = input('Please choose a direction: ')
                            if Dir in ('right', 'left', 'up'):
                                Dir = Dir.capitalize()
                        print((i,j))
                        print(P)
                        print((i,j) in P)
                        board[i][j] = 0 ; P.remove((i,j))
                        v=moves[Dir][0] ; h = moves[Dir][1]
                        if Dir=='Up' and v==0:
                            board[v][h] = me[1] ; D.append((v, h))
                        else:
                            board[v][h] = me[0] ; P.append((v, h))
                        playing=False
                    else: back=True
            elif choose in D:
                moves=dama_moves(board, i ,j)
                if len(moves)==0:
                    print("You can't move this dama.")
                elif len(moves)==1:
                    for Dir in moves:
                        if len(moves[Dir])==1:
                            if Dir in ('Up','Down'):
                                print('This dama can only move one step "'+Dir+'".')
                            else:
                                print('This dama can only move one step to the "'+Dir+'".')
                            con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            while con not in ('ok', 'Ok', 'OK', 'no', 'No', 'NO'):
                                con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            if con in ('ok', 'Ok', 'OK'):
                                board[i][j] = 0 ; D.remove((i,j))
                                v=moves[Dir][0][0] ; h=moves[Dir][0][1]
                                board[v][h] = me[1] ; D.append((v,h))
                                playing=False
                            else: back=True
                        else:
                            if Dir in ('Up','Down'):
                                print('This dama can only move "'+Dir+'" to cells:',moves[Dir])
                            else:
                                print('This dama can only move to the "'+Dir+'" to cells:',moves[Dir])
                            con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            while con not in ('ok', 'Ok', 'OK', 'no', 'No', 'NO'):
                                con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                            if con in ('ok', 'Ok', 'OK'):
                                to=choose_piece(player,'','','',to=True)
                                while to not in moves[Dir]:
                                    print("You can't move here!")
                                    to = choose_piece(player, '', '', '', to=True)
                                board[i][j] = 0 ; D.remove((i,j))
                                board[to[0]][to[1]] = me[1] ; D.append((to[0],to[1]))
                                playing=False
                            else: back=True
                else:
                    print('This dama can move:')
                    for Dir in moves:
                        print(Dir+':',moves[Dir])
                    con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                    while con not in ('ok', 'Ok', 'OK', 'no', 'No', 'NO'):
                        con = input('Please type "ok" if you want to move it. Otherwise, type "no": ')
                    if con in ('ok', 'Ok', 'OK'):
                        Dir = input('Please choose a direction: ')
                        if Dir in ('right', 'left', 'up', 'down'):
                            Dir = Dir.capitalize()
                        while Dir not in moves:
                            print('Bad input!')
                            Dir = input('Please choose a direction: ')
                            if Dir in ('right', 'left', 'up', 'down'):
                                Dir = Dir.capitalize()
                        to = choose_piece(player, '', '', '', to=True)
                        while to not in moves[Dir]:
                            print("You can't move here!")
                            to = choose_piece(player, '', '', '', to=True)
                        board[i][j] = 0 ; D.remove((i,j))
                        board[to[0]][to[1]] = me[1] ; D.append((to[0],to[1]))
                        playing=False
                    else: back=True
    return board
####################################################################

print('Welcome to the game of "Dama". Hope you enjoy it :)')
print('Each turn you choose a pawn to move by typing its index in the board. The board is numbered '\
      +'from 0 to 7 vertically')
print('starting from the top, and horizontally starting from the left.')
print('For example, you can type "3 1" to select the pawn in line 4 and column 2.')
print('Just as the example, please leave a space between the vertical and horizontal indexes.')
print('Remember that killing opponent pieces is mandatory. The game will detect mandatory moves '\
      +'automatically, so you don\'t have to worry about them.')
P1_name=input('Player 1, please enter your name: ')
P2_name=input('Player 2, please enter your name: ')
Board=[[0]*8]+[[2]*8 for i in range(2)]+[[0]*8 for i in range(2)]+[[1]*8 for i in range(2)]+[[0]*8]
P1=[(5,i) for i in range(8)]+[(6,i) for i in range(8)]
P2=[(1,i) for i in range(8)]+[(2,i) for i in range(8)]
D1=[] ; D2=[]
l1=len(P1)+len(D1) ; l2=len(P2)+len(D2)
while l1!=0 and l2!=0:
    ### player 1 turn:
    Board=play(Board,P1,D1,P2,D2,[1,'d1'],[2,'d2'],P1_name)
    ### Flip the board and pieces:
    Board=flip_Board(Board)
    P1 = flip_pieces(P1) ; P2 = flip_pieces(P2) ; D1 = flip_pieces(D1) ; D2 = flip_pieces(D2)
    l2 = len(P2) + len(D2)
    if l2==0: break
    ### player 2 turn:
    Board=play(Board,P2,D2,P1,D1,[2,'d2'],[1,'d1'],P2_name)
    ### Flip the board and pieces:
    Board = flip_Board(Board)
    P1 = flip_pieces(P1) ; P2 = flip_pieces(P2) ; D1 = flip_pieces(D1) ; D2 = flip_pieces(D2)
    l1 = len(P1) + len(D1)

print('\n')
if l1==0:
    print(P2_name,'is the winner. You still got',l2,'pieces. Congrats :)')
else:
    print(P1_name,'is the winner. You still got',l1,'pieces. Congrats :)')