Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'release/0.2.2'

  • Loading branch information...
commit ad1756f9a33d460f6042ef1e0fb585f1e5269bb2 2 parents 465eed6 + d9370f6
@amalloy amalloy authored
View
4 config.clj
@@ -4,4 +4,6 @@
:db-pwd nil
:host "smtp.googlemail.com"
:user "team@4clojure.com"
- :pass ""}
+ :pass ""
+ :problem-submission false
+ :advanced-user-count 50}
View
5 project.clj
@@ -1,4 +1,4 @@
-(defproject foreclojure "0.2.1"
+(defproject foreclojure "0.2.2"
:description "4clojure - a website for lisp beginners"
:dependencies [[org.clojure/clojure "1.2.1"]
[org.clojure/clojure-contrib "1.2.0"]
@@ -7,11 +7,12 @@
[clojail "0.4.0-SNAPSHOT"]
[sandbar "0.4.0-SNAPSHOT"]
[org.clojars.christophermaier/congomongo "0.1.4-SNAPSHOT"]
- [org.jasypt/jasypt "1.7"]
+ [org.jasypt/jasypt "1.7"]
[amalloy/utils "[0.3.7,)"]
[clj-github "1.0.1"]
[ring "0.3.7"]
[clj-config "0.1.0"]
+ [incanter/incanter-charts "1.2.1"]
[org.apache.commons/commons-email "1.2"]]
:dev-dependencies [[lein-ring "0.4.0"]
[swank-clojure "1.2.1"]]
View
34 resources/public/css/style.css
@@ -7,6 +7,9 @@ body {
text-align: center;
}
+img {border:none}
+
+
#account{
float:right;
width: 100%;
@@ -58,6 +61,9 @@ body {
width: 95%;
padding: 15px 0px 15px 0px;
}
+div#top a {
+ border: 0;
+}
h3 {
font-size: 18px;
@@ -110,6 +116,7 @@ a.novisited {color: #00e;}
#logo {
float:left;
padding:15px 0px 15px 0px;
+ border: 0;
}
#welcome{
@@ -223,11 +230,26 @@ div#restrictions {
margin-left: 1.3em;
}
-textarea#code-box {
+textarea#code-box, #problem-submission textarea {
width: 500px;
height: 250px;
}
+form#problem-submission label {
+ font-weight: bold;
+ display: block;
+ margin: 10px 0 4px;
+}
+
+form#problem-submission #problem-description {
+ height: 4em;
+}
+
+div#problems-error {
+ font-size: 1.4em;
+ margin: 2px 0 10px;
+}
+
#code-div{
width: 100%;
height: 250px;
@@ -310,3 +332,13 @@ button:active {
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#A1CC59', endColorstr='#A7D45C')";
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#A1CC59', endColorstr='#A7D45C');
}
+
+#golfgraph{
+ width: 90%
+}
+
+#golfgraph img{
+ height: 300px;
+ padding-top: 25px;
+}
+
View
BIN  resources/public/favicon.ico
Binary file not shown
View
BIN  resources/public/images/favicon.ico
Binary file not shown
View
3  resources/public/script/foreclojure.js
@@ -35,9 +35,8 @@ function configureCodeBox(){
//If we have javascript on then we remove it and replace it with
//the proper div
var oldBox = $('#code-box');
- oldBox.replaceWith("<div id=\"code-div\"> <pre id=\"editor\">" + oldBox.val() + "</pre></div>");
var hiddenCodeInput = "<input type=\"hidden\" value=\"blank\" name=\"code\" id=\"code\">";
- $(hiddenCodeInput).insertBefore('#id');
+ oldBox.replaceWith("<div id=\"code-div\"> <pre id=\"editor\">" + oldBox.val() + "</pre></div>"+hiddenCodeInput);
if ($("#run-button").length){
var editor = ace.edit("editor");
View
2  src/foreclojure/config.clj
@@ -4,4 +4,4 @@
(def config-file (file (System/getProperty "user.dir") "config.clj"))
-(def config (safely read-config config-file))
+(def config (safely read-config config-file))
View
10 src/foreclojure/core.clj
@@ -1,11 +1,11 @@
(ns foreclojure.core
(:use compojure.core
- [foreclojure static problems login register
- users config social version db-utils utils]
+ [foreclojure static problems login register golf
+ users config social version graphs db-utils utils]
ring.adapter.jetty
somnium.congomongo
- ring.middleware.stacktrace
- [ring.middleware.reload :only [wrap-reload]])
+ (ring.middleware (reload :only [wrap-reload])
+ (stacktrace :only [wrap-stacktrace])))
(:require [compojure [route :as route] [handler :as handler]]
[sandbar.stateful-session :as session]
[ring.util.response :as response]))
@@ -34,6 +34,8 @@
static-routes
social-routes
version-routes
+ graph-routes
+ golf-routes
(route/resources "/")
(route/not-found "Page not found"))
View
169 src/foreclojure/data_set.clj
@@ -10,6 +10,7 @@
:times-solved 0
:description "This is a clojure form. Enter a value which will make the form evaluate to true. Don't over think it! If you are confused, see the <a href='/directions'>getting started</a> page. Hint: true is equal to true."
:tags ["elementary"]
+ :approved true
:tests ["(= __ true)"]})
(insert! :problems
@@ -18,6 +19,7 @@
:times-solved 0
:description "If you are not familiar with <a href='http://en.wikipedia.org/wiki/Polish_notation'>polish notation</a>, simple arithmetic might seem confusing."
:tags ["elementary"]
+ :approved true
:tests ["(= (- 10 (* 2 3)) __)"]})
(insert! :problems
@@ -26,6 +28,7 @@
:times-solved 0
:description "Clojure strings are Java strings. This means that you can use any of the Java string methods on Clojure strings."
:tags["elementary"]
+ :approved true
:tests ["(= __ (.toUpperCase \"hello world\"))"]})
@@ -35,6 +38,7 @@
:times-solved 0
:description "Lists can be constructed with either a function or a quoted form."
:tags["elementary"]
+ :approved true
:tests ["(= (list __) '(:a :b :c))"]})
(insert! :problems
@@ -43,6 +47,7 @@
:times-solved 0
:description "When operating on a list, the conj function will return a new list with one or more items \"added\" to the front."
:tags["elementary"]
+ :approved true
:tests ["(= __ (conj '(2 3 4) 1))"
"(= __ (conj '(3 4) 2 1))"]})
@@ -52,6 +57,7 @@
:times-solved 0
:description "Vectors can be constructed several ways. You can compare them with lists."
:tags["elementary"]
+ :approved true
:tests ["(= [__] (list :a :b :c) (vec '(:a :b :c)) (vector :a :b :c))"]})
(insert! :problems
@@ -60,6 +66,7 @@
:times-solved 0
:description "When operating on a Vector, the conj function will return a new vector with one or more items \"added\" to the end."
:tags["elementary"]
+ :approved true
:tests ["(= __ (conj [1 2 3] 4))"
"(= __ (conj [1 2] 3 4))"]})
@@ -70,6 +77,7 @@
:times-solved 0
:description "Sets are collections of unique values."
:tags["elementary"]
+ :approved true
:tests ["(= __ (set '(:a :a :b :c :c :c :c :d :d)))"
"(= __ (clojure.set/union #{:a :b :c} #{:b :c :d}))"]})
@@ -79,6 +87,7 @@
:times-solved 0
:description "When operating on a set, the conj function returns a new set with one or more keys \"added\"."
:tags["elementary"]
+ :approved true
:tests ["(= #{1 2 3 4} (conj #{1 4 3} __))"]})
@@ -88,6 +97,7 @@
:times-solved 0
:description "Maps store key-value pairs. Both maps and keywords can be used as lookup functions. Commas can be used to make maps more readable, but they are not required."
:tags["elementary"]
+ :approved true
:tests ["(= __ ((hash-map :a 10, :b 20, :c 30) :b))"
"(= __ (:b {:a 10, :b 20, :c 30}))"]})
@@ -97,6 +107,7 @@
:times-solved 0
:description "When operating on a map, the conj function returns a new map with one or more key-value pairs \"added\"."
:tags["elementary"]
+ :approved true
:tests ["(= {:a 1, :b 2, :c 3} (conj {:a 1} __ [:c 3]))"]})
(insert! :problems
@@ -105,6 +116,7 @@
:times-solved 0
:description "All Clojure collections support sequencing. You can operate on sequences with functions like first, second, and last."
:tags["elementary"]
+ :approved true
:tests ["(= __ (first '(3 2 1)))"
"(= __ (second [2 3 4]))"
"(= __ (last (list 1 2 3)))"]})
@@ -115,6 +127,7 @@
:times-solved 0
:description "The rest function will return all the items of a sequence except the first."
:tags["elementary"]
+ :approved true
:tests ["(= __ (rest [10 20 30 40]))"]})
(insert! :problems
@@ -123,6 +136,7 @@
:times-solved 0
:description "Clojure has many different ways to create functions."
:tags["elementary"]
+ :approved true
:tests ["(= __ ((fn add-five [x] (+ x 5)) 3))"
"(= __ ((fn [x] (+ x 5)) 3))"
"(= __ (#(+ % 5) 3))"
@@ -134,6 +148,7 @@
:times-solved 0
:description "Write a function which doubles a number."
:tags ["elementary"]
+ :approved true
:tests ["(= (__ 2) 4)"
"(= (__ 3) 6)"
"(= (__ 11) 22)"
@@ -146,6 +161,7 @@
:description "Write a function which returns a personalized greeting."
:tags ["elementary"]
+ :approved true
:tests ["(= (__ \"Dave\") \"Hello, Dave!\")"
"(= (__ \"Jenn\") \"Hello, Jenn!\")"
"(= (__ \"Rhea\") \"Hello, Rhea!\")"]})
@@ -156,6 +172,7 @@
:times-solved 0
:description "The map function takes two arguments: a function (f) and a sequence (s). Map returns a new sequence consisting of the result of applying f to each item of s. Do not confuse the map function with the map data structure."
:tags["elementary"]
+ :approved true
:tests ["(= __ (map #(+ % 5) '(1 2 3)))"]})
(insert! :problems
@@ -164,6 +181,7 @@
:times-solved 0
:description "The filter function takes two arguments: a predicate function (f) and a sequence (s). Filter returns a new sequence consisting of all the items of s for which (f item) returns true."
:tags["elementary"]
+ :approved true
:tests ["(= __ (filter #(> % 5) '(3 4 5 6 7)))"]})
(insert! :problems
@@ -171,8 +189,10 @@
:title "Last Element"
:times-solved 0
:restricted ["last"]
+ :approved true
:description "Write a function which returns the last element in a sequence."
:tags ["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ [1 2 3 4 5]) 5)"
"(= (__ '(5 4 3)) 3)"
"(= (__ [\"b\" \"c\" \"d\"]) \"d\")"]})
@@ -183,6 +203,7 @@
:times-solved 0
:description "Write a function which returns the second to last element from a sequence."
:tags["easy" "seqs"]
+ :approved true
:tests ["(= (__ (list 1 2 3 4 5)) 4)"
"(= (__ [\"a\" \"b\" \"c\"]) \"b\")"
"(= (__ [[1 2] [3 4]]) [1 2])"]})
@@ -194,6 +215,7 @@
:restricted ["nth"]
:description "Write a function which returns the Nth element from a sequence."
:tags["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ '(4 5 6 7) 2) 6)"
"(= (__ [:a :b :c] 0) :a)"
"(= (__ [1 2 3 4] 1) 2)"
@@ -206,6 +228,7 @@
:restricted ["count"]
:description "Write a function which returns the total number of elements in a sequence."
:tags["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ '(1 2 3 3 1)) 5)"
"(= (__ \"Hello World\") 11)"
"(= (__ [[1 2] [3 4] [5 6]]) 3)"
@@ -219,6 +242,7 @@
:restricted ["reverse"]
:description "Write a function which reverses a sequence."
:tags["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ [1 2 3 4 5]) [5 4 3 2 1])"
"(= (__ (sorted-set 5 7 2 7)) '(7 5 2))"
"(= (__ [[1 2][3 4][5 6]]) [[5 6][3 4][1 2]])"]})
@@ -229,6 +253,7 @@
:times-solved 0
:description "Write a function which returns the sum of a sequence of numbers."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= (__ [1 2 3]) 6)"
"(= (__ (list 0 -2 5 5)) 8)"
"(= (__ #{4 2 1}) 7)"
@@ -241,6 +266,7 @@
:times-solved 0
:description "Write a function which returns only the odd numbers from a sequence."
:tags["easy" "seqs"]
+ :approved true
:tests ["(= (__ #{1 2 3 4 5}) '(1 3 5))"
"(= (__ [4 2 1 6]) '(1))"
"(= (__ [2 2 4 6]) '())"
@@ -252,6 +278,7 @@
:times-solved 0
:description "Write a function which returns the first X fibonacci numbers."
:tags["easy" "Fibonacci" "seqs"]
+ :approved true
:tests ["(= (__ 3) '(1 1 2))"
"(= (__ 6) '(1 1 2 3 5 8))"
"(= (__ 8) '(1 1 2 3 5 8 13 21))"]})
@@ -263,6 +290,7 @@
:description "Write a function which returns true if the given sequence is a palindrome.<br/><br>
Hint: \"racecar\" does not equal '(\\r \\a \\c \\e \\c \\a \\r)"
:tags["easy" "seqs"]
+ :approved true
:tests ["(false? (__ '(1 2 3 4 5)))"
"(true? (__ \"racecar\"))"
"(true? (__ [:foo :bar :foo]))"
@@ -277,6 +305,7 @@
:description "Write a function which flattens a sequence."
:tags["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ '((1 2) 3 [4 [5 6]])) '(1 2 3 4 5 6))"
"(= (__ [\"a\" [\"b\"] \"c\"]) '(\"a\" \"b\" \"c\"))"
"(= (__ '((((:a))))) '(:a))"]})
@@ -287,6 +316,7 @@
:times-solved 0
:description "Write a function which takes a string and returns a new string containing only the capital letters."
:tags["easy" "strings"]
+ :approved true
:tests ["(= (__ \"HeLlO, WoRlD!\") \"HLOWRD\")"
"(empty? (__ \"nothing\"))"
"(= (__ \"$#A(*&987Zf\") \"AZ\")"]})
@@ -297,6 +327,7 @@
:times-solved 0
:description "Write a function which removes consecutive duplicates from a sequence."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= (apply str (__ \"Leeeeeerrroyyy\")) \"Leroy\")"
"(= (__ [1 1 2 3 3 2 2 3]) '(1 2 3 2 3))"
"(= (__ [[1 2] [1 2] [3 4] [1 2]]) '([1 2] [3 4] [1 2]))"]})
@@ -307,6 +338,7 @@
:times-solved 0
:description "Write a function which packs consecutive duplicates into sub-lists."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= (__ [1 1 2 1 1 1 3 3]) '((1 1) (2) (1 1 1) (3 3)))"
"(= (__ [:a :a :b :b :c]) '((:a :a) (:b :b) (:c)))"
"(= (__ [[1 2] [1 2] [3 4]]) '(([1 2] [1 2]) ([3 4])))"]})
@@ -317,6 +349,7 @@
:times-solved 0
:description "Write a function which duplicates each element of a sequence."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= (__ [1 2 3]) '(1 1 2 2 3 3))"
"(= (__ [:a :a :b :b]) '(:a :a :a :a :b :b :b :b))"
"(= (__ [[1 2] [3 4]]) '([1 2] [1 2] [3 4] [3 4]))"
@@ -328,6 +361,7 @@
:times-solved 0
:description "Write a function which replicates each element of a sequence a variable number of times."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= (__ [1 2 3] 2) '(1 1 2 2 3 3))"
"(= (__ [:a :b] 4) '(:a :a :a :a :b :b :b :b))"
"(= (__ [4 5 6] 1) '(4 5 6))"
@@ -341,6 +375,7 @@
:restricted ["range"]
:description "Write a function which creates a list of all integers in a given range."
:tags ["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ 1 4) '(1 2 3))"
"(= (__ -2 2) '(-2 -1 0 1))"
"(= (__ 5 8) '(5 6 7))"]})
@@ -351,6 +386,7 @@
:times-solved 0
:description "Clojure lets you give local names to values using the special let-form."
:tags ["elementary" "syntax"]
+ :approved true
:tests ["(= __ (let [x 5] (+ 2 x)))"
"(= __ (let [x 3, y 10] (- y x)))"
"(= __ (let [x 21] (let [y 3] (/ x y))))"]})
@@ -361,6 +397,7 @@
:times-solved 0
:description "Can you bind x, y, and z so that these are all true?"
:tags ["elementary" "math" "syntax"]
+ :approved true
:tests ["(= 10 (let __ (+ x y)))"
"(= 4 (let __ (+ y z)))"
"(= 1 (let __ z))"]})
@@ -371,6 +408,7 @@
:times-solved 0
:description "Regex patterns are supported with a special reader macro."
:tags ["elementary" "regex" "syntax"]
+ :approved true
:tests ["(= __ (apply str (re-seq #\"[A-Z]+\" \"bA1B3Ce \")))"]})
(insert! :problems
@@ -380,6 +418,7 @@
:restricted ["max" "max-key"]
:description "Write a function which takes a variable number of parameters and returns the maximum value."
:tags ["easy" "core-functions"]
+ :approved true
:tests ["(= (__ 1 8 3 4) 8)"
"(= (__ 30 20) 30)"
"(= (__ 45 67 11) 67)"]})
@@ -392,6 +431,7 @@
:restricted ["interleave"]
:description "Write a function which takes two sequences and returns the first item from each, then the second item from each, then the third, etc."
:tags ["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ [1 2 3] [:a :b :c]) '(1 :a 2 :b 3 :c))"
"(= (__ [1 2] [3 4 5 6]) '(1 3 2 4))"
"(= (__ [1 2 3 4] [5]) [1 5])"
@@ -406,6 +446,7 @@
:restricted ["interpose"]
:description "Write a function which separates the items of a sequence by an arbitrary value."
:tags ["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ 0 [1 2 3]) [1 0 2 0 3])"
"(= (apply str (__ \", \" [\"one\" \"two\" \"three\"])) \"one, two, three\")"
"(= (__ :z [:a :b :c :d]) [:a :z :b :z :c :z :d])"]})
@@ -416,6 +457,7 @@
:times-solved 0
:description "Write a function which drops every Nth item from a sequence."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= (__ [1 2 3 4 5 6 7 8] 3) [1 2 4 5 7 8])"
"(= (__ [:a :b :c :d :e :f] 2) [:a :c :e])"
"(= (__ [1 2 3 4 5 6] 4) [1 2 3 5 6])"]})
@@ -426,6 +468,7 @@
:times-solved 0
:description "Write a function which calculates factorials."
:tags ["easy" "math"]
+ :approved true
:tests ["(= (__ 1) 1)"
"(= (__ 3) 6)"
"(= (__ 5) 120)"
@@ -437,6 +480,7 @@
:times-solved 0
:description "Write a function which reverses the interleave process into x number of subsequences."
:tags ["medium" "seqs"]
+ :approved true
:tests ["(= (__ [1 2 3 4 5 6] 2) '((1 3 5) (2 4 6)))"
"(= (__ (range 9) 3) '((0 3 6) (1 4 7) (2 5 8)))"
"(= (__ (range 10) 5) '((0 5) (1 6) (2 7) (3 8) (4 9)))"]})
@@ -447,6 +491,7 @@
:times-solved 0
:description "Write a function which can rotate a sequence in either direction."
:tags ["medium" "seqs"]
+ :approved true
:tests ["(= (__ 2 [1 2 3 4 5]) '(3 4 5 1 2))"
"(= (__ -2 [1 2 3 4 5]) '(4 5 1 2 3))"
"(= (__ 6 [1 2 3 4 5]) '(2 3 4 5 1))"
@@ -459,6 +504,7 @@
:times-solved 0
:description "The iterate function can be used to produce an infinite lazy sequence."
:tags ["easy" "seqs"]
+ :approved true
:tests ["(= __ (take 5 (iterate #(+ 3 %) 1)))"]})
(insert! :problems
@@ -467,6 +513,7 @@
:times-solved 0
:description "Write a higher-order function which flips the order of the arguments of an input function."
:tags ["medium" "higher-order-functions"]
+ :approved true
:tests ["(= 3 ((__ nth) 2 [1 2 3 4 5]))"
"(= true ((__ >) 7 8))"
"(= 4 ((__ quot) 2 8))"
@@ -478,6 +525,7 @@
:times-solved 0
:description "The contains? function checks if a KEY is present in a given collection. This often leads beginner clojurians to use it incorrectly with numerically indexed collections like vectors and lists."
:tags ["easy"]
+ :approved true
:tests ["(contains? #{4 5 6} __)"
"(contains? [1 1 1 1 1] __)"
"(contains? {4 :a 2 :b} __)"
@@ -489,6 +537,7 @@
:times-solved 0
:description "The some function takes a predicate function and a collection. It returns the first logical true value of (predicate x) where x is an item in the collection."
:tags ["easy"]
+ :approved true
:tests ["(= __ (some #{2 7 6} [5 6 7 8]))"
"(= __ (some #(when (even? %) %) [5 6 7 8]))"]})
@@ -499,6 +548,7 @@
:restricted ["split-at"]
:description "Write a function which will split a sequence into two parts."
:tags ["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ 3 [1 2 3 4 5 6]) [[1 2 3] [4 5 6]])"
"(= (__ 1 [:a :b :c :d]) [[:a] [:b :c :d]])"
"(= (__ 2 [[1 2] [3 4] [5 6]]) [[[1 2] [3 4]] [[5 6]]])"]})
@@ -509,6 +559,7 @@
:times-solved 0
:description "Write a function which takes a sequence consisting of items with different types and splits them up into a set of homogeneous sub-sequences. The internal order of each sub-sequence should be maintained, but the sub-sequences themselves can be returned in any order (this is why 'set' is used in the test cases)."
:tags ["medium" "seqs"]
+ :approved true
:tests ["(= (set (__ [1 :a 2 :b 3 :c])) #{[1 2 3] [:a :b :c]})"
"(= (set (__ [:a \"foo\" \"bar\" :b])) #{[:a :b] [\"foo\" \"bar\"]})"
"(= (set (__ [[1 2] :a [3 4] 5 6 :b])) #{[[1 2] [3 4]] [:a :b] [5 6]})"]})
@@ -519,6 +570,7 @@
:times-solved 0
:description "Here is an example of some more sophisticated destructuring."
:tags ["easy" "destructuring"]
+ :approved true
:tests ["(= [1 2 [3 4 5] [1 2 3 4 5]] (let [[a b & c :as d] __] [a b c d]))"]})
(insert! :problems
@@ -527,14 +579,16 @@
:times-solved 0
:description "Let bindings and function parameter lists support destructuring."
:tags ["easy" "destructuring"]
+ :approved true
:tests ["(= [2 4] (let [[a b c d e f g] (range)] __))"]})
(insert! :problems
{:_id 53
:title "Longest Increasing Sub-Seq"
:times-solved 0
- :description "Given a vector of integers, find the longest consecutive sub-sequence of increasing numbers. If two sub-sequences have the same length, use the one that occurs first."
+ :description "Given a vector of integers, find the longest consecutive sub-sequence of increasing numbers. If two sub-sequences have the same length, use the one that occurs first. An increasing sub-sequence must have a length of 2 or greater to qualify."
:tags ["hard" "seqs"]
+ :approved true
:tests ["(= (__ [1 0 1 2 3 0 4 5]) [0 1 2 3])"
"(= (__ [5 6 1 3 2 7]) [5 6])"
"(= (__ [2 3 3 4 5]) [3 4 5])"
@@ -547,6 +601,7 @@
:restricted ["partition" "partition-all"]
:description "Write a function which returns a sequence of lists of x items each. Lists of less than x items should not be returned."
:tags ["medium" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ 3 (range 9)) '((0 1 2) (3 4 5) (6 7 8)))"
"(= (__ 2 (range 8)) '((0 1) (2 3) (4 5) (6 7)))"
"(= (__ 3 (range 8)) '((0 1 2) (3 4 5)))"]})
@@ -558,6 +613,7 @@
:restricted ["frequencies"]
:description "Write a function which returns a map containing the number of occurences of each distinct item in a sequence."
:tags ["medium" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ [1 1 2 3 2 1 1]) {1 4, 2 2, 3 1})"
"(= (__ [:b :a :b :a :b]) {:a 2, :b 3})"
"(= (__ '([1 2] [1 3] [1 3])) {[1 2] 1, [1 3] 2})"]})
@@ -569,6 +625,7 @@
:restricted ["distinct"]
:description "Write a function which removes the duplicates from a sequence. Order of the items must be maintained."
:tags ["medium" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ [1 2 1 3 1 2 4]) [1 2 3 4])"
"(= (__ [:a :a :b :b :c :c]) [:a :b :c])"
"(= (__ '([2 4] [1 2] [1 3] [1 3])) '([2 4] [1 2] [1 3]))"]})
@@ -580,6 +637,7 @@
:restricted ["distinct"]
:description "Write a function which removes the duplicates from a sequence. Order of the items must be maintained."
:tags ["medium" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ [1 2 1 3 1 2 4]) [1 2 3 4])"
"(= (__ [:a :a :b :b :c :c]) [:a :b :c])"
"(= (__ '([2 4] [1 2] [1 3] [1 3])) '([2 4] [1 2] [1 3]))"
@@ -591,6 +649,7 @@
:times-solved 0
:description "A recursive function is a function which calls itself. This is one of the fundamental techniques used in functional programming."
:tags ["elementary" "recursion"]
+ :approved true
:tests ["(= __ ((fn foo [x] (when (> x 0) (conj (foo (dec x)) x))) 5))"]})
(insert! :problems
@@ -600,6 +659,7 @@
:restricted ["comp"]
:description "Write a function which allows you to create function compositions. The parameter list should take a variable number of functions, and create a function applies them from right-to-left."
:tags ["medium" "higher-order-functions" "core-functions"]
+ :approved true
:tests ["(= [3 2 1] ((__ rest reverse) [1 2 3 4]))"
"(= 5 ((__ (partial + 3) second) [1 2 3 4]))"
"(= true ((__ zero? #(mod % 8) +) 3 5 7 9))"
@@ -612,6 +672,7 @@
:restricted ["juxt"]
:description "Take a set of functions and return a new function that takes a variable number of arguments and returns a sequence containing the result of applying each function left-to-right to the argument list."
:tags ["medium" "higher-order-functions" "core-functions"]
+ :approved true
:tests ["(= [21 6 1] ((__ + max min) 2 3 5 1 6 4))"
"(= [\"HELLO\" 5] ((__ #(.toUpperCase %) count) \"hello\"))"
"(= [2 6 4] ((__ :a :c :b) {:a 2, :b 4, :c 6, :d 8 :e 10}))"]})
@@ -623,6 +684,7 @@
:restricted ["reductions"]
:description "Write a function which behaves like reduce, but returns each intermediate value of the reduction. Your function must accept either two or three arguments, and the return sequence must be lazy."
:tags ["medium" "seqs" "core-functions"]
+ :approved true
:tests ["(= (take 5 (__ + (range))) [0 1 3 6 10])"
"(= (__ conj [1] [2 3 4]) [[1] [1 2] [1 2 3] [1 2 3 4]])"
"(= (last (__ * 2 [3 4 5])) (reduce * 2 [3 4 5]) 120)"]})
@@ -634,6 +696,7 @@
:restricted ["zipmap"]
:description "Write a function which takes a vector of keys and a vector of values and constructs a map from them."
:tags ["easy" "core-functions"]
+ :approved true
:tests ["(= (__ [:a :b :c] [1 2 3]) {:a 1, :b 2, :c 3})"
"(= (__ [1 2 3 4] [\"one\" \"two\" \"three\"]) {1 \"one\", 2 \"two\", 3 \"three\"})"
"(= (__ [:foo :bar] [\"foo\" \"bar\" \"baz\"]) {:foo \"foo\", :bar \"bar\"})"]})
@@ -645,6 +708,7 @@
:restricted ["iterate"]
:description "Given a side-effect free function f and an initial value x write a function which returns an infinite lazy sequence of x, (f x), (f (f x)), (f (f (f x))), etc."
:tags ["easy" "seqs" "core-functions"]
+ :approved true
:tests ["(= (take 5 (__ #(* 2 %) 1)) [1 2 4 8 16])"
"(= (take 100 (__ inc 0)) (take 100 (range)))"
"(= (take 9 (__ #(inc (mod % 3)) 1)) (take 9 (cycle [1 2 3])))"]})
@@ -656,6 +720,7 @@
:restricted ["group-by"]
:description "Given a function f and a sequence s, write a function which returns a map. The keys should be the values of f applied to each item in s. The value at each key should be a vector of corresponding items in the order they appear in s."
:tags ["medium" "seqs" "core-functions"]
+ :approved true
:tests ["(= (__ #(> % 5) #{1 3 6 8}) {false [1 3], true [6 8]})"
"(= (__ #(apply / %) [[1 2] [2 4] [4 6] [3 6]])\n {1/2 [[1 2] [2 4] [3 6]], 2/3 [[4 6]]})"
"(= (__ count [[1] [1 2] [3] [1 2 3] [2 3]])\n {1 [[1] [3]], 2 [[1 2] [2 3]], 3 [[1 2 3]]})"]})
@@ -666,6 +731,7 @@
:times-solved 0
:description "<a href='http://clojuredocs.org/clojure_core/clojure.core/reduce'>Reduce</a> takes a 2 argument function and an optional starting value. It then applies the function to the first 2 items in the sequence (or the starting value and the first element of the sequence). In the next iteration the function will be called on the previous return value and the next item from the sequence, thus reducing the entire collection to one value. Don't worry, it's not as complicated as it sounds."
:tags ["elementary" "seqs"]
+ :approved true
:tests ["(= 15 (reduce __ [1 2 3 4 5]))"
"(= 0 (reduce __ []))"
"(= 6 (reduce __ 1 [2 3]))"]})
@@ -676,6 +742,7 @@
:times-solved 0
:description "Clojure has many collection types, which act in subtly different ways. The core functions typically convert them into a uniform \"sequence\" type and work with them that way, but it can be important to understand the behavioral and performance differences so that you know which kind is appropriate for your application.<br /><br />Write a function which takes a collection and returns one of :map, :set, :list, or :vector - describing the type of collection it was given.<br />You won't be allowed to inspect their class or use the built-in predicates like list? - the point is to poke at them and understand their behavior."
:tags ["hard" "seqs" "testing"]
+ :approved true
:tests ["(= :map (__ {:a 1, :b 2}))",
"(= :list (__ (range (rand-int 20))))",
"(= :vector (__ [1 2 3 4 5 6]))",
@@ -691,6 +758,7 @@
:description "Given two integers, write a function which
returns the greatest common divisor."
:tags ["easy"]
+ :approved true
:tests ["(= (__ 2 4) 2)"
"(= (__ 10 5) 5)"
"(= (__ 5 7) 1)"
@@ -703,6 +771,7 @@ returns the greatest common divisor."
:description "Write a function which returns the first x
number of prime numbers."
:tags ["medium" "primes"]
+ :approved true
:tests ["(= (__ 2) [2 3])"
"(= (__ 5) [2 3 5 7 11])"
"(= (last (__ 100)) 541)"]})
@@ -713,6 +782,7 @@ number of prime numbers."
:times-solved 0
:description "Clojure only has one non-stack-consuming looping construct: recur. Either a function or a loop can be used as the recursion point. Either way, recur rebinds the bindings of the recursion point to the values it is passed. Recur must be called from the tail-position, and calling it elsewhere will result in an error."
:tags ["elementary" "recursion"]
+ :approved true
:tests ["(= __\n (loop [x 5\n result []]\n (if (> x 0)\n (recur (dec x) (conj result (+ 2 x)))\n result)))"]})
(insert! :problems
@@ -722,6 +792,7 @@ number of prime numbers."
:restricted ["merge-with"]
:description "Write a function which takes a function f and a variable number of maps. Your function should return a map that consists of the rest of the maps conj-ed onto the first. If a key occurs in more than one map, the mapping(s) from the latter (left-to-right) should be combined with the mapping in the result by calling (f val-in-result val-in-latter)"
:tags ["medium" "core-functions"]
+ :approved true
:tests ["(= (__ * {:a 2, :b 3, :c 4} {:a 2} {:b 2} {:c 5})\n {:a 4, :b 6, :c 20})"
"(= (__ - {1 10, 2 20} {1 3, 2 10, 3 15})\n {1 7, 2 10, 3 15})"
"(= (__ concat {:a [3], :b [6]} {:a [4 5], :c [8 9]} {:b [7]})\n {:a [3 4 5], :b [6 7], :c [8 9]})"]})
@@ -732,20 +803,112 @@ number of prime numbers."
:times-solved 0
:description "Write a function which splits a sentence up into a sorted list of words. Capitalization should not affect sort order and punctuation should be ignored."
:tags ["medium" "sorting"]
+ :approved true
:tests ["(= (__ \"Have a nice day.\")\n [\"a\" \"day\" \"Have\" \"nice\"])"
"(= (__ \"Clojure is a fun language!\")\n [\"a\" \"Clojure\" \"fun\" \"is\" \"language\"])"
"(= (__ \"Fools fall for foolish follies.\")\n [\"fall\" \"follies\" \"foolish\" \"Fools\" \"for\"])"]})
+
+ (insert! :problems
+ {:_id 71
+ :title "Rearranging Code: ->"
+ :times-solved 0
+ :description "The -> macro threads an expression x through a variable number of forms. First, x is inserted as the second item in the first form, making a list of it if it is not a list already. Then the first form is inserted as the second item in the second form, making a list of that form if necessary. This process continues for all the forms. Using -> can sometimes make your code more readable."
+ :tags ["elementary"]
+ :approved true
+ :tests ["(= (__ (sort (rest (reverse [2 5 4 1 3 6]))))\n (-> [2 5 4 1 3 6] reverse rest sort __)\n 5)"]})
+
+ (insert! :problems
+ {:_id 72
+ :title "Rearranging Code: ->>"
+ :times-solved 0
+ :description "The ->> macro threads an expression x through a variable number of forms. First, x is inserted as the last item in the first form, making a list of it if it is not a list already. Then the first form is inserted as the last item in the second form, making a list of that form if necessary. This process continues for all the forms. Using ->> can sometimes make your code more readable."
+ :tags ["elementary"]
+ :approved true
+ :tests ["(= (__ (map inc (take 3 (drop 2 [2 5 4 1 3 6]))))\n (->> [2 5 4 1 3 6] (drop 2) (take 3) (map inc) (__))\n 11)"]})
+
+ (insert! :problems
+ {:_id 73
+ :title "Analyze a Tic-Tac-Toe Board"
+ :times-solved 0
+ :description "A <a href=\"http://en.wikipedia.org/wiki/Tic-tac-toe\">tic-tac-toe</a> board is represented by a two dimensional vector. X is represented by :x, O is represented by :o, and empty is represented by :e. A player wins by placing three Xs or three Os in a horizontal, vertical, or diagonal row. Write a function which analyzes a tic-tac-toe board and returns :x if X has won, :o if O has won, and nil if neither player has won."
+ :tags ["medium" "game"]
+ :tests ["(= nil (__ [[:e :e :e]\n [:e :e :e]\n [:e :e :e]]))"
+ "(= :x (__ [[:x :e :o]\n [:x :e :e]\n [:x :e :o]]))"
+ "(= :o (__ [[:e :x :e]\n [:o :o :o]\n [:x :e :x]]))"
+ "(= nil (__ [[:x :e :o]\n [:x :x :e]\n [:o :x :o]]))"
+ "(= :x (__ [[:x :e :e]\n [:o :x :e]\n [:o :e :x]]))"
+ "(= :o (__ [[:x :e :o]\n [:x :o :e]\n [:o :e :x]]))"
+ "(= nil (__ [[:x :o :x]\n [:x :o :x]\n [:o :x :o]]))"]})
+
+ (insert! :problems
+ {:_id 74
+ :title "Filter Perfect Squares"
+ :times-solved 0
+ :description "Given a string of comma separated integers, write a function which returns a new comma separated string that only contains the numbers which are perfect squares."
+ :tags ["medium"]
+ :tests ["(= (__ \"4,5,6,7,8,9\") \"4,9\")"
+ "(= (__ \"15,16,25,36,37\") \"16,25,36\")"]})
+
+ (insert! :problems
+ {:_id 75
+ :title "Euler's Totient Function"
+ :times-solved 0
+ :description "Two numbers are coprime if their greatest common divisor equals 1. Euler's totient function f(x) is defined as the number of positive integers less than x which are coprime to x. The special case f(1) equals 1. Write a function which calculates Euler's totient function."
+ :tags ["medium"]
+ :tests ["(= (__ 1) 1)"
+ "(= (__ 10) (count '(1 3 7 9)) 4)"
+ "(= (__ 40) 16)"
+ "(= (__ 99) 60)"]})
+
+ (insert! :problems
+ {:_id 76
+ :title "Intro to Trampoline"
+ :times-solved 0
+ :description "The trampoline function takes a function f and a variable number of parameters. Trampoline calls f with any parameters that were supplied. If f returns a function, trampoline calls that function with no arguments. This is repeated, until the return value is not a function, and then trampoline returns that non-function value. This is useful for implementing mutually recursive algorithms in a way that won't consume the stack."
+ :tags ["medium", "recursion"]
+ :tests ["(= __\n (letfn\n [(foo [x y] #(bar (conj x y) y))\n (bar [x y] (if (> (last x) 10)\n x\n #(foo x (+ 2 y))))]\n (trampoline foo [] 1)))"]})
+
+ (insert! :problems
+ {:_id 77
+ :title "Create an Equation"
+ :times-solved 0
+ :description "Write a function which takes three or more integers. Using these integers, your function should generate clojure code representing an equation. The following rules for the equation must be satisfied:\n\n 1. All integers must be used once and only once.\n 2. The order of the integers must be maintained when reading the equation left-to-right.\n 3. The only functions you may use are +, *, or =.\n 4. The equation must use the minimum number of parentheses.\n 5. If no satisfying equation exists, return nil."
+ :tags ["hard", "code-generation"]
+ :tests ["(= (__ 3 4 7) '(= (+ 3 4) 7))"
+ "(= (__ 3 4 12) '(= (* 3 4) 12))"
+ "(= (__ 3 4 14) nil)"
+ "(= (__ 3 4 5 35) '(= (* (+ 3 4) 5) 35))"
+ "(= (__ 3 4 5 60) '(= (+ (* 3 4) 5) 60))"
+ "(= (__ 3 4 5 23) '(= (+ 3 (* 4 5)) 23))"
+ "(= (__ 3 4 5 27) '(= (* 3 (+ 4 5)) 27))"
+ "(= (__ 3 4 5 6) nil)"
+ "(= (__ 1 2 10 100 2001) '(= (+ 1 (* 2 10 100)) 2001)"
+ "(= (__ 1 2 10 100 1300) '(= (* (+ 1 2 10) 100) 1300)"]})
+
(insert! :problems
- {:_id 71
+ {:_id 78
+ :title "Reimplement Trampoline"
+ :times-solved 0
+ :restricted ["trampoline"]
+ :description "Reimplement the function described in <a href=\"76\"> \"Intro to Trampoline\"</a>."
+ :approved true
+ :tags ["medium" "core-functions"]
+ :tests ["(= (letfn [(triple [x] #(sub-two (* 3 x)))\n (sub-two [x] #(stop?(- x 2)))\n (stop? [x] (if (> x 50) x #(triple x)))]\n (__ triple 2))\n 82)"
+ "(= (letfn [(my-even? [x] (if (zero? x) true #(my-odd? (dec x))))\n (my-odd? [x] (if (zero? x) false #(my-even? (dec x))))]\n (map (partial __ my-even?) (range 6)))\n [true false true false true false])"]})
+
+ (insert! :problems
+ {:_id 79
:title "Power Set"
:times-solved 0
:description "A power set is the set of all subsets of a given set. Given a list, produce a set of sublists while preserving the order of elements."
- :tags ["medium" "seqs"]
+ :approved true
+ :tags ["hard" "seqs"]
:tests ["(= (__ '(1 :a)) '#{(1 :a) (:a) () (1)})"
"(= (__ '()) '#{()})"
"(= (__ '(1 2 3)) '#{() (1) (2) (3) (1 2) (1 3) (2 3) (1 2 3)})"
"(= (count (__ (range 10))) 1024)"]})
+
))
(load-problems)
View
23 src/foreclojure/golf.clj
@@ -0,0 +1,23 @@
+(ns foreclojure.golf
+ (:use hiccup.form-helpers
+ hiccup.page-helpers
+ [foreclojure utils config]
+ compojure.core
+ somnium.congomongo)
+ (:require [ring.util.response :as response]))
+
+(def-page golfer-page []
+ "Your preferences have been saved.")
+
+(defn set-golfer [opt-in]
+ (with-user [{:keys [_id]}]
+ (update! :users
+ {:_id _id}
+ {:$set {:golfer (boolean opt-in)}})
+ (response/redirect "/golf/opt-in")))
+
+(defroutes golf-routes
+ (POST "/golf/opt-in" [opt-in]
+ (set-golfer opt-in))
+ (GET "/golf/opt-in" []
+ (golfer-page)))
View
64 src/foreclojure/graphs.clj
@@ -0,0 +1,64 @@
+(ns foreclojure.graphs
+ (:use compojure.core
+ (foreclojure utils)
+ somnium.congomongo
+ (amalloy.utils [transform :only [with-adjustments]]
+ [reorder :only [reorder]]))
+ (:require (incanter [charts :as chart]
+ [core :as incanter]
+ [stats :as stats]))
+ (:import (java.io ByteArrayInputStream
+ ByteArrayOutputStream)))
+
+(defn un-group
+ "Turn a compact set of [data-point num-repetitions] pairs into a
+ bunch of repeated data points so that incanter will make a histogram
+ of them."
+ [frequencies]
+ (mapcat (partial apply (reorder repeat))
+ frequencies))
+
+(defn fetch-score-frequencies [problem-id]
+ (into {}
+ (for [[k v] (:scores
+ (from-mongo
+ (fetch-one :problems
+ :where {:_id problem-id}
+ :only [:scores])))]
+ [(Integer/parseInt (name k)), v])))
+
+(defn make-problem-plot [id best curr]
+ (let [freqs (fetch-score-frequencies id)
+ chart-data (un-group freqs)
+ [best curr] [(or best curr) (or curr best)]
+ chart (chart/histogram chart-data
+ :title (str "League scores: problem " id)
+ :x-label "Solution length"
+ :y-label "How often")]
+ (when best
+ (chart/add-pointer chart best (freqs best 0)
+ :text "best"
+ :angle :se))
+ (when (and curr (not= curr best))
+ (chart/add-pointer chart curr (freqs curr 0)
+ :text "this"
+ :angle :se))
+ (when-not (> (count freqs) 1)
+ (chart/add-text chart best (freqs best 0)
+ "Very little golfing data - chart may suck"))
+ (doto chart (chart/set-theme :bw))))
+
+(defn serve-plot [plot]
+ (let [out (ByteArrayOutputStream.)
+ in (do
+ (incanter/save plot out)
+ (ByteArrayInputStream. (.toByteArray out)))]
+ {:response 200
+ :headers {"Content-Type" "image/png"}
+ :body in}))
+
+(defroutes graph-routes
+ (GET "/leagues/golf/:id" [id best curr]
+ (with-adjustments #(when (seq %) (Integer/parseInt %)) [id best curr]
+ (serve-plot (make-problem-plot id best curr)))))
+
View
39 src/foreclojure/login.clj
@@ -2,7 +2,7 @@
(:import org.jasypt.util.password.StrongPasswordEncryptor)
(:use hiccup.form-helpers
hiccup.page-helpers
- [foreclojure utils config]
+ [foreclojure utils config users]
compojure.core
[amalloy.utils :only [rand-in-range]]
somnium.congomongo)
@@ -47,18 +47,31 @@
(flash-error "Error logging in." "/login"))))
(def-page update-password-page []
- (with-user [{:keys [user]}]
- [:div#update-pwd
- [:h2 "Change password for " user]
- [:span.error (session/flash-get :error)]
- [:table
- (form-to [:post "/login/update"]
- (map form-row
- [[password-field :old-pwd "Current password"]
- [password-field :pwd "New password"]
- [password-field :repeat-pwd "Repeat password"]])
- [:tr
- [:td [:button {:type "submit"} "Reset now"]]])]]))
+ (with-user [{:keys [user] :as user-obj}]
+ [:div#account-settings
+ [:div#update-pwd
+ [:h2 "Change password for " user]
+ [:span.error (session/flash-get :error)]
+ [:table
+ (form-to [:post "/login/update"]
+ (map form-row
+ [[password-field :old-pwd "Current password"]
+ [password-field :pwd "New password"]
+ [password-field :repeat-pwd "Repeat password"]])
+ [:tr
+ [:td [:button {:type "submit"} "Reset now"]]])]]
+ (when (:golfing-active config)
+ [:div#golf-opt-in
+ [:h2 "Code golf!"]
+ [:table
+ (form-to [:post "/golf/opt-in"]
+ [:tr
+ [:td
+ (check-box :opt-in
+ (golfer? user-obj))
+ [:label {:for "opt-in"}
+ "I want to join the golf league and compete to find the shortest solutions"]]]
+ [:tr [:td [:button {:type "submit"} "Update"]]])]])]))
(defn do-update-password! [old-pwd new-pwd repeat-pwd]
(with-user [{:keys [user pwd]}]
View
168 src/foreclojure/problems.clj
@@ -1,24 +1,20 @@
(ns foreclojure.problems
- (:use (foreclojure utils
+ (:use (foreclojure utils config
[social :only [tweet-link gist!]]
- [feeds :only [create-feed]])
+ [feeds :only [create-feed]]
+ [users :only [golfer?]])
[clojail core testers]
somnium.congomongo
(hiccup form-helpers page-helpers core)
- [amalloy.utils.debug :only [?]]
+ (amalloy.utils [debug :only [?]]
+ [reorder :only [reorder]])
+ [amalloy.utils :only [defcomp]]
compojure.core)
(:require [sandbar.stateful-session :as session]
[clojure.string :as s]))
(def total-solved (agent 0))
-(defn get-solved [user]
- (set
- (:solved (from-mongo
- (fetch-one :users
- :where {:user user}
- :only [:solved])))))
-
(defn get-problem [x]
(from-mongo
(fetch-one :problems :where {:_id x})))
@@ -27,8 +23,13 @@
(from-mongo
(fetch :problems
:only [:_id :title :tags :times-solved]
+ :where {:approved true}
:sort {:_id 1})))
+(defn get-next-id []
+ (from-mongo
+ (inc (count (fetch :problems)))))
+
(defn next-unsolved-problem [solved-problems]
(when-let [unsolved (->> (get-problem-list)
(remove (comp (set solved-problems) :_id))
@@ -53,6 +54,44 @@
()
(get-recent-problems n)))
+(defcomp mongo-key-from-number
+ "Turn an integer into a key suitable for fetching from mongodb."
+ [id]
+ keyword str int)
+
+(defn code-length [code]
+ (count (remove #(Character/isWhitespace %)
+ code)))
+
+(defn record-golf-score! [user-name problem-id score]
+ (let [user-score-key (keyword (str "scores." problem-id))
+ problem-score-key (keyword (str "scores." score))
+ [problem-scores-key user-subkey] (map mongo-key-from-number
+ [score problem-id])]
+ (when-let [{:keys [_id scores] :as user}
+ (from-mongo
+ (fetch-one :users
+ :where {:user user-name}))]
+ (let [old-score-real (get scores user-subkey)
+ old-score-test (or old-score-real 1e6)
+ old-score-key (keyword (str "scores." old-score-real))]
+ (when (golfer? user)
+ (session/session-put! :golf-chart
+ {:id problem-id
+ :score score
+ :best old-score-real}))
+ (when (< score old-score-test)
+ (update! :problems
+ {:_id problem-id,
+ old-score-key {:$gt 0}}
+ {:$inc {old-score-key -1}})
+ (update! :problems
+ {:_id problem-id}
+ {:$inc {problem-score-key 1}})
+ (update! :users
+ {:_id _id}
+ {:$set {user-score-key score}}))))))
+
(defn mark-completed [id code & [user]]
(let [user (or user (session/session-get :user))
gist-link (html [:div.share
@@ -66,6 +105,7 @@
(update! :users {:user user} {:$addToSet {:solved id}})
(update! :problems {:_id id} {:$inc {:times-solved 1}})
(send total-solved inc))
+ (record-golf-score! user id (code-length code))
(str "Congratulations, you've solved the problem!"
"<br />" (next-problem-link id)))
(str "You've solved the problem! If you "
@@ -101,30 +141,42 @@
(catch Exception e
(flash-msg (.getMessage e) this-url))))))
+(defn render-test-cases [tests]
+ [:table {:class "testcases"}
+ (let [fail (session/flash-get :failing-test)]
+ (for [[idx test] (map-indexed list tests)]
+ [:tr
+ [:td
+ [:img {:src (cond
+ (or (nil? fail) (> idx fail)) "/images/bluelight.png"
+ (= idx fail) "/images/redlight.png"
+ :else "/images/greenlight.png")}]]
+ [:td
+ [:pre {:class "brush: clojure;gutter: false;toolbar: false;light: true"}
+ test]]]))])
+
+(defn render-golf-chart []
+ (let [{:keys [id best score] :as settings}
+ (session/session-get :golf-chart)
+
+ url (str "/leagues/golf/" id "?best=" best "&curr=" score)]
+ (session/session-delete-key! :golf-chart)
+ (when settings
+ [:img {:src url}])))
(def-page code-box [id]
- (let [problem (get-problem (Integer. id))]
+ (let [{:keys [title tags description restricted tests]}
+ (get-problem (Integer. id))]
[:div
- [:span {:id "prob-title"} (problem :title)]
+ [:span {:id "prob-title"} title]
[:hr]
[:div {:id "tags"} "Tags: "
- (s/join " " (problem :tags))]
+ (s/join " " tags)]
[:br]
[:div {:id "prob-desc"}
- (problem :description)[:br]
- [:table {:class "testcases"}
- (let [tests (:tests problem)]
- (for [i (range (count tests))]
- [:tr
- [:td
- (let [f (session/flash-get :failing-test)]
- (cond (or (nil? f) (> i f)) [:img {:src "/images/bluelight.png"}]
- (= i f) [:img {:src "/images/redlight.png"}]
- :else [:img {:src "/images/greenlight.png"}]))]
- [:td
- [:pre {:class "brush: clojure;gutter: false;toolbar: false;light: true"}
- (nth tests i)]]]))]
- (if-let [restricted (problem :restricted)]
+ description[:br]
+ (render-test-cases tests)
+ (when restricted
[:div {:id "restrictions"}
[:u "Special Restrictions"] [:br]
(map (partial vector :li) restricted)])]
@@ -132,14 +184,19 @@
[:div.message (session/flash-get :message)]
[:b "Code which fills in the blank:" [:br]]]
(form-to [:post *url*]
- (text-area {:id "code-box"
- :spellcheck "false"}
- :code (session/flash-get :code))
- (hidden-field :id id)
- [:br]
- [:button.large {:id "run-button" :type "submit"} "Run"])]))
+ (text-area {:id "code-box"
+ :spellcheck "false"}
+ :code (session/flash-get :code))
+ [:div#golfgraph
+ (render-golf-chart)]
+ (hidden-field :id id)
+ [:br]
+ [:button.large {:id "run-button" :type "submit"} "Run"])
+ ]))
(def-page problem-page []
+ [:div.message (session/flash-get :message)]
+ [:div.error {:id "problems-error"} (session/flash-get :error)]
(link-to "/problems/rss" [:div {:class "rss"}])
[:table#problem-table.my-table
[:thead
@@ -166,9 +223,54 @@
"/images/empty-sq.png")}]]])
problems))])
+
+(def-page problem-submission-page []
+ [:div.instructions
+ [:p "Thanks for choosing to submit a problem. Please make sure that you own the rights to the code you are submitting and that you wouldn't
+ mind having us use the code as a 4clojure problem."]]
+ (form-to {:id "problem-submission"} [:post "/problems/submit"]
+ (label :title "Problem Title")
+ (text-field :title)
+ (label :tags "Tags (space separated)")
+ (text-field :tags)
+ (label :description "Problem Description")
+ (text-area {:id "problem-description"} :description)
+ [:br]
+ (label :code-box "Problem test cases. Use two underscores (__) for user input. Multiple tests ought to be on one line each.")
+ (text-area {:id "code-box" :spellcheck "false"}
+ :code (session/flash-get :code))
+ [:p
+ [:button.large {:id "run-button" :type "submit"} "Submit"]])
+ )
+
+(defn create-problem
+ "create a user submitted problem"
+ [title tags description code]
+ (if (and (:problem-submission config)
+ (>= (count (get-solved (session/session-get :user)))
+ (:advanced-user-count config)))
+ (do
+ (mongo! :db :mydb)
+ (insert! :problems
+ {:_id (get-next-id)
+ :title title
+ :times-solved 0
+ :description description
+ :tags (s/split tags #"\s+")
+ :tests (s/split-lines code)
+ :user (session/session-get :user)
+ :approved false})
+ (flash-msg "Thank you for submitting a problem! Be sure to check back to see it posted." "/problems"))
+ (flash-error "You are not authorized to submit a problem." "/problems")))
+
+
+
(defroutes problems-routes
(GET "/problems" [] (problem-page))
(GET "/problem/:id" [id] (code-box id))
+ (GET "/problems/submit" [] (problem-submission-page))
+ (POST "/problems/submit" [title tags description code]
+ (create-problem title tags description code))
(POST "/problem/:id" [id code]
(run-code (Integer. id) code))
(GET "/problems/rss" [] (create-feed
View
9 src/foreclojure/static.clj
@@ -2,10 +2,17 @@
(:use compojure.core
[foreclojure problems utils]))
+(def df
+ (let [df (java.text.DecimalFormat.)
+ dfs (java.text.DecimalFormatSymbols.)]
+ (.setGroupingSeparator dfs \,)
+ (.setDecimalFormatSymbols df dfs)
+ df))
+
(def-page welcome-page []
[:div#welcome
[:div#totalcount
- (deref total-solved) " problems solved and counting!"]
+ (.format df (deref total-solved)) " problems solved and counting!"]
[:div
[:h3 "What is 4Clojure?"]
[:p "4Clojure is a resource to help fledgling clojurians learn the language through interactive problems. The first few problems are easy enough that even someone with no prior experience should find the learning curve forgiving. See 'Getting Started' for more information."]]
View
9 src/foreclojure/users.clj
@@ -1,9 +1,13 @@
(ns foreclojure.users
- (:use foreclojure.utils
+ (:use [foreclojure utils config]
somnium.congomongo
compojure.core
[hiccup.page-helpers :only (link-to)]))
+(def golfer-tags (into [:contributor]
+ (when (:golfing-active config)
+ [:golfer])))
+
(defn get-users []
(let [users (from-mongo
(fetch :users
@@ -11,6 +15,9 @@
sortfn (comp count :solved)]
(reverse (sort-by sortfn users))))
+(defn golfer? [user]
+ (some user golfer-tags))
+
(def-page users-page []
[:div
[:span.contributor "*"] " "
View
17 src/foreclojure/utils.clj
@@ -4,7 +4,8 @@
javascript-tag link-to include-js]]
[form-helpers :only [label]])
[amalloy.utils.transform :only [transform-if]]
- somnium.congomongo)
+ somnium.congomongo
+ [foreclojure.config])
(:require [sandbar.stateful-session :as session]
(ring.util [response :as response])
[clojure.walk :as walk])
@@ -84,7 +85,14 @@
{:class (if (even? x)
"evenrow"
"oddrow")})
-
+
+(defn get-solved [user]
+ (set
+ (:solved (from-mongo
+ (fetch-one :users
+ :where {:user user}
+ :only [:solved])))))
+
(defn html-doc [& body]
(html
(doctype :html5)
@@ -92,6 +100,7 @@
[:head
[:title "4Clojure"]
[:link {:rel "alternate" :type "application/atom+xml" :title "Atom" :href "http://4clojure.com/problems/rss"}]
+ [:link {:rel "shortcut icon" :href "/favicon.ico"}]
(include-js "/script/jquery-1.5.2.min.js" "/script/jquery.dataTables.min.js")
(include-js "/script/foreclojure.js")
(include-js "/script/xregexp.js" "/script/shCore.js" "/script/shBrushClojure.js")
@@ -116,6 +125,10 @@
[:a.menu {:href "/directions"} "Getting Started"]
[:a.menu {:href "http://try-clojure.org"} "REPL"]
[:a.menu {:href "http://clojuredocs.org"} "Docs"]
+ (if (and (:problem-submission config)
+ (>= (count (get-solved (session/session-get :user)))
+ (:advanced-user-count config)))
+ [:a.menu {:href "/problems/submit"} "Submit a Problem"])
[:span#user-info
(if-let [user (session/session-get :user)]
[:div
Please sign in to comment.
Something went wrong with that request. Please try again.