Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 64 additions & 24 deletions agent_uno/core/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,66 @@
PlayerInfo = dict[str, Optional[int | str | bool]]


GAME_EXPLANATION = """You are the Black side in every game, meaning that you will always
control the black pieces. The chessboard is represented in ASCII format, and here’s
how you should understand it:

Uppercase letters represent white pieces:
'K' = White King
'Q' = White Queen
'R' = White Rook
'N' = White Knight
'B' = White Bishop
'P' = White Pawn

Lowercase letters represent black pieces (your pieces):
'k' = Black King
'q' = Black Queen
'r' = Black Rook
'n' = Black Knight
'b' = Black Bishop
'p' = Black Pawn

Empty squares are represented by a dot '.' or a number indicating the count of
consecutive empty squares in that rank. For example, a series of dots (e.g., ...)
means there are empty squares in those positions.
Board Layout:

The chessboard is structured in ranks (1 to 8) and files (a to h). The ranks
represent horizontal rows, with rank 1 at the bottom (white side) and rank 8 at the
top (black side). The files represent vertical columns, labelled from 'a' to 'h'
from left to right.

Here is an example of an initial game state:

a b c d e f g h
8 r n b q k b n r
7 p p p p p p p p
6 . . . . . . . .
5 . . . . . . . .
4 . . . . . . . .
3 . . . . . . . .
2 P P P P P P P P
1 R N B Q K B N R

Black pieces (your pieces) are located on ranks 7 and 8.
White pieces are located on ranks 1 and 2.

The game is set up such that pawns are in front, and other pieces (rooks, knights,
bishops, queens, and kings) are placed behind the pawns.

Your Role:
As the Black player, you will be making moves using the lowercase pieces
('r', 'n', 'b', 'q', 'k', 'p'). If you are currently in check, you must make to
protect your king, no other moves will be valid.

Summary of the Board:
The agent (you) will always play as Black.
White's pieces are uppercase letters, and your pieces (Black) are lowercase.
The game begins with pieces in their starting positions, and your task is to move
your pieces strategically to win the game."""


class AccountInfo(BaseModel):
"""The account info of a LiChess user."""

Expand Down Expand Up @@ -95,36 +155,16 @@ class CurrentState(BaseModel):
class BoardRepresentation(BaseModel):
"""The board representation of the game."""

explanation: str = Field(
default="""The board representation of the game.
The board is presented from rows 8-1 and a-h.

This is an example board layout:

r . b q . r k .
p p . n . p p p
. . p B p n . .
. . . p . . . .
. . . P . . . .
. . P B P . . .
P P . . N P P P
R N . Q K . . R

Where 'r . b q . r k .' is row 8 and 'R N . Q K . . R' is row 1.

Capital letter pieces are white and lowercase letter pieces are black.
"""
)
explanation: str = Field(default=GAME_EXPLANATION)
board: str
previous_moves: list[str]
check: bool


class GameStateMsg(StrEnum):
"""Messages for the game state, e.g., whose turn it is."""

NOT_STARTED = """The game has not started yet please poll the is_opponent_turn
endpoint until 10 times until the game starts. If this does not produce a response
in 10 polls then exit.
"""
NOT_STARTED = """The game has not started yet please wait until the game has
started."""
AGENT_TURN = "It is your turn to make a move."
OPPONENT_TURN = "It is the opponent's turn. Please wait for them to make a move."
22 changes: 14 additions & 8 deletions agent_uno/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,20 @@ async def get_previous_moves() -> list[str]:
return cast(list[str], current_state.state.moves.split())


async def get_board() -> str:
"""An endpoint for getting the current board as an ASCII representation."""
moves = await get_previous_moves()
async def _get_fen() -> str:
return cast(
str,
next(SESSION_STATE["client"].games.stream_game_moves(SESSION_STATE["id"]))[
"fen"
],
)

board = Board()

for move in moves:
board.push_uci(move)
async def get_board() -> Board:
"""An endpoint for getting the current board as an ASCII representation."""
fen = await _get_fen()

return cast(str, board.__str__())
return Board(fen)


@mcp.tool(
Expand All @@ -181,7 +185,9 @@ async def get_board_representation() -> BoardRepresentation | GameStateMsg:
board = await get_board()
previous_moves = await get_previous_moves()

return BoardRepresentation(board=board, previous_moves=previous_moves)
return BoardRepresentation(
board=board.__str__(), previous_moves=previous_moves, check=board.is_check()
)


if __name__ == "__main__":
Expand Down