-
Notifications
You must be signed in to change notification settings - Fork 0
/
questionbot.swift
168 lines (139 loc) · 4.93 KB
/
questionbot.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import Foundation
struct Task {
let statement: String
let solution: String
}
struct Chapter {
let title: String
var tasks: [Task] = []
}
func parse(taskfile path: String) -> [Chapter] {
var chapters: [Chapter] = []
var text = ""
do {
text = try String(contentsOfFile: path, encoding: .utf8)
} catch {
print(error)
}
var currentChapter = Chapter(title: "Unassigned")
var currentStatement: String? = nil
var currentSolution: String? = nil
text.components(separatedBy: .newlines).forEach { line in
if line.starts(with: "# ") {
// New chapter
let title = String(line.suffix(from: line.index(line.startIndex, offsetBy: 2)))
if let statement = currentStatement, let solution = currentSolution {
currentChapter.tasks.append(Task(statement: statement, solution: solution))
currentStatement = nil
currentSolution = nil
}
if currentChapter.tasks.count > 0 {
chapters.append(currentChapter)
}
currentChapter = Chapter(title: title)
} else if line.starts(with: "## ") {
if let statement = currentStatement, let solution = currentSolution {
currentChapter.tasks.append(Task(statement: statement, solution: solution))
}
currentStatement = String(line.suffix(from: line.index(line.startIndex, offsetBy: 3)))
currentSolution = nil
} else {
if currentSolution == nil {
if !line.trimmingCharacters(in: .whitespaces).isEmpty {
currentSolution = line
}
} else {
currentSolution! += "\n" + line
}
}
}
if let statement = currentStatement, let solution = currentSolution {
currentChapter.tasks.append(Task(statement: statement, solution: solution))
}
if currentChapter.tasks.count > 0 {
chapters.append(currentChapter)
}
return chapters
}
var taskfilePath: String? = nil
for argument in CommandLine.arguments[1..<CommandLine.arguments.count] {
switch argument {
case "-h", "--help":
print(
"""
This tool expects a file containing \"Questions\" in Markdown format.
h1 Elements are interpreted as Chapters, h2 Elements as Questions
and Text under h2 Elements as answers to said questions.
Example task file:
# Chapter 1: Introduction
## What is the meaning of Life?
42
## Give 5 reasons to stop procrastinating
* Better grades
* Less halve-finished sideprojects
...
"""
)
exit(0)
default:
taskfilePath = argument
}
}
guard let taskfile = taskfilePath else {
print("Usage: ./\(CommandLine.arguments[0]) <path-to-markdown-tasks-file>")
exit(1)
}
let chapters = parse(taskfile: taskfile)
// ========================
// COMMAND LINE INTERFACE
// ========================
// Clear screen
print("", terminator: "\u{001B}[2J\u{001B}[0;01H")
print("Identified the following Chapters:")
chapters.enumerated().forEach { (index, chapter) in
print(" [\(index)]: \(chapter.title) (\(chapter.tasks.count) questions)")
}
var index = 0
if (chapters.count > 1) {
print("Which chapter do you want to practice? ", terminator: "")
fflush(stdout)
var answer = readLine()
fflush(stdout)
while answer == nil || Int(answer!) == nil || Int(answer!)! >= chapters.count || Int(answer!)! < 0 {
print("Invalid answer, please choose one of (\(chapters.enumerated().map({ (i, _) in "\(i)" }).joined(separator: ", "))): ", terminator: "")
fflush(stdout)
answer = readLine()
}
index = Int(answer!)!
}
let selectedChapter = chapters[index]
print("Selected chapter \(index): \(selectedChapter.title)")
var correctCnt = 0
var answeredCnt = 0
var questions = selectedChapter.tasks.shuffled()
while !questions.isEmpty {
let task = questions.removeFirst()
answeredCnt += 1
// Clear screen
print("", terminator: "\u{001B}[2J\u{001B}[0;01H")
print("Question \(answeredCnt)/\(answeredCnt + questions.count): \u{001B}[1m\(task.statement)\u{001B}[0m")
fflush(stdout)
_ = readLine()
print("\u{001B}[3m\(task.solution)\u{001B}[0m")
print("Was your answer correct? [y/n]: ", terminator: "")
fflush(stdout)
var answer = readLine()
while answer == nil || (answer! != "y" && answer! != "n") {
print("Unrecognized input. Was your answer correct? [y/n]: ", terminator: "")
fflush(stdout)
answer = readLine()
}
if (answer! == "y") {
correctCnt += 1
} else {
questions.append(task)
}
}
print("", terminator: "\u{001B}[2J\u{001B}[0;01H")
var correctPercentage = Int(Float(correctCnt) / Float(answeredCnt) * 100)
print("\u{001B}[1mCongratulations, you answered \(correctCnt) out of \(answeredCnt) questions correctly (\(correctPercentage)%)\u{001B}[0m https://youtu.be/dQw4w9WgXcQ?t=18")