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


Groups > comp.lang.python > #38832 > unrolled thread

First attempt at a Python prog (Chess)

Started byChris Hinsley <chris.hinsley@gmail.com>
First post2013-02-13 23:25 +0000
Last post2013-02-16 14:39 +0000
Articles 20 on this page of 26 — 14 participants

Back to article view | Back to comp.lang.python


Contents

  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

Page 1 of 2  [1] 2  Next page →


#38832 — First attempt at a Python prog (Chess)

FromChris Hinsley <chris.hinsley@gmail.com>
Date2013-02-13 23:25 +0000
SubjectFirst attempt at a Python prog (Chess)
Message-ID<2013021323250974803-chrishinsley@gmailcom>
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

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

def piece_moves(board, index, dx, dy, capture_flag, distance):
    piece = board[index]
    type = piece_type(piece)
    cx = index % 8
    cy = index / 8
    for step in range(distance):
        nx = cx + (dx * (step + 1))
        ny = cy + (dy * (step + 1))
        if nx in range(8) and ny in range(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 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
                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

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):
        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_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:
            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()

[toc] | [next] | [standalone]


#38834

FromOscar Benjamin <oscar.j.benjamin@gmail.com>
Date2013-02-13 23:55 +0000
Message-ID<mailman.1752.1360799742.2939.python-list@python.org>
In reply to#38832
On 13 February 2013 23:25, Chris Hinsley <chris.hinsley@gmail.com> wrote:
> New to Python, which I really like BTW.

Glad to hear it.

> 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.
[SNIP program]

Your program looks good. Were you looking for feedback (I'm sure
someone would give some if so)?


Oscar

[toc] | [prev] | [next] | [standalone]


#38837

FromChris Hinsley <chris.hinsley@gmail.com>
Date2013-02-14 01:31 +0000
Message-ID<2013021401315964158-chrishinsley@gmailcom>
In reply to#38834
On 2013-02-13 23:55:20 +0000, Oscar Benjamin said:

> On 13 February 2013 23:25, Chris Hinsley <chris.hinsley@gmail.com> wrote:
>> New to Python, which I really like BTW.
> 
> Glad to hear it.
> 
>> 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.
> [SNIP program]
> 
> Your program looks good. Were you looking for feedback (I'm sure
> someone would give some if so)?
> 
> 
> Oscar

I suppose so yes. I was just 'putting it out there' to see if it was 
interesting/useful to anyone. No doubt there are many Python features I 
could use to improve on this. First thing that occurred to me on 
deciding to try Python was the idea of Generator functions to enumerate 
all the possible moves of a Chess game, and that 'yield' would make the 
prog relatively compact.

I wish Shedskin could cope with them ! PyPy runs it OK.

Chris

[toc] | [prev] | [next] | [standalone]


#38845

FromTim Roberts <timr@probo.com>
Date2013-02-13 22:05 -0800
Message-ID<5evoh8p6a9sqsdq1b98ahs3ki6s4d70f9l@4ax.com>
In reply to#38832
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.

It looks a little like a C program ported line-by-line to Python.  For
example, in Python, there's no reason not to keep the board as an 8x8 array
instead of a 64-element list.  That by itself would make the code easier to
read.  It would also let you replace this:
    for row in range(8):
        for col in range(8):

with the more Pythonic:
    for row in board:
        for cell in row:

I would probably replace the piece_type function with a map that maps the
piece number directly to the piece 
-- 
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

[toc] | [prev] | [next] | [standalone]


#38871

FromChris Hinsley <chris.hinsley@gmail.com>
Date2013-02-14 17:48 +0000
Message-ID<2013021417481038756-chrishinsley@gmailcom>
In reply to#38845
On 2013-02-14 06:05:13 +0000, Tim Roberts said:

> 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.
> 
> It looks a little like a C program ported line-by-line to Python.  For
> example, in Python, there's no reason not to keep the board as an 8x8 array
> instead of a 64-element list.  That by itself would make the code easier to
> read.  It would also let you replace this:
>     for row in range(8):
>         for col in range(8):
> 
> with the more Pythonic:
>     for row in board:
>         for cell in row:
> 
> I would probably replace the piece_type function with a map that maps the
> piece number directly to the piece

Is a Python list as fast as a bytearray ? I didn't copy a C prog BTW !

Yep, after I posted the code I started thinking about how to do that 
sort of thing :) Thanks, for the advice.

Chris

[toc] | [prev] | [next] | [standalone]


#38875

FromIan Kelly <ian.g.kelly@gmail.com>
Date2013-02-14 11:30 -0700
Message-ID<mailman.1776.1360866699.2939.python-list@python.org>
In reply to#38871
On Thu, Feb 14, 2013 at 10:48 AM, Chris Hinsley <chris.hinsley@gmail.com> wrote:
> Is a Python list as fast as a bytearray ? I didn't copy a C prog BTW !

>>> from timeit import Timer
>>> t1 = Timer("board[36] = board[20]; board[20] = ' '", "board = bytearray('RNBQKBNRPPPPPPPP                                pppppppprnbqkbnr')")
>>> min(t1.repeat(10))
0.1678651895701826
>>> t2 = Timer("board[3][4] = board[1][4]; board[1][4] = ' '", "board = [list('RNBQKBNR'), ['P'] * 8] + [[' ']*8 for i in range(4)] + [['p'] * 8, list('rnbqkbnr')]")
>>> min(t2.repeat(10))
0.2080088391122672

Natively, it looks like the bytearray is about 19% faster for moving a
piece from one square to another.  Those array offsets aren't
calculated for free, though.  Look what happens when we have to do the
math:

>>> t3 = Timer("board[r1*8+c1] = board[r2*8+c2]; board[r2*8+c2] = ' '", "board = bytearray('RNBQKBNRPPPPPPPP                                pppppppprnbqkbnr'); r1 = 3; r2 = 1; c1 = c2 = 4")
>>> min(t3.repeat(10))
0.314191887516472
>>> t4 = Timer("board[r1][c1] = board[r2][c2]; board[r2][c2] = ' '", "board = [list('RNBQKBNR'), ['P'] * 8] + [[' ']*8 for i in range(4)] + [['p'] * 8, list('rnbqkbnr')]; r1 = 3; r2 = 1; c1 = c2 = 4")
>>> min(t4.repeat(10))
0.24427881197186707

That said, the philosophy of Python focuses more on readability than
on speed.  The goalpost for speed in a Python program is that it be
"fast enough".  If you have more stringent speed requirements than
that, then Python may not be the right tool for the job.

[toc] | [prev] | [next] | [standalone]


#39181

FromTim Roberts <timr@probo.com>
Date2013-02-18 20:15 -0800
Message-ID<jsu5i8pfmvsnqhq1nobtg3e5shcnhoehuh@4ax.com>
In reply to#38871
Chris Hinsley <chris.hinsley@gmail.com> wrote:
>
>Is a Python list as fast as a bytearray ?

Python does not actually have a native array type.  Everything in your
program that looked like an array was actually a list.
-- 
Tim Roberts, timr@probo.com
Providenza & Boekelheide, Inc.

[toc] | [prev] | [next] | [standalone]


#39187

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-02-19 06:24 +0000
Message-ID<51231a9e$0$11119$c3e8da3@news.astraweb.com>
In reply to#39181
On Mon, 18 Feb 2013 20:15:28 -0800, Tim Roberts wrote:

> Chris Hinsley <chris.hinsley@gmail.com> wrote:
>>
>>Is a Python list as fast as a bytearray ?
> 
> Python does not actually have a native array type.  Everything in your
> program that looked like an array was actually a list.

Actually it does, but you have to import it first, it is not a built-in 
data type.


py> import array
py> arr = array.array('f')  # array of floats (C singles)
py> arr.append(0.1)
py> arr.append(0.2)
py> print(arr)
array('f', [0.10000000149011612, 0.20000000298023224])
py> arr.append("foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: a float is required


See the documentation for array to see the available type-codes.

http://docs.python.org/2/library/array.html
http://docs.python.org/3/library/array.html

As part of the standard library, Jython and IronPython are required to 
implement arrays as well (although they don't use C arrays, so the 
implementation may be different).

http://www.jython.org/docs/library/array.html
http://ironpython-test.readthedocs.org/en/latest/library/array.html

IronPython also gives you access to .Net arrays, although of course that 
is not standard Python:

http://www.ironpython.info/index.php/Typed_Arrays_in_IronPython



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#39270

FromIan Kelly <ian.g.kelly@gmail.com>
Date2013-02-19 12:41 -0700
Message-ID<mailman.2058.1361302926.2939.python-list@python.org>
In reply to#39181
On Mon, Feb 18, 2013 at 9:15 PM, Tim Roberts <timr@probo.com> wrote:
> Chris Hinsley <chris.hinsley@gmail.com> wrote:
>>
>>Is a Python list as fast as a bytearray ?
>
> Python does not actually have a native array type.  Everything in your
> program that looked like an array was actually a list.

How do you mean?

>>> isinstance(bytearray(b'RNBQKBNR'), list)
False

[toc] | [prev] | [next] | [standalone]


#38884

Fromjkn <jkn_gg@nicorp.f9.co.uk>
Date2013-02-14 13:14 -0800
Message-ID<6f3d0b87-f481-4859-9757-c9f657061596@googlegroups.com>
In reply to#38832
Hi Chris

On Wednesday, 13 February 2013 23:25:09 UTC, Chris Hinsley  wrote:
> New to Python, which I really like BTW.

Welcome aboard! But aren't you supposed to be writing Forth? ;-)

    Cheers
    Jon N
 

[toc] | [prev] | [next] | [standalone]


#38890

FromChris Hinsley <chris.hinsley@gmail.com>
Date2013-02-14 22:13 +0000
Message-ID<2013021422134358279-chrishinsley@gmailcom>
In reply to#38884
On 2013-02-14 21:14:03 +0000, jkn said:

> Hi Chris
> 
> On Wednesday, 13 February 2013 23:25:09 UTC, Chris Hinsley  wrote:
>> New to Python, which I really like BTW.
> 
> Welcome aboard! But aren't you supposed to be writing Forth? ;-)
> 
>     Cheers
>     Jon N
> 

Well, I'm experimenting with other things too ! :) I might yet even 
have another bash at Lisp...

Chris

[toc] | [prev] | [next] | [standalone]


#38904

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2013-02-14 22:09 -0500
Message-ID<mailman.1796.1360897781.2939.python-list@python.org>
In reply to#38890
On Thu, 14 Feb 2013 22:13:43 +0000, Chris Hinsley
<chris.hinsley@gmail.com> declaimed the following in
gmane.comp.python.general:

> On 2013-02-14 21:14:03 +0000, jkn said:
> 
> > Hi Chris
> > 
> > On Wednesday, 13 February 2013 23:25:09 UTC, Chris Hinsley  wrote:
> >> New to Python, which I really like BTW.
> > 
> > Welcome aboard! But aren't you supposed to be writing Forth? ;-)
> > 
> >     Cheers
> >     Jon N
> > 
> 
> Well, I'm experimenting with other things too ! :) I might yet even 
> have another bash at Lisp...
>
	Rather than a Tcl at BASH?
-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
        wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

[toc] | [prev] | [next] | [standalone]


#38905

FromRick Johnson <rantingrickjohnson@gmail.com>
Date2013-02-14 21:05 -0800
Message-ID<cf2667eb-b722-4a80-b80d-0ea68ead577b@googlegroups.com>
In reply to#38832
On Thursday, February 14, 2013 11:48:10 AM UTC-6, Chris Hinsley wrote:

> Is a Python list as fast as a bytearray?

Why would you care about that now? Are you running this code on the Xerox Alto? Excuse me for the sarcasm but your post title has perplexed me:

 "First attempt at a Python prog (Chess)" 
 
Okay, but how does that translate to: 

"The fastest, most efficient, most brain fricked python code ever released in the form of a game, that just happens to look an awful lot like C source"? 

    http://en.wikipedia.org/wiki/Optimization_%28computer_science%29#When_to_optimize

Why bother to use Python if what you really want to write is C code? If you want to write good code (not just python), you need to write code that is maintainable. Yes i KNOW, this is just some stupid chess game, but i can assure you that this style of code is only going to harm your evolution. This code is obfuscated at best and BF'ed at worst. And forget about the algorithms for now, the first problems to address are superficial.

First of all your naming conventions suck. You've used the "interface" style for every function in this game so i can't /easily/ eyeball parse the /real/ interface functions from the helper functions -- and i'm not going to even try, because i don't read ugly code! Try to learn the Python style guide as soon as you can (In particular pay attention to naming conventions):

 http://www.python.org/dev/peps/pep-0008/

Secondly this game could benefit from some OOP paradigm (not sure if are familiar with OOP or not???). But don't go bat-crazy with OOP! You don't need an object to represent /every/ single piece on the board (That would be nuts!). You need just enough OOP to encapsulate the data and create a proper interface. 

A good litmus test is based on the "three little bears":

 "Papa bears bed is too hard"

A hard utilization of paradigms wields too little OOP and therefore ends up being too difficult (aka: "hard") to maintain because there is no logical interface; just a massive collection of functions stuffed into global space until BF reaches critical mass and you're forced to do a complete re-write! (Of course sometimes you don't need OOP at all, just interface)
 
 "Mama bears bed is too soft"

A soft utilization of paradigms wields too much OOP whereby you are surrounded by big FAT objects which are smothering you to death, and they smell because they cannot properly wash themselves between the rolls of fat.

 "but baby bears is just right" 

Ahhh, the blissful comfort of a paradigm utilization that is "just right". This is where your code should be, you want a level of OOP usage that is "just right" for the occasion; not any more, not any less. 

## START EXAMPLE CODE ##
class GameBoard(???):
    def __init__(self):
        self.board = self._createBoard()
        
    def __str__(self):
        """Override:"""
        # return a string represention of the board
        # suitable for writing to stdout
        
    def _createBoard(self):
        """Internal:"""
        self.board = [blah]
       
    def make_move(self, piece, vector):
        """Interface: move a game piece based on vector"""
        # Find and move the piece. Whether the pieces
        # are objects or not doesn't matter.
        
class GamePiece(object):
    def __init__(self, typename, color):
        self.typeName = typeName
        self.color = color
        self.captureFlag = self._computeFlag()


def main():
    board = Board()
    playing = True
    while playing is not False
        i = input('PieceName - MoveVec:')
        n, v = parse(i)
        result = board.make_move(n, v)
        if result == 'GameOver':
            playing = False
        else:
            # clear the stdout
            str(board)
        

if __name__ == '__main__:
    main()
## END EXAMPLE CODE ##

And now you have the added benefit of exporting the objects for use elsewhere. 

[toc] | [prev] | [next] | [standalone]


#38914

FromOscar Benjamin <oscar.j.benjamin@gmail.com>
Date2013-02-15 11:22 +0000
Message-ID<mailman.1802.1360927363.2939.python-list@python.org>
In reply to#38832
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

[toc] | [prev] | [next] | [standalone]


#38927

FromNeil Cerutti <neilc@norwich.edu>
Date2013-02-15 16:17 +0000
Message-ID<ao75buF5atfU1@mid.individual.net>
In reply to#38914
On 2013-02-15, Oscar Benjamin <oscar.j.benjamin@gmail.com> 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

[toc] | [prev] | [next] | [standalone]


#38940

FromMRAB <python@mrabarnett.plus.com>
Date2013-02-15 17:52 +0000
Message-ID<mailman.1827.1360950752.2939.python-list@python.org>
In reply to#38927
On 2013-02-15 16:17, Neil Cerutti wrote:
> On 2013-02-15, Oscar Benjamin <oscar.j.benjamin@gmail.com> 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:
>

[toc] | [prev] | [next] | [standalone]


#39275

FromNeil Cerutti <neilc@norwich.edu>
Date2013-02-19 21:10 +0000
Message-ID<aoi82kFk2eeU2@mid.individual.net>
In reply to#38940
On 2013-02-15, MRAB <python@mrabarnett.plus.com> wrote:
> On 2013-02-15 16:17, Neil Cerutti wrote:
>> On 2013-02-15, Oscar Benjamin <oscar.j.benjamin@gmail.com> 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

[toc] | [prev] | [next] | [standalone]


#38941

FromMatt Jones <matt.walker.jones@gmail.com>
Date2013-02-15 12:03 -0600
Message-ID<mailman.1830.1360951412.2939.python-list@python.org>
In reply to#38927

[Multipart message — attachments visible in raw view] — view raw

"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 <python@mrabarnett.plus.com> wrote:

> On 2013-02-15 16:17, Neil Cerutti wrote:
>
>> On 2013-02-15, Oscar Benjamin <oscar.j.benjamin@gmail.com> 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>
>

[toc] | [prev] | [next] | [standalone]


#38915

FromTim Golden <mail@timgolden.me.uk>
Date2013-02-15 11:36 +0000
Message-ID<mailman.1803.1360928209.2939.python-list@python.org>
In reply to#38832
On 15/02/2013 11:22, Oscar Benjamin wrote:
> 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', ...}


I'm laughing slightly here because, at the monthly London Python
Dojo, we often find ourselves implementing board-game mechanics
of one sort or another: Boggle, Battleships, Sliding block,
Connect 4, Noughts-and-Crosses, even things like Game of Life
(which has a board of sorts).

And the "how shall we represent the board?" question is pretty
much the first thing any team asks themselves. And you always
get someone in favour of lists of lists, someone for one long
list, someone who likes a string, someone (me) who likes a sparse
dict keyed on coords, someone else likes nested defaultdicts,
and occasionally more outlandish schemes.

We even went to the extent of having a Dojo a few months back
which was solely about implementing the ideal board for varying
characteristics, but we didn't come to any conclusions :)

(Also I seem to remember that the OP was advised earlier precisely
to abandon lists of lists in favour of something else).

TJG

[toc] | [prev] | [next] | [standalone]


#38918

FromOscar Benjamin <oscar.j.benjamin@gmail.com>
Date2013-02-15 13:11 +0000
Message-ID<mailman.1806.1360933939.2939.python-list@python.org>
In reply to#38832
On 15 February 2013 11:36, Tim Golden <mail@timgolden.me.uk> wrote:
> On 15/02/2013 11:22, Oscar Benjamin wrote:
>> 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', ...}
>
> I'm laughing slightly here because, at the monthly London Python
> Dojo, we often find ourselves implementing board-game mechanics
> of one sort or another: Boggle, Battleships, Sliding block,
> Connect 4, Noughts-and-Crosses, even things like Game of Life
> (which has a board of sorts).
>
> And the "how shall we represent the board?" question is pretty
> much the first thing any team asks themselves. And you always
> get someone in favour of lists of lists, someone for one long
> list,

I always get confused when doing this about which of my coordinates
needs to be multiplied (i.e. whether I am in Fortran or C order).

> someone who likes a string, someone (me) who likes a sparse
> dict keyed on coords,

Clearly better than the others.

> someone else likes nested defaultdicts,
> and occasionally more outlandish schemes.
>
> We even went to the extent of having a Dojo a few months back
> which was solely about implementing the ideal board for varying
> characteristics, but we didn't come to any conclusions :)

In this case the innermost loop of the program is over the pieces on
the board. Clearly you want a data structure that allows you to
iterate directly over them. (Actually since that loop is to calculate
the score I would replace it with a function that computes the change
in score as a result of each move).

> (Also I seem to remember that the OP was advised earlier precisely
> to abandon lists of lists in favour of something else).

Actually the suggestion was for the list of lists (instead of a flat list).


Oscar

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.python


csiph-web