Nothing coming soon.

ChessTAI (WIP)

(photo of the chess board)

ChessTAI is a chess board and a chess bot. The bot is still work in progress. Made in python.

from os import system
from copy import deepcopy
from random import randint

# move_num = monesko vuoro menossa (alkaa 1 ja kasvaa 1 joka valkoisen pelaajan vuoro)
# move_side = kumman nappuloiden vuoro (0 = white, 1 = black)
# player_side = kummalla puolella ihminen on (0 = white, 1 = black)
# player_pieces = mitkä nappulat ihmisellä
# white_castle_moved = (0 = ei, 1 = kyllä) [onko valkoinen kuningas, vasen torni, oikea torni liikkunut vielä]
# black_castle_moved = (0 = ei, 1 = kyllä) [onko musta kuningas, vasen torni, oikea torni liikkunut vielä]
# last_double_pawn_move = [x, y, pelaaja (0 = white, 1 = black)]
# last_advancing_move = milloin viimeksi syöty nappula tai liikutettu sotilasta [move_num, side]
# computer_last_move = (x, y) mihin tietokone vastustaja viimeksi liikutti
# all_positions = lista kaikista laudan aiemmista vaiheista
stats = {
    "move_num": 0,
    "move_side": 0,
    "player_side": 0,
    "player_pieces": "PNBRQK",
    "opponent_pieces": "pnbrqk",
    "white_castle_moved": [0, 0, 0],
    "black_castle_moved": [0, 0, 0],
    "last_double_pawn_move": [],
    "last_advancing_move": (0, 0),
    "computer_last_move": (-1, -1),
    "all_positions": {}
}

board = [
    ["r", "n", "b", "q", "k", "b", "n", "r"],
    ["p", "p", "p", "p", "p", "p", "p", "p"],
    [" ", " ", " ", " ", " ", " ", " ", " "],
    [" ", " ", " ", " ", " ", " ", " ", " "],
    [" ", " ", " ", " ", " ", " ", " ", " "],
    [" ", " ", " ", " ", " ", " ", " ", " "],
    ["P", "P", "P", "P", "P", "P", "P", "P"],
    ["R", "N", "B", "Q", "K", "B", "N", "R"]
]

evaluation = {
    "game_evaluation": 0,
    "piece_value": {
        "P": 100,
        "N": 300,
        "B": 300,
        "R": 500,
        "Q": 900,
        "p": -100,
        "n": -300,
        "b": -300,
        "r": -500,
        "q": -900,
    },  # nappuloiden arvot
    "pawn_placement": [[2, 2, 2, 0, 0, 2, 2, 2],
                       [5, 5, 8, 10, 10, 5, 5, 5],
                       [8, 8, 14, 18, 18, 10, 8, 8],
                       [14, 15, 18, 25, 25, 16, 15, 14],
                       [18, 18, 25, 35, 35, 25, 18, 18],
                       [45, 45, 45, 45, 45, 45, 45, 45],
                       [65, 65, 65, 65, 65, 65, 65, 65]],  # from first position to second last row
    "multiple_pawn_in_column_per_over_pawn": -30,  # kaksi tai enemmän pawnia samalla pystysuoralla kaksi -> -x, kolme -> -2x, neljä -> -3x
    "isolated_pawn": -10,  # pawnin viereisillä pystyriveillä ei ole pawnia
    "pawn_wall": 10,  # pawni suojelee toista pawnia
    "knight_value_per_move": 8,  # arvo jokaisesta knightin näkemästä ruudusta
    "bishop_value_per_move": 7,  # arvo jokaisesta bishopin näkemästä ruudusta
    "rook_value_per_move": 7,  # arvo jokaisesta rookin näkemästä ruudusta
    "queen_value_per_move": 3,  # arvo jokaisesta rookin näkemästä ruudusta
    "castling_value": [10, -10, -25, -30],  # arvo kun [voi castlata, ei voi vasemmalle, ei voi oikealle, ei voi ollenkaan]
    "move_turn": 20,  # paljonko saa lisäarvoa siitä, että liikuttaa seuraavaksi
}
"""connected rooks, protected pieces and pawns, """

# tekstin värit
colors = {
    "basic": '\033[m',
    "white": '\033[37m',
    "red": '\033[31m',
    "green": '\033[32m',
    "yellow": '\033[33m',
    "blue": '\033[34m',
    "magenta": '\033[35m',
    "cyan": '\033[36m',
    "grey": '\033[90m',
    "black": '\033[30m'
}

# tekstin taustavärit
bg_colors = {
    "white": '\033[107m',
    "red": '\033[41m',
    "green": '\033[42m',
    "yellow": '\033[43m',
    "blue": '\033[44m',
    "magenta": '\033[45m',
    "cyan": '\033[46m',
    "grey": '\033[100m',
    "black": '\033[40m'
}

logo = f"""
{colors['yellow']}        __                  {colors['blue']}_________    ____
{colors['yellow']}  _____/ /_  ___  _________{colors['blue']}/_  __/   |  /  _/
{colors['yellow']} / ___/ __ \/ _ \/ ___/ ___/{colors['blue']}/ / / /| |  / /  
{colors['yellow']}/ /__/ / / /  __(__  |__  ){colors['blue']}/ / / ___ |_/ /   
{colors['yellow']}\___/_/ /_/\___/____/____/{colors['blue']}/_/ /_/  |_/___/                                              
{colors['basic']}
Version 1"""

start_menu = """
1) player vs AI
2) player vs player
3) train AI
4) quit

Choose: """

x_convert = {"A": 0, "B": 1, "C": 2, "D": 3, "E": 4, "F": 5, "G": 6, "H": 7}
y_convert = {"1": 7, "2": 6, "3": 5, "4": 4, "5": 3, "6": 2, "7": 1, "8": 0}


def clear_console():
    system('cls')


# index alkaa 0
# 0: kumman vuoro (0 valkoinen, 1 musta)
# 1: player_side
# 2-65: pelilauta vasen->oikea ylös->alas
# 65-67: white_castle_moved
# 68-70: black_castle_moved
# 71-73: last_double_pawn_move
# 74-(kunnes piste): move_num
# (pisteen jälkeen)-(kunnes piste): last_advancing_move[0]
# (pisteen jälkeen): last_advancing_move[1]
# last_advancing_move[1] jälkeen: kuinka monta eri positionia on all_positions
# len(all_positions) jälkeen: repeat(position ja jälkeen määrä)
def save():
    save_text = str(stats["move_side"])
    for line in board:
        for cell in line:
            if cell != " ":
                save_text += cell
            else:
                save_text += "#"
    for cell in stats["white_castle_moved"]:
        save_text += str(cell)
    for cell in stats["black_castle_moved"]:
        save_text += str(cell)
    for cell in stats["last_double_pawn_move"]:
        save_text += str(cell)
    save_text += str(stats["move_num"])
    save_text += "."
    save_text += str(stats["last_advancing_move"][0])
    save_text += "."
    save_text += str(stats["last_advancing_move"][1])
    save_text += str(len(stats["all_positions"]))
    for item, value in stats["all_positions"]:
        for row in item:
            for cell in row:
                if cell == " ":
                    save_text += "#"
                else:
                    save_text += cell
        save_text += str(value)

    return save_text


def load(save_text):
    pass


# x = 0-7, y = 0-7
def pawn_moves(x, y, own_pieces, opponent_pieces, i_board, i_stats, computer):
    possible_moves = []

    if i_board[y][x] == "p":
        if i_board[y + 1][x] == " ":
            possible_moves.append(((x, y), (x, y + 1)))
            if y == 1 and i_board[y + 2][x] == " ":
                possible_moves.append(((x, y), (x, y + 2)))

        if x < 7 and i_board[y + 1][x + 1] in opponent_pieces:
            possible_moves.append(((x, y), (x + 1, y + 1)))

        if x > 0 and i_board[y + 1][x - 1] in opponent_pieces:
            possible_moves.append(((x, y), (x - 1, y + 1)))

        if len(i_stats["last_double_pawn_move"]) == 3:
            if x + 1 == i_stats["last_double_pawn_move"][0] and y == i_stats["last_double_pawn_move"][1]:
                possible_moves.append(((x, y), (x + 1, y + 1)))
            elif x - 1 == i_stats["last_double_pawn_move"][0] and y == i_stats["last_double_pawn_move"][1]:
                possible_moves.append(((x, y), (x - 1, y + 1)))

        if y == 6 and computer:
            promote_moves = []
            for i in range(len(possible_moves)):
                for j in range(1, 5):
                    promote_moves.append(((possible_moves[i][0][0], possible_moves[i][0][1]), (possible_moves[i][1][0], possible_moves[i][1][1], own_pieces[j])))
            possible_moves = promote_moves

    else:
        if i_board[y - 1][x] == " ":
            possible_moves.append(((x, y), (x, y - 1)))
            if y == 6 and i_board[y - 2][x] == " ":
                possible_moves.append(((x, y), (x, y - 2)))

        if x < 7 and i_board[y - 1][x + 1] in opponent_pieces:
            possible_moves.append(((x, y), (x + 1, y - 1)))

        if x > 0 and i_board[y - 1][x - 1] in opponent_pieces:
            possible_moves.append(((x, y), (x - 1, y - 1)))

        if len(i_stats["last_double_pawn_move"]) == 3:
            if x + 1 == i_stats["last_double_pawn_move"][0] and y == i_stats["last_double_pawn_move"][1]:
                possible_moves.append(((x, y), (x + 1, y - 1)))
            elif x - 1 == i_stats["last_double_pawn_move"][0] and y == i_stats["last_double_pawn_move"][1]:
                possible_moves.append(((x, y), (x - 1, y - 1)))

        if y == 1 and computer:
            promote_moves = []
            for i in range(len(possible_moves)):
                for j in range(1, 5):
                    promote_moves.append(((possible_moves[i][0][0], possible_moves[i][0][1]), (possible_moves[i][1][0], possible_moves[i][1][1], own_pieces[j])))
            possible_moves = promote_moves

    return possible_moves


# x = 0-7, y = 0-7
def knight_moves(x, y, own_pieces, i_board):
    possible_moves = []

    # check 2up left
    i_x = x - 1
    i_y = y - 2
    if (i_x > -1) and (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check up 2left
    i_x = x - 2
    i_y = y - 1
    if (i_x > -1) and (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check 2up right
    i_x = x + 1
    i_y = y - 2
    if (i_x < 8) and (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check up 2right
    i_x = x + 2
    i_y = y - 1
    if (i_x < 8) and (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check down 2left
    i_x = x - 2
    i_y = y + 1
    if (i_x > -1) and (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check 2down left
    i_x = x - 1
    i_y = y + 2
    if (i_x > -1) and (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check 2down right
    i_x = x + 1
    i_y = y + 2
    if (i_x < 8) and (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check down 2right
    i_x = x + 2
    i_y = y + 1
    if (i_x < 8) and (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    return possible_moves


# x = 0-7, y = 0-7
def rook_moves(x, y, opponent_pieces, i_board):
    possible_moves = []

    # moves to right
    i = x + 1
    while i < 8:
        if i_board[y][i] == " ":
            possible_moves.append(((x, y), (i, y)))
            i += 1
            continue
        elif i_board[y][i] in opponent_pieces:
            possible_moves.append(((x, y), (i, y)))
        break

    # moves to left
    i = x - 1
    while i > -1:
        if i_board[y][i] == " ":
            possible_moves.append(((x, y), (i, y)))
            i -= 1
            continue
        elif i_board[y][i] in opponent_pieces:
            possible_moves.append(((x, y), (i, y)))
        break

    # moves to up
    i = y - 1
    while i > -1:
        if i_board[i][x] == " ":
            possible_moves.append(((x, y), (x, i)))
            i -= 1
            continue
        elif i_board[i][x] in opponent_pieces:
            possible_moves.append(((x, y), (x, i)))
        break

    # moves to down
    i = y + 1
    while i < 8:
        if i_board[i][x] == " ":
            possible_moves.append(((x, y), (x, i)))
            i += 1
            continue
        elif i_board[i][x] in opponent_pieces:
            possible_moves.append(((x, y), (x, i)))
        break

    return possible_moves


# x = 0-7, y = 0-7
def bishop_moves(x, y, opponent_pieces, i_board):
    possible_moves = []

    # moves up right
    i_x = x + 1
    i_y = y - 1
    while i_x < 8 and i_y > -1:
        if i_board[i_y][i_x] == " ":
            possible_moves.append(((x, y), (i_x, i_y)))
            i_x += 1
            i_y -= 1
            continue
        elif i_board[i_y][i_x] in opponent_pieces:
            possible_moves.append(((x, y), (i_x, i_y)))
        break

    # moves up left
    i_x = x - 1
    i_y = y - 1
    while i_x > -1 and i_y > -1:
        if i_board[i_y][i_x] == " ":
            possible_moves.append(((x, y), (i_x, i_y)))
            i_x -= 1
            i_y -= 1
            continue
        elif i_board[i_y][i_x] in opponent_pieces:
            possible_moves.append(((x, y), (i_x, i_y)))
        break

    # moves down left
    i_x = x - 1
    i_y = y + 1
    while i_x > -1 and i_y < 8:
        if i_board[i_y][i_x] == " ":
            possible_moves.append(((x, y), (i_x, i_y)))
            i_x -= 1
            i_y += 1
            continue
        elif i_board[i_y][i_x] in opponent_pieces:
            possible_moves.append(((x, y), (i_x, i_y)))
        break

    # moves down right
    i_x = x + 1
    i_y = y + 1
    while i_x < 8 and i_y < 8:
        if i_board[i_y][i_x] == " ":
            possible_moves.append(((x, y), (i_x, i_y)))
            i_x += 1
            i_y += 1
            continue
        elif i_board[i_y][i_x] in opponent_pieces:
            possible_moves.append(((x, y), (i_x, i_y)))
        break

    return possible_moves


# x = 0-7, y = 0-7
def king_moves(x, y, own_pieces, opponent_pieces, check_castle, i_board, i_stats):
    possible_moves = []

    # check up left
    i_x = x - 1
    i_y = y - 1
    if (i_x > -1) and (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check up
    i_x = x
    i_y = y - 1
    if (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check up right
    i_x = x + 1
    i_y = y - 1
    if (i_x < 8) and (i_y > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check left
    i_x = x - 1
    i_y = y
    if (i_x > -1) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check right
    i_x = x + 1
    i_y = y
    if (i_x < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check down left
    i_x = x - 1
    i_y = y + 1
    if (i_x > -1) and (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check down
    i_x = x
    i_y = y + 1
    if (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    # check down right
    i_x = x + 1
    i_y = y + 1
    if (i_x < 8) and (i_y < 8) and (i_board[i_y][i_x] not in own_pieces):
        possible_moves.append(((x, y), (i_x, i_y)))

    if not check_castle:
        return possible_moves

    if i_board[y][x] == "k" and i_stats["black_castle_moved"][0] == 0:
        if i_stats["black_castle_moved"][1] == 0 and i_board[0][1] == " " and i_board[0][2] == " " and i_board[0][3] == " " and check_opponent_moves(own_pieces, opponent_pieces, i_board, i_stats, [(2, 0), (3, 0)]):
            possible_moves.append(((x, y), (2, 0)))
        if i_stats["black_castle_moved"][2] == 0 and i_board[0][5] == " " and i_board[0][6] == " " and check_opponent_moves(own_pieces, opponent_pieces, i_board, i_stats, [(5, 0), (6, 0)]):
            possible_moves.append(((x, y), (6, 0)))

    if i_board[y][x] == "K" and i_stats["white_castle_moved"][0] == 0:
        if i_stats["white_castle_moved"][1] == 0 and i_board[7][1] == " " and i_board[7][2] == " " and i_board[7][3] == " " and check_opponent_moves(own_pieces, opponent_pieces, i_board, i_stats, [(2, 7), (3, 7)]):
            possible_moves.append(((x, y), (2, 7)))
        if i_stats["white_castle_moved"][2] == 0 and i_board[7][5] == " " and i_board[7][6] == " " and check_opponent_moves(own_pieces, opponent_pieces, i_board, i_stats, [(5, 7), (6, 7)]):
            possible_moves.append(((x, y), (6, 7)))

    return possible_moves


def show_board(start, side=-1, possible_moves=None, piece_pos=()):
    if possible_moves is None:
        possible_moves = []
    print("    A  B  C  D  E  F  G  H")
    x = 0
    y = 0
    for row in board:
        row_string = f"{colors['white']} {8 - y} "
        for cell in row:
            text_color = colors["black"]
            if board[y][x] in "RNBQKP":
                text_color = colors["white"]

            if start and stats["computer_last_move"] == (x, y):
                back_color = bg_colors["red"]
            elif (piece_pos, (x, y)) in possible_moves:
                back_color = bg_colors["yellow"]
            elif (x, y) == piece_pos:
                back_color = bg_colors["magenta"]
            elif (x + y) % 2 == 0:
                back_color = bg_colors["green"]
            else:
                back_color = bg_colors["cyan"]

            row_string += f"{back_color}{text_color} {cell} "
            x += 1
        row_string += f"{bg_colors['black']}{colors['basic']} {str(8 - y)}"
        if y == 1:
            row_string += f"  Turn: {stats['move_num']}"
        elif y == 2:
            if side == 0:
                row_string += "  White turn"
            elif side == 1:
                row_string += "  Black turn"
        elif y == 3:
            row_string += f"  {evaluation['game_evaluation']}"
        print(row_string)
        y += 1
        x = 0
    print("    A  B  C  D  E  F  G  H\n")


def check_possible_moves(piece, target_pieces, other_pieces, start_x, start_y, check_castle, i_board, i_stats, computer):
    possible_moves = []

    if piece == target_pieces[0]:
        possible_moves = pawn_moves(start_x, start_y, target_pieces, other_pieces, i_board, i_stats, computer)
    elif piece == target_pieces[1]:
        possible_moves = knight_moves(start_x, start_y, target_pieces, i_board)
    elif piece == target_pieces[2]:
        possible_moves = bishop_moves(start_x, start_y, other_pieces, i_board)
    elif piece == target_pieces[3]:
        possible_moves = rook_moves(start_x, start_y, other_pieces, i_board)
    elif piece == target_pieces[4]:
        possible_moves = rook_moves(start_x, start_y, other_pieces, i_board) + bishop_moves(start_x, start_y, other_pieces, i_board)
    elif piece == target_pieces[5]:
        possible_moves = king_moves(start_x, start_y, target_pieces, other_pieces, check_castle, i_board, i_stats)

    return possible_moves


def add_position_repetiotion(i_board, i_stats, checking=False):
    tuple_board = (tuple(i_board[0]), tuple(i_board[1]), tuple(i_board[2]), tuple(i_board[3]), tuple(i_board[4]), tuple(i_board[5]), tuple(i_board[6]), tuple(i_board[7]))
    if tuple_board in i_stats["all_positions"]:
        i_stats["all_positions"][tuple_board] += 1
    else:
        i_stats["all_positions"][tuple_board] = 1

    for value in i_stats["all_positions"].values():
        if value > 2:
            if not checking:
                input("Stalemate.")
                raise KeyboardInterrupt
            else:
                return True
    return False


# side 0 white, 1 black
def player_turn(side):
    if side == 0:
        own_pieces = stats["player_pieces"]
        opponent_pieces = stats["opponent_pieces"]
    else:
        own_pieces = stats["opponent_pieces"]
        opponent_pieces = stats["player_pieces"]

    # disable the ability to en passant after opponent's turn
    if len(stats["last_double_pawn_move"]) == 3 and stats["last_double_pawn_move"][2] == side:
        stats["last_double_pawn_move"] = []

    i_board = deepcopy(board)
    i_stats = deepcopy(stats)
    possible_moves = check_moves(own_pieces, opponent_pieces, i_board, i_stats)

    if not possible_moves:
        if check_opponent_moves(opponent_pieces, own_pieces, i_board, i_stats):  # stalemate
            print("Stalemate")
            raise KeyboardInterrupt
        else:  # checkmate
            print("Checkmate,", "white" if side == 1 else "black", "won")
            raise KeyboardInterrupt

    while True:
        try:
            clear_console()
            show_board(True, side)
            
            start_square = input("From which square: ")

            if start_square == "stats":
                input(stats)
                continue
            elif start_square == "quit":
                raise KeyboardInterrupt
            elif start_square == "save":
                input("\n" + save() + "\n")
                continue

            start_x = x_convert[start_square[0].upper()]
            start_y = y_convert[start_square[1]]
            piece = board[start_y][start_x]

            if piece not in own_pieces:
                input("That is either an empty square or your opponent's piece.")
                continue

            able_to_move = False
            for possible_move in possible_moves:
                if (start_x, start_y) == possible_move[0]:
                    able_to_move = True
                    break

            if not able_to_move:
                input("That piece has no possible moves")
                continue

            if len(start_square) != 4:
                clear_console()
                show_board(False, side, possible_moves, (start_x, start_y))

                end_square = input("To which square: ")
                end_x = x_convert[end_square[0].upper()]
                end_y = y_convert[end_square[1]]
            else:
                end_x = x_convert[start_square[2].upper()]
                end_y = y_convert[start_square[3]]

            if ((start_x, start_y), (end_x, end_y)) in possible_moves:
                move_piece(start_x, start_y, end_x, end_y, board, stats, False)
                add_position_repetiotion(board, stats)
                clear_console()
                show_board(False, 1-side)
                break
            else:
                input("That move is not possible")

        except (KeyError, IndexError):
            input("You typed wrong. First letter (A-H) then number (1-8), for example F7.")


def evaluate_position(i_board, i_stats, side, own_pieces, oppponent_pieces):
    score = 0

    y = 0
    white_pawn_x = []
    black_pawn_x = []
    for row in i_board:
        x = 0
        for cell in row:
            if cell != " ":
                score += evaluation["piece_value"].get(cell, 0)
                if cell == "P":
                    score += evaluation["pawn_placement"][6-y][x]
                    if x in white_pawn_x:
                        score += evaluation["multiple_pawn_in_column_per_over_pawn"]
                    else:
                        white_pawn_x.append(x)
                elif cell == "p":
                    score -= evaluation["pawn_placement"][y-1][x]
                    if x in black_pawn_x:
                        score -= evaluation["multiple_pawn_in_column_per_over_pawn"]
                    else:
                        black_pawn_x.append(x)
                elif cell == "B":
                    score += len(bishop_moves(x, y, oppponent_pieces, i_board)) * evaluation["bishop_value_per_move"]
                elif cell == "b":
                    score -= len(bishop_moves(x, y, own_pieces, i_board)) * evaluation["bishop_value_per_move"]
                elif cell == "N":
                    score += len(knight_moves(x, y, own_pieces, i_board)) * evaluation["knight_value_per_move"]
                elif cell == "n":
                    score -= len(knight_moves(x, y, oppponent_pieces, i_board)) * evaluation["knight_value_per_move"]
                elif cell == "R":
                    score += len(rook_moves(x, y, oppponent_pieces, i_board)) * evaluation["rook_value_per_move"]
                elif cell == "r":
                    score -= len(rook_moves(x, y, own_pieces, i_board)) * evaluation["rook_value_per_move"]
                elif cell == "Q":
                    score += len(rook_moves(x, y, oppponent_pieces, i_board) + bishop_moves(x, y, oppponent_pieces, i_board)) * evaluation["queen_value_per_move"]
                elif cell == "q":
                    score -= len(rook_moves(x, y, own_pieces, i_board) + bishop_moves(x, y, own_pieces, i_board)) * evaluation["queen_value_per_move"]
            x += 1
        y += 1

    # isolated_pawn
    for x in white_pawn_x:
        if x == 0:
            if 1 not in white_pawn_x:
                score += evaluation["isolated_pawn"]
        elif x == 7:
            if 6 not in white_pawn_x:
                score += evaluation["isolated_pawn"]
        else:
            if (x-1 not in white_pawn_x) or (x+1 not in white_pawn_x):
                score += evaluation["isolated_pawn"]

    for x in black_pawn_x:
        if x == 0:
            if 1 not in black_pawn_x:
                score += evaluation["isolated_pawn"]
        elif x == 7:
            if 6 not in black_pawn_x:
                score += evaluation["isolated_pawn"]
        else:
            if (x-1 not in black_pawn_x) or (x+1 not in black_pawn_x):
                score += evaluation["isolated_pawn"]

    # Castling values
    if i_stats["white_castle_moved"][0] == 1 or (i_stats["white_castle_moved"][1] == 1 and i_stats["white_castle_moved"][2] == 1):
        score += evaluation["castling_value"][3]
    elif i_stats["white_castle_moved"][1] == 1:
        score += evaluation["castling_value"][1]
    elif i_stats["white_castle_moved"][2] == 1:
        score += evaluation["castling_value"][2]
    else:
        score += evaluation["castling_value"][0]

    if i_stats["black_castle_moved"][0] == 1 or (i_stats["black_castle_moved"][1] == 1 and i_stats["black_castle_moved"][2] == 1):
        score += evaluation["castling_value"][3]
    elif i_stats["black_castle_moved"][1] == 1:
        score += evaluation["castling_value"][1]
    elif i_stats["black_castle_moved"][2] == 1:
        score += evaluation["castling_value"][2]
    else:
        score += evaluation["castling_value"][0]

    """
    "pawn_wall": 10,  # pawni suojelee toista pawnia
    """

    # move_first
    if side == i_stats["move_side"]:
        score += evaluation["move_turn"] * (1 if side == 0 else -1)

    return score


def evaluate_moves(possible_moves, own_pieces, opponent_pieces, side, depth):
    evaluations = []
    for possible_move in possible_moves:
        i_board = deepcopy(board)
        i_stats = deepcopy(stats)
        promote = ""
        start_x, start_y, end_x, end_y = possible_move[0][0], possible_move[0][1], possible_move[1][0], possible_move[1][1]
        if i_board[start_y][start_x] == own_pieces[0] and len(possible_move[1]) == 3:
            promote = possible_move[1][2]

        move_piece(start_x, start_y, end_x, end_y, i_board, i_stats, False, promote)
        add_position_repetiotion(i_board, i_stats, True)

        i_stats["move_side"] = 1 - i_stats["move_side"]

        if not i_stats["move_side"]:
            i_stats["move_num"] += 1

        # disable the ability to en passant after opponent's turn
        if len(i_stats["last_double_pawn_move"]) == 3 and i_stats["last_double_pawn_move"][2] == i_stats["move_side"]:
            i_stats["last_double_pawn_move"] = ()

        opponent_possible_moves = check_moves(opponent_pieces, own_pieces, i_board, i_stats, True)

        if (not opponent_possible_moves) and depth == 1:
            if check_opponent_moves(opponent_pieces, own_pieces, i_board, i_stats):  # stalemate
                evaluations.append(0)
            else:  # checkmate
                input("checkmate found #001")  # testing
                evaluations.append(10000000 * (1 if side == 0 else -1))
            continue

        scores = []
        for opponent_possible_move in opponent_possible_moves:
            j_board = deepcopy(i_board)
            j_stats = deepcopy(i_stats)

            promote = ""
            start_x, start_y, end_x, end_y = opponent_possible_move[0][0], opponent_possible_move[0][1], opponent_possible_move[1][0], opponent_possible_move[1][1]
            if j_board[start_y][start_x] == opponent_pieces[0] and len(opponent_possible_move[1]) == 3:
                promote = opponent_possible_move[1][2]

            move_piece(start_x, start_y, end_x, end_y, j_board, j_stats, False, promote)

            j_stats["move_side"] = 1 - j_stats["move_side"]

            if not j_stats["move_side"]:
                j_stats["move_num"] += 1

            # disable the ability to en passant after opponent's turn
            if len(j_stats["last_double_pawn_move"]) == 3 and j_stats["last_double_pawn_move"][2] == j_stats["move_side"]:
                j_stats["last_double_pawn_move"] = []

            scores.append(evaluate_position(j_board, j_stats, j_stats["move_side"], own_pieces, opponent_pieces))
        evaluations.append(scores)

    if side == 0:
        i = 0
        i_max = 0
        # j_max = 0
        max_score = -100000000000000
        for move_scores in evaluations:
            move_max = min(move_scores)
            if move_max > max_score:
                i_max = i
                max_score = move_max
                # input(f"new max move found: {max_score} #004")  # testing
                # j_max = move_scores.index(move_max)
            i += 1
        # input(f"best move {possible_moves[i_max]} with the score {max_score} #005")  # testing
        evaluation["game_evaluation"] = max_score
        return possible_moves[i_max]
    else:
        i = 0
        i_min = 0
        # j_min = 0
        min_score = 100000000000000
        for move_scores in evaluations:
            move_min = max(move_scores)
            if move_min < min_score:
                i_min = i
                min_score = move_min
                # input(f"new min move found: {min_score} #003")  # testing
                # j_min = move_scores.index(move_min)
            i += 1
        # input(f"best move {possible_moves[i_min]} with the score {min_score} #002")  # testing
        evaluation["game_evaluation"] = min_score
        return possible_moves[i_min]


def computer_turn(side):
    if side == 0:
        own_pieces = stats["player_pieces"]
        opponent_pieces = stats["opponent_pieces"]
    else:
        own_pieces = stats["opponent_pieces"]
        opponent_pieces = stats["player_pieces"]

    # disable the ability to en passant after opponent's turn
    if len(stats["last_double_pawn_move"]) == 3 and stats["last_double_pawn_move"][2] == side:
        stats["last_double_pawn_move"] = []

    i_board = deepcopy(board)
    i_stats = deepcopy(stats)
    possible_moves = check_moves(own_pieces, opponent_pieces, i_board, i_stats, True)

    if not possible_moves:
        if check_opponent_moves(opponent_pieces, own_pieces, i_board, i_stats):  # stalemate
            print("Stalemate")
            raise KeyboardInterrupt
        else:  # checkmate
            print("Checkmate,", "white" if side == 1 else "black", "won")
            raise KeyboardInterrupt

    selected_move = evaluate_moves(possible_moves, own_pieces, opponent_pieces, side, 1)

    start_x, start_y, end_x, end_y = selected_move[0][0], selected_move[0][1], selected_move[1][0], selected_move[1][1]

    promote = ""
    if board[start_y][start_x] == own_pieces[0] and len(selected_move[1]) == 3:
        promote = selected_move[1][2]

    stats["computer_last_move"] = (end_x, end_y)

    move_piece(start_x, start_y, end_x, end_y, board, stats, False, promote)
    add_position_repetiotion(board, stats)


# x = 0-7, y = 0-7
def move_piece(start_x, start_y, end_x, end_y, i_board, i_stats, checking, promote=""):
    # en passant
    if (i_board[start_y][start_x] in "pP") and (i_board[end_y][end_x] == " ") and (start_x != end_x):
        i_board[start_y][end_x] = " "

    # actual moving of the piece
    i_board[end_y][end_x] = i_board[start_y][start_x]
    i_board[start_y][start_x] = " "

    # double pawn move for stats and the ability for opponent to en passant
    if i_board[end_y][end_x] == "p" and end_y - start_y == 2:
        i_stats["last_double_pawn_move"] = (end_x, end_y, 1)
    elif i_board[end_y][end_x] == "P" and start_y - end_y == 2:
        i_stats["last_double_pawn_move"] = (end_x, end_y, 0)

    # pawn promotion
    if not checking:
        if promote != "":
            i_board[end_y][end_x] = promote
        if i_board[end_y][end_x] == "p" and end_y == 7:
            while True:
                choice = input("What piece to promoto to (nbrq): ").lower()
                if len(choice) == 1 and choice in "nbrq":
                    i_board[end_y][end_x] = choice
                    break
        elif i_board[end_y][end_x] == "P" and end_y == 0:
            while True:
                choice = input("What piece to promoto to (NBRQ): ").upper()
                if len(choice) == 1 and choice in "NBRQ":
                    i_board[end_y][end_x] = choice
                    break

    # castle
    if i_board[end_y][end_x] in "kK" and abs(end_x - start_x) == 2:
        if end_x == 2:
            move_piece(0, end_y, 3, end_y, i_board, i_stats, True)
        else:
            move_piece(7, end_y, 5, end_y, i_board, i_stats, True)

    # stop castling rights
    if not checking:
        if i_board[end_y][end_x] == "k":
            i_stats["black_castle_moved"][0] = 1
        elif i_board[end_y][end_x] == "r":
            if start_x == 0 and start_y == 0:
                i_stats["black_castle_moved"][1] = 1
            elif start_x == 7 and start_y == 0:
                i_stats["black_castle_moved"][2] = 1
        elif i_board[end_y][end_x] == "K":
            i_stats["white_castle_moved"][0] = 1
        elif i_board[end_y][end_x] == "R":
            if start_x == 0 and start_y == 7:
                i_stats["white_castle_moved"][1] = 1
            elif start_x == 7 and start_y == 7:
                i_stats["white_castle_moved"][2] = 1


# returns all moves that are possible ((start_x, start_y), (end_x, end_y))
def check_moves(own_pieces, opponent_pieces, i_board, i_stats, computer=False):
    moves = []
    y = 0
    for row in i_board:
        x = 0
        for cell in row:
            if cell in own_pieces:
                possible_moves = check_possible_moves(cell, own_pieces, opponent_pieces, x, y, True, i_board, i_stats, computer)
                for possible_move in possible_moves:
                    check_board = deepcopy(i_board)
                    check_stats = deepcopy(i_stats)
                    move_piece(possible_move[0][0], possible_move[0][1], possible_move[1][0], possible_move[1][1], check_board, check_stats, True)
                    if check_opponent_moves(own_pieces, opponent_pieces, check_board, check_stats):
                        moves.append(possible_move)
            x += 1
        y += 1

    return moves


# returns True if opponent can't move to king's position, otherwise False
def check_opponent_moves(own_pieces, opponent_pieces, i_board, i_stats, positions=None):
    if positions is None:
        positions = []
    y = 0
    all_possible_moves = set()
    king_pos = (-1, -1)
    for row in i_board:
        x = 0
        for cell in row:
            if cell in opponent_pieces:
                new_possible_moves = check_possible_moves(cell, opponent_pieces, own_pieces, x, y, False, i_board, i_stats, False)
                for new_possible_move in new_possible_moves:
                    all_possible_moves.update([new_possible_move[1]])
            elif cell == own_pieces[5]:
                king_pos = (x, y)
            x += 1
        y += 1

    if king_pos == (-1, -1):
        input("Error. King not found.")
        raise KeyboardInterrupt
    elif king_pos in all_possible_moves:
        return False
    for position in positions:
        if position in all_possible_moves:
            return False
    return True


def main():
    try:
        system("title chessTAI")
        print(logo)
        choice = input(start_menu)
        if choice == "1":
            color = input("\nYou are playing against a computer.\nWhat color do you want to play (white/black/random)\n").lower()
            if color == "w" or color == "white":
                p_side = 0
            elif color == "b" or color == "black":
                p_side = 1
            else:
                p_side = randint(0, 1)
                input(f"You are playing {'white' if p_side == 0 else 'black'}.")

            while True:
                if not stats["move_side"]:
                    stats["move_num"] += 1

                if stats["move_side"] == p_side:
                    player_turn(p_side)
                else:
                    computer_turn(stats["move_side"])

                stats["move_side"] = 1 - stats["move_side"]

        elif choice == "2":
            while True:
                if not stats["move_side"]:
                    stats["move_num"] += 1

                player_turn(stats["move_side"])

                stats["move_side"] = 1 - stats["move_side"]

        elif choice == "3":
            pass

    except KeyboardInterrupt:
        print("\nYou stopped the code")

    input("Exiting...")


if __name__ == '__main__':
    main()

Keywords:

Leave a comment

Design a site like this with WordPress.com
Get started