Re: First attempt at a Python prog (Chess)

Discussion in 'Python' started by Oscar Benjamin, Feb 15, 2013.

  1. On 13 February 2013 23:25, Chris Hinsley <> wrote:
    > 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.
    >
    > #!/usr/bin/python -tt
    > # -*- coding: utf-8 -*-
    > # Copyright (C) 2013 Chris Hinsley, GPL V3 License
    >
    > import sys
    > import random
    > import os
    >
    > PLY = 3
    >
    > EMPTY = 0
    > BLACK = 1
    > WHITE = 2
    > NO_CAPTURE = 3
    > MAY_CAPTURE = 4
    > MUST_CAPTURE = 5
    >
    > def piece_type(piece):
    > return EMPTY if piece == 32 else BLACK if chr(piece) in 'KQRBNP' else
    > WHITE


    You call chr(piece) many times in this program. It would be better to
    just have piece be a string rather than always converting it to one
    every time you want to do something. Also comparing it with a numeric
    code is fairly cryptic. I guess that ascii 32 is a space character but
    I'd have to look that up to be sure.

    >
    > def display_board(board):
    > print ' a b c d e f g h'
    > print '+---+---+---+---+---+---+---+---+'
    > for row in range(8):
    > for col in range(8):


    Why not make board a list of lists. Then you can do:

    for row in board:
    for piece in row:

    rather than using range().

    Or perhaps you could have a dict that maps position tuples to pieces,
    e.g.: {(1, 2): 'k', ...}

    > sys.stdout.write('| ')
    > sys.stdout.write(chr(board[row * 8 + col]))
    > sys.stdout.write(' ')
    > sys.stdout.write('|')
    > print 8 - row
    > print '+---+---+---+---+---+---+---+---+'


    You seem to be using sys.stdout.write as a way of printing without a
    trailing newline. In Python 2 you can get this effect by using:
    print 'foo',

    (note the trailing comma). In Python 3 you would do

    print('foo', end=' ')

    You can use the Python 3 syntax in your Python 2 script if you do
    "from __future__ import print_function" so that your script works on
    Python 2 and 3.

    Also I would probably separate the function that generates the text
    representing the board from the code that actually sends that
    information to stdout.


    >
    > def piece_moves(board, index, dx, dy, capture_flag, distance):
    > piece = board[index]
    > type = piece_type(piece)
    > cx = index % 8
    > cy = index / 8


    You can use divmod for this:
    cx, cy = divmod(index, 8)

    Also in Python 3 index / 8 will return a float. Use // for floor
    division in both versions ("from __future__ import division").

    > for step in range(distance):
    > nx = cx + (dx * (step + 1))
    > ny = cy + (dy * (step + 1))


    Why not make a function that yields these values and loop over that?

    def continued_moves(x, y, dx, dy):
    while 0 <= x < 8 and 0 <= y < 8:
    x += dx
    y += dy
    yield x, y

    > if nx in range(8) and ny in range(8):


    Use chained comparisons 0 <= x < 8 rather than testing for membership
    in a range object. "x in range(N, M)" creates (in Python 2) a list
    integers and then (in 2 or 3) iterates over that list to find an
    object equal to x. This is inefficient and not as clear.

    > newindex = ny * 8 + nx
    > newpiece = board[newindex]


    With a list of lists you could access the board with board[ny][nx]
    which is clearer. Or with the dict: board[(nx, ny)].

    The code below is overly indented. consider factoring it into functions.

    > newtype = piece_type(newpiece)
    > if capture_flag == MUST_CAPTURE:
    > if newtype != EMPTY and newtype != type:
    > board[index] = ' '
    > if (ny == 0 or ny == 7) and chr(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 or newtype != type:
    > board[index], board[newindex] = ' ', piece
    > yield board
    > board[index], board[newindex] = piece, newpiece


    Rather than modifying and unmodifying the board in place (which is
    fragile), could you not just have the compute_score function compute
    the score as if the move had taken place? Then you could just yield
    the move and the score.

    > break
    > elif newtype == EMPTY:
    > board[index] = ' '
    > if (ny == 0 or ny == 7) and chr(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
    >
    > 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


    Do you really need separate functions for black and white pawns?

    >
    > 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, turn):
    > for index, piece in enumerate(board):
    > if piece_type(piece) == turn:
    > for new_board in moves[chr(piece)](board, index):
    > yield new_board
    >
    > piece_values = {'K' : (1000000, 0), 'k' : (0, 1000000), \
    > '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)}
    >
    > 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]
    >
    > def score_board(board):
    > black_score, white_score = 0, 0
    > for index, piece in enumerate(board):


    Iterating over the whole board is wasteful when it is usually empty
    the dict approach would be better in this case.

    > type = piece_type(piece)
    > if type != EMPTY:
    > position_value = position_values[index]
    > if type == BLACK:
    > black_score += position_value
    > else:
    > white_score += position_value
    > black_value, white_value = piece_values[chr(piece)]
    > black_score += black_value
    > white_score += white_value
    > return (black_score, white_score)
    >
    > def turn_score(board, turn):
    > black_score, white_score = score_board(board)
    > return (white_score - black_score) if turn == WHITE else (black_score -
    > white_score)
    >
    > def best_move(board, turn, ply):
    > best_score = -10000000


    best_score = None

    > best_boards = []
    > for new_board in all_moves(board, turn):
    > if ply:
    > next_turn = BLACK if turn == WHITE else WHITE
    > score = turn_score(best_move(new_board, next_turn, ply - 1),
    > turn)
    > else:
    > score = turn_score(new_board, turn)
    > if score > best_score or not best_boards:


    if score > best_score or best_score is None:

    > best_score = score
    > best_boards = [new_board[:]]
    > elif score == best_score:
    > best_boards.append(new_board[:])
    > if best_boards:
    > return random.choice(best_boards)
    > return board[:]
    >
    > def main():
    > board = bytearray('RNBQKBNRPPPPPPPP
    > pppppppprnbqkbnr')
    > turn = WHITE
    > while True:
    > board = best_move(board, turn, PLY)
    > turn = BLACK if turn == WHITE else WHITE
    > os.system('clear')
    > display_board(board)
    > #raw_input()
    >
    > if __name__ == '__main__':
    > main()



    Oscar
     
    Oscar Benjamin, Feb 15, 2013
    #1
    1. Advertising

  2. Oscar Benjamin

    Neil Cerutti Guest

    On 2013-02-15, Oscar Benjamin <> wrote:
    > if score > best_score or best_score is None:


    You need the None check first to avoid an exception from the
    comparison.

    if best_score is None or score > best_score:

    --
    Neil Cerutti
     
    Neil Cerutti, Feb 15, 2013
    #2
    1. Advertising

  3. Oscar Benjamin

    MRAB Guest

    On 2013-02-15 16:17, Neil Cerutti wrote:
    > On 2013-02-15, Oscar Benjamin <> wrote:
    >> if score > best_score or best_score is None:

    >
    > You need the None check first to avoid an exception from the
    > comparison.
    >

    Only in Python 3.

    > if best_score is None or score > best_score:
    >
     
    MRAB, Feb 15, 2013
    #3
  4. Oscar Benjamin

    Matt Jones Guest

    "Only in Python 3."

    Use best practices always, not just when you have to.

    *Matt Jones*


    On Fri, Feb 15, 2013 at 11:52 AM, MRAB <> wrote:

    > On 2013-02-15 16:17, Neil Cerutti wrote:
    >
    >> On 2013-02-15, Oscar Benjamin <> wrote:
    >>
    >>> if score > best_score or best_score is None:
    >>>

    >>
    >> You need the None check first to avoid an exception from the
    >> comparison.
    >>
    >> Only in Python 3.

    >
    >
    > if best_score is None or score > best_score:
    >>
    >>

    > --
    > http://mail.python.org/**mailman/listinfo/python-list<http://mail.python.org/mailman/listinfo/python-list>
    >
     
    Matt Jones, Feb 15, 2013
    #4
  5. Oscar Benjamin

    Neil Cerutti Guest

    On 2013-02-15, MRAB <> wrote:
    > On 2013-02-15 16:17, Neil Cerutti wrote:
    >> On 2013-02-15, Oscar Benjamin <> wrote:
    >>> if score > best_score or best_score is None:

    >>
    >> You need the None check first to avoid an exception from the
    >> comparison.

    >
    > Only in Python 3.


    It is a more difficult to find bug in Python 2, which will not
    even throw an exception, but instead silently do the wrong thing.

    --
    Neil Cerutti
     
    Neil Cerutti, Feb 19, 2013
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Oscar Benjamin

    Re: First attempt at a Python prog (Chess)

    Oscar Benjamin, Feb 13, 2013, in forum: Python
    Replies:
    0
    Views:
    170
    Oscar Benjamin
    Feb 13, 2013
  2. Ian Kelly
    Replies:
    0
    Views:
    131
    Ian Kelly
    Feb 14, 2013
  3. jkn
    Replies:
    1
    Views:
    119
    Dennis Lee Bieber
    Feb 15, 2013
  4. Rick Johnson
    Replies:
    2
    Views:
    178
    Robert Kern
    Apr 30, 2014
  5. Tim Golden
    Replies:
    0
    Views:
    136
    Tim Golden
    Feb 15, 2013
Loading...

Share This Page