Skip to content

Commit

Permalink
Four Basic Sorts
Browse files Browse the repository at this point in the history
Every level but one of what I consider the "five basic sorts" is
available except for quicksort, because its complexity warrants new
features that belong in a later update.

[ADDED]
- Insertion sort, selection sort, and merge sort
- A new "dimmed" color style to aid visual comprehension
  • Loading branch information
danielzting committed Jul 22, 2020
2 parents 6092637 + 8ebc0f7 commit f13d0b7
Show file tree
Hide file tree
Showing 20 changed files with 473 additions and 169 deletions.
6 changes: 0 additions & 6 deletions assets/levels.json

This file was deleted.

34 changes: 34 additions & 0 deletions assets/levels.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="StreamTexture"
path="res://.import/levels.png-a6e910382d32e95b5c3f382bb559bde4.stex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://assets/levels.png"
dest_files=[ "res://.import/levels.png-a6e910382d32e95b5c3f382bb559bde4.stex" ]

[params]

compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0
25 changes: 16 additions & 9 deletions levels/bogo_sort.gd
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
extends ComparisonSort
class_name BogoSort
extends ComparisonSort

func _init(array).(array):
pass
const NAME = "BOGOSORT"
const ABOUT = """
Generates random permutations until the array is sorted.
"""
const CONTROLS = """
Keep on hitting RIGHT ARROW to CONTINUE and hope for the best!
"""

func check(action):
return true
func _init(array).(array):
pass

func next():
array = ArrayModel.new(array.size)
func next(action):
array = ArrayModel.new(array.size)
if array.is_sorted():
emit_signal("done")

func emphasized(i):
return false
func get_effect(i):
return EFFECTS.NONE
61 changes: 40 additions & 21 deletions levels/bubble_sort.gd
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
extends ComparisonSort
class_name BubbleSort
extends ComparisonSort

var swapped = false
const NAME = "BUBBLE SORT"
const ABOUT = """
Bubble sort iterates through the array and looks at each pair of
elements, swapping them if they are out of order. When it has gone
through the entire array without swapping a single pair, it has
finished. Though simple to understand, bubble sort is hopelessly
inefficient on all but the smallest of arrays.
"""
const CONTROLS = """
If the two highlighted elements are out of order, hit LEFT ARROW to swap
them. Otherwise, hit RIGHT ARROW to continue.
"""

func _init(array).(array):
pass
var _index = 0 # First of two elements being compared
var _end = array.size # Beginning of sorted subarray
var _swapped = false

func check(action):
if array.get(index) > array.get(index + 1):
return action == "swap"
else:
return action == "no_swap"
func _init(array).(array):
pass

func next():
if array.get(index) > array.get(index + 1):
array.swap(index, index + 1)
swapped = true
index += 1
if index == array.size - 1:
if not swapped:
emit_signal("done")
index = 0
swapped = false
func next(action):
if array.at(_index) > array.at(_index + 1):
if action != null and action != ACTIONS.SWAP:
return emit_signal("mistake")
array.swap(_index, _index + 1)
_swapped = true
elif action != null and action != ACTIONS.NO_SWAP:
return emit_signal("mistake")
_index += 1
# Prevent player from having to spam tap through the end
if _index + 1 == _end:
if not _swapped or _end == 2: # Stop if only one element left
emit_signal("done")
_index = 0
_end -= 1
_swapped = false

func emphasized(i):
return i == index or i == index + 1
func get_effect(i):
if i == _index or i == _index + 1:
return EFFECTS.HIGHLIGHTED
if i >= _end:
return EFFECTS.DIMMED
return EFFECTS.NONE
66 changes: 39 additions & 27 deletions levels/comparison_sort.gd
Original file line number Diff line number Diff line change
@@ -1,43 +1,55 @@
extends Node
class_name ComparisonSort
extends Node

signal done
signal mistake

const ACTIONS = ["swap", "no_swap"]
const ACTIONS = {
"SWAP": "ui_left",
"NO_SWAP": "ui_right",

"LEFT": "ui_left",
"RIGHT": "ui_right",
}

const EFFECTS = {
"NONE": GlobalTheme.GREEN,
"HIGHLIGHTED": GlobalTheme.ORANGE,
"DIMMED": GlobalTheme.DARK_GREEN,
}

const DISABLE_TIME = 1.0

var array: ArrayModel
var index = 0
var timer = Timer.new()
var active = true

var _timer = Timer.new()

func _init(array):
self.array = array
timer.one_shot = true
timer.connect("timeout", self, "_on_Timer_timeout")
add_child(timer)
self.connect("mistake", self, "_on_ComparisonSort_mistake")
"""Initialize array and timer."""
self.array = array
_timer.one_shot = true
_timer.connect("timeout", self, "_on_Timer_timeout")
add_child(_timer)
self.connect("mistake", self, "_on_ComparisonSort_mistake")

func check(action):
pass
func _input(event):
"""Pass input events for checking and take appropriate action."""
if not active:
return
for action in ACTIONS.values():
if event.is_action_pressed(action):
return next(action)

func next():
pass
func next(action):
"""Check the action and advance state or emit signal as needed."""
push_error("NotImplementedError")

func _on_ComparisonSort_mistake():
active = false
timer.start(1)
"""Disable the controls for one second."""
active = false
_timer.start(DISABLE_TIME)

func _on_Timer_timeout():
active = true

func _input(event):
if not active:
return

for action in ACTIONS:
if event.is_action_pressed(action):
if check(action):
next()
else:
emit_signal("mistake")
"""Reenable the controls."""
active = true
48 changes: 48 additions & 0 deletions levels/insertion_sort.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
class_name InsertionSort
extends ComparisonSort

const NAME = "INSERTION SORT"
const ABOUT = """
Insertion sort goes through the array and inserts each
element into its correct position. It is most similar to how most people
would sort a deck of cards. It is also slow on large arrays but it is
one of the faster quadratic algorithms. It is often used to sort smaller
subarrays in hybrid sorting algorithms.
"""
const CONTROLS = """
Hit LEFT ARROW to swap the two highlighted elements as long as they are
out of order. When this is no longer the case, hit RIGHT ARROW to
advance.
"""

var _end = 1 # Size of the sorted subarray
var _index = 1 # Position of element currently being inserted

func _init(array).(array):
pass

func next(action):
if array.at(_index - 1) > array.at(_index):
if action != null and action != ACTIONS.SWAP:
return emit_signal("mistake")
array.swap(_index - 1, _index)
_index -= 1
if _index == 0:
_grow()
else:
if action != null and action != ACTIONS.NO_SWAP:
return emit_signal("mistake")
_grow()

func get_effect(i):
if i == _index or i == _index - 1:
return EFFECTS.HIGHLIGHTED
if i < _end:
return EFFECTS.DIMMED
return EFFECTS.NONE

func _grow():
_end += 1
if _end == array.size:
emit_signal("done")
_index = _end
76 changes: 76 additions & 0 deletions levels/merge_sort.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class_name MergeSort
extends ComparisonSort

const NAME = "MERGE SORT"
const ABOUT = """
Merge sort is an efficient sorting algorithm that splits the array into
single-element chunks. Then it merges each pair of chunks until only one
sorted chunk is left by repeatedly choosing the smaller element at the
head of each chunk and moving the head back. However, it needs an entire
array's worth of auxiliary memory.
"""
const CONTROLS = """
Press the ARROW KEY corresponding to the side that the smaller
highlighted element is on. If you've reached the end of one side, press
the other side's ARROW KEY.
"""

var _left = 0 # Index of left subarray pointer
var _right = 1 # Index of right subarray pointer
var _sub_size = 2 # Combined size of left and right subarrays
var _sub_no = 0 # Currently being merged left-right pair number

func _init(array).(array):
pass

func next(action):
if _left == _get_middle():
if action != null and action != ACTIONS.RIGHT:
return emit_signal("mistake")
_right += 1
elif _right == _get_end():
if action != null and action != ACTIONS.LEFT:
return emit_signal("mistake")
_left += 1
elif array.at(_left) <= array.at(_right):
if action != null and action != ACTIONS.LEFT:
return emit_signal("mistake")
_left += 1
else:
if action != null and action != ACTIONS.RIGHT:
return emit_signal("mistake")
_right += 1
# If both ends have been reached, merge and advance to next block
if _left == _get_middle() and _right == _get_end():
array.sort(_get_begin(), _get_end())
_sub_no += 1
# If last block has been completed, go up a level
if _sub_no == array.size / (_sub_size):
_sub_size *= 2
_sub_no = 0
if _sub_size == array.size * 2:
emit_signal("done")
# Update pointers
_left = _get_begin()
_right = _get_middle()

func get_effect(i):
var is_left = _left != _get_middle() and i == _left
var is_right = _right != _get_end() and i == _right
if is_left or is_right:
return EFFECTS.HIGHLIGHTED
if i < _left or i >= _get_middle() and i < _right or i >= _get_end():
return EFFECTS.DIMMED
return EFFECTS.NONE

func _get_begin():
"""Get the index of the left subarray's head."""
return _sub_no * _sub_size

func _get_middle():
"""Get the index of the right subarray's head."""
return _sub_no * _sub_size + _sub_size / 2

func _get_end():
"""Get the index of one past the right subarray's tail."""
return _sub_no * _sub_size + _sub_size
46 changes: 46 additions & 0 deletions levels/selection_sort.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
class_name SelectionSort
extends ComparisonSort

const NAME = "SELECTION SORT"
const ABOUT = """
Selection sort incrementally builds a sorted array by repeatedly looking
for the smallest element and swapping it onto the end of the sorted
portion of the array, which initially starts with size zero but grows
after each round. It is faster than an unoptimized bubble sort but
slower than insertion sort.
"""
const CONTROLS = """
Keep on hitting RIGHT ARROW until you encounter an element that is
smaller than the left highlighted element, then hit LEFT ARROW and
repeat.
"""

var _base = 0 # Size of sorted subarray
var _min = 0 # Index of smallest known element
var _index = 1 # Element currently being compared

func _init(array).(array):
pass

func next(action):
if array.at(_index) < array.at(_min):
if action != null and action != ACTIONS.SWAP:
return emit_signal("mistake")
_min = _index
elif action != null and action != ACTIONS.NO_SWAP:
return emit_signal("mistake")
_index += 1
if _index == array.size:
array.swap(_base, _min)
_base += 1
_min = _base
_index = _base + 1
if _base == array.size - 1:
emit_signal("done")

func get_effect(i):
if i == _min or i == _index:
return EFFECTS.HIGHLIGHTED
if i < _base:
return EFFECTS.DIMMED
return EFFECTS.NONE
Loading

0 comments on commit f13d0b7

Please sign in to comment.