Mandelbrot Set with Different Paramaters (Source Code)

Using the center coordinate and zoom factor to draw the following Mandelbrot Set visualizations.

(-0.75, 0), Zoom=1
(-0.103865, -0.9584393), Zoom=167466.7.
(-0.761574, -0.0847596) Zoom=3125
(-0.761574, -0.0847596) Zoom=78125
(-0.59990625, -0.4290703125) Zoom=1024
(-1.62917, -0.0203968) Zoom=3125
(0.2613577, -0.002018128) Zoom=3354.786

Source Code:

from PIL import Image
import colorsys
import math

px, py = -0.7746806106269039, -0.1374168856037867 #Tante Renate
px, py, zoom = -0.74384935657398, -0.13170134084746293, 5788441.443619884
px, py, zoom = 2.613577e-1, -2.018128e-3, 3.354786e+3
px, py, zoom = -0.59990625, -0.4290703125, 1024
px, py, zoom = -1.038650e-1, -9.584393e-1, 1.674667e+5
px, py, zoom = -0.761574, -0.0847596, 78125
px, py, zoom = -1.62917,-0.0203968, 3125
px, py, zoom = -0.75,0,1
R = 3 
max_iteration = 512
w, h = 1250,1250
mfactor = 1

def Mandelbrot(x,y,max_iteration,minx,maxx,miny,maxy):
    zx = 0
    zy = 0
    RX1, RX2, RY1, RY2 = px-R/2, px+R/2,py-R/2,py+R/2
    cx = (x-minx)/(maxx-minx)*(RX2-RX1)+RX1
    cy = (y-miny)/(maxy-miny)*(RY2-RY1)+RY1
    i=0
    while zx**2 + zy**2 <= 4 and i < max_iteration:
        temp = zx**2 - zy**2
        zy = 2*zx*zy + cy
        zx = temp + cx
        i += 1
    return i

def gen_Mandelbrot_image():
  bitmap = Image.new("RGB", (w, h), "white")
  pix = bitmap.load()
  for x in range(w):
    for y in range(h):
      c=Mandelbrot(x,y,max_iteration,0,w-1,0,h-1)
      v = c**mfactor/max_iteration**mfactor
      hv = 0.67-v*2
      #if hv<0: hv+=1
      r,g,b = colorsys.hsv_to_rgb(hv,1,1-(v-0.1)**2/0.9**2)
      r = min(255,round(r*255))
      g = min(255,round(g*255))
      b = min(255,round(b*255))
      pix[x,y] = int(r) + (int(g) << 8) + (int(b) << 16)
  bitmap.save("Mandelbrot_"+str(px)+"_"+str(py)+"_"+str(zoom)+".jpg")
  bitmap.show()
  
R=3/zoom
gen_Mandelbrot_image()

Zooming 10^13 into Mandelbrot Set Animation with Python (Source Code)

Write a Python program to generate 1000+ images of Mandelbrot Set with ever zooming ranges. Please note that Turtle is not used in this project for speed consideration. Then use an external software to combine the images into a video:

Zooming 10^13 into Mandelbrot Set

Source Code:

from PIL import Image
import colorsys
import math

px, py = -0.7746806106269039, -0.1374168856037867 #Tante Renate
R = 3 
max_iteration = 2500
w, h = 1024,1024
mfactor = 0.5

def Mandelbrot(x,y,max_iteration,minx,maxx,miny,maxy):
    zx = 0
    zy = 0
    RX1, RX2, RY1, RY2 = px-R/2, px+R/2,py-R/2,py+R/2
    cx = (x-minx)/(maxx-minx)*(RX2-RX1)+RX1
    cy = (y-miny)/(maxy-miny)*(RY2-RY1)+RY1
    i=0
    while zx**2 + zy**2 <= 4 and i < max_iteration:
        temp = zx**2 - zy**2
        zy = 2*zx*zy + cy
        zx = temp + cx
        i += 1
    return i

def gen_Mandelbrot_image(sequence):
  bitmap = Image.new("RGB", (w, h), "white")
  pix = bitmap.load()
  for x in range(w):
    for y in range(h):
      c=Mandelbrot(x,y,max_iteration,0,w-1,0,h-1)
      v = c**mfactor/max_iteration**mfactor
      hv = 0.67-v
      if hv<0: hv+=1
      r,g,b = colorsys.hsv_to_rgb(hv,1,1-(v-0.1)**2/0.9**2)
      r = min(255,round(r*255))
      g = min(255,round(g*255))
      b = min(255,round(b*255))
      pix[x,y] = int(r) + (int(g) << 8) + (int(b) << 16)
  bitmap.save("Mandelbrot_"+str(sequence)+".jpg")

R=3
f = 0.975
RZF = 1/1000000000000
k=1
while R>RZF:
  if k>100: break
  mfactor = 0.5 + (1/1000000000000)**0.1/R**0.1
  print(k,mfactor)
  gen_Mandelbrot_image(k)
  R *= f
  k+=1

This program generates over 1000 images and may be quite slow. You can make it faster by dividing the work to multiple programs each doing a portion of those images.

Zooming 10^13 Times into Julia Set Animation

Write a Python program to generate hundreds of images of Julia Set with ever zooming ranges. Please note that Turtle is not used in this project for speed consideration. Then use an external software to combine the images into a video:

Zooming 10^13 Times into Julia Set

The video above zooms into the center at 0.0958598997051 + 1.1501990019993i with c = 0.355 + 0.355i. In each iteration the range shrinks by 5% until the range becomes 1/10^13 the size of the original range. You can modify the code to try different centers and c. The following is the source code:

from PIL import Image
import colorsys

cx = -0.7269
cy = 0.1889
R = (1+(1+4*(cx**2+cy**2)**0.5)**0.5)/2+0.001
max_iteration = 512
print(R)

w, h, zoom = 1500,1500,1

def julia(cx,cy,RZ,px,py,R,max_iteration,x,y,minx,maxx,miny,maxy):
    zx = (x-minx)/(maxx-minx)*2*RZ-RZ+px
    zy = (y-miny)/(maxy-miny)*2*RZ-RZ+py
    i=0
    while zx**2 + zy**2 < R**2 and i < max_iteration:
        temp = zx**2 - zy**2
        zy = 2*zx*zy + cy
        zx = temp + cx
        i += 1
    return i

def gen_julia_image(sequence,cx,cy,RZ):
  bitmap = Image.new("RGB", (w, h), "white")
  pix = bitmap.load()
  for x in range(w):
    for y in range(h):
        # smllaer: right,down
      c=julia(cx,cy,RZ,0.0958598997051,1.1501990019993,R,max_iteration,x,y,0,w-1,0,h-1)
      v = c/max_iteration
      hv = 0.67-v*0.67
      r,g,b = colorsys.hsv_to_rgb(hv,1,v/2+0.45)
      r = min(255,round(r*255))
      g = min(255,round(g*255))
      b = min(255,round(b*255))
      pix[x,y] = int(r) + (int(g) << 8) + (int(b) << 16)
  bitmap.save("julia_"+str(sequence)+".jpg")
#  bitmap.show()

f = 0.95
RZF = 1/1000000000000
RZ = 1
k=1
while RZ>RZF:
  print(k,RZ)
  gen_julia_image(k,0.355,0.355,RZ)
  RZ *= f
  k+=1

Related Projects:

Julia Set Fractal Animation

Julia Set with Different Parameters

Julia Set with Different Parameters

In this project, we are generate several Julia Sets by varying the constant c in the normal Julia Set function. Turtle will be too slow to do this work. Instead we are going to use the PIL library. Please check out Julia Set Animation project for source code.

c = -0.618 (Max Iteration=38)
c = 0.355534-0.337292i (Max Iteration=4064)
c = 0.355+0.355i (Max Iteration=512)
c = 0.285+0.01i (Max Iteration=204)
c = -0.8+0.156i (Max Iteration=924)
c = -0.835+0.2321i (Max Iteration=104)
c = 0.7269+0.1889i (Max Iteration=1024)

Related Projects:

Zooming 10^13 Times into Julia Set Animation

Julia Set Fractal Animation

Julia Set Fractal Animation

Julia Set Animation

In this project, we are going to animate the Julia Set by varying the constant c in the normal Julia Set function. Turtle will be too slow to do this work. Instead we are going to use the PIL library to generate 360 images. Then, a video editing software can be used to combine 360 images into a video file. The following is the source code:

from PIL import Image
import colorsys
import math

cx = 0.7885
cy = 0
R = (1+(1+4*(cx**2+cy**2)**0.5)**0.5)/2+0.001
max_iteration = 200

w, h, zoom = 1000,1000,1

def julia(cx,cy,R,max_iteration,x,y,minx,maxx,miny,maxy):
    zx = (x-minx)/(maxx-minx)*2*R-R
    zy = (y-miny)/(maxy-miny)*2*R-R
    i=0
    while zx**2 + zy**2 < R**2 and i < max_iteration:
        temp = zx**2 - zy**2
        zy = 2*zx*zy + cy
        zx = temp + cx
        i += 1
    return i

def gen_julia_image(sequence,cx,cy):
  bitmap = Image.new("RGB", (w, h), "white")
  pix = bitmap.load()
  for x in range(w):
    for y in range(h):
      c=julia(cx,cy,R,max_iteration,x,y,0,w-1,0,h-1)
      r,g,b = colorsys.hsv_to_rgb(c/max_iteration,1,0.9)
      r = min(255,round(r*255))
      g = min(255,round(g*255))
      b = min(255,round(b*255))
      pix[x,y] = int(b) + (int(g) << 8) + (int(r) << 16)
  bitmap.save("julia_"+str(sequence)+".jpg")


for i in range(360):
  cx = 0.7885*math.cos(i*math.pi/180)
  cy = 0.7885*math.sin(i*math.pi/180)
  gen_julia_image(i,cx,cy)

Related Projects:

Julia Set with Different Parameters

Zooming 10^13 Times into Julia Set Animation

AI Tic-Tac-Toe with Minimax Tree (Source Code Included)

Use minimax tree search algorithm with alpha-beta pruning to write AI Tic-Tac-Toe player. You may also want to add randomness to your AI player so that it won’t play the same move every time. Here is more detail information on minimax tree and alpha-beta pruning. Your program should play against a human player. Therefore, you need to use onclick() function to handle mouse click events.

Tic-Tac-Toe with Minimax Tree

Source Code:

import turtle
import copy
import random

screen = turtle.Screen()
screen.setup(800,800)
screen.title("Tic Tac Toe - PythonTurtle.Academy")
screen.setworldcoordinates(-5,-5,5,5)
screen.bgcolor('light gray')
screen.tracer(0,0)
turtle.hideturtle()

def draw_board():
    turtle.pencolor('green')
    turtle.pensize(10)
    turtle.up()
    turtle.goto(-3,-1)
    turtle.seth(0)
    turtle.down()
    turtle.fd(6)
    turtle.up()
    turtle.goto(-3,1)
    turtle.seth(0)
    turtle.down()
    turtle.fd(6)
    turtle.up()
    turtle.goto(-1,-3)
    turtle.seth(90)
    turtle.down()
    turtle.fd(6)
    turtle.up()
    turtle.goto(1,-3)
    turtle.seth(90)
    turtle.down()
    turtle.fd(6)

def draw_circle(x,y):
    turtle.up()
    turtle.goto(x,y-0.75)
    turtle.seth(0)
    turtle.color('red')
    turtle.down()
    turtle.circle(0.75, steps=100)

def draw_x(x,y):
    turtle.color('blue')
    turtle.up()
    turtle.goto(x-0.75,y-0.75)
    turtle.down()
    turtle.goto(x+0.75,y+0.75)
    turtle.up()
    turtle.goto(x-0.75,y+0.75)
    turtle.down()
    turtle.goto(x+0.75,y-0.75)
    
def draw_piece(i,j,p):
    if p==0: return
    x,y = 2*(j-1), -2*(i-1)
    if p==1:
        draw_x(x,y)
    else:
        draw_circle(x,y)
    
def draw(b):
    draw_board()
    for i in range(3):
        for j in range(3):
            draw_piece(i,j,b[i][j])
    screen.update()

# return 1 if player 1 wins, 2 if player 2 wins, 3 if tie, 0 if game is not over
def gameover(b):
    if b[0][0]>0 and b[0][0] == b[0][1] and b[0][1] == b[0][2]: return b[0][0]
    if b[1][0]>0 and b[1][0] == b[1][1] and b[1][1] == b[1][2]: return b[1][0]
    if b[2][0]>0 and b[2][0] == b[2][1] and b[2][1] == b[2][2]: return b[2][0]
    if b[0][0]>0 and b[0][0] == b[1][0] and b[1][0] == b[2][0]: return b[0][0]
    if b[0][1]>0 and b[0][1] == b[1][1] and b[1][1] == b[2][1]: return b[0][1]
    if b[0][2]>0 and b[0][2] == b[1][2] and b[1][2] == b[2][2]: return b[0][2]
    if b[0][0]>0 and b[0][0] == b[1][1] and b[1][1] == b[2][2]: return b[0][0]
    if b[2][0]>0 and b[2][0] == b[1][1] and b[1][1] == b[0][2]: return b[2][0]
    p = 0
    for i in range(3):
        for j in range(3):
            p += (1 if b[i][j] > 0 else 0)
    if p==9: return 3
    else: return 0
    
def play(x,y):
    global turn
    if turn=='x': return
    
    i = 3-int(y+5)//2
    j = int(x+5)//2 - 1
    if i>2 or j>2 or i<0 or j<0 or b[i][j]!=0: return
    if turn == 'x': b[i][j], turn = 1, 'o'
    else: b[i][j], turn = 2, 'x'
    draw(b)
    r = gameover(b)
    if r==1:
        screen.textinput("Game over!","X won!")
    elif r==2:
        screen.textinput("Game over!","O won!")
    elif r==3:
        screen.textinput("Game over!", "Tie!")
    if r>0: turtle.bye()
    _,move = max_node(b,-2,2)
    b[move[0]][move[1]] = 1
    draw(b)
    r = gameover(b)
    if r==1:
        screen.textinput("Game over!","X won!")
    elif r==2:
        screen.textinput("Game over!","O won!")
    elif r==3:
        screen.textinput("Game over!", "Tie!")
    if r>0: turtle.bye()
    turn = 'o'
    
b = [ [ 0,0,0 ], [ 0,0,0 ], [ 0,0,0 ] ]    
draw(b)
turn = 'x'
screen.onclick(play)
#turtle.mainloop()

def max_node(b,alpha,beta):
    r = gameover(b)
    if r==1: return 1,None
    elif r==2: return -1,None
    elif r==3: return 0,None

    score = -2
    # find all possible next moves
    pm = list()
    for i in range(3):
        for j in range(3):
            if b[i][j] == 0: pm.append((i,j))
    random.shuffle(pm)
    for (i,j) in pm:
        if b[i][j] == 0:
            nb = copy.deepcopy(b)
            nb[i][j] = 1
            cs,_ = min_node(nb,alpha,beta)
            if score<cs:
                score=cs
                move = (i,j)
            alpha = max(alpha,cs)
            if alpha>=beta: return score,move
    return score,move

def min_node(b,alpha,beta):
    r = gameover(b)
    if r==1: return 1,None
    elif r==2: return -1,None
    elif r==3: return 0,None

    score = 2
    # find all possible next moves
    pm = list()
    random.shuffle(pm)
    for i in range(3):
        for j in range(3):
            if b[i][j] == 0: pm.append((i,j))
    for (i,j) in pm:
        if b[i][j] == 0:
            nb = copy.deepcopy(b)
            nb[i][j] = 2
            cs,_ = max_node(nb,alpha,beta)
            if score>cs:
                score=cs
                move = (i,j)
            beta = min(beta,cs)
            if alpha>=beta: return score,move
    return score,move

_,move = max_node(b,-2,2)
b[move[0]][move[1]] = 1
draw(b)
turn = 1
screen.mainloop()    

Related Projects:

Animating Knight’s Minimum Move with Python Turtle (Source Code Included)

Write a program that animates the move of a knight in a chess board from any source to any destination with the minimum number of steps. You may need to use Breadth First Search (BFS) with Queue data structure.

Source Code:

import turtle
import time
import sys

turtle.setup(800,800)
turtle.setworldcoordinates(-250,-250,250,250)
turtle.title("Knight's Minimum Moves - PythonTurtle.Academy")

def draw_square(x,y,size,color):
    turtle.up()
    turtle.goto(x,y)
    turtle.seth(0)
    turtle.down()
    turtle.begin_fill()
    turtle.fillcolor(color)
    for i in range(4):
        turtle.fd(size)
        turtle.left(90)
    turtle.end_fill()
        
def draw_board(src=(-2,-2),dest=(-2,-2), mlist=[]):
    turtle.pencolor("black")
    y = -200
    piecesize = 50
    startpiececolor = "darkgray"
    for i in range(8):
        x = -200
        piececolor = startpiececolor
        for j in range(8):
            if (j+1,i+1)==src:
                draw_square(x,y,piecesize,'light blue')
            elif (j+1,i+1)==dest:
                draw_square(x,y,piecesize,'pink')
            elif (j+1,i+1) in mlist:
                draw_square(x,y,piecesize,'light green')                
            else:   
                draw_square(x,y,piecesize,piececolor)
            x += piecesize
            if piececolor == "darkgray":
                piececolor = "lightgray"
            else:
                piececolor = "darkgray"
        y += piecesize
        if startpiececolor == "darkgray":
            startpiececolor = "lightgray"
        else:
            startpiececolor = "darkgray"
    
def draw_knight(pos):
    xpos = 50*(pos[0]-1) - 175
    ypos = 50*(pos[1]-1) - 195
    turtle.up()
    turtle.goto(xpos,ypos)
    turtle.write('\u265e',align='center',font=('Arial',40,'normal'))

class node:
    def __init__(self,val,nxt=None):
        self.value=val
        self.next=nxt

class LinkedList:
    def __init__(self,it=None):
        self.head = None
        self.tail = None
        self.length = 0
        if it is None: return
        for x in it:
            self.insert_at_tail(x)

    def insert_at_head(self,k):
        if self.head is None:
            self.head = node(k)
            self.tail = self.head
        else:
            p = node(k,self.head)
            self.head = p
        self.length += 1

    def insert_after(self,p,k):
        if self.length < p or p < 0:
            raise Exception('Linked List doesn\'t have position '+str(p))
        if p==0:
            self.insert_at_head(k)
            return
        q = self.head
        for _ in range(p-1):
            q = q.next
        q.next = node(k,q.next)
        
    def insert_at_tail(self,k):
        if self.tail is None:
            self.head = node(k)
            self.tail = self.head
        else:
            self.tail.next = node(k)
            self.tail = self.tail.next
        self.length += 1

    def __iter__(self):
        p = self.head
        while p is not None:
            yield p.value
            p = p.next
            
    def __len__(self):
        return self.length
    
    def __str__(self):
        s = ""
        p = self.head
        while p is not None:
            s += str(p.value) + '->'
            p = p.next
        s += 'None'
        return s

    def delete_from_head(self):
        if self.head is None: return
        self.length -= 1
        self.head = self.head.next
        if self.head is None:
            self.tail = None

    def delete_from_tail(self):
        if self.head is None: return
        self.length -= 1
        if self.head == self.tail: self.head = self.tail = None
        p = self.head
        while p.next != self.tail:
            p = p.next
        p.next = None
        self.tail = p

class Queue:
    def __init__(self):
        self.ll = LinkedList()
        self.length = len(self.ll)

    def push(self,k):
        self.ll.insert_at_tail(k)

    def pop(self):
        if len(self.ll)==0:
            raise Exception('Queue is empty')
        self.ll.delete_from_head()

    def top(self):
        if len(self.ll)==0:
            raise Exception('Queue is empty')
        return self.ll.head.value;
        
    def empty(self):
        return len(self.ll)==0

    def __len__(self):
        return len(self.ll)

class position:
    def __init__(self,pos,dist,parent):
        self.pos = pos
        self.dist = dist
        self.parent = parent

moves = [ (1,2), (1,-2), (-1,2),(-1,-2), (2,-1), (2,1), (-2,1), (-2,-1) ]

def valid_position(pos):
    if pos[0] < 1 or pos[0] > 8 or pos[1] < 1 or pos[1] > 8: return False
    return True

def minimum_knight_move(src, dest):
    q = Queue()
    s = set()
    q.push(position(src,0,None))
    s.add(src)
    while not q.empty():
        current = q.top()
        pos = current.pos
        dist = current.dist
        q.pop()
        # try all possible moves from here
        for m in moves:
            new_pos = (pos[0]+m[0],pos[1]+m[1])
            if valid_position(new_pos) and new_pos not in s:
                if new_pos == dest: return current
                s.add(new_pos)
                q.push(position(new_pos,dist+1,current))
    return -1
    
if __name__=='__main__':
    turtle.speed(0)
    turtle.hideturtle()
    turtle.tracer(0)
    while True:
        turtle.clear()
        draw_board()
        src = (-1,-1)
        dest = (-1,-1)
        while src[0] < 0 or src[0] > 8 or src[1] < 0 or src[1] > 8:
            srctext=turtle.textinput("Begin Position","Please enter the starting position (e.g. 2 4):")
            if srctext is None: sys.exit(0)
            try:
                src = tuple(map(int,srctext.split()))
            except:
                src = (-1,-1)
        draw_board(src)
        draw_knight(src)
        while dest[0] < 0 or dest[0] > 8 or dest[1] < 0 or dest[1] > 8:
            desttext=turtle.textinput("End Position","Please enter the target position (e.g. 8 5):")
            if desttext is None: sys.exit(0)
            try:
                dest = tuple(map(int,desttext.split()))
            except:
                dest = (-1,-1)
        draw_board(src,dest)
        draw_knight(dest)
        current = minimum_knight_move(src,dest)
        s = LinkedList()
        s.insert_at_head(dest)
        mlist = list()
        while current is not None:
            s.insert_at_head(current.pos)
            current = current.parent
        for pos in s:
            turtle.clear()
            mlist.append(pos)
            draw_board(src,dest,mlist)
            draw_knight(pos)
            time.sleep(1)
            turtle.update()

24 Game Solver with Python Turtle (Source Code Included)

24 Game is a mathematical puzzle that make 4 numbers make evaluate to 24 with basic arithmetical operators ( +, -, ×, ÷). For example, given 4 numbers 1,5,5,5, we can make expression (5-(1÷5))×5, which equals to 24.

We can use brute force to solve this problem with a Python Program. There are at most 24×5×4×4×4 = 7,680 different expressions can be made with 4 numbers. It may appear to be hard for us to do by hand, it should be very easy for computer program to solve. The following is the source code for this project:

import turtle
screen = turtle.Screen()
screen.setup(500,500)
screen.title("24 Game Solver - PythonTurtle.Academy")
turtle.hideturtle()
turtle.speed(0)
turtle.up()

def permutation(a):
    if len(a) == 1: return [[a[0]]]
    res = permutation(a[1:])
    r = []
    for x in res:
        # need to insert n into x: into all possible position
        for i in range(len(x)+1):
            y = x.copy()
            y.insert(i,a[0])
            if y not in r:
                r.append(y)
    return r

op = ['+', '−', '×', '÷']
def evaluate(a):
    v = []
    for x in a:
        if type(x) is int:
            v.append(x)
        else:
            num2 = v.pop()
            num1 = v.pop()
            if x=='+': v.append(num1+num2)
            elif x=='−': v.append(num1-num2)
            elif x=='×': v.append(num1*num2)
            else:
                if num2 != 0: v.append(num1/num2)
                else: return 0
    return v[0]


class tree_node:
    def __init__(self,value,left,right):
        self.value = value
        self.left = left
        self.right = right

def inorder(root):
    if type(root.value) is int: return str(root.value)
    if type(root.left.value) is not int: left = '('+inorder(root.left)+')'
    else: left = inorder(root.left)
    if type(root.right.value) is not int: right = '('+inorder(root.right)+')'
    else: right = inorder(root.right)    
    return left + root.value + right
    
def convert_to_infix(a):
    # convert to infix
    # convert to tree first
    v = list()
    for x in a:
        if type(x) is int:
            v.append(tree_node(x,None,None))
        else:
            t2 = v.pop()
            t1 = v.pop()
            v.append(tree_node(x,t1,t2))
    return inorder(v[0])
            
def twentyfour(a):
    # postfix
    # try n,n,n,n, p,p,p
    for i in range(4):
        for j in range(4):
            for k in range(4):
                b = a.copy()
                b.append(op[i])
                b.append(op[j])
                b.append(op[k])
                v = evaluate(b)
                if v==24.0:
                    return(convert_to_infix(b))

    # try n,n,n,p,p,n,p
    for i in range(4):
        for j in range(4):
            for k in range(4):
                b = a[:3]
                b.append(op[i])
                b.append(op[j])
                b.append(a[3])
                b.append(op[k])
                v = evaluate(b)
                if v==24.0:
                    return(convert_to_infix(b))

    # try n,n,n,p,n,p,p
    for i in range(4):
        for j in range(4):
            for k in range(4):
                b = a[:3]
                b.append(op[i])
                b.append(a[3])
                b.append(op[j])
                b.append(op[k])
                v = evaluate(b)
                if v==24.0:
                    return(convert_to_infix(b))

    # try n,n,p,n,p,n,p
    for i in range(4):
        for j in range(4):
            for k in range(4):
                b = a[:2]
                b.append(op[i])
                b.append(a[2])
                b.append(op[j])
                b.append(a[3])
                b.append(op[k])
                v = evaluate(b)
                if v==24.0:
                    return(convert_to_infix(b))
    # try n,n,p,n,n,p,p
    for i in range(4):
        for j in range(4):
            for k in range(4):
                b = a[:2]
                b.append(op[i])
                b.append(a[2])
                b.append(a[3])
                b.append(op[j])
                b.append(op[k])
                v = evaluate(b)
                if v==24.0:
                    return(convert_to_infix(b))
    return ''            

while True:
    fournumbers = []
    while len(fournumbers) != 4:
        numbers = screen.textinput("24 Game Solver", "Enter four numbers separated by spaces: ")
        fournumbers = list(map(int,numbers.split()))
    p = permutation(fournumbers)
    foundsolution = False
    turtle.clear()
    for a in p:
        r = twentyfour(a)
        if len(r)>0:
            turtle.goto(0,0)
            turtle.color('royal blue')
            turtle.write(r,font=('Courier', 45, 'bold'), align='center')
            foundsolution = True
            break
    if not foundsolution:
        turtle.color('red')
        turtle.write("No solution",font=('Courier', 45, 'bold'), align='center')

Quick Sort Animation with Python and Turtle (with Source Code)

Merge Sort algorithm is fast but it requires additional array and additional copying. Quick Sort is even faster than Merge Sort. Implement Quick Sort algorithm and animate it!

Here is the source code:

import turtle
import random
import time

screen = turtle.Screen()
screen.setup(1000,1000)
screen.tracer(0,0)
screen.title('Quick Sort Animation - PythonTurtle.Academy')
turtle.speed(0)
turtle.hideturtle()

def draw_bar(x,y,w,h):
    turtle.up()
    turtle.goto(x,y)
    turtle.seth(0)
    turtle.down()
    turtle.begin_fill()
    turtle.fd(w)
    turtle.left(90)
    turtle.fd(h)
    turtle.left(90)
    turtle.fd(w)
    turtle.left(90)
    turtle.fd(h)
    turtle.left(90)
    turtle.end_fill()

def draw_bars(v,n,currenti=-1,currentj=-1):
    turtle.clear()
    x = -400
    w = 800/n
    for  i in range(n):
        if i == currenti: turtle.fillcolor('red')
        elif i == currentj: turtle.fillcolor('blue')
        else: turtle.fillcolor('gray')
        draw_bar(x,-400,w,v[i])
        x += w
    screen.update()

def partition(v,x,y):
    p = random.randint(x,y)
    v[p], v[y] = v[y], v[p]
    pivot = v[y]
    i, j = x, y-1
    while i <= j:
        while v[i] <= pivot and i <= j:
            draw_bars(v,n,i,j)
            i += 1
        while v[j] > pivot and j >= i:
            draw_bars(v,n,i,j)
            j -= 1
        if i < j:
            draw_bars(v,n,i,j)
            v[i], v[j] = v[j], v[i]
    v[i], v[y] = v[y], v[i]
    draw_bars(v,n,i,y)
    return i

def quick_sort(v,x,y):
    if x >= y:
        return
    m = partition(v,x,y)
    quick_sort(v,x,m-1)
    quick_sort(v,m+1,y)
    
n = 50
v = [0] * n
for i in range(n):
    v[i] = random.randint(1,800)

t1 = time.time()
quick_sort(v,0,n-1)
turtle.clear()
draw_bars(v,n,-1)
turtle.update()
t2 = time.time()
print('elapsed time=', t2-t1)

Merge Sort Animation with Python and Turtle (with Source Code)

In previous sorting animations, we animated selection, insertion, and bubble sort. They are slow O(n^2) slow sorting algorithm. Merge Sort, a divide and conquer algorithm, is a much faster sorting algorithm with the worst case running time of O(n*logn). However, unlike previous sorting algorithms, it requires another array to be able to merge. Implement Merge Sorting and animate it!

Here is the source code:

import turtle
import random
import time

screen = turtle.Screen()
screen.setup(1000,1000)
screen.setworldcoordinates(-1000,-1000,1000,1000)
screen.tracer(0,0)
screen.title('Merge Sort Animation - PythonTurtle.Academy')
turtle.speed(0)
turtle.hideturtle()

def draw_bar(x,y,w,h):
    turtle.up()
    turtle.goto(x,y)
    turtle.seth(0)
    turtle.down()
    turtle.begin_fill()
    turtle.fd(w)
    turtle.left(90)
    turtle.fd(h)
    turtle.left(90)
    turtle.fd(w)
    turtle.left(90)
    turtle.fd(h)
    turtle.left(90)
    turtle.end_fill()

def draw_bars(v,w,n,currenti=-1,currentj=-1):
    turtle.clear()
    x = -400
    wd = 800/n
    for  i in range(n):
        if i == currenti: turtle.fillcolor('red')
        elif i == currentj: turtle.fillcolor('blue')
        else: turtle.fillcolor('gray')
        draw_bar(x,100,wd,v[i])
        x += wd
        
    x = -400
    for  i in range(n):
        turtle.fillcolor('gray')
        draw_bar(x,-900,wd,w[i])
        x += wd
    screen.update()

def merge(v,w,a,b,x,y):
    i,j, k = a,x,a
    while i<=b and j<=y:
        if v[i] < v[j]: w[k],i = v[i], i+1
        else: w[k], j = v[j], j+1
        draw_bars(v,w,n,i,j)
        k += 1
    while i<=b:
        w[k],i,k = v[i],i+1,k+1
        draw_bars(v,w,n,i,j)
    while j<=y:
        w[k],j,k = v[j],j+1,k+1
        draw_bars(v,w,n,i,j)
    for i in range(a,y+1):
        v[i] = w[i]
        draw_bars(v,w,n,i)
    
def merge_sort(v,w,x,y):
    if x >= y:
        return
    m = (x+y)//2
    merge_sort(v,w,x,m)
    merge_sort(v,w,m+1,y)
    merge(v,w,x,m,m+1,y)
    
n = 50
v = [0] * n
w = [0] * n
for i in range(n):
    v[i] = random.randint(1,800)

t1 = time.time()
merge_sort(v,w,0,n-1)
t2 = time.time()
print('elapsed time=', t2-t1)

What’s Next?

Quick Sort Animation