pygame 欢乐五子棋(二)

上期,我们做好了游戏的棋盘,本期我们处理如何落子,及设计我们的AI
废话少说,先看看如何落子

落子

落子的过程大概为

  1. 获取鼠标的位置
  2. 计算位置所对应的落子点
  3. 画出棋子

看一下主函数中的流程

if event.type == pygame.QUIT:
    running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
    # 1.有鼠标点击事件发生了,我们获取鼠标的位置

    # 2.计算鼠标点击所在网格点的位置

    # 3.添加棋子
  1. 获取鼠标位置
    获取鼠标的位置很简单,pygame为我们做好了各种事件的检测及记录,我们只需要看有没有鼠标落下事件的发生,然后获取位置就好了
    好了,我们看如何获取鼠标的位置,只有一个语句就好了
pos = event.pos
  1. 计算网格点的位置
    计算网格点的位置也很简单,只要用坐标值除以网格的宽度就好了,这里有一点需要注意,就是我们不可能每次都点击到网格点上,因此需要有一个四舍五入的过程,就是我们点击的位置距离哪个点近,我们就默认用户点击了哪一个点。
grid = (int(round(event.pos[0] / (GRID_WIDTH + .0))),
        int(round(event.pos[1] / (GRID_WIDTH + .0))))
  1. 添加棋子
    要画出棋子,我们必须记录下我们走的每一步棋,并且在刷新屏幕的时候将这些棋子全部画出来。我们定义一个全局变量movements用于我们的每一步棋。然后,每次落子之后,我们就将落子信息存储在里面。
movements = []
def add_coin(screen, pos, color):
    movements.append(((pos[0] * GRID_WIDTH, pos[1] * GRID_WIDTH), color))
    pygame.draw.circle(screen, color,
        (pos[0] * GRID_WIDTH, pos[1] * GRID_WIDTH), 16)

再定义一个画出每一步棋的函数

def draw_movements(screen):
    for m ini movements:
        # m[0] 存的是位置,m[1]存的是颜色
        pygame.draw.circle(screen, m[1], m[0], 16)

好了,我们在添加棋子的注释下面添加一句

add_coin(screen, pos, BLACK)

在刷新屏幕之前调用画出棋子的函数

draw_movements(screen)

好了,我们运行一下,在屏幕中画一个五字~~

wu.png

OK,落子顺利完成,
下一步就是我们游戏的核心了,我们要写我们自己的AI~~

五子棋 AI

在我们开发我们的AI之前我们还有一个事情要做

判断游戏结束

如何判断游戏结束?只要有五个子连成线就好了!对的,每次落子的时候我们只要判断所落的子周围有没有统一颜色的棋子可以连成五子的,因此我们需要一个矩阵记录每个位置子的颜色~

color_metrix = [[None] * 20 for i in range(20)]

此时,我们就可以定义我们的判断游戏结束的函数了,函数的逻辑很简单,只要判断所落子的周围是否有五子连成线一共有四个方向

def game_is_over(pos, color):
    hori = 1
    verti = 1
    slash = 1
    backslash = 1
    left = pos[0] - 1
    while left > 0 and color_metrix[left][pos[1]] == color:
        left -= 1
        hori += 1

    right = pos[0] + 1
    while right < 20 and color_metrix[right][pos[1]] == color:
        right += 1
        hori += 1

    up = pos[1] - 1
    while up > 0 and color_metrix[pos[0]][up] == color:
        up -= 1
        verti += 1

    down = pos[1] + 1
    while down < 20 and color_metrix[pos[0]][down] == color:
        down += 1
        verti += 1

    left = pos[0] - 1
    up = pos[1] - 1
    while left > 0 and up > 0 and color_metrix[left][up] == color:
        left -= 1
        up -= 1
        backslash += 1

    right = pos[0] + 1
    down = pos[1] + 1
    while right < 20 and down < 20 and color_metrix[right][down] == color:
        right += 1
        down += 1
        backslash += 1

    right = pos[0] + 1
    up = pos[1] - 1
    while right < 20 and up > 0 and color_metrix[right][up] == color:
        right += 1
        up -= 1
        slash += 1

    left = pos[0] - 1
    down = pos[1] + 1
    while left > 0 and down < 20 and color_metrix[left][down] == color:
        left -= 1
        down += 1
        slash += 1

    if max([hori, verti, backslash, slash]) == 5:
        return True

好的在我们画出棋子之后加入游戏结束的判断

if game_is_over(grid, BLACK):
    running = False

对了记得在添加棋子的函数里将改变我们color_metrix的语句加进去。这样只要我们有五个同色子连成线游戏就结束了!
先前我们是直接在主循环中处理事件的这样不好,我们提取出一个函数来用于处理用户走子,及AI的响应函数接口像这样:

def move(surf, pos):
    '''
    Args:
        surf: 我们的屏幕
        pos: 用户落子的位置
    Returns a tuple or None:
        None: if move is invalid else return a
        tuple (bool, player):
            bool: True is game is not over else False
            player: winner (USER or AI)
    '''

这个函数首先判断落子的位置是否已近有子,有的话返回None, 否则落子为合法的,我们调用add_coin, 最后我们调用respond函数
这个函数是我们用来运行我们的AI,并决定下一步动作的,返回值和我们的move一样,这样,我们的move就变成了下面这样

def move(surf, pos):
    '''
    Args:
        surf: 我们的屏幕
        pos: 用户落子的位置
    Returns a tuple or None:
        None: if move is invalid else return a
        tuple (bool, player):
            bool: True is game is not over else False
            player: winner (USER or AI)
    '''
    grid = (int(round(pos[0] / (GRID_WIDTH + .0))),
            int(round(pos[1] / (GRID_WIDTH + .0))))

    if grid[0] <= 0 or grid[0] > 19:
        return
    if grid[1] <= 0 or grid[1] > 19:
        return

    pos = (grid[0] * GRID_WIDTH, grid[1] * GRID_WIDTH)

    # num_pos = gridpos_2_num(grid)
    # if num_pos not in remain:
    #     return None
    if color_metrix[grid[0]][grid[1]] is not None:
        return None

    curr_move = (pos, BLACK)
    add_coin(surf, BLACK, grid, USER)

    if game_is_over(grid, BLACK):
        return (False, USER)

    return respond(surf, movements, curr_move)

这里我们给add_coin 添加了一个参数,就是当前落子的角色,其中

USER, AI = 1, 0

我们用随机落子代替我们的AI

def respond(surf, movements, curr_move):
    # 测试用,随机落子

    grid_pos = (random.randint(1, 19), random.randint(1, 19))
    # print(grid_pos)
    add_coin(surf, WHITE, grid_pos, 16)
    if game_is_over(grid_pos, WHITE):
        return (False, AI)

    return None

看一下效果~

屏幕快照_2017-08-16_下午8.50.16.png

好了~ 我们的五子棋完成了!

什么?? 不满意?

好吧,AI确实比较麻烦,这里我给大家介绍一下思路,完整的代码大家可以到我的github上去下载,下载链接在文章的最后~

AI思路

五子棋的规则大家都懂,没下一个棋子都对周围的区域会产生影响,我们的AI可以记录每个位置的价值,每次下棋的时候可以选择得分最高的那个位置,具体操作我们可以这样,连成子的数目越多,价值越高,如果可以在多个方向上连成子,我们可以将价值相应的增加倍数,同时在对方有三个子连成线(独立的,即两边没有其他子)的时候必须堵住对方。大概就是这样的。
具体实现我们就不讲解了,看一下最终效果


final.png

我试了一下,和AI下我基本上有输的,也有赢的。可能是我的水平比较差,大家可以写自己的AI或者在我的AI基础上再改进!
完整的AI代码可以去我的 github 看,点击这里进入GitHub。
如果这篇文章对您有帮助,赞赏一下吧~
您的支持是我继续创作的动力~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 介绍内容 前些时间,阿尔法狗对战柯洁围棋大赛很热门,那只是人工智能中的一个方向,展示了机器能代替人做某些事情。而围...
    allen151阅读 9,264评论 5 15
  • 内容介绍 上一篇我们讲到五子棋的UI篇的实现,现在这一篇我们来讲五子棋的AI篇的实现。如果你还没看过UI篇,建议先...
    allen151阅读 9,453评论 9 14
  • 本文参加#致我们单纯的小美好活动,本人承诺,文章内容为原创,且未在其他平台发表过。 一 苏柑觉得很惶恐。 ...
    Echoecho声声阅读 794评论 0 0
  • 为什突然做这个,因为这是个笔试题,拖了一个月才写(最近终于闲了O(∩_∩)O),废话不多说,说说这个题吧 题目要求...
    Stevenzwzhai阅读 2,688评论 0 5
  • 非主流原意本身是一个很小资和小众的圈子代表词,但在中国被严重恶俗化了。所谓的非主流,已经不是真正意义上的非主流,而...
    JianChun1阅读 1,278评论 2 15