用 Python 跟自己下棋

简介: 再厉害的程序员,也是从“hello world”程序开始写起。再“聪明”的机器,也是从零样本开始“训练”出来的。


今天,李世乭终于在与 AlphaGo 的人机大战中扳回一局。但计算机 AI 可以在围棋上战胜人类顶尖棋手的时代已经到来。可以预见,人工智能和机器人将会在更多领域做到比人力更高效、准确、安全。所以未来,掌握编程技能显得更加重要。与其现在感叹所谓的“机器威胁论”,还不如现在动起手来,磨练自己的技能。


再厉害的程序员,也是从“hello world”程序开始写起。再“聪明”的机器,也是从零样本开始“训练”出来的。所以今天就来写一个最简单棋类游戏:


Tic Tac Toe,又叫井字棋。


本篇将实现游戏框架,让你可以和电脑对战,但提升电脑的“智能”会在下一篇中细说。另外,文末会介绍一个 Github 上的 Python 版 AlphaGo 项目。


大致说下井字棋的规则:

  • 棋盘为 3*3 共 9 格,类似汉字“井”;
  • 一方为 o,一方为 x,轮流落子;
  • 任一方先有连成一条线的 3 个棋子(横、竖、斜皆可)则为胜利;
  • 棋盘摆满仍没有一方胜利,则为平局。


我打算在控制台下实现这个游戏,所以我需要用一个格式把棋盘的状态输出出来,设想是这样(发到手机上可能有点走形):


    a   b   c
   |---|---|---|
1 |    | o |    |
   |---|---|---|
2 |    | o | x |
   |---|---|---|
3 | x | o |    |
   |---|---|---|


abc 和 123 是为了更方便地标记棋盘上的位置。每走一步,就再次输出新的状态。


而棋盘本身的数据,我用一个 2 维数组来存储:


board = [
  [0, 0, 0],
  [0, 0, 0],
  [0, 0, 0],
]


0 表示没有子,落子之后,o 为 1,x 为 2。


现在,我需要一个函数,按照设想的格式,把棋盘数据输出到屏幕上。以下是我的实现:


CHESS = [' ', 'o', 'x']
def showBoard():
  print '    a   b   c  '
  for i in range(3):
    print '  |---|---|---|'
    print i+1, '|',
    for j in range(3):
      print '%s |' % CHESS[board[i][j]],
    print
  print '  |---|---|---|'



为了对应 0、1、2 和空格、o、x 的关系,我用了一个 CHESS 数组。中间的 print 较多,有些乱,但仔细对照前面的设计图看一下应不难理解。


之后考虑游戏的主体玩法部分。大体的思路是:人走一步、显示棋盘、判断是否结束、AI 走一步、显示棋盘、判断是否结束,如此循环。所以大的框架是:


yourturn = True
showBoard()
while not isFinished():
  if yourturn:
    moveMan()
  else:
    moveAI()
  showBoard()
  yourturn = not yourturn;


这里,我用一个变量 yourturn 来记录该哪一方落子,每次走完一步就交换。


isFinished 是一个判断游戏是否结束的函数,如果结束了,就返回 True,游戏主循环退出。最终结果的输出,我也打算放在这个函数里。


moveMan 和 moveAI 分别是人和 AI 落子,一个是等待控制台的输入,一个是计算出位置。


接下来要做的,就是完成这 3 个函数。


先来看 moveMan:


ROW = {'1': 0, '2': 1, '3': 2}
COL = {'a': 0, 'b': 1, 'c': 2}
def moveMan():
  print 'Your turn...'
  while True:
    try:
      move = raw_input('choose a position (e.g. a1/c2/b3...):\n')
      pos_row = ROW[move[1]]
      pos_col = COL[move[0]]
      if board[pos_row][pos_col] == 0:
        board[pos_row][pos_col] = 1
        return
    except:
      pass



用 raw_input 等待用户输入。这里约定了表示位置的输入格式。ROW 和 COL 两个 dict 是用来将用户输入对应到具体的棋盘坐标上。当判断 board 数组里,用户输入的位置没有棋子时,则指定为 1,并结束函数。while 循环和 try-except 块是为了保证用户的输入是有效的,否则就会重复提示用户输入。


再来看 moveAI:


def moveAI():
  print 'AI\'s turn...'
  while True:
    r = random.randint(0, 2)
    c = random.randint(0, 2)
    if board[r][c] == 0:
      board[r][c] = 2
      return


这个函数的目的是为了将 board 一个位置设置为 2。选取这个位置的过程,则是此游戏 AI 的算法的核心部分。今天先偷个懒,随机生成一个位置,如果为空,就作为落子的位置,并结束函数。下一篇,我们再来完善这个核心。


最后,就是判断胜负的 isFinished:


def isFinished():
  # check row
  if [1, 1, 1] in board:
    print 'You win!'
    return True
  if [2, 2, 2] in board:
    print 'AI wins!'
    return True
  # check col
  for i in range(3):
    if board[0][i] == board[1][i] == board[2][i] == 1:
      print 'You win!'
      return True
    if board[0][i] == board[1][i] == board[2][i] == 2:
      print 'AI wins!'
      return True
  # check diagonal
  if (board[0][0] == board[1][1] == board[2][2] == 1) or (
    board[2][0] == board[1][1] == board[0][2] == 1):
    print 'You win!'
    return True
  if (board[0][0] == board[1][1] == board[2][2] == 2) or (
    board[2][0] == board[1][1] == board[0][2] == 2):
    print 'AI wins!'
    return True
  # check draw game
  draw = True
  for i in range(3):
    if 0 in board[i]:
      draw = False
  if draw:
    print 'Draw game.'
    return True
  return False


稍有点长,主要分为 4 部分:分别是判断横、竖、斜、平局。


横竖斜的胜利部分,就是遍历棋盘去寻找是否有符合条件的情况,有则输出游戏结果,并返回 True。如果都没有,就去判断是否是平局。


判断平局的逻辑是这样:先设定 draw 为 True,如果遇到棋盘上有 0 的位置,则设为 False。否则遍历结束,draw 仍然为 True,就说明已没有空位,游戏以平局结束。


一个井字棋游戏已完成,截取一小段输出结果:


Your turn...
choose a position (e.g. a1/c2/b3...):
b2
     a   b   c
   |---|---|---|
1 | o |    |    |
   |---|---|---|
2 | o | o |    |
   |---|---|---|
3 | x |    | x |
   |---|---|---|
AI's turn...
     a   b   c
   |---|---|---|
1 | o |    |    |
   |---|---|---|
2 | o | o |    |
   |---|---|---|
3 | x | x | x |
   |---|---|---|
AI wins!



当然,现在的这个根本还算不上 AI。下一次,我们会让它更“机智”一点。


如果手机上看代码不方便,可移步论坛,在电脑上查看,我也会将完整代码上传。(论坛上的附件需要登录才可下载)




另外,关于前面提到的开源版 AlphaGo 项目。


项目地址:

https://github.com/Rochester-NRT/AlphaGo


有人传是 AlphaGo 开源了,但这其实只是 University of Rochester 根据 AlphaGo 的论文做的实现,用了 Python。与真正使用的程序相去甚远。可以去围观,看看代码。对机器学习、神经网络有兴趣的可以深入研究一下,甚至参与项目开发。不过如果你只是想在自己的机器上运行项目,那我要提醒你几点:


首先,项目里面用到了 SciPy,而 SciPy 的安装是需要根据不同操作系统编译的,这里面坑不少,至少我是在两个系统上折腾了几小时才安装成功。

另外,项目目前只完成了一个基本框架,和算法中一小部分,完成度很低。虽然也可以训练 AI 走棋,但效果肯定远不如 AlphaGo。

项目里虽然附带了一个 HTML5 的网页围棋接口,但应该还没有对接,所以想跟电脑对战的要失望了。

相关文章
|
人工智能 算法 Python
用 Python 跟自己下棋(续)
棋类游戏最基本的 AI 方法就是给棋盘上每个位置的优劣程度打分,然后选择的最高分的位置来走。打分算法的好坏,就决定了这个 AI 的“智能”程度。
|
2天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
2天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
4天前
|
设计模式 算法 搜索推荐
Python编程中的设计模式:优雅解决复杂问题的钥匙####
本文将探讨Python编程中几种核心设计模式的应用实例与优势,不涉及具体代码示例,而是聚焦于每种模式背后的设计理念、适用场景及其如何促进代码的可维护性和扩展性。通过理解这些设计模式,开发者可以更加高效地构建软件系统,实现代码复用,提升项目质量。 ####
|
3天前
|
机器学习/深度学习 存储 算法
探索Python编程:从基础到高级应用
【10月更文挑战第38天】本文旨在引导读者从Python的基础知识出发,逐渐深入到高级编程概念。通过简明的语言和实际代码示例,我们将一起探索这门语言的魅力和潜力,理解它如何帮助解决现实问题,并启发我们思考编程在现代社会中的作用和意义。
|
2天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
3天前
|
机器学习/深度学习 数据挖掘 开发者
Python编程入门:理解基础语法与编写第一个程序
【10月更文挑战第37天】本文旨在为初学者提供Python编程的初步了解,通过简明的语言和直观的例子,引导读者掌握Python的基础语法,并完成一个简单的程序。我们将从变量、数据类型到控制结构,逐步展开讲解,确保即使是编程新手也能轻松跟上。文章末尾附有完整代码示例,供读者参考和实践。
|
4天前
|
人工智能 数据挖掘 程序员
Python编程入门:从零到英雄
【10月更文挑战第37天】本文将引导你走进Python编程的世界,无论你是初学者还是有一定基础的开发者,都能从中受益。我们将从最基础的语法开始讲解,逐步深入到更复杂的主题,如数据结构、面向对象编程和网络编程等。通过本文的学习,你将能够编写出自己的Python程序,实现各种功能。让我们一起踏上Python编程之旅吧!
|
5天前
|
数据采集 机器学习/深度学习 人工智能
Python编程入门:从基础到实战
【10月更文挑战第36天】本文将带你走进Python的世界,从基础语法出发,逐步深入到实际项目应用。我们将一起探索Python的简洁与强大,通过实例学习如何运用Python解决问题。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供有价值的指导和灵感。让我们一起开启Python编程之旅,用代码书写想法,创造可能。
|
6天前
|
Python
不容错过!Python中图的精妙表示与高效遍历策略,提升你的编程艺术感
本文介绍了Python中图的表示方法及遍历策略。图可通过邻接表或邻接矩阵表示,前者节省空间适合稀疏图,后者便于检查连接但占用更多空间。文章详细展示了邻接表和邻接矩阵的实现,并讲解了深度优先搜索(DFS)和广度优先搜索(BFS)的遍历方法,帮助读者掌握图的基本操作和应用技巧。
23 4