This project uses semantic versioning as much as we can
Visit github issues (https://github.com/sFractal-Podii/quizquadaminos/issues) and ensure that the issues you want to create does not exist before creating the issue.
To create a pull request :
- Fork the project and clone it locally.https://github.com/sFractal-Podii/quizquadaminos.
- Create your branch for your changes. Naming of the branch should be according to the issue you are working on.
- Make the changes and create the pull request. Compare your branch on the fork against the develop branch of the original repo.
- Constantly visit your pull request to see the comments left and work on the comments.
- Issues are merged once they are approved
- For clean history of commits, rebase if the PR has more than one commit
We use github issues to track issues , checkout the project board for issues
All issues have priorities with A
being the highest priority
If you would like to contribute code to the project, here is the workflow you need to be aware of:
- We maintain linear git history by rebasing against the develop branch
- We squash all commits for each pull request
If you are working from the main repository then you need to follow the following steps
- fetch the latest changes from the origin using
git fetch origin
- create a branch based off the develop branch
git checkout -b <your-branch>
- work on the branch and create a pull request, commit as much as you wish at this stage
- once your work is ready to be merged we need to rebase it against the development branch, we can squash commits at the same time
git fetch origin
git rebase orign/develop -i
To squash the commits replace the word pick
with the letter s
on the following commits. Watch this video to see how to do it
see deployment for deployment options and how to deploy
Quadblockquiz includes both playing quadblocks and answering questions to gain points and powerups.
We currently have two folders for adding questions, it can be on the qna
directory or the courses
both on the project root directory.
Contest questions are currently on the qna
directory while courses
contains questions for a classroom setup type of questions
To add a question, you will need to add a markdown file which should meet the following conditions
- It needs to define the question type
This is defined in the 'header' of the file. The two questions types currently allowed are free-form
and multi-choice
(take note of the -
).
The header is then followed by three dashes
%{
type: "free-form"
}
---
- Needs to clearly define the question
The main question is marked by the first level markdown header (using one #
) followed by the word question
## Software Bill of Materials
A “Software Bill of Materials” (SBOM) is
effectively a nested inventory,
a list of ingredients that make up
# Question:
What does SBOM stand for?
Anything before the answers
header is considered part of the question
- The answers need to be clearly defined
Answers are marked by second level markdown header (that is two #
) with the word answers
For multichoice answers, we use the markdown list (-
) to show the options. The position of the correct answer is counted from 0 being the first option (this information will be used later when generating/updating answers)
```markdown
## Answers
- Security Bungles Obfuscate Mission
- Software Bill of Materials
- Special Bureau of Meteorology
- Security Bill of Materials
```
- Scores should be provided
We need to provide the score for both right score and wrong score
```markdown
## Score
- Right:25
- Wrong:5
```
- Power up for the question
This determines what powerup the user gets whenever they get the correct answer. Valid power ups are - deleteblock - addblock - moveblock - clearblocks - speedup - slowdown - fixvuln - fixlicense - rm_all_vulns - rm_all_lic_issues - superpower
## Powerup
DeleteBlock
There is a convenience tasks that generates default answers (0 for multichoice and "secret" for free-form questions)
$ mix gen.answers
Compiling 67 files (.ex)
Generated quadblockquiz app
12:57:05.448 [info] Generating answers for qna..
12:57:05.483 [info] Answers written to qna/answers.json
12:57:05.483 [info] Generating answers for courses..
12:57:05.493 [info] Answers written to courses/answers.json
If you run this command without any arguments, it will generate answers for both qna
and courses
directories.
The generated answers do not override any previous answer if there was any existing answers
To generate answers for a single directory only, pass in the directory name as an argument to the command
$ mix gen.answers qna # genratates only for the qna folder
12:57:05.448 [info] Generating answers for qna..
12:57:05.483 [info] Answers written to qna/answers.json
$ mix gen.answers courses # genratates only for the courses folder
12:57:05.448 [info] Generating answers for courses..
12:57:05.483 [info] Answers written to courses/answers.json
You can also run make lint-questions
which generates answers for both qna
and courses
and also checks if the questions are correctly parsed.
$ make lint-questions
mix gen.answers
18:12:42.974 [info] Generating answers for qna..
18:12:42.988 [info] Answers written to qna/answers.json
18:12:42.988 [info] Generating answers for courses..
18:12:42.993 [info] Answers written to courses/answers.json
mix validate.questions
18:12:43.589 [info] Validating questions....
18:12:43.749 [info] All Files are valid!
Once the default answers have been generated, we can open the answers.json
file and provide the correct answers. Note that the answers you provide will not be overridden by the next mix gen.answers
For development purposes, the first multichoice answer is always the correct answer, the word "secret" is always the answer to a free-form
question
If you add a new question to either qna
or courses
directory then running mix gen.answers
will add the default answer to only the new files you have added (assuming you had generated the answers before adding those two files)
This project includes a couple of convenience make
tasks. To get the full list
of the tasks run the command make targets
to see a list of current tasks. For example
Targets
---------------------------------------------------------------
compile compile the project
deploy-existing-image creates an instance using existing gcp docker image
docker-image builds docker image
format Run formatting tools on the code
lint-compile check for warnings in functions used in the project
lint-format Check if the project is well formated using elixir formatter
lint-questions Check if the questions added are correctly parsed
lint-unused Check if there is unused functions
lint Check if the project follows set conventions such as formatting
push-and-serve-gcp creates docker image then push to gcp and launches an instance with the image
push-image-gcp push image to gcp
release Build a release of the application with MIX_ENV=prod
test Run the test suite
update-instance updates image of a running instance
Currently the project is being redesigned using tailwindcss framework which enables us to build complex responsive layouts.
At the moment the project is using both phoenixcss
and tailwindcss
framework. This was done, so that we couldn't affect the agile methodology adapted earlier and also not to affect the already existing pages layouts that are using phoenixcss framework.
-
Use Prototype design layouts of the pages that are drawn on figma. Mobile design layout are available under
Android google pixel
and design layout for medium to large screens are available underWeb page
. -
Under
router.ex
add the path of the page you are redesigning inside the scope that is piped through tailwind_layout i.escope "/", quadblockquizWeb do pipe_through [:browser, :tailwind_layout] get "/how-to-play", PageController, :how_to_play live "/contest_rules", ContestRules live "/leaderboard", LeaderboardLive end
This section is just some notes on how the software works.
One simple way the game ends is because the user clicks the 'End Game' button when the game is paused (after hitting space bar while playing). How this occurs is as follows:
- the user is on the Quiz Modal and the 'End Game' button is displayed
- the user clicks the 'End Game' button which invokes the 'endgame' event
- which calls end_game
- the socket state is set to :game_over (as opposed to :playing)
- and the game over screen is rendered
One way the game ends is when the stack of blocks reaches the top of blockyard. How this occurs is as follows:
- the clock tick causes the falling block to move down one row. TetrisLive.ontick(:playing)
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz_web/live/tetris_live.ex#L1045 which calls TetrisLive.drop
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz_web/live/tetris_live.ex#394 which calls Tetris.drop
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz/Tetris.ex#L10 which calls Tetris.maybe_do_drop based on Bottom.collides?
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz/bottom.ex#L10
- the falling block 'collides' with existing blocks in the brickyard. The falling block is incorporated into the brickyard and a new block is created.
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz/Tetris.ex#L23. If the new block also collides (ie no room left), the game is over.
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz/Tetris.ex#L42 so Tetris.maybe_do_drop responds with game_over:true which Tetris.drop responds to TetrisLive.drop which puts it in response.game_over which TetrisLive.drop uses to set socket state to :game_over
- see https://github.com/sFractal-Podii/quizquadaminos/blob/develop/lib/quadblockquiz_web/live/tetris_live.ex#L433
- the socket state is set to :game_over (as opposed to :playing)
- on next tick the scores are broadcast
- and on that same tick, the game is recorded in the db
- and the game over screen is rendered
Another way the game ends is when time runs out. How this occurs is as follows:
- the max length of game time is set in TetrisLive at 15 min
- Remaining time is calculated once per sec and if remaining time is zero, endgame is called