Re: First attempt at a Python prog (Chess)
On 2013-02-13 23:25:09 +, Chris Hinsley said: 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. New version with better search and hopefully a little better Python. I know it's still far from fully correct and it'll never best Gary Kasperov. :) With pypy on my Macbook I can go to 6 PLY with resonable performance now due to the alpha-beta pruneing. Regards #!/usr/bin/python -tt # -*- coding: utf-8 -*- # Copyright (C) 2013 Chris Hinsley, GPL V3 License import sys import random import os PLY = 5 EMPTY = 0 WHITE = 1 BLACK = -1 NO_CAPTURE = 2 MAY_CAPTURE = 3 MUST_CAPTURE = 4 piece_type = {' ' : EMPTY, 'K' : BLACK, 'Q' : BLACK, 'R' : BLACK, 'B' : BLACK, 'N' : BLACK, 'P' : BLACK, \ 'k' : WHITE, 'q' : WHITE, 'r' : WHITE, 'b' : WHITE, 'n' : WHITE, 'p' : 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): print '| %c' % board[row * 8 + col], print '|', 8 - row print '+---+---+---+---+---+---+---+---+' def piece_moves(board, index, dx, dy, capture_flag, distance): piece = board[index] type = piece_type[piece] cy, cx = divmod(index, 8) for step in range(distance): nx = cx + (dx * (step + 1)) ny = cy + (dy * (step + 1)) if (0 <= nx < 8) and (0 <= ny < 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 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: board[index], board[newindex] = ' ', piece yield board board[index], board[newindex] = piece, newpiece elif newtype != type: board[index], board[newindex] = ' ', piece yield board board[index], board[newindex] = piece, newpiece break else: break elif newtype == EMPTY: board[index] = ' ' if (ny == 0 or ny == 7) and 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 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)
Re: odd behavoiur seen
On 2013-07-22 18:36:41 +, Chris Hinsley said: Folks, I have this decorator: def memoize(maxsize): def _memoize(func): lru_cache = {} lru_list = [] Other clues, I use it on a recursive function: @memoize(64) def next_move(board, colour, alpha, beta, ply): if ply <= 0: return evaluate(board) * colour for new_board in all_moves(board[:], colour): score = -next_move(new_board, -colour, -beta, -alpha, ply - 1) if score >= beta: return score if score > alpha: alpha = score return alpha And I notice I don't get the strange problem on a non-recursive function ! Or at least I don't seam to. Chris -- http://mail.python.org/mailman/listinfo/python-list
odd behavoiur seen
Folks, I have this decorator: def memoize(maxsize): def _memoize(func): lru_cache = {} lru_list = [] def memoizer(*args, **kwargs): key = str(args) + str(kwargs) if key in lru_cache: lru_list.remove(key) lru_list.append(key) return lru_cache[key] print len(lru_list), if len(lru_list) >= maxsize: del(lru_cache[lru_list[0]]) del(lru_list[0]) ret = func(*args, **kwargs) lru_cache[key] = ret lru_list.append(key) return ret return memoizer return _memoize I didn't used to do the 'len(lru_list) >= maxsize' just '==' and noticed it sailing past the max number of entries, so put in the print statement, and now I see it ocationally printing a value 1 larger than maxsize !!! So if I use it as '@memoize(64)' I see some 65's in the output ! I'm at a loss to explain it, does anyone knows why ? Is it a bug or some threading issue ? I'm not useing threads BTW, and I've noticed this in both running it with Python or Pypy. Best Regards Chris -- http://mail.python.org/mailman/listinfo/python-list
Re: odd behavoiur seen
On 2013-07-22 19:47:33 +, Peter Otten said: Chris Hinsley wrote: On 2013-07-22 18:36:41 +, Chris Hinsley said: Folks, I have this decorator: def memoize(maxsize): def _memoize(func): lru_cache = {} lru_list = [] Other clues, I use it on a recursive function: @memoize(64) def next_move(board, colour, alpha, beta, ply): if ply <= 0: return evaluate(board) * colour for new_board in all_moves(board[:], colour): score = -next_move(new_board, -colour, -beta, -alpha, ply - 1) if score >= beta: return score if score > alpha: alpha = score return alpha And I notice I don't get the strange problem on a non-recursive function ! Or at least I don't seam to. That's indeed the problem: if len(lru_list) >= maxsize: del(lru_cache[lru_list[0]]) del(lru_list[0]) ret = func(*args, **kwargs) lru_cache[key] = ret lru_list.append(key) You delete a cached item, then call the original function which causes calls of the decorated function. This causes a length check which sees the already reduced length and decides that the cache is not yet full. If you remove the oldest item after calling the original function you should be OK. Ah ! Thank you kindly sir ! Chris -- http://mail.python.org/mailman/listinfo/python-list
New github source
Hellow, just letting people know I've put up some github repo's of my Pthon projects: https://github.com/vygr/Python-PCB https://github.com/vygr/Python-Chess Regards Chris -- https://mail.python.org/mailman/listinfo/python-list
Re: First attempt at a Python prog (Chess)
On 2013-02-15 05:05:27 +, Rick Johnson said: 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. Wow, such vitriol for such a simple bear to cope with ! Maybe Papa bear would like to try some humility ! This was my very first Python prog, and my first chess prog and my attempt to learn somthing about Generators ! Do youtself a favour and leave the Python comunity for the good of the language ! Chris -- https://mail.python.org/mailman/listinfo/python-list
Latest Chess prog
Latest version of Chess test prog for anyone who might be interested. It does not do en-passon or castling. Best Regards Chris #!/opt/local/bin/pypy -u -tt #!/opt/local/bin/pypy -u -tt -m cProfile # -*- coding: utf-8 -*- # Copyright (C) 2013-2014 Chris Hinsley, GPL V3 License import sys, os, time from operator import itemgetter from array import array MAX_PLY = 10 MAX_TIME_PER_MOVE = 10 PIECE_VALUE_FACTOR = 3 KING_VALUE, QUEEN_VALUE, ROOK_VALUE = 100, 9 * PIECE_VALUE_FACTOR, 5 * PIECE_VALUE_FACTOR BISHOP_VALUE, KNIGHT_VALUE, PAWN_VALUE = 3 * PIECE_VALUE_FACTOR, 3 * PIECE_VALUE_FACTOR, 1 * PIECE_VALUE_FACTOR EMPTY, WHITE, BLACK = 0, 1, -1 NO_CAPTURE, MAY_CAPTURE, MUST_CAPTURE = 0, 1, 2 piece_type = {' ' : EMPTY, 'K' : BLACK, 'Q' : BLACK, 'R' : BLACK, 'B' : BLACK, 'N' : BLACK, 'P' : BLACK, \ 'k' : WHITE, 'q' : WHITE, 'r' : WHITE, 'b' : WHITE, 'n' : WHITE, 'p' : WHITE} def display_board(board): print print ' a b c d e f g h' print '+---+---+---+---+---+---+---+---+' for row in range(8): for col in range(8): print '| %c' % board[row * 8 + col], print '|', 8 - row print '+---+---+---+---+---+---+---+---+' print def piece_moves(board, index, vectors): piece = board[index] type = piece_type[piece] promote = 'QRBN' if type == BLACK else 'qrbn' cy, cx = divmod(index, 8) for dx, dy, length, flag in vectors: x, y = cx, cy if length == 0: if piece == 'P': length = 2 if (y == 1) else 1 else: length = 2 if (y == 6) else 1 while length > 0: x += dx; y += dy; length -= 1 if (x < 0) or (x >=8) or (y < 0) or (y >= 8): break newindex = y * 8 + x newpiece = board[newindex] newtype = piece_type[newpiece] if newtype == type: break if (flag == NO_CAPTURE) and (newtype != EMPTY): break if (flag == MUST_CAPTURE) and (newtype == EMPTY): break board[index] = ' ' if (y == 0 or y == 7) and piece in 'Pp': for promote_piece in promote: board[newindex] = promote_piece yield board else: board[newindex] = piece yield board board[index], board[newindex] = piece, newpiece if (flag == MAY_CAPTURE) and (newtype != EMPTY): break def piece_scans(board, index, vectors): cy, cx = divmod(index, 8) for dx, dy, length in vectors: x, y = cx, cy while length > 0: x += dx; y += dy; length -= 1 if (0 <= x < 8) and (0 <= y < 8): piece = board[y * 8 + x] if piece != ' ': yield piece break black_pawn_vectors = [(-1, 1, 1), (1, 1, 1)] white_pawn_vectors = [(-1, -1, 1), (1, -1, 1)] bishop_vectors = [(x, y, 7) for x, y in [(-1, -1), (1, 1), (-1, 1), (1, -1)]] rook_vectors = [(x, y, 7) for x, y in [(0, -1), (-1, 0), (0, 1), (1, 0)]] knight_vectors = [(x, y, 1) for x, y in [(-2, 1), (2, -1), (2, 1), (-2, -1), (-1, -2), (-1, 2), (1, -2), (1, 2)]] queen_vectors = bishop_vectors + rook_vectors king_vectors = [(x, y, 1) for x, y, _ in queen_vectors] black_pawn_moves = [(0, 1, 0, NO_CAPTURE), (-1, 1, 1, MUST_CAPTURE), (1, 1, 1, MUST_CAPTURE)] white_pawn_moves = [(x, -1, length, flag) for x, _, length, flag in black_pawn_moves] rook_moves = [(x, y, length, MAY_CAPTURE) for x, y, length in rook_vectors] bishop_moves = [(x, y, length, MAY_CAPTURE) for x, y, length in bishop_vectors] knight_moves = [(x, y, length, MAY_CAPTURE) for x, y, length in knight_vectors] queen_moves = bishop_moves + rook_moves king_moves = [(x, y, 1, flag) for x, y, _, flag in queen_moves] moves = {'P' : black_pawn_moves, 'p' : white_pawn_moves, 'R' : rook_moves, 'r' : rook_moves, \ 'B' : bishop_moves, 'b' : bishop_moves, 'N' : knigh
First attempt at a Python prog (Chess)
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(pi
Re: First attempt at a Python prog (Chess)
On 2013-02-13 23:55:20 +, Oscar Benjamin said: On 13 February 2013 23:25, Chris Hinsley 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 -- http://mail.python.org/mailman/listinfo/python-list
Re: First attempt at a Python prog (Chess)
On 2013-02-14 06:05:13 +, Tim Roberts said: Chris Hinsley wrote: New to Python, which I really like BTW. First serious prog. Hope you like it. I know it needs a 'can't move if your King would be put into check' test. But the weighted value of the King piece does a surprising emergent job. 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 -- http://mail.python.org/mailman/listinfo/python-list
Re: First attempt at a Python prog (Chess)
On 2013-02-14 21:14:03 +, 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 -- http://mail.python.org/mailman/listinfo/python-list