diff --git a/CREDITS.md b/CREDITS.md new file mode 100644 index 0000000..5061d41 --- /dev/null +++ b/CREDITS.md @@ -0,0 +1,61 @@ +This project is made possible by open source projects; their required copyrights are listed below. + +# [Godot](https://godotengine.org) +Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. Copyright (c) 2014-2020 Godot Engine contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# [FreeType](https://www.freetype.org) +Portions of this software are copyright © 2019 The FreeType +Project (www.freetype.org). All rights reserved. + +# [Hack](https://sourcefoundry.org/hack) +The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License + +The work in the DejaVu project was committed to the public domain. + +Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bitstream Vera License with Reserved Font Names "Bitstream" and "Vera" + +### MIT License + +Copyright (c) 2018 Source Foundry Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +### BITSTREAM VERA LICENSE + +Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions: + +The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces. + +The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera". + +This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names. + +The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself. + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org. diff --git a/README.md b/README.md index 3622972..9fa6671 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,13 @@ # Human Computer Simulator -*Gamifying computer science education* +*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. diff --git a/assets/hack-regular.ttf b/assets/hack-regular.ttf new file mode 100644 index 0000000..92a90cb Binary files /dev/null and b/assets/hack-regular.ttf differ diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..3f82f29 Binary files /dev/null and b/assets/icon.png differ diff --git a/assets/icon.png.import b/assets/icon.png.import new file mode 100644 index 0000000..4e9f7bb --- /dev/null +++ b/assets/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icon.png" +dest_files=[ "res://.import/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.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 diff --git a/assets/levels.json b/assets/levels.json new file mode 100644 index 0000000..2cc1797 --- /dev/null +++ b/assets/levels.json @@ -0,0 +1,6 @@ +{ + "BUBBLE SORT": + { + "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." + } +} diff --git a/assets/levels.png b/assets/levels.png new file mode 100644 index 0000000..b269675 Binary files /dev/null and b/assets/levels.png differ diff --git a/assets/splash.png b/assets/splash.png new file mode 100644 index 0000000..f4140ff Binary files /dev/null and b/assets/splash.png differ diff --git a/assets/splash.png.import b/assets/splash.png.import new file mode 100644 index 0000000..166174c --- /dev/null +++ b/assets/splash.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/splash.png-8aa957744d9f4b764dd4680b37c88571.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/splash.png" +dest_files=[ "res://.import/splash.png-8aa957744d9f4b764dd4680b37c88571.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 diff --git a/assets/theme.theme b/assets/theme.theme new file mode 100644 index 0000000..8d9e5b5 Binary files /dev/null and b/assets/theme.theme differ diff --git a/levels/bogo_sort.gd b/levels/bogo_sort.gd new file mode 100644 index 0000000..76e5692 --- /dev/null +++ b/levels/bogo_sort.gd @@ -0,0 +1,14 @@ +extends ComparisonSort +class_name BogoSort + +func _init(array).(array): + pass + +func check(action): + return true + +func next(): + array = ArrayModel.new(array.size) + +func emphasized(i): + return false diff --git a/levels/bubble_sort.gd b/levels/bubble_sort.gd new file mode 100644 index 0000000..801cd82 --- /dev/null +++ b/levels/bubble_sort.gd @@ -0,0 +1,27 @@ +extends ComparisonSort +class_name BubbleSort + +var swapped = false + +func _init(array).(array): + pass + +func check(action): + if array.get(index) > array.get(index + 1): + return action == "swap" + else: + return action == "no_swap" + +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 emphasized(i): + return i == index or i == index + 1 diff --git a/levels/comparison_sort.gd b/levels/comparison_sort.gd new file mode 100644 index 0000000..dbb0bf0 --- /dev/null +++ b/levels/comparison_sort.gd @@ -0,0 +1,43 @@ +extends Node +class_name ComparisonSort + +signal done +signal mistake + +const ACTIONS = ["swap", "no_swap"] + +var array: ArrayModel +var index = 0 +var timer = Timer.new() +var active = true + +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") + +func check(action): + pass + +func next(): + pass + +func _on_ComparisonSort_mistake(): + active = false + timer.start(1) + +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") diff --git a/models/array_model.gd b/models/array_model.gd new file mode 100644 index 0000000..a27a86d --- /dev/null +++ b/models/array_model.gd @@ -0,0 +1,29 @@ +""" +A plain old one-dimensional random access array. +""" + +extends Reference +class_name ArrayModel + +var array = [] +var size + +func _init(size): + for i in range(1, size + 1): + array.append(i) + array.shuffle() + self.size = size + +func get(i): + return array[i] + +func is_sorted(): + for i in range(size - 1): + if array[i] > array[i + 1]: + return false + return true + +func swap(i, j): + var temp = array[i] + array[i] = array[j] + array[j] = temp diff --git a/project.godot b/project.godot index f1f8414..b66bd5c 100644 --- a/project.godot +++ b/project.godot @@ -8,17 +8,116 @@ config_version=4 -_global_script_classes=[ ] +_global_script_classes=[ { +"base": "Reference", +"class": "ArrayModel", +"language": "GDScript", +"path": "res://models/array_model.gd" +}, { +"base": "HBoxContainer", +"class": "ArrayView", +"language": "GDScript", +"path": "res://views/array_view.gd" +}, { +"base": "ComparisonSort", +"class": "BogoSort", +"language": "GDScript", +"path": "res://levels/bogo_sort.gd" +}, { +"base": "ComparisonSort", +"class": "BubbleSort", +"language": "GDScript", +"path": "res://levels/bubble_sort.gd" +}, { +"base": "Node", +"class": "ComparisonSort", +"language": "GDScript", +"path": "res://levels/comparison_sort.gd" +} ] _global_script_class_icons={ - +"ArrayModel": "", +"ArrayView": "", +"BogoSort": "", +"BubbleSort": "", +"ComparisonSort": "" } [application] config/name="Human Computer Simulator" +run/main_scene="res://scenes/menu.tscn" +run/low_processor_mode=true +boot_splash/image="res://assets/splash.png" +config/icon="res://assets/icon.png" + +[autoload] + +scene="*res://scripts/scene.gd" + +[display] + +window/size/width=1920 +window/size/height=1080 +window/size/fullscreen=true +window/dpi/allow_hidpi=true +window/stretch/mode="2d" + +[editor_plugins] + +enabled=PoolStringArray( ) + +[gui] + +theme/use_hidpi=true + +[input] + +ui_accept={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777222,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) + ] +} +ui_left={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null) + ] +} +ui_right={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null) + ] +} +ui_up={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null) + ] +} +ui_down={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null) + ] +} +swap={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null) + ] +} +no_swap={ +"deadzone": 0.5, +"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null) + ] +} [rendering] quality/driver/driver_name="GLES2" vram_compression/import_etc=true vram_compression/import_etc2=false +environment/default_clear_color=Color( 0, 0, 0, 1 ) diff --git a/scenes/credits.tscn b/scenes/credits.tscn new file mode 100644 index 0000000..68655cf --- /dev/null +++ b/scenes/credits.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://assets/theme.theme" type="Theme" id=1] +[ext_resource path="res://scripts/credits.gd" type="Script" id=2] + +[node name="MarginContainer" type="MarginContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +theme = ExtResource( 1 ) +custom_constants/margin_right = 40 +custom_constants/margin_top = 30 +custom_constants/margin_left = 40 +custom_constants/margin_bottom = 30 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Credits" type="Label" parent="."] +margin_left = 40.0 +margin_top = 30.0 +margin_right = 1880.0 +margin_bottom = 1050.0 +size_flags_vertical = 3 +autowrap = true +script = ExtResource( 2 ) diff --git a/scenes/end.tscn b/scenes/end.tscn new file mode 100644 index 0000000..39e2f70 --- /dev/null +++ b/scenes/end.tscn @@ -0,0 +1,37 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://scripts/end.gd" type="Script" id=1] +[ext_resource path="res://assets/theme.theme" type="Theme" id=2] + +[node name="Viewport" type="MarginContainer"] +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +margin_top = -62.0 +margin_bottom = 62.0 +theme = ExtResource( 2 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="EndScreen" type="VBoxContainer" parent="."] +margin_left = 20.0 +margin_top = 20.0 +margin_right = 1900.0 +margin_bottom = 104.0 +script = ExtResource( 1 ) + +[node name="Score" type="Label" parent="EndScreen"] +margin_right = 1880.0 +margin_bottom = 38.0 +size_flags_vertical = 1 +text = "SCORE" +align = 1 +valign = 2 + +[node name="Button" type="Button" parent="EndScreen"] +margin_top = 46.0 +margin_right = 1880.0 +margin_bottom = 84.0 +text = "restart" +[connection signal="pressed" from="EndScreen/Button" to="EndScreen" method="_on_Button_pressed"] diff --git a/scenes/levels.tscn b/scenes/levels.tscn new file mode 100644 index 0000000..c67e806 --- /dev/null +++ b/scenes/levels.tscn @@ -0,0 +1,94 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://scripts/levels.gd" type="Script" id=1] +[ext_resource path="res://assets/theme.theme" type="Theme" id=2] +[ext_resource path="res://scripts/border.gd" type="Script" id=3] + +[node name="Viewport" type="MarginContainer"] +anchor_top = 0.00114754 +anchor_right = 1.0 +anchor_bottom = 1.00115 +theme = ExtResource( 2 ) +custom_constants/margin_right = 30 +custom_constants/margin_top = 30 +custom_constants/margin_left = 30 +custom_constants/margin_bottom = 30 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="LevelSelect" type="HBoxContainer" parent="."] +margin_left = 30.0 +margin_top = 30.0 +margin_right = 1890.0 +margin_bottom = 1050.0 +script = ExtResource( 1 ) + +[node name="LevelsBorder" type="MarginContainer" parent="LevelSelect"] +margin_right = 480.0 +margin_bottom = 1020.0 +rect_min_size = Vector2( 480, 0 ) +script = ExtResource( 3 ) + +[node name="Levels" type="VBoxContainer" parent="LevelSelect/LevelsBorder"] +margin_left = 20.0 +margin_top = 20.0 +margin_right = 460.0 +margin_bottom = 1000.0 + +[node name="Preview" type="VBoxContainer" parent="LevelSelect"] +margin_left = 488.0 +margin_right = 1860.0 +margin_bottom = 1020.0 +size_flags_horizontal = 3 + +[node name="Display" type="MarginContainer" parent="LevelSelect/Preview"] +margin_right = 1372.0 +margin_bottom = 640.0 +rect_min_size = Vector2( 0, 640 ) +script = ExtResource( 3 ) + +[node name="Placeholder" type="Control" parent="LevelSelect/Preview/Display"] +margin_left = 20.0 +margin_top = 20.0 +margin_right = 1352.0 +margin_bottom = 620.0 + +[node name="InfoBorder" type="MarginContainer" parent="LevelSelect/Preview"] +margin_top = 648.0 +margin_right = 1372.0 +margin_bottom = 1020.0 +size_flags_vertical = 3 +script = ExtResource( 3 ) + +[node name="Info" type="HBoxContainer" parent="LevelSelect/Preview/InfoBorder"] +margin_left = 20.0 +margin_top = 20.0 +margin_right = 1352.0 +margin_bottom = 352.0 +custom_constants/separation = 50 + +[node name="Description" type="Label" parent="LevelSelect/Preview/InfoBorder/Info"] +margin_right = 810.0 +margin_bottom = 332.0 +rect_min_size = Vector2( 810, 0 ) +size_flags_vertical = 3 +text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse luctus lorem felis, in imperdiet mauris faucibus in. Vestibulum interdum mi at arcu congue congue. Cras sodales mauris odio, eget iaculis dolor tempor quis. Suspendisse nec iaculis sapien, eu sollicitudin orci. Nulla volutpat pellentesque ex nec cursus." +autowrap = true + +[node name="Instructions" type="Label" parent="LevelSelect/Preview/InfoBorder/Info"] +margin_left = 860.0 +margin_right = 1332.0 +margin_bottom = 332.0 +rect_min_size = Vector2( 450, 0 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 +text = "INSTRUCTIONS + +LEFT ARROW: CONTINUE +RIGHT ARROW: SWAP" +autowrap = true + +[node name="Timer" type="Timer" parent="."] +autostart = true +[connection signal="timeout" from="Timer" to="LevelSelect" method="_on_Timer_timeout"] diff --git a/scenes/menu.tscn b/scenes/menu.tscn new file mode 100644 index 0000000..55de3e5 --- /dev/null +++ b/scenes/menu.tscn @@ -0,0 +1,75 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://scripts/menu.gd" type="Script" id=1] +[ext_resource path="res://assets/theme.theme" type="Theme" id=2] +[ext_resource path="res://scripts/border.gd" type="Script" id=3] + +[node name="Viewport" type="MarginContainer"] +anchor_left = 0.000711028 +anchor_top = 0.00126399 +anchor_right = 1.00071 +anchor_bottom = 1.00126 +theme = ExtResource( 2 ) +custom_constants/margin_right = 40 +custom_constants/margin_top = 30 +custom_constants/margin_left = 40 +custom_constants/margin_bottom = 30 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="MainMenu" type="VBoxContainer" parent="."] +margin_left = 40.0 +margin_top = 30.0 +margin_right = 1879.0 +margin_bottom = 1049.0 +script = ExtResource( 1 ) + +[node name="Title" type="Label" parent="MainMenu"] +margin_right = 1839.0 +margin_bottom = 38.0 +text = "Human Computer Simulator" +uppercase = true + +[node name="Display" type="MarginContainer" parent="MainMenu"] +margin_top = 46.0 +margin_right = 1839.0 +margin_bottom = 965.0 +size_flags_vertical = 3 +script = ExtResource( 3 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Spacing" type="Control" parent="MainMenu"] +margin_top = 973.0 +margin_right = 1839.0 +margin_bottom = 973.0 + +[node name="Buttons" type="HBoxContainer" parent="MainMenu"] +margin_left = 555.0 +margin_top = 981.0 +margin_right = 1283.0 +margin_bottom = 1019.0 +size_flags_horizontal = 4 +custom_constants/separation = 500 + +[node name="Start" type="Button" parent="MainMenu/Buttons"] +margin_right = 95.0 +margin_bottom = 38.0 +size_flags_horizontal = 4 +text = "start" +flat = true + +[node name="Credits" type="Button" parent="MainMenu/Buttons"] +margin_left = 595.0 +margin_right = 728.0 +margin_bottom = 38.0 +text = "credits" + +[node name="Timer" type="Timer" parent="."] +wait_time = 0.25 +autostart = true +[connection signal="pressed" from="MainMenu/Buttons/Start" to="MainMenu" method="_on_Start_pressed"] +[connection signal="pressed" from="MainMenu/Buttons/Credits" to="MainMenu" method="_on_Credits_pressed"] +[connection signal="timeout" from="Timer" to="MainMenu" method="_on_Timer_timeout"] diff --git a/scenes/play.tscn b/scenes/play.tscn new file mode 100644 index 0000000..681b29e --- /dev/null +++ b/scenes/play.tscn @@ -0,0 +1,69 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://scripts/play.gd" type="Script" id=1] +[ext_resource path="res://assets/theme.theme" type="Theme" id=2] +[ext_resource path="res://scripts/border.gd" type="Script" id=3] + +[node name="Viewport" type="MarginContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +theme = ExtResource( 2 ) +custom_constants/margin_right = 30 +custom_constants/margin_top = 30 +custom_constants/margin_left = 30 +custom_constants/margin_bottom = 30 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="GameDisplay" type="VBoxContainer" parent="."] +margin_left = 30.0 +margin_top = 30.0 +margin_right = 1890.0 +margin_bottom = 1050.0 +script = ExtResource( 1 ) + +[node name="HUDBorder" type="MarginContainer" parent="GameDisplay"] +margin_right = 1860.0 +margin_bottom = 78.0 +script = ExtResource( 3 ) + +[node name="HUD" type="HBoxContainer" parent="GameDisplay/HUDBorder"] +margin_left = 20.0 +margin_top = 20.0 +margin_right = 1840.0 +margin_bottom = 58.0 + +[node name="Level" type="Label" parent="GameDisplay/HUDBorder/HUD"] +margin_right = 906.0 +margin_bottom = 38.0 +size_flags_horizontal = 3 +text = "LEVEL" + +[node name="Score" type="Label" parent="GameDisplay/HUDBorder/HUD"] +margin_left = 914.0 +margin_right = 1820.0 +margin_bottom = 38.0 +size_flags_horizontal = 3 +text = "0.000" +align = 2 + +[node name="DisplayBorder" type="MarginContainer" parent="GameDisplay"] +margin_top = 86.0 +margin_right = 1860.0 +margin_bottom = 1020.0 +size_flags_vertical = 3 +script = ExtResource( 3 ) + +[node name="Label" type="Label" parent="GameDisplay/DisplayBorder"] +margin_left = 20.0 +margin_top = 448.0 +margin_right = 1840.0 +margin_bottom = 486.0 +text = "ready..." +align = 1 + +[node name="Timer" type="Timer" parent="."] +one_shot = true +autostart = true +[connection signal="timeout" from="Timer" to="GameDisplay" method="_on_Timer_timeout"] diff --git a/scripts/border.gd b/scripts/border.gd new file mode 100644 index 0000000..1a75c0d --- /dev/null +++ b/scripts/border.gd @@ -0,0 +1,32 @@ +extends MarginContainer + +const GREEN = Color(0.2, 1, 0.2) +const RED = Color(1, 0, 0) +const WIDTH = 5 +var color = GREEN +var timer = Timer.new() +const FLASHES = 3 +var color_changes = 0 + +func _ready(): + # Input should be reenabled right after the last red flash + timer.wait_time = 1.0 / (FLASHES * 2 - 1) + timer.connect("timeout", self, "_on_Timer_timeout") + add_child(timer) + +func flash(): + _on_Timer_timeout() + timer.start() + +func _on_Timer_timeout(): + if color_changes == FLASHES * 2 - 1: + timer.stop() + color_changes = 0 + color = GREEN + else: + color = RED if color_changes % 2 == 0 else GREEN + color_changes += 1 + update() + +func _draw(): + draw_rect(Rect2(Vector2(), rect_size), color, false, WIDTH) diff --git a/scripts/credits.gd b/scripts/credits.gd new file mode 100644 index 0000000..e47684e --- /dev/null +++ b/scripts/credits.gd @@ -0,0 +1,13 @@ +extends Label + +func _ready(): + var file = File.new() + file.open("res://CREDITS.md", File.READ) + text = file.get_as_text() + file.close() + +func _process(delta): + rect_position.y -= 1 + +func _input(event): + scene.change_scene("res://scenes/menu.tscn") diff --git a/scripts/end.gd b/scripts/end.gd new file mode 100644 index 0000000..1d14369 --- /dev/null +++ b/scripts/end.gd @@ -0,0 +1,8 @@ +extends VBoxContainer + +func _ready(): + $Score.text = str(scene.get_param("score")) + $Button.grab_focus() + +func _on_Button_pressed(): + scene.change_scene("res://scenes/levels.tscn") diff --git a/scripts/levels.gd b/scripts/levels.gd new file mode 100644 index 0000000..834aa83 --- /dev/null +++ b/scripts/levels.gd @@ -0,0 +1,44 @@ +extends HBoxContainer + +var levels: Dictionary +var level + +func _ready(): + # Load level data + var descriptions = File.new() + descriptions.open("res://assets/levels.json", File.READ) + levels = parse_json(descriptions.get_as_text()) + # Dynamically add buttons + for level in levels: + var button = Button.new() + button.text = level + button.align = Button.ALIGN_LEFT + button.connect("focus_entered", self, "_on_Button_focus_changed") + button.connect("pressed", self, "_on_Button_pressed", [level]) + $LevelsBorder/Levels.add_child(button) + # Automatically focus on first button + $LevelsBorder/Levels.get_child(0).grab_focus() + +func _on_Button_focus_changed(): + var name = get_focus_owner().text + $Preview/InfoBorder/Info/Description.text = levels[name]["about"] + level = get_level(name).new(ArrayModel.new(10)) + level.active = false + # Start over when simulation is finished + level.connect("done", self, "_on_Button_focus_changed") + # Replace old display with new + for child in $Preview/Display.get_children(): + child.queue_free() + $Preview/Display.add_child(ArrayView.new(level)) + +func _on_Button_pressed(level): + scene.change_scene("res://scenes/play.tscn", + {"name": level, "level": get_level(level)}) + +func get_level(level): + match level: + "BUBBLE SORT": + return BubbleSort + +func _on_Timer_timeout(): + level.next() diff --git a/scripts/menu.gd b/scripts/menu.gd new file mode 100644 index 0000000..fa303ec --- /dev/null +++ b/scripts/menu.gd @@ -0,0 +1,17 @@ +extends VBoxContainer + +var level = BogoSort.new(ArrayModel.new(10)) + +func _ready(): + $Buttons/Start.grab_focus() + $Display.add_child(ArrayView.new(level)) + randomize() + +func _on_Start_pressed(): + scene.change_scene("res://scenes/levels.tscn") + +func _on_Credits_pressed(): + scene.change_scene("res://scenes/credits.tscn") + +func _on_Timer_timeout(): + level.next() diff --git a/scripts/play.gd b/scripts/play.gd new file mode 100644 index 0000000..295745e --- /dev/null +++ b/scripts/play.gd @@ -0,0 +1,26 @@ +extends VBoxContainer + +var start_time = -1 + +func _ready(): + $HUDBorder/HUD/Level.text = scene.get_param("name") + +func _process(delta): + if start_time >= 0: + $HUDBorder/HUD/Score.text = "%.3f" % get_score() + +func _on_Timer_timeout(): + start_time = OS.get_ticks_msec() + # Delete ready text + $DisplayBorder/Label.queue_free() + # Load level + var array = ArrayModel.new(10) + var level = scene.get_param("level").new(array) + level.connect("done", self, "_on_Level_done") + $DisplayBorder.add_child(ArrayView.new(level)) + +func get_score(): + return stepify((OS.get_ticks_msec() - start_time) / 1000.0, 0.001) + +func _on_Level_done(): + scene.change_scene("res://scenes/end.tscn", {"score": get_score()}) diff --git a/scripts/scene.gd b/scripts/scene.gd new file mode 100644 index 0000000..8b07d60 --- /dev/null +++ b/scripts/scene.gd @@ -0,0 +1,12 @@ +extends Node + +var _params = null + +func change_scene(next_scene, params=null): + _params = params + get_tree().change_scene(next_scene) + +func get_param(name): + if _params != null and _params.has(name): + return _params[name] + return null diff --git a/views/array_view.gd b/views/array_view.gd new file mode 100644 index 0000000..4bb5707 --- /dev/null +++ b/views/array_view.gd @@ -0,0 +1,29 @@ +extends HBoxContainer +class_name ArrayView + +const GREEN = Color(0.2, 1, 0.2) +const ORANGE = Color(1, 0.69, 0) + +var level +var rects = [] + +func _init(level): + level.connect("mistake", self, "_on_Level_mistake") + add_child(level) + self.level = level + for i in range(level.array.size): + var rect = ColorRect.new() + rect.size_flags_horizontal = Control.SIZE_EXPAND_FILL + rect.size_flags_vertical = Control.SIZE_SHRINK_END + rects.append(rect) + add_child(rect) + +func _process(delta): + for i in range(level.array.size): + rects[i].rect_scale.y = -1 # Override parent Control scale + rects[i].color = ORANGE if level.emphasized(i) else GREEN + var frac = float(level.array.get(i)) / level.array.size + rects[i].rect_size.y = rect_size.y * frac + +func _on_Level_mistake(): + get_parent().flash()