I’m trying to use some special Unicode characters in a string formatter for my python class “Oth”. I will provide all of the code at the bottom, but first some examples of my problem. When I run the script in VS Code terminal, I get the correct output, which can be seen here:
However, when I run the same script in Git Bash, I get the following error:
Traceback (most recent call last):
File "C:\GITSTUFF\OthBot\Othello.py", line 150, in <module>
print(oth)
File "C:\Program Files\Python312\Lib\encodings\cp1252.py", line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_table)[0]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeEncodeError: 'charmap' codec can't encode character '\u2592' in position 46: character maps to <undefined>
Strangely, I used the exact same technique in a Java program, and Git Bash was able to print that completely correctly, so my suspicion lies more with Python somehow. I also double checked that the Git Bash options had the correct locale and encoding. They were set to en_US
and UTF-8
respectively.
Here is the code for the script as of writing this:
from enum import Enum
# custom type hints
type Matrix = list[list[Tile]]
type Position = tuple[int, int]
type Move = tuple[Tile, list[Position]]
# formatting constants
ANSI_FOREGROUND_BLACK = '\x1b[30m'
ANSI_FOREGROUND_RED = '\x1b[31m'
ANSI_FOREGROUND_GREEN = '\x1b[32m'
ANSI_FOREGROUND_YELLOW = '\x1b[33m'
ANSI_FOREGROUND_BLUE = '\x1b[34m'
ANSI_FOREGROUND_MAGENTA = '\x1b[35m'
ANSI_FOREGROUND_CYAN = '\x1b[36m'
ANSI_FOREGROUND_WHITE = '\x1b[37m'
ANSI_BACKGROUND_BLACK = '\x1b[40m'
ANSI_BACKGROUND_RED = '\x1b[41m'
ANSI_BACKGROUND_GREEN = '\x1b[42m'
ANSI_BACKGROUND_YELLOW = '\x1b[43m'
ANSI_BACKGROUND_BLUE = '\x1b[44m'
ANSI_BACKGROUND_MAGENTA = '\x1b[45m'
ANSI_BACKGROUND_CYAN = '\x1b[46m'
ANSI_BACKGROUND_WHITE = '\x1b[47m'
ANSI_RESTORE_DEFAULT = ANSI_FOREGROUND_WHITE + ANSI_BACKGROUND_BLACK
UNICODE_SHADE_FULL = '█'
UNICODE_SHADE_DARK = '▓'
UNICODE_SHADE_MEDIUM = '▒'
UNICODE_SHADE_LIGHT = '░'
UNICODE_SHADE_EMPTY = ' '
PRINT_WIDTH = 4
PRINT_HEIGHT = 2
# create an enum to define what can go in a board tile
class Tile(Enum):
EMPTY = 0
BLACK = -1 # the user will always start and black always starts, so black has a negative heuristic association
WHITE = 1
# game class (rules and board)
class Oth:
# members
board: Matrix = None
blackNextToMove: bool = None
# construct a new game
def __init__(this):
this.board = Oth.createStartBoard()
this.blackNextToMove = True
# return the starting board
def createStartBoard() -> Matrix:
E = Tile.EMPTY
B = Tile.BLACK
W = Tile.WHITE
return [[E, E, E, E, E, E, E, E],
[E, E, E, E, E, E, E, E],
[E, E, E, E, E, E, E, E],
[E, E, E, W, B, E, E, E],
[E, E, E, B, W, E, E, E],
[E, E, E, E, E, E, E, E],
[E, E, E, E, E, E, E, E],
[E, E, E, E, E, E, E, E]]
# accessors and mutators
def getTileAt(this, i: int, j: int) -> Tile:
return this.board[i][j]
def setTileAt(this, tile: Tile, i: int, j: int) -> None:
if i not in range(0, 8):
return
if j not in range(0, 8):
return
this.board[i][j] = tile
# formatted printing
def __str__(this) -> str:
outstr = ANSI_RESTORE_DEFAULT
outstr += f"Next to move: {(ANSI_FOREGROUND_MAGENTA + "BLACK") if this.blackNextToMove else (ANSI_FOREGROUND_WHITE + "WHITE")}\n"
outstr += ANSI_FOREGROUND_WHITE
for row in this.board:
rowPrint=""
for tile in row:
tilePrint = ANSI_FOREGROUND_GREEN + UNICODE_SHADE_MEDIUM
if tile == Tile.BLACK:
tilePrint = ANSI_FOREGROUND_BLACK + UNICODE_SHADE_FULL
elif tile == Tile.WHITE:
tilePrint = ANSI_FOREGROUND_WHITE + UNICODE_SHADE_FULL
rowPrint += tilePrint * PRINT_WIDTH
outstr += (rowPrint + '\n') * PRINT_HEIGHT
outstr += ANSI_RESTORE_DEFAULT
return outstr
# searches the board and returns a list of tuples which are valid coordinates for next player to place a tile
def findValidMoves(this) -> list[Position]:
nextColor = Tile.WHITE
oppositeColor = Tile.BLACK
if this.blackNextToMove:
nextColor = Tile.BLACK
oppositeColor = Tile.WHITE
# find next player's color tiles in the board
nextColorPositions = this.findColorPositions(nextColor)
# check adjacent tiles for the opposite color on each of the returned next color positions
# if there is an opposite tile, follow it out until:
# 1. an empty tile is found, return the move containing all of the tiles to change to nextColor
# 2. the edge of the board is found, return None
# 3. the next color is found, return None
for position in nextColorPositions:
pass
# finds all positions of a color on the board
def findColorPositions(this, color: Tile) -> list[Position]:
colorPositions = []
for i in range(this.board):
for j in range(i):
if this.getTileAt(i, j) == color:
colorPositions.append((i, j))
return colorPositions
if __name__ == "__main__":
oth = Oth()
print(oth)
Edit: Here’s the encoding settings in Git Bash. I still get the error.
You are using a character that it is not in CP1252 (so Latin 1 + Windows extensions), and your terminal (git bash) tell Python that it has a CP1252 charset. So you must set your terminal to use UTF-8 (as default, which it is also a better default). in git bash -> option -> text -> select UTF-8 (near the locale definition)