Skip to content

Instantly share code, notes, and snippets.

Created April 19, 2014 05:02
Show Gist options
  • Save anonymous/11074621 to your computer and use it in GitHub Desktop.
Save anonymous/11074621 to your computer and use it in GitHub Desktop.
#! /usr/bin/python2
# The goal of this is to take questions that are mapped to answers
# and then map each question to a permutation of n answers, including the correct one
# then iterate through each of these mappings and ask the user to select an answer, record whether it was correct or not
# repeat the questions "infinitely" until the user hits EOF, i.e. ctrl+d
from random import random
from sys import exit
from itertools import chain, repeat
# this is simply a concise way of writing 'accessor' functions for pairs
# I could have also used a normal function to do the same thing with index notation, but this is more concise and nice looking I think
# note that _ here is just another variable name, I call it _ to make it clear it is ignored
fst = lambda (a, _): a
snd = lambda (_, b): b
def shuffle(answers):
# What this does is create a list of pairs
# each pair is a random number (between 0 and 1) and an element of the list to be shuffled
# the syntax being used here is called a list comprehension, basically it allows you to do
# what a for loop would do except more concisely, and it creates a list for you
# e.g. [x for x in xs] just copies the list 'xs', and you can put any python expression in place of the first x
randomized = [(random(), answer) for answer in answers]
# what this does is it takes the list of randomized pairs and sorts it according to the random element
# key=fst is a way of saying that the key function, that is, the function used by 'sorted' to extract the element to be compared
# the key function is the function 'fst' which we defined earlier
# the call to map here simply gives you the second element of each pair, map takes a function and applies it to each element
# of the list and returns the result as a new list with the results of each function call in place of the old elements
# e.g. map(plus2, [1,2,3,4]) would give you [3,4,5,6] assuming we had a function called plus2 which just adds 2 to whatever you give it
return map(snd, sorted(randomized, key=fst))
def permute_question(n, mapped_question, other_answers):
# this function simply shuffles around the possible answers to a question
question, correct_answer = mapped_question
# it might appear that this could result in an IndexError, but python slicing actually doesn't care if you do that
# it will just return the entire list
return (question, correct_answer, shuffle([correct_answer]+other_answers[0:n]))
def request_answer(max_amount):
# The goal here is to try to parse the input into an int type
# the % (blah, blargh) syntax is just a way of substituting in values to a string
# the "%s" stuff in the string gets substituted with whatever comes after the % in that order
try:
try:
selection = int(raw_input("Select an answer from %s to %s: " % (0, max_amount)))
except EOFError:
# if we get EOF just print a message and quit
print "Quitting..."
exit(0) # 0 signifies no errors
if selection > max_amount: # check that the integer is withing the bounds of possible answers
print "You did not select an answer within the correct boundary"
# re-request it until they do!
return request_answer(max_amount)
return selection
except ValueError:
print "Please enter a decimal integer with no other characters (you can enter spaces if you want)"
# re-request it until they enter an integer!
return request_answer(max_amount)
def ask(question):
# this simply takes a tuple (which is like a fixed size list)
# and it assigns names (variables) for each of the elements of the tuple
# for example a, b, c = (1,2,3)
# is equivalent to:
# triple = (1,2,3)
# a = triple[0]
# b = triple[1]
# c = triple[2]
# so it is just convenient syntax
text, correct_answer, possible_answers = question
print text
# enumerate gives us the index as well as the item
for n, answer in enumerate(possible_answers):
print "%s: %s" % (n, answer)
selection = request_answer(len(possible_answers)-1)
if correct_answer == possible_answers[selection]:
print "Correct! The answer is %s" % correct_answer
return True
print "False! The correct answer is %s" % correct_answer
return False
def parse_questions(input_string):
parsed = []
for line in input_string:
splitted = line.split("=") # questions are separated from answers by =
if len(splitted) != 2:
raise ValueError("Lines must be in the format of question=answer where neither question or answer contains =")
splitted[1] = splitted[1][0:-1] # remove the trailing newline
parsed.append(splitted)
return parsed
with open("./test_questions", "r") as question_file:
# test_questions is a plain text file in the form of
# question=answer\nquestion2=answer2\nquestion3=ansswer3
# and so on with however many questions you want (\n represents a newline character fyi)
# parse in the question file and shuffle around the questions and answers
questions = shuffle(parse_questions(question_file.readlines()))
# get a list of all possible answers to any question in the file
# turn it into a "set" so we can use the different method
all_answers = set(map(snd, questions))
n = 12
# chain.from_iterable(list) gives you a flattened list
# i.e. if list were [[1,2,3], [4,5,6]] it would return [1,2,3,4,5,6]
# repeat just repeats the list forever
# these work because the result is not evaluated right away
# you essentially get the results on-demand
for question, answer in chain.from_iterable(repeat(questions)):
other_answers = list(all_answers.difference(set([answer])))
ask(permute_question(n, (question, answer), other_answers))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment