python-chess icon indicating copy to clipboard operation
python-chess copied to clipboard

Board.__str__ including grid labels

Open jamesbraza opened this issue 1 year ago • 10 comments

To me, it's easier to see/understand a board's printed representation with the rank/file shown.

In [1]: import chess
In [2]: board = chess.Board()
In [3]: print(board)
r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R

It would be nice to support the ability to show the grid:

In [4]: print(board.grid())
  | 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

Alternately, an instance attribute verbose could be added that somehow enables behaviors like this when __str__ is invoked.

jamesbraza avatar Mar 02 '23 03:03 jamesbraza

I think this looks good. I wonder if it's acceptable to just change __str__, or if that would be considered a breaking change.

niklasf avatar Mar 06 '23 20:03 niklasf

Yeah I think it would be fine to change __str__ too.

One question is, how do you see board.mirror() affecting things?

jamesbraza avatar Mar 06 '23 20:03 jamesbraza

One question is, how do you see board.mirror() affecting things?

board.mirror() should not need special consideration. It's not to be confused with changing the orientation of looking at the board - chess.Board() has no concept of that. board.mirror() returns a different position, that happens to have pieces in mirrored positions.

niklasf avatar Mar 06 '23 21:03 niklasf

For reference, here is a prettifying wrapper I use around str(Board):

def diagram(b, *, flipped=False, labels=0, pretty=False):
    # pylint: disable=multiple-statements,consider-using-f-string
    assert isinstance(b, Board), f"Not a Board: {repr(b)}"
    string = b.unicode(empty_square='.') if pretty else str(b)
    if flipped:
        rows = [''.join(reversed(row)) for row in reversed(string.split('\n'))]
        if labels == 0: return '\n'.join(rows)
        elif labels == 1: return '\n'.join('%d %s' % (i,row) for i,row in enumerate(rows, start=1)) + '\n  h g f e d c b a'
        elif labels == -1: return '  h g f e d c b a\n' + '\n'.join('  %s %d' % (row,i) for i,row in enumerate(rows, start=1))
        else: return '    h g f e d c b a\n  + --------------- +\n' + '\n'.join('%d | %s | %d' % (i,row,i) for i,row in enumerate(rows, start=1)) + '\n  + --------------- +\n    h g f e d c b a'
    else:
        rows = string.split('\n')
        if labels == 0: return '\n'.join(rows)
        # pylint: disable=consider-using-in
        elif labels == 1 or labels == -1: return '\n'.join('%d %s' % (8-i,row) for i,row in enumerate(rows)) + '\n  a b c d e f g h'
        else: return '    a b c d e f g h\n  + --------------- +\n' + '\n'.join('%d | %s | %d' % (8-i,row,8-i) for i,row in enumerate(rows)) + '\n  + --------------- +\n    a b c d e f g h'

dubiousjim avatar Mar 10 '23 17:03 dubiousjim

Use pretty=True to get Unicode pieces. Use flipped=True to invert the perspective (putting black on bottom). Use labels=1 to get row/column labels to the left and bottom of the board. When flipped=True, you can also use labels=-1 to get labels on the top and right of the board instead. (When flipped=False, labels=-1 works just like labels=1.)

No grid lines. But this is a starting point that someone can use to write something more full-featured.

dubiousjim avatar Mar 10 '23 17:03 dubiousjim

@dubiousjim I like your support of unicode, nice! I am using this now 👍, with a few mods:

  • I changed labels arg name to verbosity and only support [0, 1, 2]
  • I moved from "\n" to os.linesep
  • From this comment above:

board.mirror() should not need special consideration.

I think this means no need to support flipped capability, just rely on the caller previously invoking board.apply_mirror() or board.mirror().

One possible improvement for labels/verbosity=2: maybe add the player whose turn it is with * or . as the corner. This can be discerned with board.turn == chess.WHITE and board.turn == chess.BLACK.

For example, if it's white player's turn, note the *:

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

jamesbraza avatar Mar 11 '23 22:03 jamesbraza

Flipped board just displays the 8 rank at the bottom, it doesn't change any piece positions. (White K still starts at e1, this is just now displayed at the top instead of at the bottom.) Mirrored board moves the piece that was at a6 instead to a3. These are different concepts.

dubiousjim avatar Mar 11 '23 22:03 dubiousjim

Okay I see, sorry I didn't scrutinize that much. I think the default chess behavior is 8th rank on the top.

One other cool feature in a display function would be to highlight captures, check, or checkmates, possibly via colors. This behavior could be turned on with an additional color flag.

jamesbraza avatar Mar 11 '23 23:03 jamesbraza

I would be interested in taking on this issue!

baelany avatar Mar 13 '23 18:03 baelany

I would also be interested in taking this, if someone already is in development though, please say

R3dan avatar Dec 31 '23 13:12 R3dan