-
-
Save dchelimsky/6cf747aa36c3d60031f51f435b7eb009 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns codebreaker | |
(:require [clojure.spec :as s] | |
[clojure.spec.gen :as gen] | |
[clojure.spec.test :as stest])) | |
(comment | |
(score [:r :y :g :b] [:r :g :b :b]) | |
;; => {::exact-matches 2 ;; :r and :b | |
;; ::loose-matches 1} ;; :g | |
) | |
(def peg? #{:y :g :r :c :w :b}) | |
(s/def ::code (s/coll-of peg? :min-count 4 :max-count 6)) | |
(s/fdef score | |
:args (s/cat :secret ::code :guess ::code)) | |
(comment | |
(s/exercise (:args (s/get-spec `score))) | |
) | |
(s/fdef score | |
:args (s/and (s/cat :secret ::code :guess ::code) | |
(fn [{:keys [secret guess]}] | |
(= (count secret) (count guess))))) | |
(comment | |
(s/exercise (:args (s/get-spec `score))) | |
) | |
(s/def ::exact-matches nat-int?) | |
(s/def ::loose-matches nat-int?) | |
(s/fdef score | |
:args (s/and (s/cat :secret ::code :guess ::code) | |
(fn [{:keys [secret guess]}] | |
(= (count secret) (count guess)))) | |
:ret (s/keys :req [::exact-matches ::loose-matches])) | |
(comment | |
(s/exercise (:ret (s/get-spec `score))) | |
;; note that some values are too high | |
) | |
(s/fdef score | |
:args (s/and (s/cat :secret ::code :guess ::code) | |
(fn [{:keys [secret guess]}] | |
(= (count secret) (count guess)))) | |
:ret (s/keys :req [::exact-matches ::loose-matches]) | |
:fn (fn [{{secret :secret} :args ret :ret}] | |
(<= 0 (apply + (vals ret)) (count secret)))) | |
(comment | |
(s/exercise-fn `score) | |
;; NPE | |
(s/exercise-fn #'score) | |
;; Unable to resolve var: score in this context | |
) | |
(defn score [secret guess] | |
{::exact-matches 0 | |
::loose-matches 0}) | |
(comment | |
(s/exercise-fn `score) | |
(stest/check `score) | |
) | |
(defn score [secret guess] | |
{::exact-matches 4 | |
::loose-matches 3}) | |
(comment | |
(s/exercise-fn `score) | |
(stest/check `score) | |
;; fails | |
) | |
(defn score [secret guess] | |
{::exact-matches (count (filter true? (map = secret guess))) | |
::loose-matches 0}) | |
(comment | |
(s/exercise-fn `score) | |
(stest/check `score) | |
) | |
(defn exact-matches [secret guess] | |
(count (filter true? (map = secret guess)))) | |
(defn score [secret guess] | |
{::exact-matches (exact-matches secret guess) | |
::loose-matches 0}) | |
(s/def ::secret-and-guess (s/and (s/cat :secret ::code :guess ::code) | |
(fn [{:keys [secret guess]}] | |
(= (count secret) (count guess))))) | |
(s/fdef score | |
:args ::secret-and-guess | |
:ret (s/keys :req [::exact-matches ::loose-matches]) | |
:fn (fn [{{secret :secret} :args ret :ret}] | |
(<= 0 (apply + (vals ret)) (count secret)))) | |
(s/fdef exact-matches | |
:args ::secret-and-guess | |
:ret nat-int? | |
:fn (fn [{{secret :secret} :args ret :ret}] | |
(<= 0 ret (count secret)))) | |
(comment | |
(s/exercise-fn `exact-matches) | |
(stest/check `exact-matches) | |
(stest/instrument `exact-matches) | |
(s/exercise-fn `score) | |
(stest/check `score) | |
) | |
;; introduce bug | |
(defn score [secret guess] | |
{::exact-matches (exact-matches secret (take 3 guess)) | |
::loose-matches 0}) | |
(comment | |
;; this will expose it | |
(s/exercise-fn `score) | |
) | |
;; remove bug | |
(defn score [secret guess] | |
{::exact-matches (exact-matches secret guess) | |
::loose-matches 0}) | |
(s/fdef match-count | |
:args ::secret-and-guess | |
:ret nat-int? | |
:fn (fn [{{secret :secret} :args ret :ret}] | |
(<= 0 ret (count secret)))) | |
(comment | |
(s/exercise-fn `exact-matches 10 (s/get-spec `match-count)) | |
(stest/check-fn exact-matches (s/get-spec `match-count)) | |
(stest/instrument `exact-matches {:spec {`exact-matches (s/get-spec `exact-matches)}}) | |
(s/exercise-fn `score) | |
(stest/check `score) | |
) | |
(defn all-matches [secret guess] | |
(apply + (vals (merge-with min | |
(select-keys (frequencies secret) guess) | |
(select-keys (frequencies guess) secret))))) | |
(comment | |
(s/exercise-fn `all-matches 10 (s/get-spec `match-count)) | |
) | |
(defn score [secret guess] | |
(let [exact (exact-matches secret guess) | |
all (all-matches secret guess)] | |
{::exact-matches exact | |
::loose-matches (- all exact)})) | |
(comment | |
(stest/instrument [`exact-matches `all-matches] | |
{:spec {`exact-matches (s/get-spec `match-count) | |
`all-matches (s/get-spec `match-count)}}) | |
(s/exercise-fn `score) | |
(stest/check `score) | |
) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;; END RESULT | |
(def peg? #{:y :g :r :c :w :b}) | |
(s/def ::code (s/coll-of peg? :min-count 4 :max-count 6)) | |
(s/def ::secret-and-guess (s/and (s/cat :secret ::code :guess ::code) | |
(fn [{:keys [secret guess]}] | |
(= (count secret) (count guess))))) | |
(s/fdef score | |
:args ::secret-and-guess | |
:ret (s/keys :req [::exact-matches ::loose-matches]) | |
:fn (fn [{{secret :secret} :args ret :ret}] | |
(<= 0 (apply + (vals ret)) (count secret)))) | |
(s/fdef match-count | |
:args ::secret-and-guess | |
:ret nat-int? | |
:fn (fn [{{secret :secret} :args ret :ret}] | |
(<= 0 ret (count secret)))) | |
(defn exact-matches [secret guess] | |
(count (filter true? (map = secret guess)))) | |
(defn all-matches [secret guess] | |
(apply + (vals (merge-with min | |
(select-keys (frequencies secret) guess) | |
(select-keys (frequencies guess) secret))))) | |
(defn score [secret guess] | |
(let [exact (exact-matches secret guess) | |
all (all-matches secret guess)] | |
{::exact-matches exact | |
::loose-matches (- all exact)})) | |
(comment | |
(stest/instrument [`exact-matches `all-matches] | |
{:spec {`exact-matches (s/get-spec `match-count) | |
`all-matches (s/get-spec `match-count)}}) | |
(stest/summarize-results (stest/check 'codebreaker/score)) | |
(let [result (stest/summarize-results (stest/check 'codebreaker/score))] | |
(and (= (:total result) (:check-passed result)) | |
(not (contains? result :check-failed)))) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey,
was trying to code along with your blog but ran into issues related to namespaces and required libraries. In particular you must read a long way down the spec guide before you discover that the exercise function requires the test.check library. Perhaps you could update as in:
(ns codebreaker
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as gen]
[clojure.spec.test.alpha :as stest]))
;; clojure.spec requires clojure >= 1.9.0 (current is 1.9.0 alpha 17 but you should check online for latest)
;; and clojure.spec.test depends upon test.check for various methods including exercise.
;; For leiningen your project.clj must include these dependencies
;; :dependencies [ [org.clojure/clojure "1.9.0-alpha17"]
;; [org.clojure/test.check "0.9.0"] ]