The code below works. The timings are as close as I can get to the system not packing out.
Basically telnet to a server to play tic-tac-toe. Automated it. Bottom left is 1, middle left is 2, middle right is 3 etc
Once a game is finished, I say "yes" and go again.
Anyway I can optimise it?
#!/usr/bin/env python
from telnetlib import Telnet
import sys
from pprint import pprint
from random import randint
HOST = '127.0.0.1'
PORT = 6666
tn = Telnet(HOST, PORT)
board=['','','','','','','','','']
winLines = [[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]]
def main():
startup()
while True:
readBoard()
selectMove()
tn.close()
def startup():
print tn.read_until("WordsWeNeverSee",0.1)
tn.write("O\n")
print tn.read_until("go first.", 0.1)
def foobar():
tn.write("yes\n")
print tn.read_until("WordsWeNeverSee",0.1)
tn.write("O\n")
readBoard()
def readBoard():
tIn = tn.read_until("WordsWeNeverSee",0.3)
tLines = tIn.split('\n')
sys.stdout.write(tIn)
tLines = tLines[::-1]
if len(tLines)==13:
row = tLines[3].replace(' ', '').split('|')
#X|X|O
board[0] = row[0]
board[1] = row[1]
board[2] = row[2]
row = tLines[7].replace(' ', '').split('|')
#X|X|O
board[3] = row[0]
board[4] = row[1]
board[5] = row[2]
row = tLines[11].replace(' ', '').split('|')
#X|X|O
board[6] = row[0]
board[7] = row[1]
board[8] = row[2]
elif len(tLines)==14:
row = tLines[3].replace(' ', '').split('|')
#X|X|O
board[0] = row[0]
board[1] = row[1]
board[2] = row[2]
row = tLines[7].replace(' ', '').split('|')
#X|X|O
board[3] = row[0]
board[4] = row[1]
board[5] = row[2]
row = tLines[11].replace(' ', '').split('|')
#X|X|O
board[6] = row[0]
board[7] = row[1]
board[8] = row[2]
else:
readBoard()
if tIn.find("play again") != -1:
foobar()
def countCharInCells(char, tup): # X, [2,4,6]
count = 0
for n in tup:
if board[n] == char:
count += 1
return count
def findEmpty(tup): # [2,4,6]
for n in tup:
if board[n] == '':
return n
return -1
def selectMove():
xCount = board.count('X')
# Strong first turn move
if xCount == 0:
tn.write('9')
return
# Best response to center start
if board.count('X') == 1:
if board[4] == 'X':
tn.write('9')
return
# See if we can win
for wl in winLines: #[[0,1,2], [3,4,5], [6,7,8], [0,3,6], [1,4,7], [2,5,8], [0,4,8], [2,4,6]]
if countCharInCells('O', wl) == 2:
choice = findEmpty(wl)
if choice != -1:
tn.write(str(choice+1))
return
# Try to block opposition winning moves
for wl in winLines:
if countCharInCells('X', wl) == 2:
choice = findEmpty(wl)
if choice != -1:
tn.write(str(choice+1))
return
# Try to snag a corner
choice = findEmpty([0,2,6,8])
if choice != -1:
tn.write(str(choice+1))
return
# Pick a random valid square when stumped
while True:
choice = randint(0,8)
if board[choice] == '':
tn.write(str(choice+1))
return
if __name__ == "__main__":
main()
main()
1 Answer 1
I am not particularly a python expert but can offer some feedback from a fairly generic perspective.
There are no useful (plain English) comments until line 105. There is definitely room to improve the commenting of this code.
Unless StackExchange is affecting the formatting, this doesn't appear to adhere to PEP 8 (the definitive Python style guide). For example, you are using three spaces per indentation level, not four.
As @200_success spotted in a comment, you are calling
main()
twice at the end: is this intentional? I cannot understand why this would be.foobar()
does not sound like a sensible name for a function: it gives me absolutely no clue as to what the function's purpose is.What is the significance of
"WordsWeNeverSee"
? This might be well documented in the Telnet library but it might be helpful to offer a brief explanation here, so somebody can follow this code without knowing that library well. Failing that, at least a reference to the main documentation would be good.Your main game engine architecture smells funny to me. When
main()
first callsreadBoard()
, this can then lead toreadBoard()
being called recursively, and/or control passing tofoobar()
which then callsreadBoard()
again. This feels like it might lead to a deeply-nested call stack. Without following the logic through carefully I have no idea what the rationale is for this, but it seems unlikely to be the best design approach.Relatedly, your key functions
readBoard()
andselectMove()
take no input and return no output. They are operating on data structures that exist globally, aside from their function scope - i.e. they are having side effects. This also seems unlikely to be the best approach; I'm not really qualified to say what the best or most-pythonic way might be, but a more object-oriented approach could be one way to go.readBoard()
seems to have two blocks of identical code, inside the firstif
and followingelif
. In other words, you're saying:if len(tLines)==13, then do some stuff; if len(tLines)==14, then do the same stuff; else do something different.
Can you see the quick way to simplify this code?
Explore related questions
See similar questions with these tags.
main()
twice? \$\endgroup\$