代码改变世界

井字棋(Tic-Tac-Toe)

2015-07-21 09:06  FARAMIR  阅读(1178)  评论(0)    收藏  举报

井字棋介绍:https://en.wikipedia.org/wiki/Tic-tac-toe

井字棋简单,但是获胜策略却和直觉不同,四角比中间重要性要高,而且先手有很大的获胜概率获胜(先手胜:91, 后手胜:44,平局:3),所以当你陷入劣势时,怎么选择打平就是一个不那么简单的事情。不过无聊大抵孤单,没有人一起玩就只能和电脑PK,就写了个弱智AI,没事就偷着乐!

AI的获胜策略也很简单,先遍历检测棋盘副本的空格子看下一步能否获胜,如果有机会获胜,返回格子的下标,然后再棋盘上移动,其次,检测对手能否下一步获胜,提前封堵,如果前面两步都没返回的话,就说明双方都没有一步必胜的招式,这时候就得依次抢占四个角落,中间和剩余的位置。

  1 import random
  2 
  3 def draw_board(board):
  4     """board is a 3X3 list containing the moves either ' ' representing
  5     no moves there or 'x' or 'o' representing actual moves"""
  6     for i in range(7):
  7         if i%2 == 0:
  8             print '+'.join(['---']*3)
  9         else:
 10             col = []
 11             for j in range(11):
 12                 if j%2 == 0:
 13                     col.append(' ')
 14                 elif j==3 or j==7:
 15                     col.append('|')
 16                 else:
 17                     col.append(board[i/2][j/4])
 18             print ''.join(col)
 19 
 20 def who_goes_first():
 21     if random.randint(0, 1):
 22         return 'AI'
 23     else:
 24         return 'P'
 25 
 26 def play_again():
 27     print "Once Again?(y for Yes, n for No)"
 28     return raw_input().lower().startswith('y')
 29 
 30 def make_move(board, letter, move):
 31     seq = move-1
 32     board[2-seq/3][seq%3] = letter
 33 
 34 def is_winner(bo, le):
 35     # Given a board and a player’s letter, this function returns True if that player has won.
 36     # We use bo instead of board and le instead of letter so we don’t have to type as much.
 37     return ((bo[0][0] == le and bo[0][1] == le and bo[0][2] == le) or # across the top
 38     (bo[1][0] == le and bo[1][1] == le and bo[1][2] == le) or # across the middle
 39     (bo[2][0] == le and bo[2][1] == le and bo[2][2] == le) or # across the bottom
 40     (bo[0][0] == le and bo[1][0] == le and bo[2][0] == le) or # down the left side
 41     (bo[0][1] == le and bo[1][1] == le and bo[2][1] == le) or # down the middle
 42     (bo[0][2] == le and bo[1][2] == le and bo[2][2] == le) or # down the right side
 43     (bo[0][0] == le and bo[1][1] == le and bo[2][2] == le) or # diagonal
 44     (bo[0][2] == le and bo[1][1] == le and bo[2][0] == le)) # diagonal
 45 
 46 def get_board_copy(board):
 47     dup = [[' ']*3 for i in range(3)]
 48     for i in range(3):
 49         for j in range(3):
 50             dup[i][j] = board[i][j]
 51     return dup
 52 
 53 def is_move_avail(board, move):
 54     seq = move-1
 55     return board[2-seq/3][seq%3] == ' '
 56 
 57 def get_P_moves(board):
 58     move = ''
 59     while move not in [1, 2, 3, 4, 5, 6, 7, 8, 9] or not is_move_avail(board, move):
 60         print "Please input your next move('1-9')"
 61         move = input()
 62     return move
 63     
 64 def is_board_full(board):
 65     for i in range(3):
 66         for j in range(3):
 67             if board[i][j] == ' ':
 68                 return False
 69     return True
 70 
 71 def choose_randomly(board, avail):
 72     possible_moves = []
 73     for i in avail:
 74         if is_move_avail(board, i):
 75             possible_moves.append(i)
 76     if len(possible_moves) != 0:
 77         return random.choice(possible_moves)
 78     else:
 79         return None
 80 
 81 def get_AI_moves(board, AI, P):
 82     #check if AI can win in the next move
 83     for i in range(1, 10):
 84         copy = get_board_copy(board)
 85         if is_move_avail(board, i):
 86             make_move(copy, AI, i)
 87             if is_winner(copy, AI):
 88                 return i
 89             
 90     #else check if P can win in the next move and block the first found move
 91     for i in range(1, 10):
 92         copy = get_board_copy(board)
 93         if is_move_avail(board, i):
 94             make_move(copy, P, i)
 95             if is_winner(copy, P):
 96                 return i
 97 
 98     #The key to win is to occupy the corners, so move there if available
 99     move = choose_randomly(board, [1, 3, 7, 9])
100     print move
101     if move != None:
102         return move
103 
104     #of second priority is the center element
105     if is_move_avail(board, 5):
106         return 5
107 
108     #Then the rest
109     return choose_randomly(board, [2, 4, 6, 8])
110     
111 if __name__ == '__main__':
112     
113     while True:
114         Board = [[' ']*3 for i in range(3)]
115         P, AI = 'x', 'o'
116         turn = who_goes_first()
117         print "{0} will go first".format(turn)
118         GameOn = True
119 
120         while GameOn:
121             if turn == 'P':
122                 move = get_P_moves(Board)
123                 make_move(Board, P, move)
124                 draw_board(Board)
125 
126                 if is_winner(Board, P):
127                     draw_board(Board)
128                     print "GameOver You've Won!"
129                     GameOn = False
130                 else:
131                     if is_board_full(Board):
132                         draw_board(Board)
133                         print "Tie"
134                         break
135                     else:
136                         turn = 'AI'
137 
138             else:
139                 move = get_AI_moves(Board, AI, P)
140                 make_move(Board, AI, move)
141                 draw_board(Board)
142 
143                 if is_winner(Board, AI):
144                     draw_board(Board)
145                     print "GameOver AI has Won!"
146                     GameOn = False
147                 else:
148                     if is_board_full(Board):
149                         draw_board(Board)
150                         print "Tie"
151                         break
152                     else:
153                         turn = 'P'
154 
155         if not play_again():
156             break
157 
158 ##---+---+---
159 ## x | o | x
160 ##---+---+---
161 ## o | x | o
162 ##---+---+---
163 ## x | o |
164 ##---+---+---
View Code

注:

  • python 2.7中input和raw_input的区别,python3中合并了
  • 数据结构很简单,3X3的list of lists来表示棋盘。井字棋位置和小键盘一致。
  • 参考文献: http://www.guokr.com/article/4754/