First attempt at a Python prog (Chess)

Chris Hinsley chris.hinsley at gmail.com
Thu Jul 4 17:49:08 CEST 2013


On 2013-02-13 23:25:09 +0000, Chris Hinsley said:

> New to Python, which I really like BTW.
> 
> First serious prog. Hope you like it. I know it needs a 'can't move if 
> your King would be put into check' test. But the weighted value of the 
> King piece does a surprising emergent job.

New version with better search and hopefully a little better Python. I 
know it's still far from fully correct and it'll never best Gary 
Kasperov. :)

With pypy on my Macbook I can go to 6 PLY with resonable performance 
now due to the alpha-beta pruneing.

Regards

#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
# Copyright (C) 2013 Chris Hinsley, GPL V3 License

import sys
import random
import os

PLY = 5

EMPTY = 0
WHITE = 1
BLACK = -1
NO_CAPTURE = 2
MAY_CAPTURE = 3
MUST_CAPTURE = 4

piece_type = {' ' : EMPTY, 'K' : BLACK, 'Q' : BLACK, 'R' : BLACK, 'B' : 
BLACK, 'N' : BLACK, 'P' : BLACK, \
            'k' : WHITE, 'q' : WHITE, 'r' : WHITE, 'b' : WHITE, 'n' : 
WHITE, 'p' : WHITE}

def display_board(board):
    print '  a   b   c   d   e   f   g   h'
    print '+---+---+---+---+---+---+---+---+'
    for row in range(8):
        for col in range(8):
            print '| %c' % board[row * 8 + col],
        print '|', 8 - row
        print '+---+---+---+---+---+---+---+---+'

def piece_moves(board, index, dx, dy, capture_flag, distance):
    piece = board[index]
    type = piece_type[piece]
    cy, cx = divmod(index, 8)
    for step in range(distance):
        nx = cx + (dx * (step + 1))
        ny = cy + (dy * (step + 1))
        if (0 <= nx < 8) and (0 <= ny < 8):
            newindex = ny * 8 + nx
            newpiece = board[newindex]
            newtype = piece_type[newpiece]
            if capture_flag == MUST_CAPTURE:
                if newtype != EMPTY and newtype != type:
                    board[index] = ' '
                    if (ny == 0 or ny == 7) and piece in 'Pp':
                        for promote in 'QRBN' if type == BLACK else 'qrbn':
                            board[newindex] = promote
                            yield board
                    else:
                        board[newindex] = piece
                        yield board
                    board[index], board[newindex] = piece, newpiece
            elif capture_flag == MAY_CAPTURE:
                if newtype == EMPTY:
                    board[index], board[newindex] = ' ', piece
                    yield board
                    board[index], board[newindex] = piece, newpiece
                elif newtype != type:
                    board[index], board[newindex] = ' ', piece
                    yield board
                    board[index], board[newindex] = piece, newpiece
                    break
                else:
                    break
            elif newtype == EMPTY:
                board[index] = ' '
                if (ny == 0 or ny == 7) and piece in 'Pp':
                    for promote in 'QRBN' if type == BLACK else 'qrbn':
                        board[newindex] = promote
                        yield board
                else:
                    board[newindex] = piece
                    yield board
                board[index], board[newindex] = piece, newpiece
            else:
                break
        else:
            break

def pawn_moves(board, index, options):
    for x, y, flag, distance in options:
        for new_board in piece_moves(board, index, x, y, flag, distance):
            yield new_board

def other_moves(board, index, options, distance):
    for x, y in options:
        for new_board in piece_moves(board, index, x, y, MAY_CAPTURE, 
distance):
            yield new_board

def black_pawn_moves(board, index):
    distance = 2 if index in range(8, 16) else 1
    for new_board in pawn_moves(board, index, [(0, 1, NO_CAPTURE, 
distance), (-1, 1, MUST_CAPTURE, 1), (1, 1, MUST_CAPTURE, 1)]):
        yield new_board

def white_pawn_moves(board, index):
    distance = 2 if index in range(48, 56) else 1
    for new_board in pawn_moves(board, index, [(0, -1, NO_CAPTURE, 
distance), (-1, -1, MUST_CAPTURE, 1), (1, -1, MUST_CAPTURE, 1)]):
        yield new_board

def rook_moves(board, index):
    for new_board in other_moves(board, index, [(0, -1), (-1, 0), (0, 
1), (1, 0)], 7):
        yield new_board

def bishop_moves(board, index):
    for new_board in other_moves(board, index, [(-1, -1), (-1, 1), (1, 
1), (1, -1)], 7):
        yield new_board

def knight_moves(board, index):
    for new_board in other_moves(board, index, [(-2, 1), (2, -1), (2, 
1), (-1, -2), (-1, 2), (1, -2), (1, 2)], 1):
        yield new_board

def queen_moves(board, index):
    for new_board in bishop_moves(board, index):
        yield new_board
    for new_board in rook_moves(board, index):
        yield new_board

def king_moves(board, index):
    for new_board in other_moves(board, index, [(0, -1), (-1, 0), (0, 
1), (1, 0), (-1, -1), (-1, 1), (1, 1), (1, -1)], 1):
        yield new_board

moves = {'P' : black_pawn_moves, 'p' : white_pawn_moves, \
        'R' : rook_moves, 'r' : rook_moves, \
        'B' : bishop_moves, 'b' : bishop_moves, \
        'N' : knight_moves, 'n' : knight_moves, \
        'Q' : queen_moves, 'q' : queen_moves, \
        'K' : king_moves, 'k' : king_moves}

def all_moves(board, colour):
    for index, piece in enumerate(board):
        if piece_type[piece] == colour:
            for new_board in moves[piece](board, index):
                yield new_board

piece_values = {'K' : (10000, 0), 'k' : (0, 10000), \
                'P' : (1, 0), 'p' : (0, 1), \
                'N' : (3, 0), 'n' : (0, 3), \
                'B' : (3, 0), 'b' : (0, 3), \
                'R' : (5, 0), 'r' : (0, 5), \
                'Q' : (9, 0), 'q' : (0, 9)}

generic_position_values =  [0, 0, 0, 0, 0, 0, 0, 0, \
                            0, 1, 1, 1, 1, 1, 1, 0, \
                            0, 1, 2, 2, 2, 2, 1, 0, \
                            0, 1, 2, 3, 3, 2, 1, 0, \
                            0, 1, 2, 3, 3, 2, 1, 0, \
                            0, 1, 2, 2, 2, 2, 1, 0, \
                            0, 1, 1, 1, 1, 1, 1, 0, \
                            0, 0, 0, 0, 0, 0, 0, 0]

white_king_position_values =   [0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                3, 3, 3, 3, 3, 3, 3, 3]

black_king_position_values =   [3, 3, 3, 3, 3, 3, 3, 3, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0, \
                                0, 0, 0, 0, 0, 0, 0, 0]

piece_positions =  {'K' : black_king_position_values, 'k' : 
white_king_position_values, \
                    'P' : generic_position_values, 'p' : 
generic_position_values, \
                    'N' : generic_position_values, 'n' : 
generic_position_values, \
                    'B' : generic_position_values, 'b' : 
generic_position_values, \
                    'R' : generic_position_values, 'r' : 
generic_position_values, \
                    'Q' : generic_position_values, 'q' : 
generic_position_values}

def evaluate(board):
    black_score, white_score = 0, 0
    for index, piece in enumerate(board):
        type = piece_type[piece]
        if type != EMPTY:
            position_value = piece_positions[piece][index]
            if type == BLACK:
                black_score += position_value
            else:
                white_score += position_value
            black_value, white_value = piece_values[piece]
            black_score += black_value
            white_score += white_value
    return white_score - black_score

def next_move(board, colour, alpha, beta, ply):
    if ply <= 0:
        return evaluate(board) * colour
    for new_board in all_moves(board[:], colour):
        score = -next_move(new_board, -colour, -beta, -alpha, ply - 1)
        if score >= beta:
            return score
        if score > alpha:
            alpha = score
    return alpha

def best_move(board, colour, alpha, beta, ply):
    best_board = []
    for new_board in all_moves(board, colour):
        score = -next_move(new_board, -colour, -beta, -alpha, ply - 1)
        if score > alpha:
            alpha = score
            best_board = new_board[:]
    return best_board

def main():
    board = list('RNBQKBNRPPPPPPPP                                
pppppppprnbqkbnr')
    colour = WHITE
    while True:
        board = best_move(board, colour, -1000000, 1000000, PLY)
        colour = -colour
        os.system('clear')
        display_board(board)
        #raw_input()

if __name__ == '__main__':
    main()




More information about the Python-list mailing list