Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #38832 > unrolled thread
| Started by | Chris Hinsley <chris.hinsley@gmail.com> |
|---|---|
| First post | 2013-02-13 23:25 +0000 |
| Last post | 2013-02-16 14:39 +0000 |
| Articles | 20 on this page of 26 — 14 participants |
Back to article view | Back to comp.lang.python
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 →
| From | Chris Hinsley <chris.hinsley@gmail.com> |
|---|---|
| Date | 2013-02-13 23:25 +0000 |
| Subject | First 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]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Chris Hinsley <chris.hinsley@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Tim Roberts <timr@probo.com> |
|---|---|
| Date | 2013-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]
| From | Chris Hinsley <chris.hinsley@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Tim Roberts <timr@probo.com> |
|---|---|
| Date | 2013-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]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2013-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]
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2013-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]
| From | jkn <jkn_gg@nicorp.f9.co.uk> |
|---|---|
| Date | 2013-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]
| From | Chris Hinsley <chris.hinsley@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Dennis Lee Bieber <wlfraed@ix.netcom.com> |
|---|---|
| Date | 2013-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]
| From | Rick Johnson <rantingrickjohnson@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Neil Cerutti <neilc@norwich.edu> |
|---|---|
| Date | 2013-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]
| From | MRAB <python@mrabarnett.plus.com> |
|---|---|
| Date | 2013-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]
| From | Neil Cerutti <neilc@norwich.edu> |
|---|---|
| Date | 2013-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]
| From | Matt Jones <matt.walker.jones@gmail.com> |
|---|---|
| Date | 2013-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]
| From | Tim Golden <mail@timgolden.me.uk> |
|---|---|
| Date | 2013-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]
| From | Oscar Benjamin <oscar.j.benjamin@gmail.com> |
|---|---|
| Date | 2013-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