井字游戏设计一个算法_井字游戏

本文介绍了如何设计一个井字游戏的智能算法,探讨了在Python、Java和C++等编程语言中实现的可能性,并可能提及在LeetCode等平台上的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

井字游戏设计一个算法

介绍 (Introduction)

Tic-tac-toe ,consisting of Xs and Os is a paper-and-pencil game for two players, X and O, who take turns marking the spaces in a 3×3 grid. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row is the winner.

井字游戏,由Xs和Os组成,是两个玩家X和O的纸笔游戏,他们轮流在3×3的网格中标记空格。 成功将三个标记放在水平,垂直或对角线上的玩家是获胜者。

Each player takes alternative turns. In order to win a player places three of their marks in a horizontal, vertical, or diagonal row. This article focuses on the development of a programmatic approach with its GUI visualization.

每个玩家轮流进行。 为了赢得玩家,将他们的三个标记放在水平,垂直或对角线上。 本文重点介绍具有GUI可视化功能的编程方法的开发。

方法 (Approaches)

The following are different ways of thinking about building the AI with incremental complexity :

以下是构建具有递增复杂性的AI的不同思考方式:

  1. Random Placement method : In this approach, the marking is simply made by the AI randomly in any available cell. The downside is obvious, the AI is not interested in the game at all! But still it has to find out the available cells and track the termination the game. Here is the pseudocode for this approach :

    随机放置方法 :在这种方法中,标记简单地由AI在任何可用单元格中随机进行。 缺点很明显,AI对游戏根本不感兴趣! 但是它仍然必须找出可用的单元并跟踪游戏的终止。 这是此方法的伪代码:

function ai_move(curr_state){
game_has_ended(curr_state)
l = [list of all blank cells]
r,c = select one at random
curr_state[r][c] = 'X'
game_has_ended(curr_state)
}function game_has_ended(curr_state){
if all cells are filled:
exit("Game is Drawn")
for each row or column in curr_state{
if row or column is filled with same mark:
exit(" Game is won ")
}
if diagonals are filled with same mark:
exit("Game is won ")}

2. Static Scoring method : Building on top of the previous approach, a priority score is assigned to each cell in the grid. The unmarked cell with the highest priority is marked in the next move. The middle cell has the highest priority followed by the corner and then the other cells, based on the degree of control in the game offered by them. Here is the pseudocode for this approach :

2. 静态评分方法 :在先前方法的基础上,将优先级分数分配给网格中的每个单元。 下一步将标记优先级最高的未标记单元。 中间单元的优先级最高,其次是角球,然后是其他单元,具体取决于它们在游戏中提供的控制程度。 这是此方法的伪代码:

function ai_move(curr_state){
game_has_ended(curr_state)
r,c = select cell position of highest priority
curr_state[r][c] = 'X'
game_has_ended(curr_state)
}sample static scoring : |3 | 1 |3 |
|1 | 7 |1 |
|3 | 1 |3 |

3. Brute Force method : The disadvantages of both the methods above is that, they miss to read the opponent and fail to play moves to advantage. So, this method is built on the above methods. When the opponent has two markings in a row or column or diagonal, it is blocked. Otherwise, static scores are used to fill the empty cells. In the case where two cells are previously marked, it is completed to win the game. Here is the pseudocode for this approach :

3. 蛮力法 :以上两种方法的缺点是,他们错过了阅读对手的机会,无法发挥优势。 因此,此方法基于以上方法。 当对手在行,列或对角线上有两个标记时,它将被阻止。 否则,将使用静态分数来填充空白单元格。 在先前标记了两个单元格的情况下,将赢得比赛。 这是此方法的伪代码:

function ai_move(curr_state){
game_has_ended(curr_state)
for each row,col in curr_state{
if row has two markings:
fill the unfilled cell of row and return
if col has two markings,
fill the unfilled cell of column and return
}
game_has_ended(curr_state)
if row or column or diagonal exists with 2 markings:
curr_state[that_row][that_column] = 'X'
return
l = [list of all blank cells]
r,c = select one at random of highest priority
curr_state[r][c] = 'X'
game_has_ended(curr_state)
}

4. Minimax method : The approach above is enough to draw the game or winning simple games but to win by tricking a shrewd opponent is more desirable. This method is illustrated as follows :

4. Minimax方法 :上面的方法足以吸引游戏或赢得简单游戏,但更希望通过欺骗精明的对手获胜。 该方法如下所示:

Image for post
minmax approach
最小最大方法
leaf_scores -> min_of_all_scores -> max_of_all_min_scores -> min_of_all_scores -> ..... -> max_of_all_min_scores (root)
( This is applied across all children of each node )

Here, it is assumed that the opponent plays the game in the most optimal way. Thus, this approach could be used in zero-sum game scenario. Each level of the game tree deals with the actions of alternating players. The result computed is calculated with respect to the player at the root as shown above. The root in tic-tac-toe is the AI player.

在此,假定对手以最佳方式玩游戏。 因此,该方法可用于零和游戏场景。 游戏树的每个级别都处理交替玩家的动作。 如上所示,计算的结果是针对根处的玩家计算的。 井字游戏的根源是AI播放器。

The leaf nodes consists of scores at end positions of the tic-tac-toe game. The scores are based on a heuristic which is the nucleus of this approach. The heuristic assigns positive, negative or non-negative scores based on winning, losing or draw positions.

叶子节点由井字游戏末端的分数组成。 分数基于启发式方法,而启发式方法就是这种方法的核心 。 启发式方法根据获胜,失败或平局分配正,负或非负分数。

The pseudocode of the heuristic employed here is :

这里采用的启发式方法的伪代码是:

function heuristic(curr_state){
if is_draw(curr_state):
return 0
if has_won(curr_state):
number of unfilled_cells + 1
if has_lost(curr_state):
-number_of_unfilled_cells - 1
}

Recursive implementation is used. The pseudocode of the approach is :

使用递归实现。 该方法的伪代码为:

function ai_move(curr_state, ai = True){     if (res = heuristic(curr_state)) != nil: return res
poss_moves = get_all_possible_moves
for move in poss_moves{
scores.append(ai_move(move, ~ai))
}
return max(scores) if ai is True else min(scores)
}
// a move is chosen with the maximum score at the current state (root) based on priority index or randomly if scores clash

使用GUI构建井字游戏AI (Building Tic-Tac-Toe-AI With GUI)

Find the full code here : https://github.com/praguna/ai-puzzels/tree/master/tic_tac_toe

在此处找到完整的代码: https : //github.com/praguna/ai-puzzels/tree/master/tic_tac_toe

Tkinter is a Python binding to the Tk GUI toolkit. It is the standard Python interface to the Tk GUI toolkit, and is Python’s de facto standard GUI. It is a fast and easy way of creating GUI applications.

Tkinter是Python与Tk GUI工具包的绑定。 它是Tk GUI工具包的标准Python界面,也是Python的事实上的标准GUI。 这是创建GUI应用程序的快速简便的方法。

Initially, a frame is created in which all the widgets are placed :

最初,创建一个框架,其中放置了所有小部件:

class App(tk.Frame):
def __init__(self, parent, dim, **kw):
super().__init__(parent, **kw)
parent.minsize(dim[0], dim[1])
parent.title("Tic Tac Toe")
self.define_display_widgets()
messagebox.showinfo("Info", "Welcome to Tic Tac Toe Game ")
self.ttt_grid = Grid(self, {"tile_color": "red", "text": "Puzzle Grid"})def define_display_widgets(self):
self.label = tk.Label(self, text="Let's play Tic Tac Toe", font=('Verdana', 15, 'bold'))
self.label.pack(side=tk.TOP)
self.reset = tk.Button(self, text="Reset", bg="grey", fg="white", command=self.reset_all)
self.reset.pack(side=tk.LEFT, anchor=tk.N)
ttk.Separator(self, orient=tk.HORIZONTAL).pack(after=self.label, fill=tk.X)def reset_all(self):
self.ttt_grid.disable_or_reset(disable=False)

The grid class is where the tic-tac-toe game is played and it is created as follows :

网格类是玩井字游戏的地方,它的创建方式如下:

class Grid(tk.Frame):def __init__(self, parent, config, **kw):
super().__init__(parent, **kw)
self.b = [[], [], []]
self.config = config
self.draw_grid()
tk.Label(parent, text=config["text"], font=('Verdana', 20, 'bold'), pady=5).pack()
self.score = tk.Label(parent, text="", font=('Verdana', 10, 'bold'), pady=2)
self.score.pack()
self.pack(pady=15)
self.set_algo()def draw_grid(self):
# draws the grid inside the main frame
for i in range(3):
for j in range(3):
self.b[i].append(self.button())
self.b[i][j].config(command=lambda row=i, col=j: self.fill(row, col))
self.b[i][j].grid(row=i, column=j)def button(self):
return tk.Button(self, bd=5, width=2, font=('arial', 50, 'bold'))

It looks like this :

看起来像这样:

Image for post
GUI Layout
GUI布局

Once the move is played it is handled as follows in the fill method :

进行移动后,将在fill方法中按以下方式进行处理

def fill(self, i, j):
self.b[i][j].config(text=get_symbol(self.turn), state=tk.DISABLED, bg="black", fg="white")
self.algo_value[i * 3 + j] = get_symbol(self.turn)
status = self.check_if_game_ended("Player")
if status: return
self.turn = update_state(self.algo_value, i, j, self.turn)
self.ai_move()

It can be noticed that the ai_move is called here which will use the minimax algorithm in its method as follows :

可以注意到,ai_move这里称为它将使用极大极小算法在其方法如下:

def ai_move(self, start=None):
if start:
move, s = start, 0
else:
move, s = find_best_move(self.algo_value, True, self.turn) # applies minmax
self.score.config(text="current minimax score {}".format(s))
index = 0
for i in range(9):
if self.algo_value[i] != move[i]:
index = i
break
self.algo_value = move
self.b[index // 3][index % 3].config(text=get_symbol(self.turn), state=tk.DISABLED, fg="white", bg="red")
self.turn = not self.turn
self.check_if_game_ended("Computer")

The method call find_best_move implements minimax algorithm as follows :

该方法调用find_best_move工具极大极小算法如下:

def find_best_move(curr, is_ai, v):
if is_won(curr): return curr, 1 + final_score(curr) if not is_ai else -final_score(curr) - 1 # invert the flag
if is_draw(curr): return curr, 0
poss_moves = gen_moves(curr, v)
b = -10 if is_ai else 10
next_move = None
for move in poss_moves:
_, score = find_best_move(move, not is_ai, not v)
if (score > b and is_ai) or (score < b and not is_ai):
next_move, b = move, score
return next_move, b

This logic is same as the pseudocode discussed above. The game is found out to be won or drawn as follows :

该逻辑与上面讨论的伪代码相同。 发现该游戏是如下获胜或抽奖:

def has_won(self):
curr = self.algo_value
for i in range(3):
if curr[3 * i] == curr[3 * i + 1] == curr[3 * i + 2] != -1:
return True, (3 * i, 3 * i + 1, 3 * i + 2)
if curr[0 + i] == curr[3 + i] == curr[6 + i] != -1:
return True, (0 + i, 3 + i, 6 + i)
if curr[0] == curr[4] == curr[8] != -1:
return True, (0, 4, 8)
if curr[2] == curr[4] == curr[6] != -1:
return True, (2, 4, 6)
return False, Nonedef is_draw(curr: list):
return -1 not in curr

The user can also decide to let the AI play first by selecting in a prompt defined as :

用户还可以通过选择定义为的提示来决定让AI首先播放:

def set_algo(self):
val = messagebox.askquestion("Info", "Do you want me to start ?")
self.algo_value = [-1] * 9
self.turn = True
if val == "yes":
i = r.choice([2, 0, 6, 8, 1, 3, 5, 7])
m = self.algo_value[:]
m[i] = 'X'self.ai_move(m)

Lastly, the winner’s cells must get highlighted which is implemented as :

最后,获胜者的单元格必须突出显示,具体实现为:

def highlight(self, v):
for x in v:
self.b[x // 3][x % 3].config(fg="black", bg="blue")
self.disable_or_reset()# disable the grid after the game is own or lost by ai
def disable_or_reset(self, disable=True):
for i in range(3):
for j in range(3):
if disable:
self.b[i][j].config(state=tk.DISABLED)
else:
self.b[i][j].config(text="", bg=self.cget('bg'), fg="black", state=tk.NORMAL)
if not disable: self.set_algo()

Finally, Let’s see it in action

最后,让我们来看一下

Image for post
play tic-tac-toe with AI
与AI打井字游戏

Thanks for going through, Have a nice Day :)

谢谢您度过愉快的一天:)

翻译自: https://medium.com/swlh/tic-tac-toe-ai-ac14d85e52d7

井字游戏设计一个算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值