Project Option 1 |
CSE 415: Introduction to Artificial Intelligence The University of Washington, Seattle, Autumn 2019 |
OPTION 1: Baroque Chess Agents with Zobrist Hashing.
This option is might be attractive to students interested in a deeper experience with 2-person zero sum, non-stochastic game-playing. A big part of this option's challenge is writing the move generator for Baroque Chess. The capturing rules for each piece are different. To make this a lot easier, we are making "Imitator" pieces optional in the game. So you do not have to handle imitators, which are the most complex of the pieces. Starter code and a move-testing service are available for this option. |
Your player's file name should be of the form
[free_name]_BC_Player.py, where you get to choose an original
name, but that name is followed by "_BC_Player.py". An example
might be Magnifico_BC_Player.py. If you need to split your agent
to use any additional modules, please name them using the
convention: [free_name]_BC_module_[module_name].py. Here an
example could be Magnifico_BC_module_static_eval.py.
Game rules: A good overview of the rules of Baroque Chess is given in the Wikipedia article on Baroque Chess. However, since there are a number of variations of Baroque Chess, we have to specify some version, and we will use an operational definition of the rules. ''When in doubt, try it out, at the validation page.''
Image courtesy of Chess Guide at multionline.net.
It will be essential that your program use the specified representation of states, so that it is compatible with the other Baroque Chess programs developed in the class. Some code for representing states of Baroque Chess is shown near end of this page. (This is included as BC_state_etc.py in the Starter code.) Your program should be designed to anticipate time limits on moves. There are two aspects to this: (1) use iterative deepening search, and (2) poll a clock frequently in order to return a move before time runs out. Starter code is available. This code provides a game-master to handle the turn-taking between a pair of playing agents, and it provides a class definition for states of the game. Some simple agent stub files are included to help get the project going. We are playing Baroque Chess with rules that: do not permit (a) any choice of center-counter symmetry (see the Wikipedia description of Baroque Chess), or (b) long-leaper double jumping (it's considered detrimental to having balanced and dynamic games), or (c) "suicide" moves. We will assume that the game ends when either player loses a king. Your program should implement the following functions, so that they can be called by the game administration software: prepare(player2Nickname) . This function will be called by the
game administration software before a game starts, and usually before
any of the other functions below are called. If your agent needs to
initialize any data structures, this is a good place for that to be done,
as the time taken to execute this will not be "on the clock" that is
running during game moves.
parameterized_minimax(currentState, alphaBeta=False, ply=3, useBasicStaticEval=True, zobrist=True) .
This function is for testing the basic capabilities of your agent. It should be able to work for any combination
of values of arguments, within reason.
For example, it should be OK with any legal state for currentState ;
either using or not using alpha-beta pruning; a specified number of ply (maximum
depth) from 0 (statically evaluate the current state only) to perhaps 8 (which might not be too bad if there are
only a few pieces left on the board); and Zobrist hashing enabled or not.
When Do incorporate Zobrist hashing in your agent.
However, it should be disabled while running the
In order for Although your
The return value of
introduce() . This function will return a multiline string
that introduces your player, giving its full name (you get to make
that up), the names and UWNetIDs of its creators (you), and
(optionally) some words to describe its character.
nickname() . This function should return a short version of the playing agent's name (16
characters or fewer). This name will be used to identify the player's moves in game
transcripts.
makeMove(currentState, currentRemark, timeLimit=10) .
This is probably your most important function. It should return a
list of the form [[move, newState], newRemark, plies_completed, zobrist_stats] . The move is a data
item describing the chosen move, of the form
((r, c),(r1, c1)). Here (r, c) gives the starting row and column
coordinates of the piece that is being moved, and (r1, c1) give the coordinates of the
square where it ends up.
The zobrist_stats value is a dict giving the various counts for that move that
are described above. They can be useful when you are assessing the benefits of the Zobrist
hashing.
The
The
The
The
Your staticEval(state) .
This function will perform a static evaluation of the given state.
The value returned should be high if the state is good for WHITE and
low if the state is good for BLACK.
This function is not the same as your basic static evaluation function mentioned
above in association with parameterized_minimax , but should be
custom-designed by your team to help your agent be a strong player.
The staff plans to test your staticEval function separately at some
point during the grading, so it should be possible to use it by executing
code such
as the following. (This example assumes your agent is named "The_Roman_BC_Player".)
import The_Roman_BC_Player as player staticResult = player.staticEval(some_state) The quality of your agent's playing will probably depend heavily on how well your staticEval function works. In the code example above, the starting board is shown using ASCII text, and the encoding is as follows: (lower case for black, upper case for WHITE): p: pincer l: leaper i: imitator w: withdrawer k: king c: coordinator f: freezer -: empty square on the boardHere are the contents of the starter code file BC_state_etc.py .
It's presented here for convenient references, but it is part of the .tar archive
linked above.
BLACK = 0 WHITE = 1 INIT_TO_CODE = {'p':2, 'P':3, 'c':4, 'C':5, 'l':6, 'L':7, 'i':8, 'I':9, 'w':10, 'W':11, 'k':12, 'K':13, 'f':14, 'F':15, '-':0} CODE_TO_INIT = {0:'-',2:'p',3:'P',4:'c',5:'C',6:'l',7:'L',8:'i',9:'I', 10:'w',11:'W',12:'k',13:'K',14:'f',15:'F'} def who(piece): return piece % 2 def parse(bs): # bs is board string '''Translate a board string into the list of lists representation.''' b = [[0,0,0,0,0,0,0,0] for r in range(8)] rs9 = bs.split("\n") rs8 = rs9[1:] # eliminate the empty first item. for iy in range(8): rss = rs8[iy].split(' '); for jx in range(8): b[iy][jx] = INIT_TO_CODE[rss[jx]] return b INITIAL = parse(''' c l i w k i l f p p p p p p p p - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P P P P P P P P F L I W K I L C ''') INITIAL_NO_IMITATORS = parse(''' c l - w k - l f p p p p p p p p - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - P P P P P P P P F L - W K - L C ''') class BC_state: def __init__(self, old_board=INITIAL, whose_move=WHITE): new_board = [r[:] for r in old_board] self.board = new_board self.whose_move = whose_move; def __repr__(self): s = '' for r in range(8): for c in range(8): s += CODE_TO_INIT[self.board[r][c]] + " " s += "\n" if self.whose_move==WHITE: s += "WHITE's move" else: s += "BLACK's move" s += "\n" return s def __eq__(self, other): if not (type(other)==type(self)): return False if self.whose_move != other.whose_move: return False try: b1 = self.board b2 = other.board for i in range(8): for j in range(8): if b1[i][j] != b2[i][j]: return False return True except Exception as e: return False def test_starting_board(): init_state = BC_state(INITIAL, WHITE) print(init_state) test_starting_board() |
What to Turn In:
Turn in your files via Canvas. Do not zip up the files.
|
Updates and Corrections: Any changes will be described here and/or in ED. |