Skip to content

Commit 605c62a

Browse files
committed
feat: add quicksort and binary tree model
The binary tree is used to bookkeep simulated recursion on subarray bounds during quicksort. It may be removed later if I can successfully rewrite the levels using coroutines, which I will hold off until Godot 4.0 for now because of improvements to yield/await.
1 parent bef3376 commit 605c62a

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

levels/quick_sort.gd

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
class_name QuickSort
2+
extends ComparisonSort
3+
4+
const NAME = "QUICKSORT"
5+
const ABOUT = """
6+
Quicksort designates the last element as the pivot and puts everything
7+
less than the pivot before it and everything greater after it. This
8+
partitioning is done by iterating through the array while keeping track
9+
of a pointer initially set to the first element. Every time an element
10+
less than the pivot is encountered, it is swapped with the pointed
11+
element and the pointer moves forward. At the end, the pointer and pivot
12+
are swapped, and the process is repeated on the left and right halves.
13+
"""
14+
const CONTROLS = """
15+
If the highlighted element is less than the pivot or the pivot has been
16+
reached, press LEFT ARROW to swap it with the pointer. Otherwise, press
17+
RIGHT ARROW to move on.
18+
"""
19+
20+
const ACTIONS = {
21+
"SWAP": "Left",
22+
"CONTINUE": "Right",
23+
}
24+
var _index = 0
25+
var _pointer = 0
26+
# Bookkeep simulated recursion with a binary tree of subarray bounds
27+
var _stack = BinaryTreeModel.new(Vector2(0, array.size - 1))
28+
29+
func _init(array).(array):
30+
pass
31+
32+
func next(action):
33+
if _index == _pivot():
34+
if action != null and action != ACTIONS.SWAP:
35+
return emit_signal("mistake")
36+
array.swap(_index, _pointer)
37+
if _pointer - _begin() > 1:
38+
_stack.left = BinaryTreeModel.new(Vector2(_begin(), _pointer - 1))
39+
_stack = _stack.left
40+
elif _pivot() - _pointer > 1:
41+
_stack.right = BinaryTreeModel.new(Vector2(_pointer + 1, _pivot()))
42+
_stack = _stack.right
43+
else:
44+
while (_stack.parent.right != null
45+
or _stack.parent.left.value.y + 2 >= _stack.parent.value.y):
46+
_stack = _stack.parent
47+
if _stack.parent == null:
48+
return emit_signal("done")
49+
_stack.parent.right = BinaryTreeModel.new(Vector2(
50+
_stack.parent.left.value.y + 2, _stack.parent.value.y))
51+
_stack = _stack.parent.right
52+
_index = _begin()
53+
_pointer = _index
54+
elif array.at(_index) < array.at(_pivot()):
55+
if action != null and action != ACTIONS.SWAP:
56+
return emit_signal("mistake")
57+
array.swap(_index, _pointer)
58+
_index += 1
59+
_pointer += 1
60+
else:
61+
if action != null and action != ACTIONS.CONTINUE:
62+
return emit_signal("mistake")
63+
_index += 1
64+
65+
func _begin():
66+
return _stack.value.x
67+
68+
func _pivot():
69+
return _stack.value.y
70+
71+
func get_effect(i):
72+
if i < _begin() or i > _pivot():
73+
return EFFECTS.DIMMED
74+
if i == _index or i == _pivot():
75+
return EFFECTS.HIGHLIGHTED
76+
return EFFECTS.NONE
77+
78+
func get_pointer():
79+
return _pointer

models/binary_tree_model.gd

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class_name BinaryTreeModel
2+
extends Reference
3+
4+
var left: BinaryTreeModel setget set_left
5+
var right: BinaryTreeModel setget set_right
6+
var parent: BinaryTreeModel
7+
var value = null
8+
9+
func _init(value):
10+
self.value = value
11+
12+
func set_left(child: BinaryTreeModel):
13+
child.parent = self
14+
left = child
15+
16+
func set_right(child: BinaryTreeModel):
17+
child.parent = self
18+
right = child

project.godot

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ _global_script_classes=[ {
1919
"language": "GDScript",
2020
"path": "res://views/array_view.gd"
2121
}, {
22+
"base": "Reference",
23+
"class": "BinaryTreeModel",
24+
"language": "GDScript",
25+
"path": "res://models/binary_tree_model.gd"
26+
}, {
2227
"base": "ComparisonSort",
2328
"class": "BogoSort",
2429
"language": "GDScript",
@@ -45,18 +50,25 @@ _global_script_classes=[ {
4550
"path": "res://levels/merge_sort.gd"
4651
}, {
4752
"base": "ComparisonSort",
53+
"class": "QuickSort",
54+
"language": "GDScript",
55+
"path": "res://levels/quick_sort.gd"
56+
}, {
57+
"base": "ComparisonSort",
4858
"class": "SelectionSort",
4959
"language": "GDScript",
5060
"path": "res://levels/selection_sort.gd"
5161
} ]
5262
_global_script_class_icons={
5363
"ArrayModel": "",
5464
"ArrayView": "",
65+
"BinaryTreeModel": "",
5566
"BogoSort": "",
5667
"BubbleSort": "",
5768
"ComparisonSort": "",
5869
"InsertionSort": "",
5970
"MergeSort": "",
71+
"QuickSort": "",
6072
"SelectionSort": ""
6173
}
6274

scripts/levels.gd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const LEVELS = [
55
InsertionSort,
66
SelectionSort,
77
MergeSort,
8+
QuickSort,
89
]
910
const MIN_WAIT = 1.0 / 32 # Should be greater than maximum frame time
1011
const MAX_WAIT = 4

0 commit comments

Comments
 (0)