diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec686b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +/target +/classes +/checkouts +profiles.clj +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +/out +/.repl +*.log +/.env +/.sass-cache diff --git a/.rebel_readline_history b/.rebel_readline_history new file mode 100644 index 0000000..e120122 --- /dev/null +++ b/.rebel_readline_history @@ -0,0 +1 @@ +1555157766311:(reset-autobuild) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7689f30 --- /dev/null +++ b/LICENSE @@ -0,0 +1,214 @@ +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and +documentation distributed under this Agreement, and + +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from +a Contributor if it was added to the Program by such Contributor itself or +anyone acting on such Contributor's behalf. Contributions do not include +additions to the Program which: (i) are separate modules of software +distributed in conjunction with the Program under their own license +agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents" mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this +Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free copyright license to +reproduce, prepare derivative works of, publicly display, publicly perform, +distribute and sublicense the Contribution of such Contributor, if any, and +such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free patent license under +Licensed Patents to make, use, sell, offer to sell, import and otherwise +transfer the Contribution of such Contributor, if any, in source code and +object code form. This patent license shall apply to the combination of the +Contribution and the Program if, at the time the Contribution is added by the +Contributor, such addition of the Contribution causes such combination to be +covered by the Licensed Patents. The patent license shall not apply to any +other combinations which include the Contribution. No hardware per se is +licensed hereunder. + +c) Recipient understands that although each Contributor grants the licenses +to its Contributions set forth herein, no assurances are provided by any +Contributor that the Program does not infringe the patent or other +intellectual property rights of any other entity. Each Contributor disclaims +any liability to Recipient for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, each Recipient hereby +assumes sole responsibility to secure any other intellectual property rights +needed, if any. For example, if a third party patent license is required to +allow Recipient to distribute the Program, it is Recipient's responsibility +to acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright license +set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and consequential +damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are offered +by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable manner on +or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within +the Program. + +Each Contributor must identify itself as the originator of its Contribution, +if any, in a manner that reasonably allows subsequent Recipients to identify +the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a +manner which does not create potential liability for other Contributors. +Therefore, if a Contributor includes the Program in a commercial product +offering, such Contributor ("Commercial Contributor") hereby agrees to defend +and indemnify every other Contributor ("Indemnified Contributor") against any +losses, damages and costs (collectively "Losses") arising from claims, +lawsuits and other legal actions brought by a third party against the +Indemnified Contributor to the extent caused by the acts or omissions of such +Commercial Contributor in connection with its distribution of the Program in +a commercial product offering. The obligations in this section do not apply +to any claims or Losses relating to any actual or alleged intellectual +property infringement. In order to qualify, an Indemnified Contributor must: +a) promptly notify the Commercial Contributor in writing of such claim, and +b) allow the Commercial Contributor tocontrol, and cooperate with the +Commercial Contributor in, the defense and any related settlement +negotiations. The Indemnified Contributor may participate in any such claim +at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If +that Commercial Contributor then makes performance claims, or offers +warranties related to Product X, those performance claims and warranties are +such Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a +court requires any other Contributor to pay any damages as a result, the +Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON +AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER +EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR +CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A +PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the +appropriateness of using and distributing the Program and assumes all risks +associated with its exercise of rights under this Agreement , including but +not limited to the risks and costs of program errors, compliance with +applicable laws, damage to or loss of data, programs or equipment, and +unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION +LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE +EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of the +remainder of the terms of this Agreement, and without further action by the +parties hereto, such provision shall be reformed to the minimum extent +necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Program itself +(excluding combinations of the Program with other software or hardware) +infringes such Recipient's patent(s), then such Recipient's rights granted +under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and +does not cure such failure in a reasonable period of time after becoming +aware of such noncompliance. If all Recipient's rights under this Agreement +terminate, Recipient agrees to cease use and distribution of the Program as +soon as reasonably practicable. However, Recipient's obligations under this +Agreement and any licenses granted by Recipient relating to the Program shall +continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to +time. No one other than the Agreement Steward has the right to modify this +Agreement. The Eclipse Foundation is the initial Agreement Steward. The +Eclipse Foundation may assign the responsibility to serve as the Agreement +Steward to a suitable separate entity. Each new version of the Agreement will +be given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly +stated in Sections 2(a) and 2(b) above, Recipient receives no rights or +licenses to the intellectual property of any Contributor under this +Agreement, whether expressly, by implication, estoppel or otherwise. All +rights in the Program not expressly granted under this Agreement are +reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial +in any resulting litigation. diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..f114831 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: java $JVM_OPTS -cp target/dodoku.jar clojure.main -m dodoku.server diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe2d76e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +This solves sudoku I hope diff --git a/env/dev/clj/dodoku/middleware.clj b/env/dev/clj/dodoku/middleware.clj new file mode 100644 index 0000000..740a1fc --- /dev/null +++ b/env/dev/clj/dodoku/middleware.clj @@ -0,0 +1,12 @@ +(ns dodoku.middleware + (:require + [ring.middleware.content-type :refer [wrap-content-type]] + [ring.middleware.params :refer [wrap-params]] + [prone.middleware :refer [wrap-exceptions]] + [ring.middleware.reload :refer [wrap-reload]] + [ring.middleware.defaults :refer [site-defaults wrap-defaults]])) + +(def middleware + [#(wrap-defaults % site-defaults) + wrap-exceptions + wrap-reload]) diff --git a/env/dev/clj/dodoku/repl.clj b/env/dev/clj/dodoku/repl.clj new file mode 100644 index 0000000..2180f4f --- /dev/null +++ b/env/dev/clj/dodoku/repl.clj @@ -0,0 +1,33 @@ +(ns dodoku.repl + (:use dodoku.handler + figwheel-sidecar.repl-api + ring.server.standalone + [ring.middleware file-info file])) + +(defonce server (atom nil)) + +(defn get-handler [] + ;; #'app expands to (var app) so that when we reload our code, + ;; the server is forced to re-resolve the symbol in the var + ;; rather than having its own copy. When the root binding + ;; changes, the server picks it up without having to restart. + (-> #'app + ; Makes static assets in $PROJECT_DIR/resources/public/ available. + (wrap-file "resources") + ; Content-Type, Content-Length, and Last Modified headers for files in body + (wrap-file-info))) + +(defn start-server + "used for starting the server in development mode from REPL" + [& [port]] + (let [port (if port (Integer/parseInt port) 3000)] + (reset! server + (serve (get-handler) + {:port port + :auto-reload? true + :join? false})) + (println (str "You can view the site at http://localhost:" port)))) + +(defn stop-server [] + (.stop @server) + (reset! server nil)) diff --git a/env/dev/clj/user.clj b/env/dev/clj/user.clj new file mode 100644 index 0000000..4ba997c --- /dev/null +++ b/env/dev/clj/user.clj @@ -0,0 +1,11 @@ +(ns user + (:require [figwheel-sidecar.repl-api :as ra])) + +(defn start-fw [] + (ra/start-figwheel!)) + +(defn stop-fw [] + (ra/stop-figwheel!)) + +(defn cljs [] + (ra/cljs-repl)) diff --git a/env/dev/cljs/dodoku/dev.cljs b/env/dev/cljs/dodoku/dev.cljs new file mode 100644 index 0000000..076d654 --- /dev/null +++ b/env/dev/cljs/dodoku/dev.cljs @@ -0,0 +1,15 @@ +(ns ^:figwheel-no-load dodoku.dev + (:require + [dodoku.core :as core] + [devtools.core :as devtools])) + +(extend-protocol IPrintWithWriter + js/Symbol + (-pr-writer [sym writer _] + (-write writer (str "\"" (.toString sym) "\"")))) + +(devtools/install!) + +(enable-console-print!) + +(core/init!) diff --git a/env/prod/clj/dodoku/middleware.clj b/env/prod/clj/dodoku/middleware.clj new file mode 100644 index 0000000..eadabf2 --- /dev/null +++ b/env/prod/clj/dodoku/middleware.clj @@ -0,0 +1,6 @@ +(ns dodoku.middleware + (:require + [ring.middleware.defaults :refer [site-defaults wrap-defaults]])) + +(def middleware + [site-defaults]) diff --git a/env/prod/cljs/dodoku/prod.cljs b/env/prod/cljs/dodoku/prod.cljs new file mode 100644 index 0000000..75a1bc8 --- /dev/null +++ b/env/prod/cljs/dodoku/prod.cljs @@ -0,0 +1,7 @@ +(ns dodoku.prod + (:require [dodoku.core :as core])) + +;;ignore println statements in prod +(set! *print-fn* (fn [& _])) + +(core/init!) diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..d0f41da --- /dev/null +++ b/project.clj @@ -0,0 +1,109 @@ +(defproject dodoku "0.1.0-SNAPSHOT" + :description "FIXME: write description" + :url "http://example.com/FIXME" + :license {:name "Eclipse Public License" + :url "http://www.eclipse.org/legal/epl-v10.html"} + + :dependencies [[org.clojure/clojure "1.10.0"] + [ring-server "0.5.0"] + [reagent "0.8.1"] + [reagent-utils "0.3.2"] + [ring "1.7.1"] + [ring/ring-defaults "0.3.2"] + [hiccup "1.0.5"] + [yogthos/config "1.1.1"] + [org.clojure/clojurescript "1.10.520" + :scope "provided"] + [metosin/reitit "0.3.1"] + [pez/clerk "1.0.0"] + [venantius/accountant "0.2.4" + :exclusions [org.clojure/tools.reader]]] + + :plugins [[lein-environ "1.1.0"] + [lein-cljsbuild "1.1.7"] + [lein-asset-minifier "0.4.6" + :exclusions [org.clojure/clojure]]] + + :ring {:handler dodoku.handler/app + :uberwar-name "dodoku.war"} + + :min-lein-version "2.5.0" + :uberjar-name "dodoku.jar" + :main dodoku.server + :clean-targets ^{:protect false} + [:target-path + [:cljsbuild :builds :app :compiler :output-dir] + [:cljsbuild :builds :app :compiler :output-to]] + + :source-paths ["src/clj" "src/cljc"] + :resource-paths ["resources" "target/cljsbuild"] + + :minify-assets + [[:css {:source "resources/public/css/site.css" + :target "resources/public/css/site.min.css"}]] + + :cljsbuild + {:builds {:min + {:source-paths ["src/cljs" "src/cljc" "env/prod/cljs"] + :compiler + {:output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js" + :source-map "target/cljsbuild/public/js/app.js.map" + :optimizations :advanced + :infer-externs true + :pretty-print false}} + :app + {:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"] + :figwheel {:on-jsload "dodoku.core/mount-root"} + :compiler + {:main "dodoku.dev" + :asset-path "/js/out" + :output-to "target/cljsbuild/public/js/app.js" + :output-dir "target/cljsbuild/public/js/out" + :source-map true + :optimizations :none + :pretty-print true}} + + + + } + } + + :figwheel + {:http-server-root "public" + :server-port 3449 + :nrepl-port 7002 + :nrepl-middleware [cider.piggieback/wrap-cljs-repl + ] + :css-dirs ["resources/public/css"] + :ring-handler dodoku.handler/app} + + + + :profiles {:dev {:repl-options {:init-ns dodoku.repl} + :dependencies [[cider/piggieback "0.4.0"] + [binaryage/devtools "0.9.10"] + [ring/ring-mock "0.3.2"] + [ring/ring-devel "1.7.1"] + [prone "1.6.1"] + [figwheel-sidecar "0.5.18"] + [nrepl "0.6.0"] + [pjstadig/humane-test-output "0.9.0"] + + ] + + :source-paths ["env/dev/clj"] + :plugins [[lein-figwheel "0.5.18"] +] + + :injections [(require 'pjstadig.humane-test-output) + (pjstadig.humane-test-output/activate!)] + + :env {:dev true}} + + :uberjar {:hooks [minify-assets.plugin/hooks] + :source-paths ["env/prod/clj"] + :prep-tasks ["compile" ["cljsbuild" "once" "min"]] + :env {:production true} + :aot :all + :omit-source true}}) diff --git a/resources/public/css/site.css b/resources/public/css/site.css new file mode 100644 index 0000000..5ab89cd --- /dev/null +++ b/resources/public/css/site.css @@ -0,0 +1,34 @@ +.body-container { + font-family: 'Helvetica Neue', Verdana, Helvetica, Arial, sans-serif; + max-width: 600px; + margin: 0 auto; + padding-top: 72px; + -webkit-font-smoothing: antialiased; + font-size: 1.125em; + color: #333; + line-height: 1.5em; +} + +h1, h2, h3 { + color: #000; +} +h1 { + font-size: 2.5em +} + +h2 { + font-size: 2em +} + +h3 { + font-size: 1.5em +} + +a { + text-decoration: none; + color: #09f; +} + +a:hover { + text-decoration: underline; +} diff --git a/src/clj/dodoku/handler.clj b/src/clj/dodoku/handler.clj new file mode 100644 index 0000000..9dbbf5d --- /dev/null +++ b/src/clj/dodoku/handler.clj @@ -0,0 +1,39 @@ +(ns dodoku.handler + (:require + [reitit.ring :as reitit-ring] + [dodoku.middleware :refer [middleware]] + [hiccup.page :refer [include-js include-css html5]] + [config.core :refer [env]])) + +(def mount-target + [:div#app + [:h2 "Welcome to dodoku"]]) + +(defn head [] + [:head + [:meta {:charset "utf-8"}] + [:meta {:name "viewport" + :content "width=device-width, initial-scale=1"}] + (include-css (if (env :dev) "/css/site.css" "/css/site.min.css"))]) + +(defn loading-page [] + (html5 + (head) + [:body {:class "body-container"} + mount-target + (include-js "/js/app.js")])) + +(defn index-handler + [_request] + {:status 200 + :headers {"Content-Type" "text/html"} + :body (loading-page)}) + +(def app + (reitit-ring/ring-handler + (reitit-ring/router + [["/" {:get {:handler index-handler}}]]) + (reitit-ring/routes + (reitit-ring/create-resource-handler {:path "/" :root "/public"}) + (reitit-ring/create-default-handler)) + {:middleware middleware})) diff --git a/src/clj/dodoku/server.clj b/src/clj/dodoku/server.clj new file mode 100644 index 0000000..e2ca4a0 --- /dev/null +++ b/src/clj/dodoku/server.clj @@ -0,0 +1,10 @@ +(ns dodoku.server + (:require + [dodoku.handler :refer [app]] + [config.core :refer [env]] + [ring.adapter.jetty :refer [run-jetty]]) + (:gen-class)) + +(defn -main [& args] + (let [port (or (env :port) 3000)] + (run-jetty app {:port port :join? false}))) diff --git a/src/cljc/dodoku/util.cljc b/src/cljc/dodoku/util.cljc new file mode 100644 index 0000000..ea498de --- /dev/null +++ b/src/cljc/dodoku/util.cljc @@ -0,0 +1 @@ +(ns dodoku.util) diff --git a/src/cljs/dodoku/.DS_Store b/src/cljs/dodoku/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/src/cljs/dodoku/.DS_Store differ diff --git a/src/cljs/dodoku/core.cljs b/src/cljs/dodoku/core.cljs new file mode 100644 index 0000000..da43938 --- /dev/null +++ b/src/cljs/dodoku/core.cljs @@ -0,0 +1,77 @@ +(ns dodoku.core + (:require + [reagent.core :as reagent :refer [atom]] + [reagent.session :as session] + [clerk.core :as clerk] + [dodoku.sudoku :as sudoku] + [accountant.core :as accountant])) + + +(def board (atom (vec (repeat 9 (vec (repeat 9 nil)))))) +(def collisions (atom #{})) + +(defn update-board [b row column value] + (let [value (if (= value "") + nil + (int value)) + new-row (assoc (nth b row) column value) + new-b (assoc b row new-row)] + new-b)) + +(defn board-cell [row column] + (fn [] + [:td + [:select + {:on-change #(swap! board update-board row column (-> % .-target .-value)) + :value (if-let [v (get-in @board [row column])] + v + "X") + :style {:background-color (if (@collisions [row column]) + "red" + "white")}} + [:option + {:key "" + :value ""} "X"] + (for [v (range 1 10)] + [:option + {:key v + :value v} + v])]])) + +(defn board-row [row] + (fn [] + [:tr + (for [i (range 9)] + [board-cell row i])])) + +(defn board-component [] + (fn [] + [:div + [:button + {:on-click #(js/alert (str @board))} + "board"] + [:button + {:on-click #(reset! collisions (sudoku/collisions @board))} + "test collisions"] + [:button + {:on-click #(swap! board sudoku/solve)} + "solve"] + [:table + {:style {:border-collapse "collapse"}} + (for [i (range 9)] + [board-row i])]])) + +(defn home-page [] + (fn [] + (board-component))) + + +;; ------------------------- +;; Initialize app + +;;(defn mount-root [] + ;;(reagent/render [current-page] (.getElementById js/document "app"))) + + +(defn init! [] + (reagent/render [home-page] (.getElementById js/document "app"))) diff --git a/src/cljs/dodoku/sudoku.cljs b/src/cljs/dodoku/sudoku.cljs new file mode 100644 index 0000000..74f3908 --- /dev/null +++ b/src/cljs/dodoku/sudoku.cljs @@ -0,0 +1,108 @@ +(ns dodoku.sudoku + (:require [clojure.set :as set])) + +(def all-values (set (range 1 10))) + +(defn value + ([b rc] + (value b (first rc) (second rc))) + ([b r c] + (nth (nth b r) c))) + +(defn row [b r] + (set (nth b r))) + +(defn column [board i] + (set (map nth board (repeat i)))) + +(defn square-index [r c] + (+ (quot c 3) (* 3 (quot r 3)))) + + +(defn- agg-by-val [m k v] + (let [new-k (apply square-index k)] + (update m new-k conj v))) + +(def indices (into [] (for [r (range 9) + c (range 9)] + [r c]))) + +(defn values [board] + (map value (repeat board) indices)) + +(reduce-kv identity {} {}) + +(defn squares [board] + (let [indices-groups (zipmap indices (values board)) + kvs (reduce-kv agg-by-val {} indices-groups)] + (into {} (for [[k v] kvs] + [k (set v)])))) + +(defn square-by-idx [b i] + (get (squares b) i)) + +(defn square [b r c] + (get (squares b) (square-index r c))) + +(defn used + ([b rc] + (used b (first rc) (second rc))) + ([b r c] + (set (concat + (row b r) + (column b c) + (square b r c))))) + +(defn candidates + ([b r c] + (let [v (value b r c)] + (if (nil? v) + (set/difference all-values (used b r c)) + #{v}))) + ([b rc] + (candidates b (first rc) (second rc)))) + +(defn solved? [b] + (let [rows (mapcat (partial row b) (range 9)) + columns (mapcat (partial column b) (range 9)) + squares (mapcat (partial square-by-idx b) (range 9))] + (= all-values + (set (concat rows columns squares))))) + +(defn set-value + ([b r c v] + (let [new-row (assoc (nth b r) c v)] + (assoc (vec b) r new-row))) + ([b rc v] + (set-value b (first rc) (second rc) v))) + +(defn colliding? [board index] + (let [board-without-self (set-value board index nil) + cands (candidates board-without-self index) + val (value board index)] + (and (not (nil? val)) + (not (cands val))))) + +(defn collisions [board] + (set (filter (partial colliding? board) indices))) + +(defn solve + ([board _indices] + (if (empty? _indices) + board + (let [index (first _indices)] + (let [candidates (candidates board index)] + (loop [removed #{}] + (when-let [valid-candidates (set/difference candidates removed)] + (when (not (empty? valid-candidates)) + (let [candidate (first valid-candidates) + next-board (set-value board index candidate) + solution (solve next-board (rest _indices))] + (if solution + solution + (recur (conj removed candidate))))))))))) + ([board] + (if (empty? (collisions board)) + (solve board indices) + (do (js/alert "Can not solve board. Please test for collisions.") + board)))) diff --git a/system.properties b/system.properties new file mode 100644 index 0000000..5e8606c --- /dev/null +++ b/system.properties @@ -0,0 +1 @@ +java.runtime.version=1.8