Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #38914

Re: First attempt at a Python prog (Chess)

Path csiph.com!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!ecngs!feeder2.ecngs.de!newsfeed.freenet.ag!news2.euro.net!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail
Return-Path <oscar.j.benjamin@gmail.com>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'python,': 0.02; 'else:': 0.03; 'syntax': 0.03; 'elif': 0.04; 'subject:Python': 0.05; 'case.': 0.05; 'none:': 0.05; 'sys': 0.05; 'that?': 0.05; '-*-': 0.07; '__name__': 0.07; 'ascii': 0.07; 'gpl': 0.07; 'main()': 0.07; 'trailing': 0.07; 'utf-8': 0.07; 'python': 0.09; "'n'": 0.09; '(0,': 0.09; '(1,': 0.09; '-1,': 0.09; '0),': 0.09; '16)': 0.09; '3),': 0.09; '5),': 0.09; '[0,': 0.09; '__future__': 0.09; 'coding:': 0.09; 'compute': 0.09; 'dict': 0.09; 'received:mail- vc0-f174.google.com': 0.09; 'turn,': 0.09; 'cc:addr:python-list': 0.10; 'def': 0.10; 'index': 0.13; "skip:' 30": 0.15; '"from': 0.16; "'__main__':": 0.16; "'b'": 0.16; "'q'": 0.16; "'r'": 0.16; '(2,': 0.16; '(3,': 0.16; '(9,': 0.16; '(note': 0.16; '-tt': 0.16; '1))': 0.16; '1),': 0.16; '1):': 0.16; '2),': 0.16; '2):': 0.16; 'cc:name:python list': 0.16; 'chained': 0.16; 'col': 0.16; 'cryptic.': 0.16; 'flag,': 0.16; 'iterating': 0.16; 'main():': 0.16; 'row': 0.16; 'sure.': 0.16; 'surprising': 0.16; 'true:': 0.16; 'type:': 0.16; 'string': 0.17; 'wrote:': 0.17; 'comparing': 0.17; 'test.': 0.17; 'yield': 0.17; 'versions': 0.20; 'import': 0.21; 'fairly': 0.21; 'modifying': 0.22; 'sends': 0.22; 'tuples': 0.22; "i'd": 0.22; 'cc:2**0': 0.23; 'this:': 0.23; 'random': 0.24; 'script': 0.24; 'cc:addr:python.org': 0.25; 'header:In-Reply- To:1': 0.25; 'skip:[ 10': 0.26; '(which': 0.26; 'values': 0.26; 'guess': 0.27; 'separate': 0.27; 'skip:# 10': 0.27; 'skip:b 30': 0.27; 'converting': 0.27; 'functions.': 0.27; 'message- id:@mail.gmail.com': 0.27; 'chris': 0.28; 'division': 0.29; 'index,': 0.29; 'piece': 0.29; 'received:209.85.220.174': 0.29; 'yields': 0.29; 'character': 0.29; 'probably': 0.29; 'usually': 0.30; 'function': 0.30; 'lists': 0.31; 'code': 0.31; 'could': 0.32; 'print': 0.32; 'skip:s 30': 0.33; '(c)': 0.33; 'skip:d 20': 0.34; 'skip:b 20': 0.34; 'received:google.com': 0.34; 'text': 0.34; 'list': 0.35; 'lists.': 0.35; 'options:': 0.35; 'board': 0.35; 'received:209.85.220': 0.35; 'received:209.85': 0.35; 'really': 0.36; 'but': 0.36; 'skip:p 20': 0.36; 'subject: (': 0.36; 'turn': 0.36; 'does': 0.37; 'why': 0.37; 'rather': 0.37; 'received:209': 0.37; 'subject:: ': 0.38; 'step': 0.39; 'space': 0.39; 'your': 0.60; 'first': 0.61; 'distance': 0.62; 'times': 0.63; 'information': 0.63; 'license': 0.65; 'subject:First': 0.65; 'score': 0.75; '2013': 0.84; 'board:': 0.84; 'emergent': 0.84; 'moves': 0.84; 'oscar': 0.84; 'score.': 0.84; 'do:': 0.91; 'serious': 0.98
DKIM-Signature v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:mime-version:in-reply-to:references:from:date:message-id :subject:to:cc:content-type; bh=HHQyuIWufu4pLm5TAd2Z+J3Ga/A7YaiBZTqA1/XsE3I=; b=Y2oo8jObjeFHHlEY1sWz4mjj0gR/xnGpcb6a0LeYoyd7bt2ItrV4jWRk73krMPe6U4 +2tq19t5MH0KPbcvC69AffBLCRPRCjglONNNuiphVoa2KqPD6UZ8vjbZLE93bw5mfy1P TzuOLVwOTkLaBclectPa4sMDdhAwVddFvBkMgMMYrD9Aj1eYWUXjOga6bj626NaCxJX1 P+mgXkS8dgsapEfc8sz5p25yOLGD3hW7LIP8gnBbbB/ahQLj7ST0UAjf49npsDsDA3Vc nSY16U9ohLPCTC6qtTghR8LBUGvTYb9lJa0OS53t5a+tsIh+4jkp2Xv1hvAcd2MXH5W4 9N+g==
X-Received by 10.52.89.83 with SMTP id bm19mr2156475vdb.123.1360927354641; Fri, 15 Feb 2013 03:22:34 -0800 (PST)
MIME-Version 1.0
In-Reply-To <2013021323250974803-chrishinsley@gmailcom>
References <2013021323250974803-chrishinsley@gmailcom>
From Oscar Benjamin <oscar.j.benjamin@gmail.com>
Date Fri, 15 Feb 2013 11:22:14 +0000
Subject Re: First attempt at a Python prog (Chess)
To Chris Hinsley <chris.hinsley@gmail.com>
Content-Type text/plain; charset=ISO-8859-1
Cc Python List <python-list@python.org>
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.15
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <http://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <http://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Newsgroups comp.lang.python
Message-ID <mailman.1802.1360927363.2939.python-list@python.org> (permalink)
Lines 298
NNTP-Posting-Host 2001:888:2000:d::a6
X-Trace 1360927363 news.xs4all.nl 6860 [2001:888:2000:d::a6]:59061
X-Complaints-To abuse@xs4all.nl
Xref csiph.com comp.lang.python:38914

Show key headers only | View raw


On 13 February 2013 23:25, Chris Hinsley <chris.hinsley@gmail.com> 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

Back to comp.lang.python | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

First attempt at a Python prog (Chess) Chris Hinsley <chris.hinsley@gmail.com> - 2013-02-13 23:25 +0000
  Re: First attempt at a Python prog (Chess) Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-02-13 23:55 +0000
    Re: First attempt at a Python prog (Chess) Chris Hinsley <chris.hinsley@gmail.com> - 2013-02-14 01:31 +0000
  Re: First attempt at a Python prog (Chess) Tim Roberts <timr@probo.com> - 2013-02-13 22:05 -0800
    Re: First attempt at a Python prog (Chess) Chris Hinsley <chris.hinsley@gmail.com> - 2013-02-14 17:48 +0000
      Re: First attempt at a Python prog (Chess) Ian Kelly <ian.g.kelly@gmail.com> - 2013-02-14 11:30 -0700
      Re: First attempt at a Python prog (Chess) Tim Roberts <timr@probo.com> - 2013-02-18 20:15 -0800
        Re: First attempt at a Python prog (Chess) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-02-19 06:24 +0000
        Re: First attempt at a Python prog (Chess) Ian Kelly <ian.g.kelly@gmail.com> - 2013-02-19 12:41 -0700
  Re: First attempt at a Python prog (Chess) jkn <jkn_gg@nicorp.f9.co.uk> - 2013-02-14 13:14 -0800
    Re: First attempt at a Python prog (Chess) Chris Hinsley <chris.hinsley@gmail.com> - 2013-02-14 22:13 +0000
      Re: First attempt at a Python prog (Chess) Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2013-02-14 22:09 -0500
  Re: First attempt at a Python prog (Chess) Rick Johnson <rantingrickjohnson@gmail.com> - 2013-02-14 21:05 -0800
  Re: First attempt at a Python prog (Chess) Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-02-15 11:22 +0000
    Re: First attempt at a Python prog (Chess) Neil Cerutti <neilc@norwich.edu> - 2013-02-15 16:17 +0000
      Re: First attempt at a Python prog (Chess) MRAB <python@mrabarnett.plus.com> - 2013-02-15 17:52 +0000
        Re: First attempt at a Python prog (Chess) Neil Cerutti <neilc@norwich.edu> - 2013-02-19 21:10 +0000
      Re: First attempt at a Python prog (Chess) Matt Jones <matt.walker.jones@gmail.com> - 2013-02-15 12:03 -0600
  Re: First attempt at a Python prog (Chess) Tim Golden <mail@timgolden.me.uk> - 2013-02-15 11:36 +0000
  Re: First attempt at a Python prog (Chess) Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-02-15 13:11 +0000
  Re: First attempt at a Python prog (Chess) Tim Golden <mail@timgolden.me.uk> - 2013-02-15 15:36 +0000
    Re: First attempt at a Python prog (Chess) Jussi Piitulainen <jpiitula@ling.helsinki.fi> - 2013-02-15 18:48 +0200
  Re: First attempt at a Python prog (Chess) Chris Angelico <rosuav@gmail.com> - 2013-02-16 02:49 +1100
  Re: First attempt at a Python prog (Chess) Tim Golden <mail@timgolden.me.uk> - 2013-02-15 17:37 +0000
  Re: First attempt at a Python prog (Chess) Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2013-02-15 13:41 -0500
  Re: First attempt at a Python prog (Chess) Oscar Benjamin <oscar.j.benjamin@gmail.com> - 2013-02-16 14:39 +0000

csiph-web