> 文档中心 > 用python做几个小游戏-5/10(动图演示+源码分享)

用python做几个小游戏-5/10(动图演示+源码分享)


🐚作者简介:苏凉(专注于网络爬虫,数据分析)
🐳博客主页:苏凉.py的博客
🛸系列专栏:python小游戏
👑名言警句:海阔凭鱼跃,天高任鸟飞。
📰要是觉得博主文章写的不错的话,还望大家三连支持一下呀!!!
👉关注✨点赞👍收藏📂

这些游戏你玩过几个?

    • 1.Tk个性化签名
    • 2.五子棋(人人对战/人机对战)
      • 人机对战:
      • 人人对战:
      • 落子及游戏获胜规则
      • 人人对战源码
      • 人机对战源码
    • 3.画一个冰墩墩
    • 4.吃苹果
    • 5.围棋

1.Tk个性化签名

点击运行,输入名字自动生成个性签名。并保存到当地文件夹下。

效果演示:
在这里插入图片描述

源码分享:

from tkinter import * # python 3 是tkinterimport tkinter.messageboximport requestsimport refrom PIL import Imagedef download():  start_url = 'http://www.uustv.com/'  name = entry.get().encode('utf-8')  if not name:    tkinter.messagebox.showinfo('提示', '请输入姓名再设计!')    return  data = {    'word': name,    'sizes': '60',    'fonts': '1.ttf',    'fontcolor': '#000000'  }  headers = {      'Referer': 'http://www.uustv.com/',      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'  }  result = requests.post(start_url, data=data,headers=headers)  result.encoding = 'utf-8'  reg = '

'
img_url = start_url + re.findall(reg, result.text)[0] response = requests.get(img_url).content # 将生成的签名图片下载到本地 print(name.decode()) with open('./{}.gif'.format(name.decode()), 'wb') as f: f.write(response) try: im = Image.open('./{}.gif'.format(name.decode())) im.show() except: print('自己打开看吧')root = Tk()root.title('个性签名设计')root.geometry('+800+300') # 设置窗口出现在屏幕上面的位置Label(root, text='姓名', font=('微软雅黑', 15)).grid() # 布局方法不要混用entry = Entry(root, font=('微软雅黑', 15))entry.grid(row=0, column=1)button = Button(root, text='设计签名', font=('微软雅黑', 15), width='15', height=1, command=download)button.grid(row=1, column=1)root.mainloop()

2.五子棋(人人对战/人机对战)

任意一个颜色棋子连续五颗则获胜。

效果演示:

人机对战:

在这里插入图片描述

人人对战:

在这里插入图片描述

源码分享:

落子及游戏获胜规则

from collections import namedtupleChessman = namedtuple('Chessman', 'Name Value Color')Point = namedtuple('Point', 'X Y')BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45))WHITE_CHESSMAN = Chessman('白子', 2, (219, 219, 219))offset = [(1, 0), (0, 1), (1, 1), (1, -1)]class Checkerboard:    def __init__(self, line_points): self._line_points = line_points self._checkerboard = [[0] * line_points for _ in range(line_points)]    def _get_checkerboard(self): return self._checkerboard    checkerboard = property(_get_checkerboard)    # 判断是否可落子    def can_drop(self, point): return self._checkerboard[point.Y][point.X] == 0    def drop(self, chessman, point): """ 落子 :param chessman: :param point:落子位置 :return:若该子落下之后即可获胜,则返回获胜方,否则返回 None """ print(f'{chessman.Name} ({point.X}, {point.Y})') self._checkerboard[point.Y][point.X] = chessman.Value if self._win(point):     print(f'{chessman.Name}获胜')     return chessman    # 判断是否赢了    def _win(self, point): cur_value = self._checkerboard[point.Y][point.X] for os in offset:     if self._get_count_on_direction(point, cur_value, os[0], os[1]):  return True    def _get_count_on_direction(self, point, value, x_offset, y_offset): count = 1 for step in range(1, 5):     x = point.X + step * x_offset     y = point.Y + step * y_offset     if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:  count += 1     else:  break for step in range(1, 5):     x = point.X - step * x_offset     y = point.Y - step * y_offset     if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:  count += 1     else:  break return count >= 5

人人对战源码

import sysimport pygamefrom pygame.locals import *import pygame.gfxdrawfrom 小游戏.五子棋.checkerboard import Checkerboard, BLACK_CHESSMAN, WHITE_CHESSMAN, PointSIZE = 30  # 棋盘每个点时间的间隔Line_Points = 19  # 棋盘每行/每列点数Outer_Width = 20  # 棋盘外宽度Border_Width = 4  # 边框宽度Inside_Width = 4  # 边框跟实际的棋盘之间的间隔Border_Length = SIZE * (Line_Points - 1) + Inside_Width * 2 + Border_Width  # 边框线的长度Start_X = Start_Y = Outer_Width + int(Border_Width / 2) + Inside_Width  # 网格线起点(左上角)坐标SCREEN_HEIGHT = SIZE * (Line_Points - 1) + Outer_Width * 2 + Border_Width + Inside_Width * 2  # 游戏屏幕的高SCREEN_WIDTH = SCREEN_HEIGHT + 200  # 游戏屏幕的宽Stone_Radius = SIZE // 2 - 3  # 棋子半径Stone_Radius2 = SIZE // 2 + 3Checkerboard_Color = (0xE3, 0x92, 0x65)  # 棋盘颜色BLACK_COLOR = (0, 0, 0)WHITE_COLOR = (255, 255, 255)RED_COLOR = (200, 30, 30)BLUE_COLOR = (30, 30, 200)BLACK_STONE_COLOR = (45, 45, 45)WHITE_STONE_COLOR = (219, 219, 219)RIGHT_INFO_POS_X = SCREEN_HEIGHT + Stone_Radius2 * 2 + 10def print_text(screen, font, x, y, text, fcolor=(255, 255, 255)):    imgText = font.render(text, True, fcolor)    screen.blit(imgText, (x, y))def main():    pygame.init()    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))    pygame.display.set_caption('五子棋')    font1 = pygame.font.SysFont('SimHei', 36)    font2 = pygame.font.SysFont('SimHei', 72)    fwidth, fheight = font2.size('黑方获胜')    checkerboard = Checkerboard(Line_Points)    cur_runner = BLACK_CHESSMAN    winner = None    while True: for event in pygame.event.get():     if event.type == QUIT:  sys.exit()     elif event.type == KEYDOWN:  if event.key == K_RETURN:      if winner is not None:   winner = None   cur_runner = BLACK_CHESSMAN   checkerboard = Checkerboard(Line_Points)     elif event.type == MOUSEBUTTONDOWN:  if winner is None:      pressed_array = pygame.mouse.get_pressed()      if pressed_array[0]:   mouse_pos = pygame.mouse.get_pos()   click_point = _get_clickpoint(mouse_pos)   if click_point is not None:if checkerboard.can_drop(click_point):    winner = checkerboard.drop(cur_runner, click_point)    if cur_runner == BLACK_CHESSMAN: cur_runner = WHITE_CHESSMAN    else: cur_runner = BLACK_CHESSMAN   else:print('超出棋盘区域') # 画棋盘 _draw_checkerboard(screen) # 画棋盘上已有的棋子 for i, row in enumerate(checkerboard.checkerboard):     for j, cell in enumerate(row):  if cell == BLACK_CHESSMAN.Value:      _draw_chessman(screen, Point(j, i), BLACK_CHESSMAN.Color)  elif cell == WHITE_CHESSMAN.Value:      _draw_chessman(screen, Point(j, i), WHITE_CHESSMAN.Color) _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + 20), BLACK_STONE_COLOR) _draw_chessman_pos(screen, (SCREEN_HEIGHT + Stone_Radius2, Start_X + 20 + Stone_Radius2 * 3), WHITE_STONE_COLOR) if winner:     print_text(screen, font2, (SCREEN_WIDTH - fwidth)//2, (SCREEN_HEIGHT - fheight)//2, winner.Name + '获胜', RED_COLOR) if cur_runner == BLACK_CHESSMAN:     print_text(screen, font1, RIGHT_INFO_POS_X, Start_X, '获胜' if winner else '落子中', BLUE_COLOR) else:     print_text(screen, font1, RIGHT_INFO_POS_X, Start_X + Stone_Radius2 * 3, '获胜' if winner else '落子中', BLUE_COLOR) pygame.display.flip()# 画棋盘def _draw_checkerboard(screen):    # 填充棋盘背景色    screen.fill(Checkerboard_Color)    # 画棋盘网格线外的边框    pygame.draw.rect(screen, BLACK_COLOR, (Outer_Width, Outer_Width, Border_Length, Border_Length), Border_Width)    # 画网格线    for i in range(Line_Points): pygame.draw.line(screen, BLACK_COLOR,    (Start_Y, Start_Y + SIZE * i),    (Start_Y + SIZE * (Line_Points - 1), Start_Y + SIZE * i),    1)    for j in range(Line_Points): pygame.draw.line(screen, BLACK_COLOR,    (Start_X + SIZE * j, Start_X),    (Start_X + SIZE * j, Start_X + SIZE * (Line_Points - 1)),    1)    # 画星位和天元    for i in (3, 9, 15): for j in (3, 9, 15):     if i == j == 9:  radius = 5     else:  radius = 3     # pygame.draw.circle(screen, BLACK, (Start_X + SIZE * i, Start_Y + SIZE * j), radius)     pygame.gfxdraw.aacircle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)     pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * i, Start_Y + SIZE * j, radius, BLACK_COLOR)# 画棋子def _draw_chessman(screen, point, stone_color):    # pygame.draw.circle(screen, stone_color, (Start_X + SIZE * point.X, Start_Y + SIZE * point.Y), Stone_Radius)    pygame.gfxdraw.aacircle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)    pygame.gfxdraw.filled_circle(screen, Start_X + SIZE * point.X, Start_Y + SIZE * point.Y, Stone_Radius, stone_color)def _draw_chessman_pos(screen, pos, stone_color):    pygame.gfxdraw.aacircle(screen, pos[0], pos[1], Stone_Radius2, stone_color)    pygame.gfxdraw.filled_circle(screen, pos[0], pos[1], Stone_Radius2, stone_color)# 根据鼠标点击位置,返回游戏区坐标def _get_clickpoint(click_pos):    pos_x = click_pos[0] - Start_X    pos_y = click_pos[1] - Start_Y    if pos_x < -Inside_Width or pos_y < -Inside_Width: return None    x = pos_x // SIZE    y = pos_y // SIZE    if pos_x % SIZE > Stone_Radius: x += 1    if pos_y % SIZE > Stone_Radius: y += 1    if x >= Line_Points or y >= Line_Points: return None    return Point(x, y)if __name__ == '__main__':    main()

人机对战源码

人机对战感兴趣的小伙伴可私聊我直接领取,这里就不放啦。

3.画一个冰墩墩

用turtle画一个2022冬奥吉祥物

效果演示:
在这里插入图片描述

源码分享:

import turtleturtle.title('PythonBingDwenDwen')turtle.speed(100)  # 速度# 左手turtle.penup()turtle.goto(177, 112)turtle.pencolor("lightgray")turtle.pensize(3)turtle.fillcolor("white")turtle.begin_fill()turtle.pendown()turtle.setheading(80)turtle.circle(-45, 200)turtle.circle(-300, 23)turtle.end_fill()# 左手内turtle.penup()turtle.goto(182, 95)turtle.pencolor("black")turtle.pensize(1)turtle.fillcolor("black")turtle.begin_fill()turtle.setheading(95)turtle.pendown()turtle.circle(-37, 160)turtle.circle(-20, 50)turtle.circle(-200, 30)turtle.end_fill()# 轮廓# 头顶turtle.penup()turtle.goto(-73, 230)turtle.pencolor("lightgray")turtle.pensize(3)turtle.fillcolor("white")turtle.begin_fill()turtle.pendown()turtle.setheading(20)turtle.circle(-250, 35)# 左耳turtle.setheading(50)turtle.circle(-42, 180)# 左侧turtle.setheading(-50)turtle.circle(-190, 30)turtle.circle(-320, 45)# 左腿turtle.circle(120, 30)turtle.circle(200, 12)turtle.circle(-18, 85)turtle.circle(-180, 23)turtle.circle(-20, 110)turtle.circle(15, 115)turtle.circle(100, 12)# 右腿turtle.circle(15, 120)turtle.circle(-15, 110)turtle.circle(-150, 30)turtle.circle(-15, 70)turtle.circle(-150, 10)turtle.circle(200, 35)turtle.circle(-150, 20)# 右手turtle.setheading(-120)turtle.circle(50, 30)turtle.circle(-35, 200)turtle.circle(-300, 23)# 右侧turtle.setheading(86)turtle.circle(-300, 26)# 右耳turtle.setheading(122)turtle.circle(-53, 160)turtle.end_fill()# 右耳内turtle.penup()turtle.goto(-130, 180)turtle.pencolor("black")turtle.pensize(1)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.setheading(120)turtle.circle(-28, 160)turtle.setheading(210)turtle.circle(150, 20)turtle.end_fill()# 左耳内turtle.penup()turtle.goto(90, 230)turtle.setheading(40)turtle.begin_fill()turtle.pendown()turtle.circle(-30, 170)turtle.setheading(125)turtle.circle(150, 23)turtle.end_fill()# 右手内turtle.penup()turtle.goto(-180, -55)turtle.fillcolor("black")turtle.begin_fill()turtle.setheading(-120)turtle.pendown()turtle.circle(50, 30)turtle.circle(-27, 200)turtle.circle(-300, 20)turtle.setheading(-90)turtle.circle(300, 14)turtle.end_fill()# 左腿内turtle.penup()turtle.goto(108, -168)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.setheading(-115)turtle.circle(110, 15)turtle.circle(200, 10)turtle.circle(-18, 80)turtle.circle(-180, 13)turtle.circle(-20, 90)turtle.circle(15, 60)turtle.setheading(42)turtle.circle(-200, 29)turtle.end_fill()# 右腿内turtle.penup()turtle.goto(-38, -210)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.setheading(-155)turtle.circle(15, 100)turtle.circle(-10, 110)turtle.circle(-100, 30)turtle.circle(-15, 65)turtle.circle(-100, 10)turtle.circle(200, 15)turtle.setheading(-14)turtle.circle(-200, 27)turtle.end_fill()# 右眼# 眼圈turtle.penup()turtle.goto(-64, 120)turtle.begin_fill()turtle.pendown()turtle.setheading(40)turtle.circle(-35, 152)turtle.circle(-100, 50)turtle.circle(-35, 130)turtle.circle(-100, 50)turtle.end_fill()# 眼珠turtle.penup()turtle.goto(-47, 55)turtle.fillcolor("white")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(25, 360)turtle.end_fill()turtle.penup()turtle.goto(-45, 62)turtle.pencolor("darkslategray")turtle.fillcolor("darkslategray")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(19, 360)turtle.end_fill()turtle.penup()turtle.goto(-45, 68)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(10, 360)turtle.end_fill()turtle.penup()turtle.goto(-47, 86)turtle.pencolor("white")turtle.fillcolor("white")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(5, 360)turtle.end_fill()# 左眼# 眼圈turtle.penup()turtle.goto(51, 82)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.setheading(120)turtle.circle(-32, 152)turtle.circle(-100, 55)turtle.circle(-25, 120)turtle.circle(-120, 45)turtle.end_fill()# 眼珠turtle.penup()turtle.goto(79, 60)turtle.fillcolor("white")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(24, 360)turtle.end_fill()turtle.penup()turtle.goto(79, 64)turtle.pencolor("darkslategray")turtle.fillcolor("darkslategray")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(19, 360)turtle.end_fill()turtle.penup()turtle.goto(79, 70)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(10, 360)turtle.end_fill()turtle.penup()turtle.goto(79, 88)turtle.pencolor("white")turtle.fillcolor("white")turtle.begin_fill()turtle.pendown()turtle.setheading(0)turtle.circle(5, 360)turtle.end_fill()# 鼻子turtle.penup()turtle.goto(37, 80)turtle.fillcolor("black")turtle.begin_fill()turtle.pendown()turtle.circle(-8, 130)turtle.circle(-22, 100)turtle.circle(-8, 130)turtle.end_fill()# 嘴turtle.penup()turtle.goto(-15, 48)turtle.setheading(-36)turtle.begin_fill()turtle.pendown()turtle.circle(60, 70)turtle.setheading(-132)turtle.circle(-45, 100)turtle.end_fill()# 彩虹圈turtle.penup()turtle.goto(-135, 120)turtle.pensize(5)turtle.pencolor("cyan")turtle.pendown()turtle.setheading(60)turtle.circle(-165, 150)turtle.circle(-130, 78)turtle.circle(-250, 30)turtle.circle(-138, 105)turtle.penup()turtle.goto(-131, 116)turtle.pencolor("slateblue")turtle.pendown()turtle.setheading(60)turtle.circle(-160, 144)turtle.circle(-120, 78)turtle.circle(-242, 30)turtle.circle(-135, 105)turtle.penup()turtle.goto(-127, 112)turtle.pencolor("orangered")turtle.pendown()turtle.setheading(60)turtle.circle(-155, 136)turtle.circle(-116, 86)turtle.circle(-220, 30)turtle.circle(-134, 103)turtle.penup()turtle.goto(-123, 108)turtle.pencolor("gold")turtle.pendown()turtle.setheading(60)turtle.circle(-150, 136)turtle.circle(-104, 86)turtle.circle(-220, 30)turtle.circle(-126, 102)turtle.penup()turtle.goto(-120, 104)turtle.pencolor("greenyellow")turtle.pendown()turtle.setheading(60)turtle.circle(-145, 136)turtle.circle(-90, 83)turtle.circle(-220, 30)turtle.circle(-120, 100)turtle.penup()# 爱心turtle.penup()turtle.goto(220, 115)turtle.pencolor("brown")turtle.pensize(1)turtle.fillcolor("brown")turtle.begin_fill()turtle.pendown()turtle.setheading(36)turtle.circle(-8, 180)turtle.circle(-60, 24)turtle.setheading(110)turtle.circle(-60, 24)turtle.circle(-8, 180)turtle.end_fill()# 五环turtle.penup()turtle.goto(-5, -170)turtle.pendown()turtle.pencolor("blue")turtle.circle(6)turtle.penup()turtle.goto(10, -170)turtle.pendown()turtle.pencolor("black")turtle.circle(6)turtle.penup()turtle.goto(25, -170)turtle.pendown()turtle.pencolor("brown")turtle.circle(6)turtle.penup()turtle.goto(2, -175)turtle.pendown()turtle.pencolor("lightgoldenrod")turtle.circle(6)turtle.penup()turtle.goto(16, -175)turtle.pendown()turtle.pencolor("green")turtle.circle(6)turtle.penup()turtle.pencolor("black")turtle.goto(-16, -160)turtle.write("BEIJING 2022", font=('Arial', 10, 'bold italic'))turtle.hideturtle()turtle.done()

4.吃苹果

通过方向键控制小人去吃苹果,吃到所有苹果则游戏结束。

效果演示:
在这里插入图片描述

源码分享:

import itertools, sys, time, random, math, pygamefrom pygame.locals import *from MyLibrary import *def calc_velocity(direction, vel=1.0):    velocity = Point(0,0)    if direction == 0: #上 velocity.y = -vel    elif direction == 2: #右 velocity.x = vel    elif direction == 4: #下 velocity.y = vel    elif direction == 6: #左 velocity.x = -vel    return velocitypygame.init()screen = pygame.display.set_mode((800,600))pygame.display.set_caption("吃苹果")font = pygame.font.Font(None, 36)timer = pygame.time.Clock()#创建精灵组player_group = pygame.sprite.Group()food_group = pygame.sprite.Group()#初始化玩家精灵组player = MySprite()player.load("farmer walk.png", 96, 96, 8)player.position = 80, 80player.direction = 4player_group.add(player)#初始化food精灵组for n in range(1,50):    food = MySprite();    food.load("food_low.png", 35, 35, 1)    food.position = random.randint(0,780),random.randint(0,580)    food_group.add(food)game_over = Falseplayer_moving = Falseplayer_health = 0while True:    timer.tick(30)    ticks = pygame.time.get_ticks()    for event in pygame.event.get(): if event.type == QUIT:     pygame.quit()     sys.exit()    keys = pygame.key.get_pressed()    if keys[K_ESCAPE]: sys.exit()    elif keys[K_UP] or keys[K_w]: player.direction = 0 player_moving = True    elif keys[K_RIGHT] or keys[K_d]: player.direction = 2 player_moving = True    elif keys[K_DOWN] or keys[K_s]: player.direction = 4 player_moving = True    elif keys[K_LEFT] or keys[K_a]: player.direction = 6 player_moving = True    else: player_moving = False    if not game_over: #根据角色的不同方向,使用不同的动画帧 player.first_frame = player.direction * player.columns player.last_frame = player.first_frame + player.columns-1 if player.frame < player.first_frame:     player.frame = player.first_frame if not player_moving:     #当停止按键(即人物停止移动的时候),停止更新动画帧     player.frame = player.first_frame = player.last_frame else:      player.velocity = calc_velocity(player.direction, 1.5)     player.velocity.x *= 1.5     player.velocity.y *= 1.5 #更新玩家精灵组 player_group.update(ticks, 50) #移动玩家 if player_moving:     player.X += player.velocity.x     player.Y += player.velocity.y     if player.X < 0: player.X = 0     elif player.X > 700: player.X = 700     if player.Y < 0: player.Y = 0     elif player.Y > 500: player.Y = 500 #检测玩家是否与食物冲突,是否吃到果实 attacker = None attacker = pygame.sprite.spritecollideany(player, food_group) if attacker != None:     if pygame.sprite.collide_circle_ratio(0.65)(player,attacker):  player_health +=2;  food_group.remove(attacker); if player_health > 100: player_health = 100 #更新食物精灵组 food_group.update(ticks, 50) if len(food_group) == 0:     game_over = True    #清屏    screen.fill((50,50,100))    #绘制精灵    food_group.draw(screen)    player_group.draw(screen)    #绘制玩家血量条    pygame.draw.rect(screen, (50,150,50,180), Rect(300,570,player_health*2,25))    pygame.draw.rect(screen, (100,200,100,180), Rect(300,570,200,25), 2)    if game_over: print_text(font, 300, 100, "G A M E   O V E R") pygame.display.update()    

5.围棋

效果演示:
在这里插入图片描述

源码分享:

from tkinter import *# ttk覆盖tkinter部分对象,ttk对tkinter进行了优化from tkinter.ttk import *# 深拷贝时需要用到copy模块import copyimport tkinter.messagebox# 围棋应用对象定义class Application(Tk):# 初始化棋盘,默认九路棋盘def __init__(self,my_mode_num=9):Tk.__init__(self)# 模式,九路棋:9,十三路棋:13,十九路棋:19self.mode_num=my_mode_num# 窗口尺寸设置,默认:1.8self.size=1.8# 棋盘每格的边长self.dd=360*self.size/(self.mode_num-1)# 相对九路棋盘的矫正比例self.p=1 if self.mode_num==9 else (2/3 if self.mode_num==13 else 4/9)# 定义棋盘阵列,超过边界:-1,无子:0,黑棋:1,白棋:2self.positions=[[0 for i in range(self.mode_num+2)] for i in range(self.mode_num+2)]# 初始化棋盘,所有超过边界的值置-1for m in range(self.mode_num+2):for n in range(self.mode_num+2):if (m*n==0 or m==self.mode_num+1 or n==self.mode_num+1):self.positions[m][n]=-1# 拷贝三份棋盘“快照”,悔棋和判断“打劫”时需要作参考self.last_3_positions=copy.deepcopy(self.positions)self.last_2_positions=copy.deepcopy(self.positions)self.last_1_positions=copy.deepcopy(self.positions)# 记录鼠标经过的地方,用于显示shadow时self.cross_last=None# 当前轮到的玩家,黑:0,白:1,执黑先行self.present=0 # 初始停止运行,点击“开始游戏”运行游戏self.stop=True# 悔棋次数,次数大于0才可悔棋,初始置0(初始不能悔棋),悔棋后置0,下棋或弃手时恢复为1,以禁止连续悔棋self.regretchance=0# 图片资源,存放在当前目录下的/Pictures/中self.photoW=PhotoImage(file = "./Pictures/W.png")self.photoB=PhotoImage(file = "./Pictures/B.png")self.photoBD=PhotoImage(file = "./Pictures/"+"BD"+"-"+str(self.mode_num)+".png")self.photoWD=PhotoImage(file = "./Pictures/"+"WD"+"-"+str(self.mode_num)+".png")self.photoBU=PhotoImage(file = "./Pictures/"+"BU"+"-"+str(self.mode_num)+".png")self.photoWU=PhotoImage(file = "./Pictures/"+"WU"+"-"+str(self.mode_num)+".png")# 用于黑白棋子图片切换的列表self.photoWBU_list=[self.photoBU,self.photoWU]self.photoWBD_list=[self.photoBD,self.photoWD]# 窗口大小self.geometry(str(int(600*self.size))+'x'+str(int(400*self.size)))# 画布控件,作为容器self.canvas_bottom=Canvas(self,bg='#369',bd=0,width=600*self.size,height=400*self.size)self.canvas_bottom.place(x=0,y=0)# 几个功能按钮self.startButton=Button(self,text='开始游戏',command=self.start)self.startButton.place(x=480*self.size,y=200*self.size)self.passmeButton=Button(self,text='弃一手',command=self.passme)self.passmeButton.place(x=480*self.size,y=225*self.size)self.regretButton=Button(self,text='悔棋',command=self.regret)self.regretButton.place(x=480*self.size,y=250*self.size)# 初始悔棋按钮禁用self.regretButton['state']=DISABLEDself.replayButton=Button(self,text='重新开始',command=self.reload)self.replayButton.place(x=480*self.size,y=275*self.size)self.newGameButton1=Button(self,text=('十三' if self.mode_num==9 else '九')+'路棋',command=self.newGame1)self.newGameButton1.place(x=480*self.size,y=300*self.size)self.newGameButton2=Button(self,text=('十三' if self.mode_num==19 else '十九')+'路棋',command=self.newGame2)self.newGameButton2.place(x=480*self.size,y=325*self.size)self.quitButton=Button(self,text='退出游戏',command=self.quit)self.quitButton.place(x=480*self.size,y=350*self.size)# 画棋盘,填充颜色self.canvas_bottom.create_rectangle(0*self.size,0*self.size,400*self.size,400*self.size,fill='#c51')# 刻画棋盘线及九个点# 先画外框粗线self.canvas_bottom.create_rectangle(20*self.size,20*self.size,380*self.size,380*self.size,width=3)# 棋盘上的九个定位点,以中点为模型,移动位置,以作出其余八个点for m in [-1,0,1]:for n in [-1,0,1]:self.oringinal=self.canvas_bottom.create_oval(200*self.size-self.size*2,200*self.size-self.size*2,200*self.size+self.size*2,200*self.size+self.size*2,fill='#000')self.canvas_bottom.move(self.oringinal,m*self.dd*(2 if self.mode_num==9 else (3 if self.mode_num==13 else 6)),n*self.dd*(2 if self.mode_num==9 else (3 if self.mode_num==13 else 6)))# 画中间的线条for i in range(1,self.mode_num-1):self.canvas_bottom.create_line(20*self.size,20*self.size+i*self.dd,380*self.size,20*self.size+i*self.dd,width=2)self.canvas_bottom.create_line(20*self.size+i*self.dd,20*self.size,20*self.size+i*self.dd,380*self.size,width=2)# 放置右侧初始图片self.pW=self.canvas_bottom.create_image(500*self.size+11, 65*self.size,image=self.photoW)self.pB=self.canvas_bottom.create_image(500*self.size-11, 65*self.size,image=self.photoB)# 每张图片都添加image标签,方便reload函数删除图片self.canvas_bottom.addtag_withtag('image',self.pW)self.canvas_bottom.addtag_withtag('image',self.pB)# 鼠标移动时,调用shadow函数,显示随鼠标移动的棋子self.canvas_bottom.bind('',self.shadow)# 鼠标左键单击时,调用getdown函数,放下棋子self.canvas_bottom.bind('',self.getDown)# 设置退出快捷键+,快速退出游戏self.bind('',self.keyboardQuit)# 开始游戏函数,点击“开始游戏”时调用def start(self):# 删除右侧太极图self.canvas_bottom.delete(self.pW)self.canvas_bottom.delete(self.pB)# 利用右侧图案提示开始时谁先落子if self.present==0:self.create_pB()self.del_pW()else:self.create_pW()self.del_pB()# 开始标志,解除stopself.stop=None# 放弃一手函数,跳过落子环节def passme(self):# 悔棋恢复if not self.regretchance==1:self.regretchance+=1else:self.regretButton['state']=NORMAL# 拷贝棋盘状态,记录前三次棋局self.last_3_positions=copy.deepcopy(self.last_2_positions)self.last_2_positions=copy.deepcopy(self.last_1_positions)self.last_1_positions=copy.deepcopy(self.positions)self.canvas_bottom.delete('image_added_sign')# 轮到下一玩家if self.present==0:self.create_pW()self.del_pB()self.present=1else:self.create_pB()self.del_pW()self.present=0# 悔棋函数,可悔棋一回合,下两回合不可悔棋def regret(self):# 判定是否可以悔棋,以前第三盘棋局复原棋盘if self.regretchance==1:self.regretchance=0self.regretButton['state']=DISABLEDlist_of_b=[]list_of_w=[]self.canvas_bottom.delete('image')if self.present==0:self.create_pB()else:self.create_pW()for m in range(1,self.mode_num+1):for n in range(1,self.mode_num+1):self.positions[m][n]=0for m in range(len(self.last_3_positions)):for n in range(len(self.last_3_positions[m])):if self.last_3_positions[m][n]==1:list_of_b+=[[n,m]]elif self.last_3_positions[m][n]==2:list_of_w+=[[n,m]]self.recover(list_of_b,0)self.recover(list_of_w,1)self.last_1_positions=copy.deepcopy(self.last_3_positions)for m in range(1,self.mode_num+1):for n in range(1,self.mode_num+1):self.last_2_positions[m][n]=0self.last_3_positions[m][n]=0# 重新加载函数,删除图片,序列归零,设置一些初始参数,点击“重新开始”时调用def reload(self):if self.stop==1:self.stop=0self.canvas_bottom.delete('image')self.regretchance=0self.present=0self.create_pB()for m in range(1,self.mode_num+1):for n in range(1,self.mode_num+1):self.positions[m][n]=0self.last_3_positions[m][n]=0self.last_2_positions[m][n]=0self.last_1_positions[m][n]=0# 以下四个函数实现了右侧太极图的动态创建与删除def create_pW(self):self.pW=self.canvas_bottom.create_image(500*self.size+11, 65*self.size,image=self.photoW)self.canvas_bottom.addtag_withtag('image',self.pW)def create_pB(self):self.pB=self.canvas_bottom.create_image(500*self.size-11, 65*self.size,image=self.photoB)self.canvas_bottom.addtag_withtag('image',self.pB)def del_pW(self):self.canvas_bottom.delete(self.pW)def del_pB(self):self.canvas_bottom.delete(self.pB)# 显示鼠标移动下棋子的移动def shadow(self,event):if not self.stop:# 找到最近格点,在当前位置靠近的格点出显示棋子图片,并删除上一位置的棋子图片if (20*self.size<event.x<380*self.size) and (20*self.size<event.y<380*self.size):dx=(event.x-20*self.size)%self.dddy=(event.y-20*self.size)%self.ddself.cross=self.canvas_bottom.create_image(event.x-dx+round(dx/self.dd)*self.dd+22*self.p, event.y-dy+round(dy/self.dd)*self.dd-27*self.p,image=self.photoWBU_list[self.present])self.canvas_bottom.addtag_withtag('image',self.cross)if self.cross_last!=None:self.canvas_bottom.delete(self.cross_last)self.cross_last=self.cross# 落子,并驱动玩家的轮流下棋行为def getDown(self,event):if not self.stop:# 先找到最近格点if (20*self.size-self.dd*0.4<event.x<self.dd*0.4+380*self.size) and (20*self.size-self.dd*0.4<event.y<self.dd*0.4+380*self.size):dx=(event.x-20*self.size)%self.dddy=(event.y-20*self.size)%self.ddx=int((event.x-20*self.size-dx)/self.dd+round(dx/self.dd)+1)y=int((event.y-20*self.size-dy)/self.dd+round(dy/self.dd)+1)# 判断位置是否已经被占据if self.positions[y][x]==0:# 未被占据,则尝试占据,获得占据后能杀死的棋子列表self.positions[y][x]=self.present+1self.image_added=self.canvas_bottom.create_image(event.x-dx+round(dx/self.dd)*self.dd+4*self.p, event.y-dy+round(dy/self.dd)*self.dd-5*self.p,image=self.photoWBD_list[self.present])self.canvas_bottom.addtag_withtag('image',self.image_added)# 棋子与位置标签绑定,方便“杀死”self.canvas_bottom.addtag_withtag('position'+str(x)+str(y),self.image_added)deadlist=self.get_deadlist(x,y)self.kill(deadlist)# 判断是否重复棋局if not self.last_2_positions==self.positions:# 判断是否属于有气和杀死对方其中之一if len(deadlist)>0 or self.if_dead([[x,y]],self.present+1,[x,y])==False:# 当不重复棋局,且属于有气和杀死对方其中之一时,落下棋子有效if not self.regretchance==1:self.regretchance+=1else:self.regretButton['state']=NORMALself.last_3_positions=copy.deepcopy(self.last_2_positions)self.last_2_positions=copy.deepcopy(self.last_1_positions)self.last_1_positions=copy.deepcopy(self.positions)# 删除上次的标记,重新创建标记self.canvas_bottom.delete('image_added_sign')self.image_added_sign=self.canvas_bottom.create_oval(event.x-dx+round(dx/self.dd)*self.dd+0.5*self.dd, event.y-dy+round(dy/self.dd)*self.dd+0.5*self.dd,event.x-dx+round(dx/self.dd)*self.dd-0.5*self.dd, event.y-dy+round(dy/self.dd)*self.dd-0.5*self.dd,width=3,outline='#3ae')self.canvas_bottom.addtag_withtag('image',self.image_added_sign)self.canvas_bottom.addtag_withtag('image_added_sign',self.image_added_sign)if self.present==0:self.create_pW()self.del_pB()self.present=1else:self.create_pB()self.del_pW()self.present=0else:# 不属于杀死对方或有气,则判断为无气,警告并弹出警告框self.positions[y][x]=0self.canvas_bottom.delete('position'+str(x)+str(y))self.bell()self.showwarningbox('无气',"你被包围了!")else:# 重复棋局,警告打劫self.positions[y][x]=0self.canvas_bottom.delete('position'+str(x)+str(y))self.recover(deadlist,(1 if self.present==0 else 0))self.bell()self.showwarningbox("打劫","此路不通!")else:# 覆盖,声音警告self.bell()else:# 超出边界,声音警告self.bell()# 判断棋子(种类为yourChessman,位置为yourPosition)是否无气(死亡),有气则返回False,无气则返回无气棋子的列表# 本函数是游戏规则的关键,初始deadlist只包含了自己的位置,每次执行时,函数尝试寻找yourPosition周围有没有空的位置,有则结束,返回False代表有气;# 若找不到,则找自己四周的同类(不在deadlist中的)是否有气,即调用本函数,无气,则把该同类加入到deadlist,然后找下一个邻居,只要有一个有气,返回False代表有气;# 若四周没有一个有气的同类,返回deadlist,至此结束递归# def if_dead(self,deadlist,yourChessman,yourPosition):def if_dead(self,deadList,yourChessman,yourPosition):for i in [-1,1]:if [yourPosition[0]+i,yourPosition[1]] not in deadList:if self.positions[yourPosition[1]][yourPosition[0]+i]==0:return Falseif [yourPosition[0],yourPosition[1]+i] not in deadList:if self.positions[yourPosition[1]+i][yourPosition[0]]==0:return Falseif ([yourPosition[0]+1,yourPosition[1]] not in deadList) and (self.positions[yourPosition[1]][yourPosition[0]+1]==yourChessman):midvar=self.if_dead(deadList+[[yourPosition[0]+1,yourPosition[1]]],yourChessman,[yourPosition[0]+1,yourPosition[1]])if not midvar:return Falseelse:deadList+=copy.deepcopy(midvar)if ([yourPosition[0]-1,yourPosition[1]] not in deadList) and (self.positions[yourPosition[1]][yourPosition[0]-1]==yourChessman):midvar=self.if_dead(deadList+[[yourPosition[0]-1,yourPosition[1]]],yourChessman,[yourPosition[0]-1,yourPosition[1]])if not midvar:return Falseelse:deadList+=copy.deepcopy(midvar)if ([yourPosition[0],yourPosition[1]+1] not in deadList) and (self.positions[yourPosition[1]+1][yourPosition[0]]==yourChessman):midvar=self.if_dead(deadList+[[yourPosition[0],yourPosition[1]+1]],yourChessman,[yourPosition[0],yourPosition[1]+1])if not midvar:return Falseelse:deadList+=copy.deepcopy(midvar)if ([yourPosition[0],yourPosition[1]-1] not in deadList) and (self.positions[yourPosition[1]-1][yourPosition[0]]==yourChessman):midvar=self.if_dead(deadList+[[yourPosition[0],yourPosition[1]-1]],yourChessman,[yourPosition[0],yourPosition[1]-1])if not midvar:return Falseelse:deadList+=copy.deepcopy(midvar)return deadList# 警告消息框,接受标题和警告信息def showwarningbox(self,title,message):self.canvas_bottom.delete(self.cross)tkinter.messagebox.showwarning(title,message)# 落子后,依次判断四周是否有棋子被杀死,并返回死棋位置列表def get_deadlist(self,x,y):deadlist=[]for i in [-1,1]:if self.positions[y][x+i]==(2 if self.present==0 else 1) and ([x+i,y] not in deadlist):killList=self.if_dead([[x+i,y]],(2 if self.present==0 else 1),[x+i,y])if not killList==False:deadlist+=copy.deepcopy(killList)if self.positions[y+i][x]==(2 if self.present==0 else 1) and ([x,y+i] not in deadlist):killList=self.if_dead([[x,y+i]],(2 if self.present==0 else 1),[x,y+i])if not killList==False:deadlist+=copy.deepcopy(killList)return deadlist# 恢复位置列表list_to_recover为b_or_w指定的棋子def recover(self,list_to_recover,b_or_w):if len(list_to_recover)>0:for i in range(len(list_to_recover)):self.positions[list_to_recover[i][1]][list_to_recover[i][0]]=b_or_w+1self.image_added=self.canvas_bottom.create_image(20*self.size+(list_to_recover[i][0]-1)*self.dd+4*self.p, 20*self.size+(list_to_recover[i][1]-1)*self.dd-5*self.p,image=self.photoWBD_list[b_or_w])self.canvas_bottom.addtag_withtag('image',self.image_added)self.canvas_bottom.addtag_withtag('position'+str(list_to_recover[i][0])+str(list_to_recover[i][1]),self.image_added)# 杀死位置列表killList中的棋子,即删除图片,位置值置0def kill(self,killList):if len(killList)>0:for i in range(len(killList)):self.positions[killList[i][1]][killList[i][0]]=0self.canvas_bottom.delete('position'+str(killList[i][0])+str(killList[i][1]))# 键盘快捷键退出游戏def keyboardQuit(self,event):self.quit()# 以下两个函数修改全局变量值,newApp使主函数循环,以建立不同参数的对象def newGame1(self):global mode_num,newAppmode_num=(13 if self.mode_num==9 else 9)newApp=Trueself.quit()def newGame2(self):global mode_num,newAppmode_num=(13 if self.mode_num==19 else 19)newApp=Trueself.quit()# 声明全局变量,用于新建Application对象时切换成不同模式的游戏global mode_num,newAppmode_num=9newApp=Falseif __name__=='__main__':# 循环,直到不切换游戏模式while True:newApp=Falseapp=Application(mode_num)app.title('围棋')app.mainloop()if newApp:app.destroy()else:break