-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Exercise tree #560
Exercise tree #560
Conversation
Turns out that the required structure is a vine, not a tree. Who knew? I'll rework these later. |
Just going to take bites at the general ordering problem here. We have 19 difficulty 1 problems, and ~80 total problems, so we want two of our difficulty 1 problems to be core. How, then to organize the 19? I did it like this: Hello world is the root of the tree, the first core exercise. Once complete, it branches into three topic areas: strings, math, and control flow. Raindrops is the first stringsy problem. Once complete, the rest of the strings problems are unlocked: raindrops, reverse-string, bob, proverb. Gigasecond is the first math problem. Once complete, the rest of the math problems are unlocked: leap, nth-prime, grains, prime-factors, sum-of-multiples, armstrong-numbers. Beer-song is the first control flow problem. Once complete, the other one, difference-of-squares, is also unlocked. The second core exercise is pythagorean-triplets. This was chosen because it's simple enough to be quick to implement a naive version, but there's a lot of potential for optimization. Once complete, it unlocks the rest of the more challenging level 1 problems: series, diffie-hellmann, and collatz-conjecture.
Rough tree heads: - saddle-points: iterators - say: algorithms - clock: data structures - luhn: algorithms - atbash-cipher: ciphers - bucket-push: use a trait - space-age: implement a trait - macros: macros - sublist: generics
There were a few core problems here from which the others derived: - poker: algos / search - anagram: lifetimes - minesweeper: complex structs / state - parallel-letter-freq: concurrency - forth: compilers/interpreters
… by core The idea was simple: for every exercise, while its parent is not core, update its parent to its grandparent. This was implemented with the following script (python3.6): ```python3 import json def load(): with open('config.json', 'r') as fp: return json.load(fp) def save(cfg): with open('config.new.json', 'w') as fp: return json.dump(cfg, fp, indent=2) def update_exercises(exercises): slugs = {e['slug']: e for e in exercises} new_ex = [] for e in exercises: if e['core']: print('{} is core'.format(e['slug'])) new_ex.append(e) continue while e['unlocked_by'] is not None and not slugs[e['unlocked_by']]['core']: print('{} parent is {}'.format(e['slug'], e['unlocked_by'])) e['unlocked_by'] = slugs[e['unlocked_by']]['unlocked_by'] new_ex.append(e) return new_ex if __name__ == '__main__': config = load() lce = len(config['exercises']) print(f"len exercises: {lce}") config['exercises'] = update_exercises(config['exercises']) print(f"len new exercises: {len(config['exercises'])}") if lce == len(config['exercises']): save(config) ```
309c324
to
6ade18f
Compare
c72d6aa
to
043ca39
Compare
@exercism/rust This now passes all its checks, and is the last thing blocking #543, so I'd appreciate it if you guys could take a look at it. |
As a first step to reviewing this. I know that there is a graphical visualiser for the new exercise structure. Given that every exercise is core or unlocked by core, I think I don't need the full graphical visualiser (and/or install whatever is necessary to get it running) and I just want to write a textual one. So here is one real quick: require 'json'
r = JSON.parse(ARGV.empty? ? File.read("#{__dir__}/config.json") : ARGF.read)
cores = []
unlocks = Hash.new { |h, k| h[k] = [] }
r['exercises'].each { |x|
if x['deprecated']
puts "[deprecated] #{x['slug']}"
elsif x['core']
cores << x['slug']
else
unlocks[x['unlocked_by']] << x['slug']
end
}
cores.each { |x|
puts x
unlocks[x].each { |u| puts " #{u}" }
} And the output:
|
There is the following conflict between my responsibilities and my time: It should be my responsibility as a track maintainer to make sure the track progression is good. However, the time I currently have on my hands means it is difficult for me to think deeply about any proposed scheme, or engage in discourse about whether this progression or that progression be better. At present, I am able to give an Approval that simply means "this is a reasonable track progression with no obvious errors", but warn that I truly have given little thought as to whether it is better or worse than any other. Another factor in this is that I feel I will make some mistake no matter what I may propose, and it may make more sense to simply try it out and refine it based on feedback. The obvious risk is that if there are egregious errors, the first batch of students will have a bumpier path than if I had taken the proper time to engage in discourse on improving this admitted draft. I am currently forced to take the action of a low-effort Approval, acknowledging the risk that the contrast in time spent can be seen as disrespectful. One person puts in the work, expects others to put in at least some effort, and the other person doesn't come through. I apologise for the disrespect this action may cause. It is interesting that Luhn is not the exercise to unlock its two variants (luhn-from and luhn-trait), but I surmise there must be a good reason for that. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a low-effort Approval, with the caveat in my given explanation.
I agree wholeheartedly. Topics are complex, and ideally, we could all sit down and hash out a good progression over the course of a week. Instead, what we have is the progression that I could put together over the course of a few hours. We all have external responsibilities and other claims on our time. Do not feel bad if you cannot devote more time to this than you already have.
That was my hope, when putting it together. The chance that I got it perfect on the first try seems low. We can always change it later.
They are in different categories. I forget which category I assigned to Luhn itself, but luhn-from (and bracket-push) are about implementing external traits, and luhn-trait (and space-age) are about potentially implementing your own. |
My intention, unless anyone objects, is to leave this until Friday the 29th, then merge. |
Previous script failed to show the require 'json'
r = JSON.parse(ARGV.empty? ? File.read("#{__dir__}/config.json") : ARGF.read)
cores = []
unlocks = Hash.new { |h, k| h[k] = [] }
r['exercises'].each { |x|
if x['deprecated']
puts "[deprecated] #{x['slug']}"
elsif x['core']
cores << x['slug']
else
unlocks[x['unlocked_by']] << x['slug']
end
}
unlocks[nil].each { |x| puts "[free] #{x}" }
cores.each { |x|
puts x
unlocks[x].each { |u| puts " #{u}" }
} Since there are no such non-deprecated exercises for the Rust track, the output is unchanged. |
Organize exercises into a tree structure. Contributes to #543. Closes #552 by completion and closes #548 and closes #547 by exclusion.
Individual commits contain the reasoning about individual exercises. The general approach was to group them into topics, and have the easiest member introduce a topic.
Due to the large number of individual topics chosen, we ended up with a moderately high number of core exercises. I believe that this number is not excessive, but am very willing to discuss the arrangement. This is a draft, but I do not harbor the delusion that it is perfect.
All non-deprecated exercises are either core or depend on another exercise:
$ jq '.exercises[] | select((.deprecated|not) and (.core|not) and (.unlocked_by|not))' config.json
Core exercises: