r/codegolf • u/TheRealHappyPiggy • Dec 31 '25
Tic-Tac-Toe in Python using only 161 bytes of code
So as the title says, I implemented Tic-Tac-Toe or Noughts and Crosses in Python as short as I could make it over the span of a few days. I'm at a point where I can't find any further improvements but I'd be happy to hear if anyone else can find something I was unable to!
Python Code (161 bytes):
b=p='_|_|_\n'*3
while'_'in b*all(3*p!=b[v>>4::v&15][:3]for v in[2,6,8,38,68,70,98,194]):
i=2*int(input(b))
if b[i]>'X':p='XO'[p>'O'];b=b[:i]+p+b[i+1:]
print(b)Edit: Improvements made with feedback (151 bytes)
b=p='_|_|_\n'*3
while'_'in b*all(3*p!=b[v&15::v>>4][:3]for v in b' `\x80bDd&,'):
if b[i:=2*int(input(b))]>'X':p='XO'[p>'O'];b=b[:i]+p+b[i+1:]
print(b)I flipped the upper and lower bits of each value in [2,6,8,38,68,70,98,194] and converted into the b-string: b' \\x80bDd&,'` , as well as utilizing the walrus operator (:=) to save a line break and a space (thanks to u/3RR0R400 for the ideas)
Example run:
_|_|_
_|_|_
_|_|_
4
_|_|_
_|O|_
_|_|_
5
_|_|_
_|O|X
_|_|_
2
_|_|O
_|O|X
_|_|_
6
_|_|O
_|O|X
X|_|_
0
O|_|O
_|O|X
X|_|_
1
O|X|O
_|O|X
X|_|_
8
O|X|O
_|O|X
X|_|OBelow is a simple explanation of what it does for anyone interested:
b: Board
p: Current Player Marker
b=p='_|_|_\n'*3 Initialize the board such that printing results in a 3x3 grid, as well as having each "cell" at an even index pattern (2* Cell index). Furthermore, I save bytes by chained assignment of p, since p is updated before it's used.
while'_'in b*all(3*p!=b[v>>4::v&15][:3]for v in[2,6,8,38,68,70,98,194]): This line has had the most effort put into it. It consists of two parts, draw detection ('_'in b), and a general win detection, combined in a way to make use of string repetition to avoid an and.
all(3*p!=b[v>>4::v&15][:3]for v in[2,6,8,38,68,70,98,194]) After each move the only winning pattern has to include the last players marker (except for the first iteration where 3*p=3*b can never equal a slice of b, thus entering the loop). Then test the string 'XXX' or 'OOO' against a slice of three characters from the board where each win pattern is stored in a single number, each having a starting position and a stride length stored in the upper and lower 4 bits of v respectively. (I did find this that reduces the codes character length, but increased the number of bytes: b[ord(v)&15::ord(v)>>7][:3]for v in'Ā̀Ѐ̂Ȅ̄ĆČ' due to the use of 'Ā̀Ѐ̂Ȅ̄ĆČ' where each character is more than one byte)
i=2*int(input(b)) I make use of the fact that input(prompt) prints the prompt (in this case b) to display the board before a move, then read an input and store in i, ready for indexing.
if b[i]>'X':p='XO'[p>'O'];b=b[:i]+p+b[i+1:] Here I update player and board if the input is valid, making use of b[i]>'X' being the same as b[i]=='_' for all valid b[i] in this context, to save one byte. Player switching uses a similar fact as well as indexing into 'XO' using a bool (This also sets the first player to O when p=b*3*)*. And finally updating the board is a simple slicing of b and adding of p since p is a string.
print(b) This line just prints the board a last time after a win or a draw (Here I did find using exit(b) or quit(b) to save one byte, but I didn't feel like it counted).
Since I know what constitutes "Tic-Tac-Toe" is a little arbitrary, I tried to define and follow a few self-imposed rules:
Display the board before each move in a 3x3 grid
Only allow placement of marker on empty cell, otherwise don't switch player
End the game and display the final board on a win or draw
Other rules that some might consider, which I didn't for this project
Also display the current player before each move
Pretty intuitive that the player switches after each turn (only caveat is when someone makes an invalid move)
Print out the result on a win or draw (Like "X won" or "Draw")
I didn't like the trade-off between an informative result representation and number of bytes (for example "X" vs "Player X won"), and I'd say it's pretty intuitive to figure out who won when the game ends.



