This project is a prototype for a serious game called "Lost in Code", developed for the purpose of teaching a programming language to beginners using said game as the teaching method. The project itself contains multiple parts.
- A playable game where you're an Astronaut having to repair a spaceship by solving Programming Tasks.
- An Administrative application for managing users and programming tasks for the game.
- An API that is used to control the data flow between Admin-App, Game and Database
- A php server for executing code snippets from users created when solving programming Tasks
To run this project you need to have docker installed on your system
- Clone the project onto your system
- Duplicate the env.db.template file, rename it to
.env.dband fill in your database information in the fields marked with the< >brackets (make sure to remove the brackets) - run the
docker compose buildcommand in the root directory of the project to build and the application (more information on the docker compose build command)
- run the
docker compose upcommand in the root directory of the project to run the application (more information on the docker compose up command) - The application will now be accessible via
localhostor the ip address of your system
To use the game, visit the game endpoint mentioned under endpoints and login with an account (you can use the default admin account)
To use the admin application, visit the admin endpoint mentioned under
endpoints and login with an account with the ADMIN
status. Here you can manage the users, user groups and curricula.
The different endpoints for the systems are:
| Endpoint | System |
|---|---|
/ |
the Admin-App |
/game |
the game |
/api |
the api |
/php |
the php server |
(All endpoints have to be prefixed with either localhost or your systems IP
address)
| Username | admin |
admin@admin |
|
| Password | test |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
GET |
/api |
None | Returns a Hello World response to check if server is live |
GET |
/api/me |
None | Checks and responds if the user is logged in |
POST |
/api/php |
None | Proxies the request to the PHP server |
| Method | Endpoint | Role Requirements | Description |
|---|---|---|---|
POST |
/api/users |
Admin | Create user |
GET |
/api/users |
Admin | Get all users |
GET |
/api/users/me/curriculum_data |
None | Get specific user data (for current user) |
GET |
/api/users/:id/curriculum_data |
User (only himself), Admin | Get specific user's curriculum data |
GET |
/api/users/:id |
User (only himself), Admin | Get specific user |
PUT |
/api/users/:id |
User (only himself), Admin | Update specific user |
DELETE |
/api/users/:id |
Admin | Delete specific user |
POST |
/api/users/:userId/groups/:groupId |
Admin | Move user to specific group |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/groups/ |
Admin | Create new group |
GET |
/api/groups/ |
Admin | Get all groups |
GET |
/api/groups/:id |
Admin | Get a specific group |
GET |
/api/groups/:id/full |
Admin | Get a FULL specific group |
PUT |
/api/groups/:id |
Admin | Update a specific group |
DELETE |
/api/groups/:id |
Admin | Delete a specific group |
POST |
/api/groups/:curriculumId/groups/:groupId |
Admin | Set curriculum for specific group |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/curriculums/ |
Admin | Create new curriculum |
POST |
/api/curriculums/full |
Admin | Create new FULL curriculum |
GET |
/api/curriculums/ |
None | Get all curriculums |
GET |
/api/curriculums/:id |
None | Get specific curriculum via ID |
GET |
/api/curriculums/:id/full |
None | Get specific FULL curriculum via ID |
PUT |
/api/curriculums/:id |
Admin | Update specific curriculum |
DELETE |
/api/curriculums/:id |
Admin | Delete specific curriculum |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/chapters/ |
Admin | Create new chapter |
POST |
/api/chapters/full |
Admin | Create new FULL chapter with questions |
GET |
/api/chapters/ |
None | Get all chapters |
GET |
/api/chapters/me |
None | Get all chapters from user's curriculum |
GET |
/api/chapters/:id |
None | Get specific chapter |
GET |
/api/chapters/:id/full |
None | Get specific FULL chapter and all questions |
PUT |
/api/chapters/:id |
Admin | Update specific chapter |
DELETE |
/api/chapters/:id |
Admin | Delete specific chapter |
POST |
/api/chapters/:chapterId/curriculums/:curriculumId |
Admin | Change associated curriculum for chapter |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/questions/ |
Admin | Create new question |
POST |
/api/questions/full |
Admin | Create new FULL question |
GET |
/api/questions/ |
None | Get all questions |
GET |
/api/questions/:id |
None | Get a specific question |
GET |
/api/questions/:id/full |
None | Get a FULL specific question |
PUT |
/api/questions/:id |
Admin | Update a question |
DELETE |
/api/questions/:id |
Admin | Delete a specific question |
POST |
/api/questions/:questionId/chapters/:chapterId |
Admin | Change the chapter associated with a question |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/gamestates/ |
None | Create a new game state |
GET |
/api/gamestates/me |
None | Get game state for the current user |
GET |
/api/gamestates/:userId |
None | Get game state for a specific user by their ID |
PUT |
/api/gamestates/me |
None | Update game state for the current user |
PUT |
/api/gamestates/:userId |
None | Update game state for a specific user by ID |
DELETE |
/api/gamestates/:userId |
None | Delete game state for a specific user by ID |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/question_elements/ |
Admin | Create a new question element |
GET |
/api/question_elements/ |
None | Retrieve all question elements |
GET |
/api/question_elements/:id |
None | Retrieve a specific question element by its ID |
PUT |
/api/question_elements/:id |
Admin | Update a specific question element by its ID |
DELETE |
/api/question_elements/:id |
Admin | Delete a specific question element by its ID |
POST |
/api/question_elements/:questionElementId/question/:questionId |
Admin | Change question ID for a specific question element |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/correct_answers/ |
Admin | Create a new correct answer |
GET |
/api/correct_answers/ |
None | Retrieve all correct answers |
GET |
/api/correct_answers/:id |
None | Retrieve a specific correct answer by its ID |
PUT |
/api/correct_answers/:id |
Admin | Update a specific correct answer by its ID |
DELETE |
/api/correct_answers/:id |
Admin | Delete a specific correct answer by its ID |
POST |
/api/correct_answers/:correctAnswerId/question_element/:questionElementId |
Admin | Change the question_element_id for a specific correct answer |
| Method | Endpoint | Request Body | Description |
|---|---|---|---|
POST |
/api/login |
{ email, password } |
Authenticates the user and logs them in. If successful, returns user data and sets an authentication token in a HttpOnly cookie. |
| Method | Endpoint | Requirements | Description |
|---|---|---|---|
POST |
/api/logout/ |
None | Logout the user |
For using the database or uploading a new curriculum to the admin application, the following JSON-structures are used.
{
"name": "Name of the curriculum (Bsp. PHP)",
"description": "Description of the curriculum",
"prog_lang": "php",
"chapters": [...] //The Chapters inside this curriculum
}
{
"name": "PHP für einsteiger",
"description": "Ein curriculum zum vermitteln von PHP basics",
"prog_lang": "php",
"chapters": [
{
"name": "Einführung",
"material": "Was ist php? PHP ist eine scripting sprache welche ....",
"order_position": 1,
"questions": [
{
"question_text": "Was ist php?",
"type": "CHOICE",
"difficulty": 1,
"elements": [
{
"content": "Ein Computer",
"is_correct": false
},
{
"content": "Eine Scripting sprache",
"is_correct": true
},
{
"content": "Etwas zum essen",
"is_correct": false
}
]
},
{
"question_text": "Was ist der output dieses code abschnitts?",
"code_text": "<?php echo 'Hello There'; ?>",
"hint": "schau hinter dem echo",
"type": "SINGLE_INPUT",
"difficulty": 1,
"elements": [
{
"element_identifier": "i1",
"correct_answers": ["Hello There", "hello there"]
}
]
},
{
"question_text": "Vervollständige den Code sodass 5 als ausgabe rauskommt",
"code_text": "<?php \n$x = 3; \n$y = 2; \necho '###INPUT|i1|20|true###'; \n?>",
"type": "CLOZE",
"difficulty": 3,
"elements": [
{
"element_identifier": "i1",
"correct_answers": ["$x+$y"]
}
]
},
{
"question_text": "Bringe die code abschnitte in die richtige reihenfolge",
"type": "DRAG_DROP",
"difficulty": 3,
"elements": [
{
"content": "<?php \n$x = 3;",
"correct_order_position": 1
},
{
"content": "$y = 2; \necho '$x';",
"correct_order_position": 2
},
{
"content": "?>",
"correct_order_position": 3
}
]
},
{
"question_text": "Welche dieser code blöcke ist eine function?",
"type": "SELECT_ONE",
"difficulty": 2,
"elements": [
{
"content": "<?php \n$x = 3; \n$y = 2; \necho '$x+$y'; \n?>",
"is_correct": false
},
{
"content": "<?php \nfunction functionName() { \n$y = 2; \necho '$y+5'; \n} \n?>",
"is_correct": true
}
]
},
{
"question_text": "Complete this function so that it adds 2 numbers together!",
"code_text": "function calculateSum($num1, $num2){\n ###INPUT|i1|50|true### \n}",
"hint": null,
"type": "CREATE",
"difficulty": 5,
"elements": [
{
"content": "echo calculateSum(5,10);",
"element_identifier": "i1",
"correct_order_position": null,
"correct_answers": ["calculateSum(5,10) == 15", "calculateSum(20,20) == 40"],
"is_correct": null
}
]
}
]
}
]
}{
"name": "Chapter Name (Syntax)",
"material": "The Learning-material like explanations etc. goes here",
"order_position": 1, //Position of the chapter inside the curriculum
"questions": [...] //The contained questions inside this Chapter
}{
"question_text": "The Question the user sees at the top of the screen",
"code_text": "<?php echo 'Hello There'; ?>", //Here code can be entered which will be displayed to the user in some question types
"hint": "A hint that could theoretically be displayed for the user", //currently not used
"type": "CHOICE / SINGLE_INPUT / CLOZE / DRAG_DROP / SELECT_ONE / CREATE", //One of the these types have to be used
"difficulty": 1, //Subjective difficulty level defined by question creator
"elements": [] //Question elements needed for the different question types
}{
"question_text": "Was ist php?",
"type": "CHOICE",
"difficulty": 1,
"elements": [
{
"content": "Ein Computer",
"is_correct": false
},
{
"content": "Eine Scripting sprache",
"is_correct": true
},
{
"content": "Etwas zum essen",
"is_correct": false
}
]
}Here an input field is displayed underneath a code block
{
"question_text": "Was ist der output dieses code abschnitts?",
"code_text": "<?php echo 'Hello There'; ?>",
"hint": "schau hinter dem echo",
"type": "SINGLE_INPUT",
"difficulty": 1,
"elements": [
{
"element_identifier": "i1",
"correct_answers": ["Hello There", "hello there"]
}
]
}Here input fields are displayed inside the code block to fill in the blanks.
{
"question_text": "Vervollständige den Code sodass 5 als ausgabe rauskommt",
"code_text": "<?php \n$x = 3; \n$y = 2; \necho '###INPUT|i1|20|true###'; \n?>",
"type": "CLOZE",
"difficulty": 3,
"elements": [
{
"element_identifier": "i1",
"correct_answers": ["$x+$y"]
}
]
}The following piece of code
###INPUT|i1|20|true###
describes an input field with the corresponding element id i1 and a maximum of 20 characters as input
This question displays code blocks that have to be reordered into the correct order
{
"question_text": "Bringe die code abschnitte in die richtige reihenfolge",
"type": "DRAG_DROP",
"difficulty": 3,
"elements": [
{
"content": "<?php \n$x = 3;",
"correct_order_position": 1
},
{
"content": "$y = 2; \necho '$x';",
"correct_order_position": 2
},
{
"content": "?>",
"correct_order_position": 3
}
]
}In this question the correct code block / code blocks have to be selected to answer the question.
{
"question_text": "Welche dieser code blöcke ist eine function?",
"type": "SELECT_ONE",
"difficulty": 2,
"elements": [
{
"content": "<?php \n$x = 3; \n$y = 2; \necho '$x+$y'; \n?>",
"is_correct": false
},
{
"content": "<?php \nfunction functionName() { \n$y = 2; \necho '$y+5'; \n} \n?>",
"is_correct": true
}
]
}Here a user has to write code that will be evaluated on a server to answer the question
{
"question_text": "Complete this function so that it adds 2 numbers together!",
"code_text": "function calculateSum($num1, $num2){\n ###INPUT|i1|50|true### \n}", //The code the user will see and the defined input field for the user
"hint": null,
"type": "CREATE",
"difficulty": 5,
"elements": [
{
"content": "echo calculateSum(5,10);", //The code that will be executed when the user wants to evaluate his current code without committing his answer
"element_identifier": "i1",
"correct_order_position": null,
"correct_answers": ["calculateSum(5,10) == 15", "calculateSum(20,20) == 40"], //The tests that have to be passed by the written code to complete the question
"is_correct": null
}
]
}The question elements that are used inside the question, such as:
- multiple / single choice buttons
- Drag and Drop Code blocks
- Selectable code blocks
- input fields
See question type examples for examples of how to use this element
{
"content": "The text that will be displayed on a multiple choice button / the code that will be shown inside a drag and drop code element",
"element_identifier": "can be anything but must be unique to other identifiers in the same question (is used to distinguish html input elements e.g. input1 or i1 etc )",
"correct_order_position": 1, //For drag and drop questions this is the correct position of the code blocks
"correct_answers": ["answer", "options", "for a text field"], //For text inputs
"is_correct": true //For single or multiple choice questions, this marks which element is correct
}