您现在的位置是:首页 > 技术教程 正文

Python 一步一步教你用pyglet制作汉诺塔游戏

admin 阅读: 2024-03-25
后台-插件-广告管理-内容页头部广告(手机)

目录

汉诺塔游戏

1. 抓取颜色

2. 绘制圆盘

3. 九层汉塔

4. 绘制塔架

5. 叠加圆盘

6. 游戏框架


 

汉诺塔游戏

汉诺塔(Tower of Hanoi),是一个源于印度古老传说的益智玩具。这个传说讲述了大梵天创造世界的时候,他做了三根金刚石柱子,并在其中一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门将这些圆盘从下面开始按大小顺序重新摆放在另一根柱子上,并规定在小圆盘上不能放大圆盘,同时在三根柱子之间一次只能移动一个圆盘。当盘子的数量增加时,移动步骤的数量会呈指数级增长,圆盘数为n时,总步骤数steps为2^n - 1。

n = 64, steps = 2^64 - 1 = 18446744073709551616 ≈ 1.845 x 10^19

汉诺塔问题是一个递归问题,也可以使用非递归法来解决,例如使用栈来模拟递归过程。这个问题不仅是一个数学和逻辑问题,也是一个很好的教学工具,可以用来教授递归、算法和逻辑思考等概念。同时,汉诺塔游戏也具有一定的娱乐性,人们可以通过解决不同规模的汉诺塔问题来挑战自己的智力和耐心。

1. 抓取颜色

本篇将展示如何用python pyglet库制作这个小游戏,首先在上图中抓取出需要用到的颜色RGB值,每种颜色画一个矩形块:

Rectangle(x, y, width, height, color=color)

代码:

  1. import pyglet
  2. window = pyglet.window.Window(800, 500, caption='汉诺塔')
  3. pyglet.gl.glClearColor(1, 1, 1, 1)
  4. batch = pyglet.graphics.Batch()
  5. Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138)
  6. class Rect:
  7. def __init__(self, x, y, color=(0,0,0), width=180, height=60):
  8. self.rect = pyglet.shapes.Rectangle(x, y, width, height, color=color, batch=batch)
  9. @window.event
  10. def on_draw():
  11. window.clear()
  12. batch.draw()
  13. rectangle = [None]*9
  14. for i,color in enumerate(Color):
  15. rectangle[i] = Rect(110+i//3*200, 120+i%3*100, color)
  16. pyglet.app.run()

2. 绘制圆盘

圆盘用矩形加2个半圆表示,半圆用扇形控件绘制:

Sector(x, y, radius=R, angle=pi, start_angle=-pi/2, color=color)

注意圆盘类中的矩形的宽度和坐标需要调整,整个圆盘类的宽度是矩形宽度加2倍扇形半径,圆盘的中心是矩形的中心而不是矩形左下角。

  1. import pyglet
  2. window = pyglet.window.Window(800, 500, caption='汉诺塔')
  3. pyglet.gl.glClearColor(1, 1, 1, 1)
  4. batch = pyglet.graphics.Batch()
  5. pi = 3.141592653589793
  6. Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138)
  7. class Bead:
  8. def __init__(self, x, y, width=180, height=60):
  9. self.sec1 = pyglet.shapes.Sector(x+width/2-height/2, y, radius=height/2, angle=pi, start_angle=-pi/2, color=Color[5], batch=batch)
  10. self.sec2 = pyglet.shapes.Sector(x-width/2+height/2, y, radius=height/2, angle=pi, start_angle=pi/2, color=Color[5], batch=batch)
  11. self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=Color[1], batch=batch)
  12. @window.event
  13. def on_draw():
  14. window.clear()
  15. batch.draw()
  16. ead1 = Bead(window.width/2, window.height/2)
  17. pyglet.app.run()

3. 九层汉塔

叠加多个圆盘,绘制出汉诺塔的样子:

代码:

  1. import pyglet
  2. window = pyglet.window.Window(800, 500, caption='汉诺塔')
  3. pyglet.gl.glClearColor(1, 1, 1, 1)
  4. batch = pyglet.graphics.Batch()
  5. pi = 3.141592653589793
  6. Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138)
  7. class Disk:
  8. def __init__(self, x, y, color=(0,0,0), width=200, height=20):
  9. self.sec1 = pyglet.shapes.Sector(x+width/2-height/2, y, radius=height/2, angle=pi, start_angle=-pi/2, color=color, batch=batch)
  10. self.sec2 = pyglet.shapes.Sector(x-width/2+height/2, y, radius=height/2, angle=pi, start_angle=pi/2, color=color, batch=batch)
  11. self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=color, batch=batch)
  12. assert(width>height and x-width/2+height/2>0)
  13. @window.event
  14. def on_draw():
  15. window.clear()
  16. batch.draw()
  17. x, y = window.width/2, window.height/2
  18. width, height = 200, 40
  19. disk = []
  20. for i in range(9):
  21. disk.append(Disk(x, y+height*(i-4), Color[i], width=width-20*(i-1), height=height))
  22. pyglet.app.run()

4. 绘制塔架

把圆盘变簿(高度换成厚度),再加一条粗直线(直线的宽度等于圆盘的厚度)表示出“竖杆”,就画出叠放的架子来:

        self.pole = pyglet.shapes.Line(x, y, x, y+height, width=thickness, color=color)
        self.disk = Disk(x, y, color=color, width=width, height=thickness)

代码:

  1. import pyglet
  2. window = pyglet.window.Window(800, 500, caption='汉诺塔')
  3. pyglet.gl.glClearColor(1, 1, 1, 1)
  4. batch = pyglet.graphics.Batch()
  5. pi = 3.141592653589793
  6. Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138)
  7. class Disk:
  8. def __init__(self, x, y, color=(0,0,0), width=200, height=20):
  9. self.sec1 = pyglet.shapes.Sector(x+width/2-height/2, y, radius=height/2, angle=pi, start_angle=-pi/2, color=color, batch=batch)
  10. self.sec2 = pyglet.shapes.Sector(x-width/2+height/2, y, radius=height/2, angle=pi, start_angle=pi/2, color=color, batch=batch)
  11. self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=color, batch=batch)
  12. assert(width>height and x-width/2+height/2>0)
  13. class Hann:
  14. def __init__(self, x, y, color=(0,0,0), width=220, height=300, thickness=20):
  15. self.pole = pyglet.shapes.Line(x, y, x, y+height, width=thickness, color=color, batch=batch)
  16. self.disk = Disk(x, y, color=color, width=width, height=thickness)
  17. @window.event
  18. def on_draw():
  19. window.clear()
  20. batch.draw()
  21. pole1 = Hann(window.width/2-250, 100)
  22. pole2 = Hann(window.width/2, 100, color=Color[0])
  23. pole3 = Hann(window.width/2+250, 100, color=Color[1])
  24. pyglet.app.run()

5. 叠加圆盘

把多个圆盘叠加磊在塔架上,圆盘数至少为2。 注意Color颜色列表共有9种颜色,Color[i%8+1]只取后8种颜色,Color[0]仅用于塔架的涂色。

Hann类中各控件的坐标计算有点复杂,以下方案可以解决问题但未必是最佳方案:

        self.x, self.y = x, y
        self.width = width
        self.height = (height-thickness*2)/order
        self.step = (width-thickness)/(order+1)
        self.beads = []
        self.coordinates = []
        for i in range(order):
            self.coordinates.append([self.x, self.y+(i+1)*self.height-(self.height-thickness)/2])

 代码:

  1. import pyglet
  2. window = pyglet.window.Window(800, 500, caption='汉诺塔')
  3. pyglet.gl.glClearColor(1, 1, 1, 1)
  4. batch = pyglet.graphics.Batch()
  5. pi = 3.141592653589793
  6. Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138)
  7. class Disk:
  8. def __init__(self, x, y, color=(0,0,0), width=200, height=20):
  9. self.sec1 = pyglet.shapes.Sector(x+width/2-height/2, y, radius=height/2, angle=pi, start_angle=-pi/2, color=color, batch=batch)
  10. self.sec2 = pyglet.shapes.Sector(x-width/2+height/2, y, radius=height/2, angle=pi, start_angle=pi/2, color=color, batch=batch)
  11. self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=color, batch=batch)
  12. assert(width>height and x-width/2+height/2>0)
  13. class Hann:
  14. def __init__(self, x, y, order=2, thickness=20, width=220, height=300):
  15. assert(order>1)
  16. self.pole = pyglet.shapes.Line(x, y, x, y+height, width=thickness, color=Color[0], batch=batch)
  17. self.disk = Disk(x, y, color=Color[0], width=width+thickness, height=thickness)
  18. self.x, self.y = x, y
  19. self.width = width
  20. self.height = (height-thickness*2)/order
  21. self.step = (width-thickness)/(order+1)
  22. self.beads = []
  23. self.coordinates = []
  24. for i in range(order):
  25. self.coordinates.append([self.x, self.y+(i+1)*self.height-(self.height-thickness)/2])
  26. self.fillup()
  27. def fillup(self):
  28. for i,xy in enumerate(self.coordinates):
  29. self.beads.append(Disk(*xy, Color[i%8+1], width=self.width-i*self.step, height=self.height))
  30. @window.event
  31. def on_draw():
  32. window.clear()
  33. batch.draw()
  34. hann1 = Hann(window.width/2-260, 100, 2)
  35. hann2 = Hann(window.width/2-22, 180, 5, 25)
  36. hann3 = Hann(window.width/2+230, 80, 10, 15, 300, 380)
  37. pyglet.app.run()

6. 游戏框架

画三个相同的塔架,左边的磊放好圆盘。另外用两个圆代替两个扇形,效果一样却省了pi常量。

Circle(x+width/2-height/2, y, radius=height/2, color=color)

代码: 

  1. import pyglet
  2. window = pyglet.window.Window(800, 500, caption='汉诺塔')
  3. pyglet.gl.glClearColor(1, 1, 1, 1)
  4. batch = pyglet.graphics.Batch()
  5. Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138)
  6. class Disk:
  7. def __init__(self, x, y, color=(0,0,0), width=200, height=20):
  8. self.cir1 = pyglet.shapes.Circle(x+width/2-height/2, y, radius=height/2, color=color, batch=batch)
  9. self.cir2 = pyglet.shapes.Circle(x-width/2+height/2, y, radius=height/2, color=color, batch=batch)
  10. self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=color, batch=batch)
  11. assert(width>height and x-width/2+height/2>0)
  12. class Hann:
  13. def __init__(self, x, y, order=2, thickness=20, width=200, height=300):
  14. assert(order>1)
  15. self.pole = pyglet.shapes.Line(x, y, x, y+height, width=thickness, color=Color[0], batch=batch)
  16. self.disk = Disk(x, y, color=Color[0], width=width+thickness, height=thickness)
  17. self.x, self.y = x, y
  18. self.width = width
  19. self.height = (height-thickness*2)/order
  20. self.step = (width-thickness)/(order+1)
  21. self.beads = []
  22. self.coordinates = []
  23. for i in range(order):
  24. self.coordinates.append([self.x, self.y+(i+1)*self.height-(self.height-thickness)/2])
  25. def fillup(self):
  26. for i,xy in enumerate(self.coordinates):
  27. self.beads.append(Disk(*xy, Color[i%8+1], width=self.width-i*self.step, height=self.height))
  28. class Game:
  29. def __init__(self, x, y, order=2, space=250):
  30. self.x, self.y = x, y
  31. self.space = space
  32. self.order = order
  33. self.hanns = Hann(x-space, y, order), Hann(x, y, order), Hann(x+space, y, order)
  34. self.hanns[0].fillup()
  35. @window.event
  36. def on_draw():
  37. window.clear()
  38. batch.draw()
  39. hann = Game(window.width/2, 100, 8)
  40. pyglet.app.run()

接下来就要添加鼠标和键盘事件,用于操作在塔架上移动圆盘。本篇完,下期继续......

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索