Skip to content

Commit

Permalink
The Redesign and Sound Update
Browse files Browse the repository at this point in the history
A huge redesign with more features!

[ADDED]
- Optional sound effects based on the average value of compared elements
- Customizable data types (random, reversed, sorted, etc.)
- Big preview mode in level select screen
- Extended, rewritten descriptions of all levels with pseudocode

[REMOVED]
- Tier system
- Sizes of 8, 128, and 256
  • Loading branch information
danielzting committed Sep 10, 2020
2 parents 5373b9e + 5abdf55 commit 310c68a
Show file tree
Hide file tree
Showing 26 changed files with 876 additions and 402 deletions.
4 changes: 0 additions & 4 deletions CONTRIBUTING.md

This file was deleted.

28 changes: 8 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
<br />
<p align="center">
<img src="assets/icon.png" alt="Logo" width="80" height="80">
<img src="assets/icon.png" alt="Logo" width="64" height="64">

<h1 align="center">Human Computer Simulator</h1>

<p align="center">
A game where you get to become your favorite algorithm or data structure!
<br />
<a href="https://github.com/DanielZTing/human-computer-simulator/issues/new?template=bug_report.md">Report Bug</a>
·
<a href="https://github.com/DanielZTing/human-computer-simulator/issues/new?template=feature_request.md">Request Feature</a>
Become your favorite sorting algorithm!
</p>
</p>

## Table of Contents

* [About the Project](#about-the-project)
* [Getting Started](#getting-started)

## About The Project

![Level select screen](assets/levels.png)
Have you ever wondered what it feels like to be a sorting algorithm‽ Now you can find out! In *Human Computer Simulator*, you control an algorithm operating on an array, trying to sort as fast as possible. With 10 different levels, a cool visualization, and optional sound effects, you can fulfill your sorting dreams...

You may have come across the famous [15 Sorting Algorithms in 6 Minutes](https://www.youtube.com/watch?v=kPRA0W1kECg) video by [Timo Bingmann](https://github.com/bingmann) at some point in your computer science career. There is currently no shortage of neat visualizations of all kinds of algorithms, but what if you could become the algorithm itself?

In *Human Computer Simulator*, you control an algorithm operating on some data structure. Right now, the game is limited to sorting arrays. The end vision is to have a library of interactive, playable levels on anything from a search on a binary tree to Dijkstra's shortest path on a graph.
A demo (large download warning: ~5 MB gzipped) is available on this repository's [Github Pages](https://danielzting.github.io/human-computer-simulator). It requires a desktop browser with support for WebAssembly and WebGL.

It's written using the Godot game engine and licensed with [almost no restrictions](LICENSE.txt). Use it to make a lecture a bit more interesting, review for an assignment, or just kill time. Hope you enjoy.
## Anti-pitch

## Getting Started
This is beta software, so there will inevitably be things that are confusing, broken, or straight up wrong. See the [issues](https://github.com/DanielZTing/human-computer-simulator/issues) for a list of caveats and don't hesitate to open another one if you find something new.

This software is in an alpha stage of development and I do not plan on releasing ready-to-run builds until a stable v1.0 release. However, it is very easy to run and hack the source code yourself. Just grab the lightweight free and open source [Godot game engine](https://godotengine.org/download), import the `project.godot` file, and hit the play button.
## Contributing

A demo version (large download warning: ~20 MB) is available on this repository's [Github Pages](https://danielzting.github.io/human-computer-simulator). It requires a desktop browser with support for WebAssembly and WebGL; mobile is not currently supported. Since this is still in alpha, some things might be dumb, make no sense whatsoever, or just break outright. I welcome any feedback you may have.
I welcome any bug reports or feature requests. Pull requests are appreciated as well, though I apologize for my code in advance. If you like this so much that you want to [throw money at me](https://venmo.com/DanielZTing), I will add any 80x24 ASCII text block of your choosing to the README and credits.
Binary file modified assets/levels.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 14 additions & 6 deletions levels/bogo_sort.gd
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
"""
BOGOSORT
class_name BogoSort
extends ComparisonSort

const NAME = "BOGOSORT"
const DESCRIPTION = """
Generates random permutations until the array is sorted.
"""
const CONTROLS = """
Keep on hitting RIGHT ARROW to CONTINUE and hope for the best!
"""

class_name BogoSort
extends ComparisonSort
const CODE = """
def bogosort(a):
while not a.sorted():
a.shuffle()
"""

func _init(array).(array):
pass
Expand All @@ -19,3 +24,6 @@ func next(action):

func get_effect(i):
return EFFECTS.NONE

func get_frac():
return array.frac(0)
39 changes: 28 additions & 11 deletions levels/bubble_sort.gd
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
"""
BUBBLE SORT
class_name BubbleSort
extends ComparisonSort

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 NAME = "BUBBLE SORT"
const DESCRIPTION = """
Bubble sort looks at consecutive pairs of elements and swaps them if
they are out of order, finishing when it has gone through the whole
array from beginning to end without a single swap. The actual level
contains an optimization that skips over elements guaranteed to be
already in place.
Due to its simplicity, it is commonly taught as the first sorting
algorithm students learn in computer science classes, but is rarely used
in real life because it is slow on large data and other simple quadratic
algorithms like insertion sort perform better.
"""
const CONTROLS = """
If the two highlighted elements are out of order, hit LEFT ARROW to swap
them. Otherwise, hit RIGHT ARROW to continue.
"""

class_name BubbleSort
extends ComparisonSort

const CODE = """
def bubble_sort(a):
swapped = true
while swapped:
swapped = false
for i in range(len(a) - 1):
if a[i] > a[i + 1]:
a.swap(i, i + 1)
swapped = true
"""
const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
Expand Down Expand Up @@ -48,3 +62,6 @@ func get_effect(i):
if i >= _end:
return EFFECTS.DIMMED
return EFFECTS.NONE

func get_frac():
return (array.frac(_index) + array.frac(_index + 1)) / 2.0
39 changes: 31 additions & 8 deletions levels/cocktail_sort.gd → levels/cocktail_shaker_sort.gd
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
"""
COCKTAIL SORT
class_name CocktailSort
extends ComparisonSort

Cocktail shaker sort is a variation of bubble sort that
alternates going backwards and forwards.
const NAME = "COCKTAIL SHAKER SORT"
const DESCRIPTION = """
Cocktail shaker sort is a variation of bubble sort that alternates going
backwards and forwards. The actual level contains an optimization that
skips over elements guaranteed to be already in place.
Because it is bidirectional, it is slightly faster than bubble sort, but
is still quadratic and therefore not used on large data.
"""
const CONTROLS = """
If the two highlighted elements are out of order, hit LEFT ARROW to swap
them. Otherwise, hit RIGHT ARROW to continue.
"""

class_name CocktailSort
extends ComparisonSort

const CODE = """
def cocktail_shaker_sort(a):
swapped = true
while swapped:
swapped = false
for i in range(len(a) - 1):
if array[i] > array[i + 1]:
a.swap(i, i + 1)
swapped = true
if not swapped:
break
swapped = false
for i in range(len(a) - 1, 0, -1)
if a[i - 1] > a[i]:
a.swap(i - 1, i)
swapped = true
"""
const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
Expand Down Expand Up @@ -56,3 +76,6 @@ func get_effect(i):
if i < _sorted and _forwards == true or i < _sorted - 1 or i >= array.size - _sorted:
return EFFECTS.DIMMED
return EFFECTS.NONE

func get_frac():
return (array.frac(_index) + array.frac(_index + 1)) / 2.0
35 changes: 28 additions & 7 deletions levels/comb_sort.gd
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
"""
COMB SORT
class_name CombSort
extends ComparisonSort

Comb sort is a variant of bubble sort that operates on gapped arrays.
const NAME = "COMB SORT"
const DESCRIPTION = """
Comb sort is a variant of bubble sort that compares elements a certain
gap apart instead of consecutive elements. This gap is divided after
every pass by an experimentally determined optimal factor of about 1.3.
Once the gap becomes 1, comb sort becomes a regular bubble sort.
This allows comb sort to get rid of small values near the end more
quickly, which turns out to be the bottleneck in bubble sort, but still
has a quadratic worst case.
"""
const CONTROLS = """
If the two highlighted elements are out of order, hit LEFT ARROW to swap
them. Otherwise, hit RIGHT ARROW to continue.
"""

class_name CombSort
extends ComparisonSort

const CODE = """
def comb_sort(a):
gap = len(a)
swapped = true
while gap != 1 or swapped:
swapped = false
gap = max(gap / 1.3, 1)
for i in range(len(a) - gap):
if a[i] > a[i + gap]:
a.swap(i, i + gap)
swapped = true
"""
const SHRINK_FACTOR = 1.3
const ACTIONS = {
"SWAP": "Left",
Expand Down Expand Up @@ -46,3 +64,6 @@ func get_effect(i):
if i >= _end:
return EFFECTS.DIMMED
return EFFECTS.NONE

func get_frac():
return (array.frac(_index) + array.frac(_index + _gap)) / 2.0
9 changes: 0 additions & 9 deletions levels/comparison_sort.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,8 @@ const EFFECTS = {
}

const DISABLE_TIME = 1.0
var NAME = _get_header().split(" ")[0]
var DESCRIPTION = _get_header().split(" ")[1]
var CONTROLS = _get_header().split(" ")[2]

var array: ArrayModel
var moves = 0
var test = _get_header().split(" ")[0]

var _timer = Timer.new()

Expand All @@ -30,16 +25,12 @@ func _init(array):
self.connect("mistake", self, "_on_ComparisonSort_mistake")
self.connect("done", self, "_on_ComparisonSort_done")

func _get_header():
return get_script().source_code.replace("\n", " ").split('"""')[1].strip_edges()

func _ready():
set_process_input(false)

func _input(event):
"""Pass input events for checking and take appropriate action."""
if event.is_pressed():
moves += 1
return next(event.as_text())

func next(action):
Expand Down
51 changes: 39 additions & 12 deletions levels/cycle_sort.gd
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
"""
CYCLE SORT
Cycle sort repeatedly counts the number of elements less than the first
and swaps it with that index until the smallest element is reached. Then
it does this process starting at the next out-of-place element.
If the highlighted element is less than the pointer, hit LEFT ARROW.
Otherwise, hit RIGHT ARROW.
"""

class_name CycleSort
extends ComparisonSort

const NAME = "CYCLE SORT"
const DESCRIPTION = """
Cycle sort looks at the first element and finds its correct final
position by counting the number of elements smaller than it. Then it
saves the element at that index, writes the first element there, and
repeats the process with the saved element. For the sake of
demonstration, in the actual level, swaps are used instead.
This results in a quadratic runtime but gives it the special property
of being optimal in the number of writes to the array. This makes cycle
sort useful in situations where writes are very expensive.
"""
const CONTROLS = """
If the highlighted element is less than the element below the blue
pointer, hit LEFT ARROW. Otherwise, hit RIGHT ARROW.
"""
const CODE = """
def cycle_sort(a):
for i in range(len(a)):
while True:
less = equal = 0
for element in a:
if element < a[i]:
less += 1
elif element == a[i]:
equal += 1
if less <= i and i < less + equal:
break
while a[i] == a[less]:
less += 1
a.swap(i, less)
"""
const ACTIONS = {
"SMALLER": "Left",
"BIGGER": "Right",
Expand All @@ -33,8 +54,11 @@ func next(action):
return emit_signal("mistake")
_index += 1
if _index == array.size:
# Skip over duplicates to avoid infinite cycling
while _smaller != _pointer and array.at(_pointer) == array.at(_smaller):
_smaller += 1
array.swap(_pointer, _smaller)
while array.at(_pointer) == _pointer + 1:
while array.is_in_place(_pointer):
_pointer += 1
if _pointer == array.size:
return emit_signal("done")
Expand All @@ -48,3 +72,6 @@ func get_effect(i):

func get_pointer():
return _pointer

func get_frac():
return array.frac(_index)
39 changes: 25 additions & 14 deletions levels/insertion_sort.gd
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
"""
INSERTION SORT
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.
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.
"""

class_name InsertionSort
extends ComparisonSort

const NAME = "INSERTION SORT"
const DESCRIPTION = """
Insertion sort goes through the array and inserts each element into its
correct place, like how most people would sort a hand of playing cards.
It is one of the fastest quadratic algorithms in practice and is
efficient on small or almost sorted data. It is also simple, stable, and
in-place. For these reasons it is sometimes used within faster divide
and conquer algorithms when the array has been divided to a small size.
"""
const CONTROLS = """
If the two highlighted elements are out of order, hit LEFT ARROW to swap
them. Otherwise, hit RIGHT ARROW to continue.
"""
const CODE = """
def insertion_sort(a):
for i in range(len(a)):
j = i
while j > 0 and a[j - 1] > a[j]:
a.swap(j - 1, j)
j -= 1
"""
const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
Expand Down Expand Up @@ -50,3 +58,6 @@ func _grow():
if _end == array.size:
emit_signal("done")
_index = _end

func get_frac():
return (array.frac(_index - 1) + array.frac(_index)) / 2.0
Loading

0 comments on commit 310c68a

Please sign in to comment.