【Python】使用numpy库实现Tic-Tac-Toe井字棋

预置环境:python版本2.或3.及以上

numpy库的安装

windows下:

win+R输入cmd打开命令控制行输入:

Linux下:

在linux桌面右键打开terminal终端输入:

sudo pip install numpy

安装完毕即可。

Tic-Tac-Toe井字棋的代码实现

################################################################################
# tictactoe.py - the first of many programs
# uses numpy arrays to represent an internal tictactoe board

# uses four global variables - board_arr, tutorial_arr, user_num, and comp_num

import numpy as np
import random

# Creates blank 3x3 array. Randomizes who goes first.
# TODO: ADD A TUTORIAL PROGRAM
def initialize():
    #global board_arr
    #global tutorial_arr
    
    # 0's act as o's
    # 1's act as x's
    # 3's act as placeholders for blank spots
    #棋盘
    board_arr = np.array([[3, 3, 3],
                          [3, 3, 3],
                          [3, 3, 3]])
    
    tutorial_arr = np.array([[1, 2, 3],
                             [4, 5, 6],
                             [7, 8, 9]])
    return board_arr, tutorial_arr

def guessfirst(): #猜先手
    # Flip a coin to see who goes first with x's
    coin_flip = np.random.randint(0, 2)
    if  coin_flip == 0:
        print("Computer goes first. Your letter is o.")
        return "AI"
        
    else:
        print("You go first. Your letter is x.")
        return "P"
        
      
# Converts internal numpy array into a visual ASCII board.       
def display_board(board_arr):
    board_list = []
    
    # loops through flattened board array to scan for 0's, 1's and 3's
    # converts them into O's, X's, and blank spots
    internal_arr = board_arr.flatten()   #降维,返回一个折叠成一维的数组
    for i in range(0, 9):
       if internal_arr[i] == 0:
           board_list.append('o')
       elif internal_arr[i] == 1:
           board_list.append('x')
       elif internal_arr[i] == 3:
           board_list.append(' ')
       else:
           raise Exception("display_board Error")
    
    # inputs O's, X's, and blank spots into an ASCII tictactoe board
    print("""
 {} | {} | {}
---+---+---
 {} | {} | {}
---+---+---
 {} | {} | {}

""".format(*board_list))
    
    
def return_open_slots(board_arr): #返回可放置棋子的空槽的位置
# Checks for open slots using Boolean arrays.
# Important when checking for winner (if draw) and checking if user's input...
# ...is valid
    open_slots = []

    bool_arr = (board_arr == 3)  #根据board_arr每个位置上的元素是否为3生成一个布尔array
    flat_bool_arr = bool_arr.flatten() #变为一维数组进行判断
    
    # is spot taken by 3's? If so, then spot is open.
    # appends (i + 1) because inputs are indexed to 1
    for i in range(0, len(flat_bool_arr)):
        if flat_bool_arr[i] == True:
            open_slots.append(i + 1)  #将还没有放置棋子的槽的位置放到open_slots列表里
            
    return open_slots
      
def check_for_winner(board_arr,firstplayer,currentplayer):
#判断当前选手是否获胜
#返回值0:平局,1:赢,2:输,-1:尚未能判断,请继续下子
    
    if return_open_slots(board_arr) == []: #如果已经无处可放,那么就是平局
    # Checks if no open slots
        return 0     
        
    for i in range(0, 3): #判断是否有一行或一列放置同样的棋子
    # Checks rows and columns for match
        rows_win = (board_arr[i, :] == currentplayer).all() #all()比较两个数组是否所有元素都相等
        cols_win = (board_arr[:, i] == currentplayer).all()
        
        if rows_win or cols_win:
            return 1             

    #判断是否有一条斜线上放置同样的棋子
    '''
        np.fliplr 矩阵左右翻转,在二维的情况下很容易理解 || 按轴1翻转
        [ [0,1,2],[3,4,5],[6,7,8] ]翻转后变为
        [ [2,1,0],[5,4,3],[8,7,6] ]

        flip()
        fliplr():按轴1翻转,等价于flip(M,0)
        flipud():按轴0翻转,等价于flip(M,0)
        [ [0,1,2],[3,4,5],[6,7,8] ] flipud翻转后变为
        [ [6,7,8],[3,4,5],[0,1,2]] 
        
    '''
    diag1_win = (np.diag(board_arr) == currentplayer).all()
    diag2_win = (np.diag(np.fliplr(board_arr)) == currentplayer).all()
    
    if diag1_win or diag2_win:
    # Checks both diagonals for match
        return 1

    return -1   
    
def place_letter(board_arr, tutorial_arr, current_num, current_input): #在current_input处放置棋子
# Takes comp_num and comp_choice (or user_num and user_choice)...
# ...and inputs that into the global board_arr
# Current_input is either randomly chosen by computer or input by user
# Current_num is either user_num or comp_num
    '''
    numpy.where (condition[, x, y])
    1. np.where(condition, x, y), 满足条件(condition),输出x,不满足输出y。
    2. np.where(condition), 只有条件 (condition),没有x和y,
       则输出满足条件元素的坐标 。
       这里的坐标以tuple的形式给出,
       通常原数组有多少维,输出的tuple中就包含几个数组,分别对应符合条件元素的各维坐标。
    '''
    index = np.where(tutorial_arr == current_input)
    board_arr[index] = current_num  #在槽中放置表示人或者机器的数字,即1或0

    
# TODO: LIST OPEN SLOTS ON ASCII BOARD
def choose_randomly(board_arr, avail):
    possible_moves = []
    slot = return_open_slots(board_arr)
    for i in avail:
        if i in slot:
            possible_moves.append(i)
    if len(possible_moves) != 0:
        return random.choice(possible_moves)
    else:
        return None
    
def get_P_moves(board_arr):
    move = 0
    slot = return_open_slots(board_arr)
    while move not in slot:
        move = int(input("Pick an open slot: "))
    return move

def user_turn(board_arr,tutorial_arr, firstplayer, user_num):
    #display_board(board_arr)
    
    user_input = get_P_moves(board_arr)
        
    place_letter(board_arr, tutorial_arr, user_num, user_input)
   
    return check_for_winner(board_arr, firstplayer, user_num)
    
def get_AI_moves(board_arr, tutorial_arr, firstplayer, comp_num):
    #check if AI can win in the next move
    
    for i in range(1, 10):
        copy = board_arr.copy() #获得副本
        slot = return_open_slots(copy)
        #print("in get_AI_moves, slot = :", slot)
        if i not in slot:
            continue
        place_letter(copy, tutorial_arr, comp_num, i)
        re = check_for_winner(copy, firstplayer, comp_num)
        if re == 1:
            return i

    #else check if P can win in the next move and block the first found move
    for i in range(1, 10):
        copy = board_arr.copy() #获得副本
        slot = return_open_slots(copy)          
        if i not in slot:
            continue
        place_letter(copy, tutorial_arr, 1-comp_num, i)
        re = check_for_winner(copy, firstplayer, 1-comp_num)
        if re == 1:
            return i

    #The key to win is to occupy the corners, so move there if available
    move = choose_randomly(board_arr, [1, 3, 7, 9])
    #print(move)
    if move != None:
        return move
    
    #of second priority is the center element
    move = choose_randomly(board_arr, [5])
    #print(move)
    if move != None:
        return move
    #Then the rest
    return choose_randomly(board_arr, [2, 4, 6, 8])

def comp_turn(board_arr, tutorial_arr, firstplayer, comp_num):
    #display_board(board_arr)
    
    AI_input = get_AI_moves(board_arr, tutorial_arr, firstplayer, comp_num)
        
    place_letter(board_arr, tutorial_arr, comp_num, AI_input)
   
    return check_for_winner(board_arr, firstplayer, comp_num)

#===========================

while True:
    board_arr, tutorial_arr = initialize()
    who = guessfirst()
    print("{0} will go first".format(who))
    if who == "P":
        user_num = 1
        comp_num = 0
    else:
        user_num = 0
        comp_num = 1
    GameOn = True
    while  GameOn:        
        if who == 'P':
            re = user_turn(board_arr, tutorial_arr, who, user_num)
            display_board(board_arr)
            if re == 1:
                print("GameOver You've Won!")
                GameOn = False
            elif re == 0:
                print("Tie")
                GameOn = False
            else:
                who = 'AI'

        else:
            re = comp_turn(board_arr, tutorial_arr, who, comp_num)
            display_board(board_arr)
            if re == 1:
                print("GameOver AI've Won!")
                GameOn = False
            elif re == 0:
                print("Tie")
                GameOn = False
            else:
                who = 'P'

    again_choice = input("Play again?(Y or N).")
    if again_choice == "N" or again_choice == "n":
        break

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mitch311

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值