How to space text printed in the python console?

68 Views Asked by At

I'm making a chess game, and I want it to be fully rendered in the print console. Yes, I know it makes more sense to do some PyGame or TKinter but that's not my goal.

I found some chess emojis that are built-in to Windows, so that's what I'm using for the pieces. Below is my code:

letters = ["::", "wP", "wB", "wN", "wR", "wQ", "wK", "bP", "bB", "bN", "bR", "bQ", "bK"]
symbols = ["*", "♙", "♗", "♘", "♖", "♕", "♔", "♟", "♝", "♞", "♜", "♛", "♚"]
board = [
    ["bR", "bN", "bB", "bQ", "bK", "bB", "bN", "bR"],
    ["bP", "bP", "bP", "bP", "bP", "bP", "bP", "bP"],
    ["::", "::", "::", "::", "::", "::", "::", "::"],
    ["::", "::", "::", "::", "::", "::", "::", "::"],
    ["::", "::", "::", "::", "::", "::", "::", "::"],
    ["::", "::", "::", "::", "::", "::", "::", "::"],
    ["wP", "wP", "wP", "wP", "wP", "wP", "wP", "wP"],
    ["wR", "wN", "wB", "wQ", "wK", "wB", "wN", "wR"]
]

def compile_board(board_grid):
    compiled_board = """"""
    for rank in board_grid:
        for square in rank:
            compiled_board += f"{symbols[letters.index(square)]} "
        compiled_board += "\n"
    return compiled_board

print_board = compile_board(board)
print(print_board)

And if you run it, you can see that the symbols don't line up. I think this comes down to the fact that emojis have different dimensions than characters.

I tried using letters (eg: black knight would be bN, white bishop would be wB) and using "::" as the spacer, and everything lined up perfectly. So I know it isn't a problem with my code. I really don't want to remove the characters and just use letters. I also want to ahve something marking each square (even if empty) so that bishop and queen movements are visible.

So I guess my question is: how can I make custom-length characters, or format an existing character to match the width of the emojis?

2

There are 2 best solutions below

3
Maaz Mohammad On BEST ANSWER

You can use full width unicode characters. Here is an example of using full width A as the spacing:

symbols = ["\uff21", "♙", "♗", "♘", "♖", "♕", "♔", "♟", "♝", "♞", "♜", "♛", "♚"]

And it prints like so:

♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ 
♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ 
A A A A A A A A 
A A A A A A A A 
A A A A A A A A 
A A A A A A A A 
♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ 
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
0
Andj On

The inherent problem with your code lies in the assumption that the chess piece characters are emoji, and have a width of two terminal cells. Using the wcwidth module, and using U+2659 (WHITE CHESS PAWN) as an example:

from wcwidth import wcwidth
print(wcwidth("♙"))
# 1

Comparing it to a single emoji:

print(wcwidth('☕'))
# 2

The chess characters have a width of one terminal cell, while emoji have a width of two terminal cells.

If we look at the relevant Unicode properties for U+2659:

import icu
def get_emoji_properties(char):
    print(f"Emoji: {icu.Char.hasBinaryProperty(char, icu.UProperty.EMOJI)}")
    print(f"Emoji component: {icu.Char.hasBinaryProperty('♙', icu.UProperty.EMOJI_COMPONENT)}")
    print(f"Emoji key cap sequence: {icu.Char.hasBinaryProperty('♙', icu.UProperty.EMOJI_KEYCAP_SEQUENCE)}")
    print(f"Emoji modifier: {icu.Char.hasBinaryProperty('♙', icu.UProperty.EMOJI_MODIFIER)}")
    print(f"Emoji modifier base: {icu.Char.hasBinaryProperty('♙', icu.UProperty.EMOJI_MODIFIER_BASE)}")
    print(f"Emoji presentation: {icu.Char.hasBinaryProperty('♙', icu.UProperty.EMOJI_PRESENTATION)}")

get_emoji_properties('♙')
# Emoji: False
# Emoji component: False
# Emoji key cap sequence: False
# Emoji modifier: False
# Emoji modifier base: False
# Emoji presentation: False

All characters you use for the board must be one terminal cell width, and will display correctly if the font used in rendering the cell is a monospaced width font that contains both chess pieces and your fill character.

Reusing the OP code:

symbols = ["□", "♙", "♗", "♘", "♖", "♕", "♔", "♟", "♝", "♞", "♜", "♛", "♚"]
print_board = compile_board(board)
print(print_board)

Which gives:

♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ 
♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ 
□ □ □ □ □ □ □ □ 
□ □ □ □ □ □ □ □ 
□ □ □ □ □ □ □ □ 
□ □ □ □ □ □ □ □ 
♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ 
♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖ 

Alternatively you can use a full width CJK character as the filler and append VS16 to each of the chess characters to render them as emoji. But considering the limited control.of fonts in the terminal ...