Skip to content

Commit c931bfa

Browse files
committed
Initial commit after Fall 2012
0 parents  commit c931bfa

18 files changed

+914
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.pyc
2+
*.class
3+
.test.diff
4+
grader
5+
pdf

Rakefile

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# Lazy hard-coding of problem slugs
2+
SLUGS = %w{freq streets war password}
3+
# Number of files to be generated for the server
4+
TEST_FILES = 50
5+
# Number of test cases to include per file (including edge cases)
6+
# We set this to 200 for the competition
7+
TEST_CASES = 20
8+
9+
task :default => [:test]
10+
11+
task :build => [:gen, :test]
12+
13+
task :gen, :slug do |t, args|
14+
puts "Generating #{ TEST_FILES } files each with #{ TEST_CASES } cases"
15+
SLUGS.each do |slug|
16+
if !args[:slug] || args[:slug] == slug
17+
# Read the edge file
18+
# We had a problem here where the edge file was not being updated to
19+
# include the correct number of inputs (aka human error), so people who
20+
# ignored the number of inputs line got too many solutions. We need to
21+
# foolproof this more.
22+
edge = File.open("#{ slug }/edge.in").read.gsub(/^# .*/, '').gsub(/^\s*$\n/, '')
23+
count = edge.lines.first.to_i
24+
edge = edge.lines.drop(1).join
25+
26+
# Make the destination folder
27+
prefix = "grader/#{ slug }/"
28+
mkpath prefix
29+
require "./#{ slug }/gen.rb"
30+
31+
print "Generating files for #{ slug }"
32+
TEST_FILES.times do |i|
33+
print "."
34+
file = "#{ prefix }/#{ i }"
35+
# Generate the input file
36+
File.open("#{ file }.in", 'w+') do |input|
37+
# Write the count
38+
input.puts TEST_CASES
39+
40+
# Write the edge cases
41+
input.write edge
42+
43+
# Add in our own from gen.rb
44+
Generator.generate(TEST_CASES - count, :without_leader => true) do |line|
45+
input.puts line
46+
end
47+
end
48+
49+
# Generate the corresponding output file
50+
File.open("#{ file }.out", 'w+') do |output|
51+
# Get our solution
52+
require "./#{ slug }/solution.rb"
53+
54+
Solver.solve(File.open("#{ file }.in")) do |line|
55+
output.puts line
56+
end
57+
end
58+
end
59+
puts "done"
60+
end
61+
end
62+
end
63+
64+
require 'open3'
65+
module Open3
66+
# A more encapsulated function that takes a command, optional file to be
67+
# written to stdin, and a block that stdout is read and passed to.
68+
def self.piped_input cmd, file=nil, &block
69+
popen3(cmd) do |stdin, stdout, stderr|
70+
stdin.write File.open(file).read if file rescue nil
71+
# stderr raises an exception and prints an error. This could be reworked,
72+
# since if you rescue the exception you wouldn't want the extra printing.
73+
unless stderr.eof?
74+
puts "Error in `#{ cmd } < #{ file }`:"
75+
print stderr.read
76+
raise :stderr
77+
end
78+
yield stdout.read if block
79+
end
80+
end
81+
end
82+
83+
module Tester
84+
TESTS = {
85+
'freq' => 'freq_Varun.py',
86+
'war' => '1DWar_Varun.py',
87+
'password' => 'Main.java',
88+
'streets' => 'Main.java',
89+
}
90+
91+
class Base
92+
def test input
93+
Open3.piped_input("#{ @cmd } #{ @file }", input) do |output|
94+
# Write output to temp file
95+
File.open('.test.diff', 'w+') do |tmp|
96+
tmp.write output
97+
end
98+
99+
# Diff with given output
100+
Open3.piped_input("colordiff -u #{ input.gsub(/in$/, 'out') } .test.diff") do |diffout|
101+
unless diffout.empty?
102+
# Diff error! Same thing here with the printing and raising.
103+
print diffout
104+
raise "Test failed on #{ input }"
105+
end
106+
end
107+
end
108+
end
109+
end
110+
111+
class Java < Base
112+
def initialize file
113+
# This is a hacky way to change the directory into the classpath and the
114+
# file name sans extension into the class name.
115+
@file = [File.dirname(file), File.basename(file, '.java')].join ' '
116+
@cmd = 'java -cp'
117+
# Compile the java file before the tests run
118+
Open3.piped_input("javac #{ file }")
119+
end
120+
end
121+
122+
class Python < Base
123+
def initialize file
124+
@file = file
125+
@cmd = 'python'
126+
end
127+
end
128+
end
129+
130+
task :test, :slug do |t, args|
131+
# Try out Varun's solutions
132+
SLUGS.each do |slug|
133+
if !args[:slug] || args[:slug] == slug
134+
# Testing :slug
135+
print "Testing #{ slug }"
136+
# Make sure there is a test defined
137+
test = Tester::TESTS[slug]
138+
next unless test
139+
140+
# Check file type
141+
ext = File.extname(test)
142+
case ext
143+
when '.java'
144+
tester = Tester::Java.new("#{ slug }/#{ test }")
145+
when '.py'
146+
tester = Tester::Python.new("#{ slug }/#{ test }")
147+
end
148+
149+
# Do for each file
150+
TEST_FILES.times do |i|
151+
tester.test "grader/#{ slug }/#{ i }.in"
152+
print "."
153+
end
154+
puts "done"
155+
end
156+
end
157+
158+
# If we get this far, everything succeeded and we can delete this temp file.
159+
# We don't really want to delete otherwise, since it can be useful for
160+
# debugging.
161+
rm '.test.diff'
162+
end

freq/description.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
Frequency Detection
2+
===================
3+
4+
One common technique when analyzing encryped data is to look for common
5+
sequences of characters. You are given a string of a certain length, and your
6+
task is to output a brief summary containing the *three* most frequent
7+
2-character pairs found in the string and how many times it was found occuring.
8+
If two or more are tied for the same frequency, sort them alphabetically.
9+
Separate the output between each case with three hyphens.
10+
11+
Single input
12+
------------
13+
<string length>
14+
<encrypted string>
15+
16+
Single output
17+
-------------
18+
<first sequence> <number of occurrences>
19+
<second sequence> <number of occurrences>
20+
<third sequence> <number of occurrences>
21+
---
22+
23+
Sample input
24+
------------
25+
2
26+
19
27+
ABCDABGHRPIJABDNRPI
28+
22
29+
OPOPOPOPPAGANGNAMSTYLE (I am so, so sorry)
30+
31+
Expected output
32+
---------------
33+
AB 3
34+
PI 2
35+
RP 2
36+
---
37+
OP 4
38+
PO 3
39+
AG 1
40+
---

freq/edge.in

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Blank lines and lines prefixed with "# " are ignored
2+
5
3+
4+
# 1. Test input
5+
19
6+
ABCDABGHRPIJABDNRPI
7+
8+
# 2. Test input
9+
22
10+
OPOPOPOPPAGANGNAMSTYLE
11+
12+
# 3. Reverse shift
13+
9
14+
ABABABABB
15+
16+
# 4. Same letter
17+
18
18+
CCCCCCBBBBBBAAAAAA
19+
20+
# 5. All unique
21+
10
22+
ABCDEFGHIJ

freq/gen.rb

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Random range of characters for one input string
2+
CHARS = 5000..8000
3+
4+
class Generator
5+
def self.generate iterations, opts = {}
6+
yield iterations unless opts[:without_leader]
7+
iterations.times do
8+
# First the line noting how many characters, then that many random
9+
# characters from A-Z as a string
10+
yield rand(CHARS).tap{|i| yield i}.times.map { (rand(26)+65).chr }.join
11+
end
12+
end
13+
end
14+
15+
if __FILE__ == $0
16+
Generator.generate((ARGV[0] || 1).to_i) do |line|
17+
puts line
18+
end
19+
end

freq/solution.rb

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class Solver
2+
def self.solve stdin
3+
n = stdin.readline.to_i
4+
n.times do
5+
freq = Hash.new 0
6+
# Read extra line for string size (not needed)
7+
stdin.readline
8+
input = stdin.readline.strip
9+
# Tally pairs of consecutive letters
10+
input.chars.each_cons(2) do |seq|
11+
freq[seq.join] += 1
12+
end
13+
# Sort by highest count and alphabetically
14+
freq = freq.to_a.sort do |a, b|
15+
if a.last == b.last
16+
a.first <=> b.first
17+
else
18+
b.last <=> a.last
19+
end
20+
end
21+
# Format for output
22+
yield freq.first(3).map{|f| f.join(' ')}.join("\n")
23+
yield "---"
24+
end
25+
end
26+
end
27+
28+
if __FILE__ == $0
29+
Solver.solve STDIN do |line|
30+
puts line
31+
end
32+
end

password/description.md

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
Password Fragments
2+
==================
3+
4+
An online site for storing bitcoins has been hacked (big surprise), and some
5+
interesting details were revealed about their security setup. Firstly, their
6+
users were not allowed to use any duplicate characters in their passwords. Not
7+
only that, but when logging in, the user only had to input a certain number of
8+
characters from their password as long as they kept them in order.
9+
10+
For example, if a user with the password `asdfjkl;` is told to input 3
11+
characters, they may input `asd`, `afl`, `aj;`, etc. As long as the letters are
12+
kept in the same order of the password, it will be accepted.
13+
14+
To add to the poor security, all successful logins were recorded to a text file
15+
for each user. You happen to stumble upon one such file and wonder if it is
16+
possible to reconstruct the actual password with the given fragments. Given a
17+
number of login attempts, try to piece them together to restore the full
18+
password.
19+
20+
Single input
21+
------------
22+
<number of fragments> <size of each fragment>
23+
<fragment>
24+
<fragment>
25+
...
26+
27+
Single output
28+
-------------
29+
<password>
30+
31+
Sample input
32+
------------
33+
2
34+
5 3
35+
123
36+
abc
37+
1b3
38+
1c2
39+
1ac
40+
6 4
41+
OGco
42+
ROom
43+
POco
44+
PRop
45+
POGp
46+
Gcmp
47+
48+
Expected output
49+
---------------
50+
1abc23
51+
PROGcomp

password/edge.in

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Blank lines and lines prefixed with "# " are ignored
2+
4
3+
4+
# 1. Test input
5+
5 3
6+
123
7+
abc
8+
1b3
9+
1c2
10+
1ac
11+
12+
# 2. Test input
13+
6 4
14+
OGco
15+
ROom
16+
POco
17+
PRop
18+
POGp
19+
Gcmp
20+
21+
# 3. Minimal set
22+
5 2
23+
ab
24+
bc
25+
cd
26+
de
27+
ef
28+
29+
# 4. Maximum set
30+
5 5
31+
abcde
32+
abcde
33+
abcde
34+
abcde
35+
abcde

0 commit comments

Comments
 (0)