Python 2: 399 401 349 333 317 370 bytes
2x Bug Fix: credit to l4m2
-52 chars: credit to undergroundmonorail
-16 chars: credit to Jonathan Frech
-26 chars: credit to user202729
def f(b):
t=4,9,2,3,5,7,8,1,6;n=lambda k:[t[i]for i,j in enumerate(b)if j==k];p,o,a,I=n(2),n(1),n(0),t.index
for i in p:
for j in p:
for k in a:
if i+j+k==15and-j+i:return I(k)
for i in o:
for j in o:
for k in a:
if i+j+k==15and-j+i:return I(k)
for i in 9,3,7,1:
if i in a and 5 in p:return I(i)
for i in 5,4,2,8,6:
if i in a:return I(i)
return I(a[0])
Try it Online!
On the first day of a linear algebra course I took last semester, my astute graduate student instructor proposed that if you represent the tic-tac-toe board as the the matrix:
4 | 9 | 2
--+---+--
3 | 5 | 7
--+---+--
8 | 1 | 6
then getting three in a row is equivalent to picking three numbers in the range [1,9] that add up to 15. This answer exploits this idea. The function takes a list containing nine numbers representing the board. 0 indicates an empty space, 1 is occupied by the opponent, and 2 represents a previous play made by the program. The first 3 lines figure out what numbers the program has picked (p), the opposition has picked (o), and are still available (a). It then looks through the available numbers and sees if any of them, combined with two numbers it has already picked add to fifteen. If it does, it will pick that square and win. If there are no immediate winning moves, it will check to see if the opponent can win using the same method. If they can, it will take their winning square. If there is neither a winning nor blocking move available, it will move in a corner. This prevents a fools mate:
- - -
- X -
- - -
- O - # Bad Move
- X -
- - -
- O X
- X -
- - -
- O X
- X -
O - -
- O X
- X -
O - X
If none of these situations occur, it will choose a square arbitrarily. The function outputs a number [0,8] representing the 0 indexed square chosen by the algorithm.
Edit: The algorithm now prioritizes the center over the diagonal, which will prevent another fools mate possibility pointed out by l4m2 and related strategies.
Edit: To clarify, the function takes in a board in the form of an array and outputs a move as an integer on [0,8]. Because this I/O strategy is so clunky , here's a wrapper script that makes it more interactive. It takes a single command line argument, which should be 1 if the player goes first, and 0 if the program goes first.
import sys
def f(b):
t=4,9,2,3,5,7,8,1,6;n=lambda k:[t[i]for i,j in enumerate(b)if j==k];p,o,a,I=n(2),n(1),n(0),t.index
for i in p:
for j in p:
for k in a:
if i+j+k==15and-j+i:return I(k)
for i in o:
for j in o:
for k in a:
if i+j+k==15and-j+i:return I(k)
for i in 9,3,7,1:
if i in a and 5 in p:return I(i)
for i in 5,4,2,8,6:
if i in a:return I(i)
return I(a[0])
board = [0,0,0,0,0,0,0,0,0]
rep = {0:"-",1:"X",2:"O"}
turn = int(sys.argv[1])
while True:
for i in range(3):
print rep[board[i*3]]+" "+rep[board[i*3+1]]+" "+rep[board[i*3+2]]
print
if turn:
move = int(raw_input("Enter Move [0-8]: "))
else:
move = f(board)
board[move] = turn+1
turn = (turn+1)%2
If a losing is given to you then your solution is invalid. You are playing with other, so the chessboard won't instantly change, so
we can assume that all previous moves of the 2nd player were also played by our engine
– l4m2 – 2017-12-29T13:17:04.913Why the
In this case you can have a reset function or reset when the game is over
part is removed? Function need to be called for multiple times, don't it? – l4m2 – 2017-12-29T13:20:02.3473https://xkcd.com/832/ – totallyhuman – 2017-12-29T14:14:04.587
1@l4m2 Just restart the interpreter. Done. Why bother with it? It just unnecessarily increase the byte count for nothing. – user202729 – 2017-12-29T14:15:07.457
1
As a reminder, you should not change the challenge in the comment.
– user202729 – 2017-12-29T14:17:15.917'll change the problem when change is decided. Pure explanation that also exist in the problem doesn't lead to editing prob, either – l4m2 – 2017-12-29T15:11:20.373
Is it acceptable to provide an interactive program that repeatedly prompts the player for their move, each time outputting the program's counter move in response? I think that is the intent of option 2 of the allowed I/O, but it isn't entirely clear to me. – James Holderness – 2017-12-29T16:38:44.480
@James Holderness Yes you can. The original contain doesn't say "call for multiple times", and someone changed it. – l4m2 – 2017-12-29T18:20:01.687
4Don't do the bonus. Either require it or remove it, don't make it optional. The bonus ruins the challenge.. – Rɪᴋᴇʀ – 2017-12-29T21:06:34.803
@Bruce Forte The program uses best stragety, but the other player may not – l4m2 – 2017-12-30T03:20:24.223
I guess that's what bonus do. I change it into "need to handle both, but can be two function " – l4m2 – 2017-12-31T03:06:19.320