Skip to content

Commit

Permalink
User Friendly Update
Browse files Browse the repository at this point in the history
A lot of small quality-of-life improvements to make it more like a
playable game!

[ADDED]
- Optional pointer to an element (used in selection sort and quicksort)
- Swapping animation for all sorts and sorting animation for merge sort
- Persistent high score with moves-per-second tier ranking system
- Configurable simulation size and speed
- Quicksort (using Lomuto partition scheme)

[CHANGED]
- Replaced useless end screen with a view of sorted array
  • Loading branch information
danielzting committed Aug 12, 2020
2 parents f13d0b7 + bd5b6c6 commit 3eeca62
Show file tree
Hide file tree
Showing 29 changed files with 649 additions and 230 deletions.
38 changes: 38 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
As I would like to submit this project to the Congressional App
Challenge, I unfortunately cannot accept contributions until Monday,
October 19th, 2020 at the earliest. Kindly hold any issues and pull
requests until then.
48 changes: 35 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
# Human Computer Simulator
*Human Computer Simulator* is a game where you get to become your
favorite algorithm and or data structure.

# Screenshots
![Level Select](assets/levels.png)

# Download
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 it yourself. Just grab the free and open source
[Godot game engine](https://godotengine.org), import the `project.godot`
file, and hit the play button.
<br />
<p align="center">
<img src="assets/icon.png" alt="Logo" width="80" height="80">

<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>
</p>
</p>

## Table of Contents

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

## About The Project

![Level select screen](assets/levels.png)

You may have come across the famous [15 Sorting Algorithms in 6 Minutes](https://www.youtube.com/watch?v=kPRA0W1kECg) video by [Timo Bingoman](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.

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.

## Getting Started

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.

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.
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.
Binary file modified assets/theme.theme
Binary file not shown.
6 changes: 5 additions & 1 deletion levels/bubble_sort.gd
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ If the two highlighted elements are out of order, hit LEFT ARROW to swap
them. Otherwise, hit RIGHT ARROW to continue.
"""

const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
}
var _index = 0 # First of two elements being compared
var _end = array.size # Beginning of sorted subarray
var _swapped = false
Expand All @@ -27,7 +31,7 @@ func next(action):
return emit_signal("mistake")
array.swap(_index, _index + 1)
_swapped = true
elif action != null and action != ACTIONS.NO_SWAP:
elif action != null and action != ACTIONS.CONTINUE:
return emit_signal("mistake")
_index += 1
# Prevent player from having to spam tap through the end
Expand Down
28 changes: 16 additions & 12 deletions levels/comparison_sort.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ extends Node
signal done
signal mistake

const ACTIONS = {
"SWAP": "ui_left",
"NO_SWAP": "ui_right",

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

const EFFECTS = {
"NONE": GlobalTheme.GREEN,
"HIGHLIGHTED": GlobalTheme.ORANGE,
Expand All @@ -22,6 +14,8 @@ const DISABLE_TIME = 1.0

var array: ArrayModel
var active = true
var done = false
var moves = 0

var _timer = Timer.new()

Expand All @@ -32,19 +26,29 @@ func _init(array):
_timer.connect("timeout", self, "_on_Timer_timeout")
add_child(_timer)
self.connect("mistake", self, "_on_ComparisonSort_mistake")
self.connect("done", self, "_on_ComparisonSort_done")

func _input(event):
"""Pass input events for checking and take appropriate action."""
if not active:
if done or not active:
return
for action in ACTIONS.values():
if event.is_action_pressed(action):
return next(action)
if event.is_pressed():
moves += 1
return next(event.as_text())

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

func get_effect(i):
return get_effect(i)

func _get_effect(i):
push_error("NotImplementedError")

func _on_ComparisonSort_done():
done = true

func _on_ComparisonSort_mistake():
"""Disable the controls for one second."""
active = false
Expand Down
8 changes: 6 additions & 2 deletions levels/insertion_sort.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ out of order. When this is no longer the case, hit RIGHT ARROW to
advance.
"""

const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
}
var _end = 1 # Size of the sorted subarray
var _index = 1 # Position of element currently being inserted

Expand All @@ -30,14 +34,14 @@ func next(action):
if _index == 0:
_grow()
else:
if action != null and action != ACTIONS.NO_SWAP:
if action != null and action != ACTIONS.CONTINUE:
return emit_signal("mistake")
_grow()

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

Expand Down
8 changes: 8 additions & 0 deletions levels/merge_sort.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ highlighted element is on. If you've reached the end of one side, press
the other side's ARROW KEY.
"""

const ACTIONS = {
"LEFT": "Left",
"RIGHT": "Right",
}
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
Expand All @@ -27,18 +31,22 @@ func next(action):
if _left == _get_middle():
if action != null and action != ACTIONS.RIGHT:
return emit_signal("mistake")
array.emit_signal("removed", _right)
_right += 1
elif _right == _get_end():
if action != null and action != ACTIONS.LEFT:
return emit_signal("mistake")
array.emit_signal("removed", _left)
_left += 1
elif array.at(_left) <= array.at(_right):
if action != null and action != ACTIONS.LEFT:
return emit_signal("mistake")
array.emit_signal("removed", _left)
_left += 1
else:
if action != null and action != ACTIONS.RIGHT:
return emit_signal("mistake")
array.emit_signal("removed", _right)
_right += 1
# If both ends have been reached, merge and advance to next block
if _left == _get_middle() and _right == _get_end():
Expand Down
79 changes: 79 additions & 0 deletions levels/quick_sort.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
class_name QuickSort
extends ComparisonSort

const NAME = "QUICKSORT"
const ABOUT = """
Quicksort designates the last element as the pivot and puts everything
less than the pivot before it and everything greater after it. This
partitioning is done by iterating through the array while keeping track
of a pointer initially set to the first element. Every time an element
less than the pivot is encountered, it is swapped with the pointed
element and the pointer moves forward. At the end, the pointer and pivot
are swapped, and the process is repeated on the left and right halves.
"""
const CONTROLS = """
If the highlighted element is less than the pivot or the pivot has been
reached, press LEFT ARROW to swap it with the pointer. Otherwise, press
RIGHT ARROW to move on.
"""

const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
}
var _index = 0
var _pointer = 0
# Bookkeep simulated recursion with a binary tree of subarray bounds
var _stack = BinaryTreeModel.new(Vector2(0, array.size - 1))

func _init(array).(array):
pass

func next(action):
if _index == _pivot():
if action != null and action != ACTIONS.SWAP:
return emit_signal("mistake")
array.swap(_index, _pointer)
if _pointer - _begin() > 1:
_stack.left = BinaryTreeModel.new(Vector2(_begin(), _pointer - 1))
_stack = _stack.left
elif _pivot() - _pointer > 1:
_stack.right = BinaryTreeModel.new(Vector2(_pointer + 1, _pivot()))
_stack = _stack.right
else:
while (_stack.parent.right != null
or _stack.parent.left.value.y + 2 >= _stack.parent.value.y):
_stack = _stack.parent
if _stack.parent == null:
return emit_signal("done")
_stack.parent.right = BinaryTreeModel.new(Vector2(
_stack.parent.left.value.y + 2, _stack.parent.value.y))
_stack = _stack.parent.right
_index = _begin()
_pointer = _index
elif array.at(_index) < array.at(_pivot()):
if action != null and action != ACTIONS.SWAP:
return emit_signal("mistake")
array.swap(_index, _pointer)
_index += 1
_pointer += 1
else:
if action != null and action != ACTIONS.CONTINUE:
return emit_signal("mistake")
_index += 1

func _begin():
return _stack.value.x

func _pivot():
return _stack.value.y

func get_effect(i):
if i < _begin() or i > _pivot():
return EFFECTS.DIMMED
if i == _index or i == _pivot():
return EFFECTS.HIGHLIGHTED
return EFFECTS.NONE

func get_pointer():
return _pointer
9 changes: 8 additions & 1 deletion levels/selection_sort.gd
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ smaller than the left highlighted element, then hit LEFT ARROW and
repeat.
"""

const ACTIONS = {
"SWAP": "Left",
"CONTINUE": "Right",
}
var _base = 0 # Size of sorted subarray
var _min = 0 # Index of smallest known element
var _index = 1 # Element currently being compared
Expand All @@ -27,7 +31,7 @@ func next(action):
if action != null and action != ACTIONS.SWAP:
return emit_signal("mistake")
_min = _index
elif action != null and action != ACTIONS.NO_SWAP:
elif action != null and action != ACTIONS.CONTINUE:
return emit_signal("mistake")
_index += 1
if _index == array.size:
Expand All @@ -44,3 +48,6 @@ func get_effect(i):
if i < _base:
return EFFECTS.DIMMED
return EFFECTS.NONE

func get_pointer():
return _min
Loading

0 comments on commit 3eeca62

Please sign in to comment.