Skip to content

Commit

Permalink
Readme, Release version 1
Browse files Browse the repository at this point in the history
  • Loading branch information
vonunwerth committed Jul 21, 2020
1 parent a9bc3f2 commit c1f1e71
Show file tree
Hide file tree
Showing 16 changed files with 223 additions and 63 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
*.db
*.ttf
./out/*.png
*.ttf
65 changes: 61 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,67 @@
# TrivialPursuitFramework

# Create your own cards
This framework creates question cards for the game Trivial Pursuit.
Just create a questions.txt, think about six categories and start the `trivial_pursuit_creator.py` script.
Just create a `questions.txt` file and insert it to the questions folder, think about six categories, put them in a `categories.txt` file in the same folder and start the `trivial_pursuit_creator.py` script.

`python trivial_pursuit_creator.py`

The script will lead you through the single steps (split up in the python scripts which can be found here) and helps to generate your printable question cards. The single scripts can also be run individually as described below in each section for the script.

Please read the following sections of the *Create your own cards* chapter to make sure, your `questions.txt` and `categories.txt` are well-formed and you understand, what is happening. Please write an issues, if you have any problems.
![Question card front](readme_res/card.png)
*Example cards for a Star Trek Trivial Pursuit*
*Example cards for a German Star Trek Trivial Pursuit*

If you execute the script, it will create the `out` folder which contains all question cards `frontX.png` (with six questions each) and their associated answer cards `backX.png`. It also creates printable card sheets in the `prints` folder, where the cards are organized in a way, that you can just print those sheets, cut a card, fold it up as shown below and glue it together, so that your are finished super fast!
This repository already contains both folders with a blank example card and sheet, so that you can better see what the script can do for you.
![Question card front](readme_res/print1.png)

## questions.txt
Your `questions.txt` file must be look as follows:

**Q: What is the name of the first president of the United States of America?**
**A: George Washington**
**C: H**

**Q: What is the longest river on earth called?**
**A: Nile**
**C: G**

...

Every questions needs a question after a **Q:**, an answer at **A:** and one letter for the category **C:**

An example can be found in the `questions.txt` provided in this repository.

## categories.txt
The `categories.txt` file is much more simple. Just put six categories in there like that:
**H: History**
**G: Geopraphy**
**P: Politics**
**A: Actors**
**I: Inventions**
**M: Mathematics**


### Prepare your question cards
You have to prepare your cards with your *categories* and your *edition*, before filling them with questions. You can do this by changing `assets/back.png` and `assets/front.png` with an image editing program of your choice. Simply add the letters for your categories in the coloured ellipses. Use the same order as in the `categories.txt` file. Then add an edition name using the **Balmorall** font. You could for example find it [here](https://www.dafontfree.net/freefonts-balmoral-icg-f114221.htm)

A question card consists of 6 questions. A questions has an answer and is assigned to a category defined in the `categories.txt` file. Each answer could contain a citation, especially useful for film quizes, which is always written in brackets ().

# Running scripts individually
In this chapter, the single steps of the `trivial_pursuit_creator.py` script will be explained.

##### questions_to_database.py
It starts with executing the `questions_to_database.py` script. This script reads the `questions.txt`, creates the SQLite database `python_sqlite.db` and write all questions, answers and their categories to the database. A good first introduction to python and SQLite can be found [here](https://www.sqlitetutorial.net/sqlite-python/sqlite-python-select/). If a questions i already in the database (questions is unique), this question will be skipped and a warning appears. Make sure, your `questions.txt` file is well-formed before starting this process. You can continue by pressing **ENTER**

##### database_tools.py
Now the script check if there are any issues with questions, answer or the categories. The questions for example could be too long for a card, also the answers. The script takes this lengths from the `constants.py` file. It also checks things like, is there a question mark or a ... (for famous phrases which should be completed) at the end of a question or are the categories valid categories from the `categories.txt` file. The script will then print warnings and errors and will guide you to the next step.

##### database_statistics.py
If there are no critical errors, it continues running the `database_statistics.py`. This script prints the number of available questions of each category and calculates how many cards can be created. Keep in mind, that each card contains six questions of the six different categories.

##### create_cards.py
Now the card creation process can be started. The script takes questions from the database (one for each category) and draw them on a card. (They are taken shuffled, to switch of this shuffling, change the shuffle variable in `constants.py` to *False*) If there are no more questions of one category, the script stops and print statistics on what it has done. Check the ignored questions count to understand what category is missing some new questions. You should now see `frontX.png` and `backX.png` files in the `out` folder. In your answers you can provide a citation, which is just done by adding somethin in brackets () at the end of your answer. You can also write a special hint or continuing information in this brackets. Those citations or information will be written to an extra line for the answer. So you can force a new line, if you want to give further information. In the example questions, you can see this at the *Nile (6650km, Wikipedia)* answer.

### Creating question cards
A question card consists of 6 questions. A questions has an answer and is assigned to a category defined in the categories.txt file. Each answer could contain a citation, which is always written in brackets ().
##### print_cards.py
Now all cards from the `out` folder are taken and put on a white piece of paper. Those can now be printed, cut and glued together and voila you have your own question card.
Binary file modified assets/back.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 7 additions & 5 deletions constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
CITATIONS = True
CITATIONS = True # Do you use citations in () brackets behind your answers?
SHUFFLE = True # Allow shuffling of all questions when creating the cards

SHUFFLE = True # Allow shuffling of all questions # TODO save which questions were used to continue later
QUESTION_MAX_LINE_LENGTH = 63 # Maximum length of a question on a card for not beeing to long
ANSWER_MAX_LINE_LENGTH = 32 # Maximum length for an answer
NEXT_QUESTION_Y_SKIP = 103 # Pixel skip between two questions and answers

QUESTION_MAX_LINE_LENGTH = 46
ANSWER_MAX_LINE_LENGTH = 32
NEXT_QUESTION_Y_SKIP = 103
CARDS_ON_SHEET_X = 2 # Not in use!
CARDS_ON_SHEET_Y = 3 # Not in use yet!
39 changes: 21 additions & 18 deletions create_cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def create_connection(db_file):
print(e)


def split_lines(text, line_length): # TODO also split at "-"
def split_lines(text, line_length):
"""
Split a text in mutliple lines at " " and returns the lines as list
:param text: Text to be split
Expand All @@ -33,10 +33,12 @@ def split_lines(text, line_length): # TODO also split at "-"
line_ends = [0] # All line endings, first line has to start at index 0
spaces = [m.start() for m in re.finditer(' ', text.strip(" "))] # Find all spaces an write indexes to a list
spaces.append(len(
text)) # Append end of text to allow last line go to end of the last word, not the one before the last (the last space)
text)) # Append end of text to allow last line go to end of the last word, not the one before the last (the
# last space)
for i in range(1, int(len(text) / line_length) + 2): # For how much lines there have to be
line_ends.append(max([s for s in spaces if
s <= i * line_length]) + 1) # Last space which index is less than QUESTION_MAX_LINE_LENGTH
s <= i * line_length]) + 1) # Last space which index is less than
# QUESTION_MAX_LINE_LENGTH
lines.append(text[line_ends[i - 1]:line_ends[
i]]) # Append line starting at last line_ends entry and going to line_ends[i]
return lines
Expand All @@ -53,8 +55,8 @@ def get_questions_of_categoriy(conn, category):
cur.execute("SELECT * FROM qac WHERE category=?", category)
question_rows = cur.fetchall()
if SHUFFLE:
shuffle(question_rows) # Shuffle questions, so that there is no order recognizable, otherwise ordered by id
return question_rows # could also use dict_factory, but indexes are ok here
shuffle(question_rows) # Shuffle questions, so that there is no order recognizable, otherwise ordered by id
return question_rows # could also use dict_factory, but indexes are ok here


def create_cards():
Expand All @@ -70,26 +72,26 @@ def create_cards():
fnt = ImageFont.truetype("arial.ttf", 25)
with conn: # Keep connection open, as long as necessary
categories, categories_long = get_categories_from_file() # get categories and long name
# TODO Write categories on blank front, back in the program in own method
card_count = 1
while True:
vorne = Image.open("assets/front_cat.png") # Load assets
hinten = Image.open("assets/back_cat.png")
dv = ImageDraw.Draw(vorne)
dh = ImageDraw.Draw(hinten)
front = Image.open("assets/front.png") # Load assets
back = Image.open("assets/back.png")
dv = ImageDraw.Draw(front)
dh = ImageDraw.Draw(back)
y = 100
for category in categories: # For each category
print category
questions = get_questions_of_categoriy(conn,
category) # Get all questions of those category from database
for question_row in questions: # question_row[ ] represents one question 0 - ID, 1 - question, 2 - answer, 3 - category
for question_row in questions: # question_row[ ] represents one question 0 - ID, 1 - question,
# 2 - answer, 3 - category
print question_row # print full question_row
if (question_row[0] in used_ids) and (
questions.index(question_row) == (len(questions) - 1)): # if no new question was found
print "\nSuccessfully created " + str(card_count - 1) + " cards. Those can be found in ./out"
print str(count_questions(conn) - 6 * (
card_count - 1)) + " Questions have been ignored. (Due to inapplicable categories)"
print "No more questions available to fill new card!" # TODO Print size
print "No more questions available to fill new card!"
print "Questions with the following id's were skipped: "
used_ids.sort()
for q_id in used_ids:
Expand Down Expand Up @@ -117,11 +119,12 @@ def create_cards():
fill=(0, 0, 0)) # Align the line in the middle (at y)

answer_lines = split_lines(answer.split("(")[0],
ANSWER_MAX_LINE_LENGTH) # Max. 2 lines (because of answer checker in database_tools), Citation will be rendered in a own line
ANSWER_MAX_LINE_LENGTH) # Max. 2 lines (because of answer checker
# in database_tools), Citation will be rendered in it's own line

citation_exists = False
if len(answer.split("(")) > 1: # append citation if there is one
citation = answer.split("(")[1] # TODO split citation in own column in database
citation = answer.split("(")[1]
answer_lines.append(citation)
citation_exists = True
if len(answer_lines) == 3: # if there are two lines and a citation
Expand All @@ -132,19 +135,19 @@ def create_cards():
elif len(answer_lines) == 2: # if there is just one line and a citation (a split was done)
dh.text((620, y - 15), answer_lines[0], font=fnt,
fill=(0, 0, 0)) # Align the line a the citation to y
if citation_exists: # 2nd line can be citation or just a second line
if citation_exists: # 2nd line can be citation or just a second line
dh.text((620, y + 15), "(" + answer_lines[1], font=fnt, fill=(0, 0, 0))
else:
dh.text((620, y + 15), answer_lines[1], font=fnt,fill=(0, 0, 0))
dh.text((620, y + 15), answer_lines[1], font=fnt, fill=(0, 0, 0))
else: # if there is just a line
dh.text((620, y), answer_lines[0], font=fnt, fill=(0, 0, 0)) # Place the answer at y

y = y + NEXT_QUESTION_Y_SKIP # go to the next question
break

vorne.save(
front.save(
"./out/front" + str(card_count) + ".png") # save front and back of the card with texts written on it
hinten.save("./out/back" + str(card_count) + ".png")
back.save("./out/back" + str(card_count) + ".png")
card_count = card_count + 1


Expand Down
26 changes: 24 additions & 2 deletions database_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import sqlite3


# https://www.sqlitetutorial.net/sqlite-python/sqlite-python-select/
def create_connection(db_file):
""" create a database connection to a SQLite database """
"""
Create a database connection to a SQLite database
:param db_file: SQLite file containing the database
:return: Connection
"""
try:
connection = sqlite3.connect(db_file)
return connection
Expand All @@ -13,18 +16,33 @@ def create_connection(db_file):


def count_categories(conn, category):
"""
Count questions of the given category
:param conn: Connection to the database
:param category: Wished category
:return: Count of questions of category
"""
cur = conn.cursor()
cur.execute("SELECT COUNT(id) FROM qac WHERE category=?", category)
return cur.fetchall()


def count_questions(conn):
"""
Count all questions
:param conn: Connection to the database
:return: Just the number of all questions
"""
cur = conn.cursor()
cur.execute("SELECT COUNT(id) FROM qac")
return cur.fetchall()[0][0]


def get_categories_from_file():
"""
Reads the category string from the categories.txt file
:return: Tupel (Categories one letter, Category long name)
"""
cat = []
cat_long = []
f = codecs.open("questions/categories.txt", "r", "utf-8")
Expand All @@ -35,6 +53,10 @@ def get_categories_from_file():


def database_statistics():
"""
Generates interesting statistics about questions count, database entries, ...
:return: Successfull execution
"""
conn = create_connection("python_sqlite.db")
categories, categories_long = get_categories_from_file()
with conn:
Expand Down
Loading

0 comments on commit c1f1e71

Please sign in to comment.