Project Option 1: Baroque Chess Agents
CSE 415: Introduction to Artificial Intelligence
The University of Washington, Seattle, Winter 2018
OPTION 1: Baroque Chess Agent.

For option 1, you will work with a partner to create a Python module (typically just a single .py file) that will be able to play a game of Baroque Chess and compete in a tournament. Although the tournament is not part of the assignment itself, your program must be technically able to participate in it. (The tournament will take place after the assignment is due; we may offer some extra credit for programs that place highly in the rankings.) 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.

It will be essential that your program use the specified representation of states, so that it is compatible with all the other Baroque Chess programs developed in the class. Here is some code for representing states of Baroque Chess. (This is included as BC_state_etc.py in the starter code.)

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
''')

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

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 will handle the turn-taking between a pair of playing agents.

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:

  • 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=10000). This is probably your most important function. It should return a list of the form [[move, newState], newRemark]. The move is a data item describing the chosen move, of the form ((i, j), (new_i, new_j)). Here (i,j) gives the row and column coordinates on the board of the piece being moved, and (new_i, new_j) gives the coordinates of the square where that piece ends up.

    The newState is the result of making the move from the given currentState. It must be a complete state and not just a board.

    The currentRemark argument is a string representing a remark from the opponent on its last move. (This may be ignored, and it's a placeholder for possible use in future versions of this assignment.)

    The timeLimit represents the number of milliseconds available for computing and returning the move.

    The newRemark to be returned must be a string. During a game, the strings from your agent and its opponent comprise a dialog. These remarks should reflect a "personality" of your agent. They could be canned remarks from a list of 10 or more, but you may make this more elaborate and context-sensitive, commenting on the current state or the direction in which the game seems to be heading.

  • 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. Although you may wish to extend the BC_state class to make staticEval a member of that, it's not necessary, and we do need to be able to call your staticEval function directly, for example using
    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 board