1 #!/usr/bin/env python3 2 """ 3 4 sorting_animation.py 5 6 A minimal sorting algorithm animation: 7 Sorts a shelf of 10 blocks using insertion 8 sort, selection sort and quicksort. 9 10 Shelfs are implemented using builtin lists. 11 12 Blocks are turtles with shape "square", but 13 stretched to rectangles by shapesize() 14 --------------------------------------- 15 To exit press space button 16 --------------------------------------- 17 """ 18 from turtle import * 19 import random 20 21 22 class Block(Turtle): 23 24 def __init__(self, size): 25 self.size = size 26 Turtle.__init__(self, shape="square", visible=False) 27 self.pu() 28 self.shapesize(size * 1.5, 1.5, 2) # square-->rectangle 29 self.fillcolor("black") 30 self.st() 31 32 def glow(self): 33 self.fillcolor("red") 34 35 def unglow(self): 36 self.fillcolor("black") 37 38 def __repr__(self): 39 return "Block size: {0}".format(self.size) 40 41 42 class Shelf(list): 43 44 def __init__(self, y): 45 "create a shelf. y is y-position of first block" 46 self.y = y 47 self.x = -150 48 49 def push(self, d): 50 width, _, _ = d.shapesize() 51 # align blocks by the bottom edge 52 y_offset = width / 2 * 20 53 d.sety(self.y + y_offset) 54 d.setx(self.x + 34 * len(self)) 55 self.append(d) 56 57 def _close_gap_from_i(self, i): 58 for b in self[i:]: 59 xpos, _ = b.pos() 60 b.setx(xpos - 34) 61 62 def _open_gap_from_i(self, i): 63 for b in self[i:]: 64 xpos, _ = b.pos() 65 b.setx(xpos + 34) 66 67 def pop(self, key): 68 b = list.pop(self, key) 69 b.glow() 70 b.sety(200) 71 self._close_gap_from_i(key) 72 return b 73 74 def insert(self, key, b): 75 self._open_gap_from_i(key) 76 list.insert(self, key, b) 77 b.setx(self.x + 34 * key) 78 width, _, _ = b.shapesize() 79 # align blocks by the bottom edge 80 y_offset = width / 2 * 20 81 b.sety(self.y + y_offset) 82 b.unglow() 83 84 def isort(shelf): 85 length = len(shelf) 86 for i in range(1, length): 87 hole = i 88 while hole > 0 and shelf[i].size < shelf[hole - 1].size: 89 hole = hole - 1 90 shelf.insert(hole, shelf.pop(i)) 91 return 92 93 def ssort(shelf): 94 length = len(shelf) 95 for j in range(0, length - 1): 96 imin = j 97 for i in range(j + 1, length): 98 if shelf[i].size < shelf[imin].size: 99 imin = i 100 if imin != j: 101 shelf.insert(j, shelf.pop(imin)) 102 103 def partition(shelf, left, right, pivot_index): 104 pivot = shelf[pivot_index] 105 shelf.insert(right, shelf.pop(pivot_index)) 106 store_index = left 107 for i in range(left, right): # range is non-inclusive of ending value 108 if shelf[i].size < pivot.size: 109 shelf.insert(store_index, shelf.pop(i)) 110 store_index = store_index + 1 111 shelf.insert(store_index, shelf.pop(right)) # move pivot to correct position 112 return store_index 113 114 def qsort(shelf, left, right): 115 if left < right: 116 pivot_index = left 117 pivot_new_index = partition(shelf, left, right, pivot_index) 118 qsort(shelf, left, pivot_new_index - 1) 119 qsort(shelf, pivot_new_index + 1, right) 120 121 def randomize(): 122 disable_keys() 123 clear() 124 target = list(range(10)) 125 random.shuffle(target) 126 for i, t in enumerate(target): 127 for j in range(i, len(s)): 128 if s[j].size == t + 1: 129 s.insert(i, s.pop(j)) 130 show_text(instructions1) 131 show_text(instructions2, line=1) 132 enable_keys() 133 134 def show_text(text, line=0): 135 line = 20 * line 136 goto(0,-250 - line) 137 write(text, align="center", font=("Courier", 16, "bold")) 138 139 def start_ssort(): 140 disable_keys() 141 clear() 142 show_text("Selection Sort") 143 ssort(s) 144 clear() 145 show_text(instructions1) 146 show_text(instructions2, line=1) 147 enable_keys() 148 149 def start_isort(): 150 disable_keys() 151 clear() 152 show_text("Insertion Sort") 153 isort(s) 154 clear() 155 show_text(instructions1) 156 show_text(instructions2, line=1) 157 enable_keys() 158 159 def start_qsort(): 160 disable_keys() 161 clear() 162 show_text("Quicksort") 163 qsort(s, 0, len(s) - 1) 164 clear() 165 show_text(instructions1) 166 show_text(instructions2, line=1) 167 enable_keys() 168 169 def init_shelf(): 170 global s 171 s = Shelf(-200) 172 vals = (4, 2, 8, 9, 1, 5, 10, 3, 7, 6) 173 for i in vals: 174 s.push(Block(i)) 175 176 def disable_keys(): 177 onkey(None, "s") 178 onkey(None, "i") 179 onkey(None, "q") 180 onkey(None, "r") 181 182 def enable_keys(): 183 onkey(start_isort, "i") 184 onkey(start_ssort, "s") 185 onkey(start_qsort, "q") 186 onkey(randomize, "r") 187 onkey(bye, "space") 188 189 def main(): 190 getscreen().clearscreen() 191 ht(); penup() 192 init_shelf() 193 show_text(instructions1) 194 show_text(instructions2, line=1) 195 enable_keys() 196 listen() 197 return "EVENTLOOP" 198 199 instructions1 = "press i for insertion sort, s for selection sort, q for quicksort" 200 instructions2 = "spacebar to quit, r to randomize" 201 202 if __name__=="__main__": 203 msg = main() 204 mainloop() 205