aboutsummaryrefslogtreecommitdiff
path: root/examples/sml/checks.sml
blob: d5b726276f2b341796f3a6222c95fe19616cac7e (plain) (blame)
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
(* Copyright (C) 2017-2018 Ryan Kavanagh <rkavanagh@cs.cmu.edu>   *)
(* Distributed under the ISC license, see COPYING for details.    *)

(* This is a hypothetical assignment where students have to       *)
(* implement addition. There are two Autolab problems testing     *)
(* their implementation on "easy" and "hard" pairs of input.      *)
(* The scoreboard just keeps track of how many bonuses problems   *)
(* their implementation solves.                                   *)
(* Because addition can be very lengthy, we timeout the execution *)
(* after a fixed amount of time.                                  *)

functor ChecksHelper (structure H : HELPER) : CHECKS where type checks = H.checks =
struct

datatype checks = datatype H.checks

(*****************************************************)
(**********       CONFIGURE ME HERE            *******)
(*****************************************************)

structure Student : ADDER = Adder

(* Run function f with arguments a with timeout of n seconds. *)
fun try n f a = Timeout.runWithTimeout (Time.fromSeconds n) f a

(* test data is of the form *)
(* ("autolab exercise name", maxScore, [(input,expected),...]) *)
val testSums = [("easy", 4, [((1,1),2), ((1,2),3), ((2,2),4), ((3,5),8)]),
		("hard", 8, [((152,203),355), ((1212,2121),3333)])]

(* Bonus problems that get used for score board *)
val bonusSums = [((10,~10),0)]
(* Pretty print problem input *)
fun inputToString (x,y) = (Int.toString x) ^ " + " ^ (Int.toString y)

(* Compare student output to expected output with a timeout, and *)
(* compute the % of correct answers. *)
fun compare tests =
  let fun c (input,expected) =
	let val testStr = inputToString input in
	    case try 10 Student.add input of
		(* No timeout *)
		SOME b => if b = expected then
			      ( H.printLn ("Passed: " ^ testStr)
			      ; (1, 0))
			  else
			      ( H.printLn ("Failed: " ^ testStr)
			      ; H.printLn ("        Expected "
					   ^ (Int.toString expected)
					   ^ " but got " ^ (Int.toString b))
			      ; (0, 1) )
	      | NONE => ( H.printLn ("Timed out after 10s on " ^ testStr)
			; (0, 1))
	end
      fun addp (a, b) (c, d) = (a + c, b + d)
      val (same,diff) = List.foldl (fn (p,acc) => addp (c p) acc)
				   (0, 0)
				   tests
  in real same / real (same + diff) end

val checks = List.map (fn (name,max,tests) =>
			  H.Problem (name, fn _ => real max * compare tests))
		      testSums

fun scoreboard score =
  ( H.printLn "Checking bonuses..."
  ; SOME [List.foldl
	      (fn ((p,b),acc) =>
		  if try 2 Student.add p = SOME b then
		      ( H.printLn ("Got bonus " ^ (inputToString p))
		      ; H.printLn ("Well done!")
		      ; acc + 1
		      )
		  else
		      ( H.printLn ("Failed to add bonus " ^ (inputToString p))
		      ; H.printLn ("in under 2 seconds. Skipping.")
		      ; acc
		      )
	      )
	      0
	      bonusSums] )

(*****************************************************)
(**********        END CONFIGURATION           *******)
(*****************************************************)

end