Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Re: First attempt at a Python prog (Chess)

Reply
Thread Tools

Re: First attempt at a Python prog (Chess)

 
 
Oscar Benjamin
Guest
Posts: n/a
 
      02-15-2013
On 13 February 2013 23:25, Chris Hinsley <(E-Mail Removed)> 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(:
> for col in range(:


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,

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( and ny in range(:


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
 
Reply With Quote
 
 
 
 
Neil Cerutti
Guest
Posts: n/a
 
      02-15-2013
On 2013-02-15, Oscar Benjamin <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
 
MRAB
Guest
Posts: n/a
 
      02-15-2013
On 2013-02-15 16:17, Neil Cerutti wrote:
> On 2013-02-15, Oscar Benjamin <(E-Mail Removed)> 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:
>


 
Reply With Quote
 
Matt Jones
Guest
Posts: n/a
 
      02-15-2013
"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 <(E-Mail Removed)> wrote:

> On 2013-02-15 16:17, Neil Cerutti wrote:
>
>> On 2013-02-15, Oscar Benjamin <(E-Mail Removed)> 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>
>


 
Reply With Quote
 
Neil Cerutti
Guest
Posts: n/a
 
      02-19-2013
On 2013-02-15, MRAB <(E-Mail Removed)> wrote:
> On 2013-02-15 16:17, Neil Cerutti wrote:
>> On 2013-02-15, Oscar Benjamin <(E-Mail Removed)> 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
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: First attempt at a Python prog (Chess) Tim Golden Python 0 02-15-2013 11:36 AM
Re: First attempt at a Python prog (Chess) Rick Johnson Python 0 02-15-2013 05:05 AM
Re: First attempt at a Python prog (Chess) jkn Python 1 02-15-2013 03:09 AM
Re: First attempt at a Python prog (Chess) Ian Kelly Python 0 02-14-2013 06:30 PM
Re: First attempt at a Python prog (Chess) Oscar Benjamin Python 0 02-13-2013 11:55 PM



Advertisments