Added virtually the whole of the Scittle distribution, as it proves to be
virtually necessary to do development.
This commit is contained in:
parent
fcb023ecc5
commit
0a200aca94
741 changed files with 174111 additions and 11 deletions
140
resources/public/cljs/bookmarklet.cljs
Normal file
140
resources/public/cljs/bookmarklet.cljs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
(ns bookmarklet
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rdom]))
|
||||
|
||||
(defn append-tag [tag {:keys [body onload onerror] :as attributes}]
|
||||
(str "var s=document.createElement('" (name tag) "');"
|
||||
(clojure.string/join ";" (map (fn [[k v]] (str "s.setAttribute('" (name k) "','" (name v) "')")) (dissoc attributes :body :onload :onerror)))
|
||||
(when body
|
||||
(str ";s.innerText=" body))
|
||||
(when onload
|
||||
(str ";s.onload=" onload))
|
||||
(when onerror
|
||||
(str ";s.onerror=" onerror))
|
||||
";document.body.appendChild(s);"))
|
||||
|
||||
(defn pr-code [code-str]
|
||||
(pr-str (str "#_CODE_" code-str "#_CODE_")))
|
||||
|
||||
(defn read-code [code-str]
|
||||
(when-let [raw-code (second (re-find #"#_CODE_(.+)#_CODE_" code-str))]
|
||||
;; Use read-string to undo escaping of characters by pr-str (e.g. newlines)
|
||||
(read-string (str "\"" raw-code "\""))))
|
||||
|
||||
(defn load-gist [gist callback]
|
||||
(let [set-content (fn [progress-event]
|
||||
(callback (.. progress-event -srcElement -responseText)))
|
||||
oreq (js/XMLHttpRequest.)]
|
||||
(.addEventListener oreq "load" set-content)
|
||||
(.open oreq "GET" (str "https://gist.githubusercontent.com/" gist "/raw"))
|
||||
(.send oreq)))
|
||||
|
||||
|
||||
(defn bookmarklet-href [code-str]
|
||||
(str "javascript:(function(){"
|
||||
"var runCode = function() {
|
||||
try {
|
||||
scittle.core.eval_string(" (pr-code code-str) ")
|
||||
} catch (error) {
|
||||
console.log('Error in code', error);
|
||||
alert('Error running code, see console')
|
||||
}
|
||||
};"
|
||||
"if(typeof scittle === 'undefined'){"
|
||||
(append-tag :script {:src "https://babashka.github.io/scittle/js/scittle.js"
|
||||
:onerror "function(){alert('Error loading ' + this.src)}"
|
||||
:onload "runCode"})
|
||||
"} else {
|
||||
runCode() }"
|
||||
"})();"))
|
||||
|
||||
(defn query-params []
|
||||
(let [query-str (.substring js/window.location.search 1)]
|
||||
(into {}
|
||||
(map (fn [pair]
|
||||
(let [[k v] (.split pair "=" 2)]
|
||||
[(keyword (js/decodeURIComponent k))
|
||||
(js/decodeURIComponent v)])))
|
||||
(.split query-str "&"))))
|
||||
|
||||
|
||||
(def *initial-name (r/atom nil))
|
||||
(def *initial-code (r/atom nil))
|
||||
|
||||
;; Initialize code
|
||||
(let [{:keys [gist code name]} (query-params)]
|
||||
(cond gist
|
||||
(do
|
||||
(reset! *initial-name "---")
|
||||
(reset! *initial-code ";; loading from gist")
|
||||
(load-gist gist (fn [content]
|
||||
(let [[code meta-str] (reverse (clojure.string/split content #";;---+\n"))
|
||||
{bookmark-name :name} (when meta-str
|
||||
(read-string meta-str))]
|
||||
(when bookmark-name
|
||||
(reset! *initial-name bookmark-name))
|
||||
(reset! *initial-code code)))))
|
||||
code
|
||||
(do
|
||||
(reset! *initial-name (or name "My first bookmarklet"))
|
||||
(reset! *initial-code code))
|
||||
:else
|
||||
(do
|
||||
(reset! *initial-name "My first bookmarklet")
|
||||
(reset! *initial-code (str "; This is the code of your bookmarklet\n"
|
||||
(pr-str '(js/alert "Hello")))))))
|
||||
|
||||
(defn bookmark-name-field [initial-name *bookmark-name]
|
||||
(let [*name (r/atom initial-name)]
|
||||
[(fn []
|
||||
[:input {:type "text"
|
||||
:placeholder "The name of the Bookmarklet"
|
||||
:value @*name
|
||||
:on-change (fn [e]
|
||||
(let [v (.. e -target -value)]
|
||||
(reset! *name v)
|
||||
(reset! *bookmark-name
|
||||
(if (clojure.string/blank? v)
|
||||
(str "Bookmarklet " (rand-int 1000))
|
||||
v))))}])]))
|
||||
|
||||
(defn editor [*code]
|
||||
[:textarea
|
||||
{:rows 10 :cols 80
|
||||
:value @*code
|
||||
:on-drop (fn [e]
|
||||
(let [bookmarklet (js/decodeURIComponent (.. e -dataTransfer (getData "text")))
|
||||
cljs-snippet (read-code bookmarklet)
|
||||
new-code (if cljs-snippet
|
||||
(str "; Extracted snippet\n" cljs-snippet)
|
||||
(str "; Failed to extract snippet\n" bookmarklet))]
|
||||
(js/console.log "Dropped" bookmarklet)
|
||||
(set! (.. e -target -value) new-code)
|
||||
(reset! *code new-code)
|
||||
(.preventDefault e)))
|
||||
:on-change (fn [e] (reset! *code (.. e -target -value)))}])
|
||||
|
||||
|
||||
|
||||
(defn workspace []
|
||||
(let [value @*initial-code
|
||||
*code (r/atom value)
|
||||
bookmark-name @*initial-name
|
||||
*bookmark-name (r/atom bookmark-name)]
|
||||
[:div
|
||||
[bookmark-name-field bookmark-name *bookmark-name]
|
||||
[:br]
|
||||
[editor *code]
|
||||
[:br]
|
||||
[:br]
|
||||
"Click the following link or drag it to the bookmarks bar: "
|
||||
[(fn []
|
||||
[(fn [] [:a {:href (bookmarklet-href @*code)} @*bookmark-name])])
|
||||
*code]
|
||||
[:br]
|
||||
[(fn []
|
||||
[:a {:href (str "?name=" (js/encodeURIComponent @*bookmark-name)
|
||||
"&code=" (js/encodeURIComponent @*code)
|
||||
"%20")} "Copy this link to share ⤴️"])]]))
|
||||
|
||||
(rdom/render [workspace] (.getElementById js/document "app"))
|
||||
56
resources/public/cljs/codemirror.cljs
Normal file
56
resources/public/cljs/codemirror.cljs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
(require '[clojure.string :as str])
|
||||
(declare cm)
|
||||
|
||||
(defn eval-me []
|
||||
(js/scittle.core.eval_string (-> cm .-state .-doc .toString)))
|
||||
|
||||
(def extension
|
||||
(.of js/cv.keymap
|
||||
(clj->js [{:key "Mod-Enter"
|
||||
:run (fn []
|
||||
(eval-me))}
|
||||
#_{:key (str modifier "-Enter")
|
||||
:shift (partial eval-top-level on-result)
|
||||
:run (partial eval-at-cursor on-result)}])))
|
||||
(def cm
|
||||
(let [doc (str/trim "
|
||||
(require '[reagent.core :as r]
|
||||
'[reagent.dom :as rdom]
|
||||
'[re-frame.core :as rf])
|
||||
|
||||
(rf/reg-event-fx ::click (fn [{:keys [db]} _] {:db (update db :clicks (fnil inc 0))}))
|
||||
(rf/reg-sub ::clicks (fn [db] (:clicks db)))
|
||||
|
||||
(defn my-component []
|
||||
(let [clicks (rf/subscribe [::clicks])]
|
||||
[:div
|
||||
[:p \"Clicks: \" @clicks]
|
||||
[:p [:button {:on-click #(rf/dispatch [::click])}
|
||||
\"Click me!\"]]]))
|
||||
|
||||
(rdom/render [my-component] (.getElementById js/document \"reagent\"))
|
||||
")]
|
||||
(js/cm.EditorView. #js {:doc doc
|
||||
:extensions #js [js/cm.basicSetup, (js/lc.clojure), (.highest js/cs.Prec extension)]
|
||||
:parent (js/document.querySelector "#app")
|
||||
#_#_:dispatch (fn [tr] (-> cm (.update #js [tr])) (eval-me))
|
||||
})))
|
||||
(set! (.-eval_me js/globalThis) eval-me)
|
||||
(set! (.-cm_instance js/globalThis) cm)
|
||||
|
||||
(defn linux? []
|
||||
(some? (re-find #"(Linux)|(X11)" js/navigator.userAgent)))
|
||||
|
||||
(defn mac? []
|
||||
(and (not (linux?))
|
||||
(some? (re-find #"(Mac)|(iPhone)|(iPad)|(iPod)" js/navigator.platform))))
|
||||
|
||||
(let [elt (js/document.getElementById "evalMe")
|
||||
txt (.-innerText elt)
|
||||
mod-symbol (if (mac?)
|
||||
"⌘"
|
||||
"⌃")
|
||||
txt (str txt " " mod-symbol"-⏎")]
|
||||
(set! (.-innerHTML elt) txt))
|
||||
|
||||
(eval-me)
|
||||
15
resources/public/cljs/nrepl_playground.cljs
Normal file
15
resources/public/cljs/nrepl_playground.cljs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(ns nrepl-playground)
|
||||
|
||||
(+ 1 2 3)
|
||||
|
||||
(->
|
||||
(js/document.getElementsByTagName "body")
|
||||
first
|
||||
(.append
|
||||
(doto (js/document.createElement "p")
|
||||
(.append
|
||||
(js/document.createTextNode "there")))))
|
||||
|
||||
(defn foo [])
|
||||
|
||||
(js/alert "Isn't this cool? :)")
|
||||
33
resources/public/cljs/replicant_tictactoe/core.cljs
Normal file
33
resources/public/cljs/replicant_tictactoe/core.cljs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
;; COPIED FROM https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/src/tic_tac_toe/core.cljs
|
||||
|
||||
(ns replicant-tictactoe.core
|
||||
(:require [replicant.dom :as r]
|
||||
[replicant-tictactoe.game :as game]
|
||||
[replicant-tictactoe.ui :as ui]))
|
||||
|
||||
(defn start-new-game [store]
|
||||
(reset! store (game/create-game {:size 3})))
|
||||
|
||||
(defn main []
|
||||
;; Set up the atom
|
||||
(let [store (atom nil)
|
||||
el (js/document.getElementById "app")]
|
||||
|
||||
;; Globally handle DOM events
|
||||
(r/set-dispatch!
|
||||
(fn [_ [action & args]]
|
||||
(case action
|
||||
:tic (apply swap! store game/tic args)
|
||||
:reset (start-new-game store))))
|
||||
|
||||
;; Render on every change
|
||||
(add-watch store ::render
|
||||
(fn [_ _ _ game]
|
||||
(->> (ui/game->ui-data game)
|
||||
ui/render-game
|
||||
(r/render el))))
|
||||
|
||||
;; Trigger the first render by initializing the game.
|
||||
(start-new-game store)))
|
||||
|
||||
(main)
|
||||
41
resources/public/cljs/replicant_tictactoe/game.cljs
Normal file
41
resources/public/cljs/replicant_tictactoe/game.cljs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
;; COPIED FROM https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/src/tic_tac_toe/game.cljs
|
||||
|
||||
(ns replicant-tictactoe.game)
|
||||
|
||||
(defn create-game [{:keys [size]}]
|
||||
{:next-player :x
|
||||
:size size})
|
||||
|
||||
(def next-player {:x :o, :o :x})
|
||||
|
||||
(defn winner? [tics path]
|
||||
(when (= 1 (count (set (map tics path))))
|
||||
path))
|
||||
|
||||
(defn get-winning-path [{:keys [size tics]} y x]
|
||||
(or (winner? tics (mapv #(vector y %) (range 0 size)))
|
||||
(winner? tics (mapv #(vector % x) (range 0 size)))
|
||||
(when (= y x)
|
||||
(winner? tics (mapv #(vector % %) (range 0 size))))))
|
||||
|
||||
(defn maybe-conclude [game y x]
|
||||
(if-let [path (get-winning-path game y x)]
|
||||
(-> (dissoc game :next-player)
|
||||
(assoc :over? true
|
||||
:victory {:player (get-in game [:tics [y x]])
|
||||
:path path}))
|
||||
(let [tie? (= (count (:tics game)) (* (:size game) (:size game)))]
|
||||
(cond-> game
|
||||
tie? (dissoc :next-player)
|
||||
tie? (assoc :over? true)))))
|
||||
|
||||
(defn tic [game y x]
|
||||
(let [player (:next-player game)]
|
||||
(if (or (get-in game [:tics [y x]])
|
||||
(<= (:size game) x)
|
||||
(<= (:size game) y))
|
||||
game
|
||||
(-> game
|
||||
(assoc-in [:tics [y x]] player)
|
||||
(assoc :next-player (next-player player))
|
||||
(maybe-conclude y x)))))
|
||||
50
resources/public/cljs/replicant_tictactoe/style.css
Normal file
50
resources/public/cljs/replicant_tictactoe/style.css
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* COPIED from https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/resources/public/styles.css */
|
||||
.cell {
|
||||
aspect-ratio: 1 / 1;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 6%;
|
||||
border: none;
|
||||
display: block;
|
||||
flex: 1 1 0%;
|
||||
outline: none;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cell-content {
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s;
|
||||
}
|
||||
|
||||
.transparent {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.cell-dim {
|
||||
background: rgba(249, 249, 240, 0.3);
|
||||
}
|
||||
|
||||
.cell-highlight {
|
||||
background: #fcfcf3;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.board {
|
||||
--gap: 0.75rem;
|
||||
background: #833ab4;
|
||||
background: linear-gradient(90deg, #833ab4 0%, #fd1d1d 50%, #fcb045 100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--gap);
|
||||
padding: var(--gap);
|
||||
max-width: 80vh;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--gap);
|
||||
}
|
||||
75
resources/public/cljs/replicant_tictactoe/ui.cljs
Normal file
75
resources/public/cljs/replicant_tictactoe/ui.cljs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
;; COPIED FROM https://github.com/cjohansen/replicant-tic-tac-toe/blob/7a33fb12f0cd6658b2f555ff673dee031d4aa921/src/tic_tac_toe/ui.cljs
|
||||
|
||||
(ns replicant-tictactoe.ui)
|
||||
|
||||
(def mark-x
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg"
|
||||
:viewBox "0 -10 108 100"}
|
||||
[:path
|
||||
{:fill "currentColor"
|
||||
:d "m1.753 69.19.36-1.08q.35-1.09 1.92-2.97 1.58-1.87 3.85-3.84 2.29-1.97 4.6-3.54 2.31-1.57 4.93-3.24 2.62-1.66 4.65-2.9 2.04-1.23 3.91-2.27 1.87-1.05 3.98-2.31 2.11-1.27 4.12-2.5 2.01-1.24 4.33-2.51l4.6-2.52q2.27-1.25 4.84-2.86 2.56-1.62 5.03-3.09 2.47-1.47 4.5-2.88 2.03-1.4 3.82-2.82t3.81-3.47q2.01-2.06 3.7-3.51 1.69-1.46 3.47-3.03 1.77-1.57 4.01-3.69 2.24-2.11 4.13-3.7 1.89-1.58 3.93-2.97 2.04-1.39 4.05-2.49 2.01-1.11 5.26-2.54 3.24-1.44 4.48-1.46 1.24-.01 2.42.37 1.18.37 2.18 1.11 1 .74 1.71 1.75.71 1.02 1.06 2.21.34 1.19.3 2.43-.05 1.24-.5 2.39-.44 1.16-1.23 2.12-.79.95-1.84 1.61-1.05.65-2.26.94-1.21.28-2.44.16-1.23-.11-2.37-.62-1.13-.5-2.04-1.34-.91-.84-1.51-1.93-.6-1.08-.81-2.3-.22-1.22-.04-2.45.18-1.23.75-2.33.56-1.1 1.45-1.97.89-.86 2.01-1.4 1.11-.54 2.35-.69 1.23-.15 2.44.1t2.29.87q1.07.63 1.88 1.56.82.93 1.29 2.08.48 1.14.56 2.38.09 1.24-.23 2.44-.31 1.19-.99 2.23-.68 1.04-1.66 1.8-.98.76-2.15 1.18l-1.16.41-2.28 1.17q-2.28 1.18-4.38 2.7-2.1 1.51-4.2 3.44-2.1 1.92-4.18 3.7-2.08 1.77-3.9 3.44-1.81 1.68-3.41 3.13-1.6 1.46-3.38 3.09-1.79 1.62-3.44 2.97-1.66 1.34-3.53 2.4-1.88 1.06-4.17 2.65-2.3 1.6-4.79 2.74-2.48 1.14-4.98 2.71-2.5 1.57-4.51 2.47-2.01.9-3.99 1.87-1.98.97-3.88 2.02-1.91 1.05-4.38 2.34-2.46 1.28-4.94 2.53-2.47 1.25-4.48 2.38-2 1.12-3.96 2.14-1.95 1.01-3.83 1.99-1.89.98-4.37 2.05-2.48 1.06-2.96 2.01-.48.96-.78 1.49-.3.53-.71.97-.41.44-.92.77-.51.34-1.09.54-.57.2-1.17.25-.6.06-1.2-.03t-1.16-.32q-.56-.23-1.05-.59-.49-.35-.89-.82-.39-.46-.65-1.01-.27-.54-.4-1.14-.13-.59-.12-1.19.02-.6.18-1.19l.16-.59Z"}]
|
||||
[:path
|
||||
{:fill "currentColor"
|
||||
:d "m28.099 4.991 2.69 1.97q2.69 1.96 4.5 3.22 1.8 1.28 4.54 3.46 2.74 2.18 4.57 3.89t3.38 3.72q1.54 2.02 2.88 4.3 1.34 2.28 2.83 4.46 1.48 2.18 2.63 4.14 1.15 1.96 2.74 4.07 1.59 2.1 3.59 4.19 1.99 2.08 4.23 4.48 2.24 2.4 3.7 4.04 1.47 1.64 2.91 3.23 1.44 1.59 3.08 3.58 1.64 1.99 3.51 4.08 1.87 2.09 3.55 3.77 1.69 1.68 4.1 3.51 2.42 1.83 3.9 2.58 1.48.74 2.14 1.34.66.6 1.15 1.33.5.74.8 1.57.31.84.4 1.72.1.88-.02 1.76-.12.88-.44 1.71-.33.82-.84 1.55-.51.72-1.19 1.3-.67.58-1.46.98-.79.41-1.65.61-.87.2-1.76.19-.88-.01-1.74-.24-.86-.22-1.64-.64-.78-.42-2.27-2.72-1.48-2.3-1.52-3.49-.03-1.19.31-2.33.35-1.14 1.04-2.11.69-.97 1.66-1.67.96-.7 2.1-1.05 1.14-.35 2.33-.32 1.19.02 2.31.43t2.05 1.15q.93.75 1.58 1.75.64 1 .93 2.15.29 1.16.2 2.35-.09 1.18-.56 2.28-.47 1.1-1.26 1.99-.79.88-1.83 1.47t-2.2.82q-1.17.23-2.35.07-1.19-.16-2.25-.68-1.07-.53-1.92-1.37-.84-.84-1.37-1.9-.54-1.07-.7-2.25-.17-1.18.06-2.35.22-1.17.8-2.21.58-1.04 1.47-1.84.88-.79 1.98-1.27 1.09-.47 2.28-.57 1.18-.1 2.34.18 1.16.29 2.16.93 1.01.63 1.76 1.56.74.93-.33-.26-1.07-1.18-.41-.58.66.59 1.15 1.33.5.74.8 1.57.31.83.4 1.72.1.88-.02 1.76-.12.88-.44 1.7-.33.83-.84 1.55-.51.73-1.19 1.31-.67.58-1.46.98-.79.41-1.65.61-.87.2-1.75.19-.89-.01-1.75-.24-.86-.22-1.64-.64-.78-.42-2.73-1.57-1.95-1.14-4.26-2.95-2.31-1.8-3.87-3.43-1.57-1.62-3.17-3.29-1.6-1.66-3.55-4.05-1.95-2.39-3.33-4.15-1.39-1.76-2.77-3.4-1.38-1.64-3.07-3.56-1.7-1.91-3.91-4.13-2.2-2.22-3.74-4.1-1.54-1.88-2.79-3.75-1.24-1.87-2.4-4.33t-2.39-4.46q-1.23-2.01-2.4-4.59-1.17-2.59-2.53-5.01-1.36-2.43-3.35-4.44-1.99-2.02-4.52-4.27-2.54-2.25-5.33-4.04-2.81-1.79-3.28-2.21-.47-.41-.83-.92-.35-.51-.58-1.1-.22-.58-.3-1.2-.08-.62-.01-1.23.08-.62.29-1.21.22-.58.58-1.1.35-.51.81-.93.47-.42 1.02-.71t1.16-.45q.61-.15 1.23-.15t1.22.14q.61.15 1.17.44l.55.28Z"}]])
|
||||
|
||||
(def mark-o
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg"
|
||||
:viewBox "0 0 114 114"}
|
||||
[:path
|
||||
{:fill "none"
|
||||
:stroke "currentColor"
|
||||
:stroke-linecap "round"
|
||||
:stroke-width "6"
|
||||
:d "M74.616 8.935c7.73 2.38 15.96 9.34 21.58 16.04 5.63 6.69 10.57 15.46 12.18 24.11 1.6 8.65.74 19.67-2.53 27.77-3.27 8.11-10.12 15.37-17.09 20.88-6.98 5.51-16.07 10.81-24.76 12.17-8.7 1.35-19.32-.76-27.42-4.06-8.1-3.29-15.73-8.93-21.21-15.73-5.48-6.81-10.32-16.5-11.67-25.09-1.35-8.6.19-18.39 3.57-26.51 3.38-8.11 9.99-16.6 16.71-22.19 6.72-5.59 13.95-10.52 23.63-11.36 9.68-.84 28.04 4.34 34.45 6.32 6.42 1.97 4.37 4.6 4.04 5.55m-48.33-9.69c7.65-3.32 19.78-3.63 28.63-2.01 8.86 1.63 17.85 5.89 24.49 11.76 6.64 5.87 12.7 15.08 15.37 23.48 2.67 8.41 2.5 18.4.65 26.95-1.85 8.54-5.98 17.59-11.77 24.34-5.78 6.74-14.56 13.05-22.93 16.11-8.37 3.06-18.75 4.19-27.29 2.25-8.54-1.93-17.37-7.89-23.96-13.87-6.59-5.97-12.89-13.58-15.57-21.96-2.69-8.39-2.31-19.94-.56-28.34 1.75-8.4 5.21-15.74 11.06-22.09 5.85-6.35 19.92-13.32 24.04-16.01 4.12-2.7.37-1.1.67-.16"}]])
|
||||
|
||||
(defn render-cell [{:keys [content on-click dim? highlight? clickable?]}]
|
||||
[:button.cell
|
||||
{:on {:click on-click}
|
||||
:class (cond-> []
|
||||
dim? (conj "cell-dim")
|
||||
highlight? (conj "cell-highlight")
|
||||
clickable? (conj "clickable"))}
|
||||
(when content
|
||||
[:div.cell-content
|
||||
{:replicant/mounting {:class "transparent"}
|
||||
:replicant/unmounting {:class "transparent"}}
|
||||
content])])
|
||||
|
||||
(defn render-board [{:keys [rows]}]
|
||||
[:div.board
|
||||
(for [row rows]
|
||||
[:div.row
|
||||
(for [cell row]
|
||||
(render-cell cell))])])
|
||||
|
||||
(defn render-game [{:keys [board button]}]
|
||||
[:div
|
||||
(render-board board)
|
||||
(when button
|
||||
[:button {:on {:click (:on-click button)}
|
||||
:style {:margin-top 20
|
||||
:font-size 20}}
|
||||
(:text button)])])
|
||||
|
||||
(def player->mark
|
||||
{:x mark-x
|
||||
:o mark-o})
|
||||
|
||||
(defn game->ui-data [{:keys [size tics victory over?]}]
|
||||
(let [highlight? (set (:path victory))]
|
||||
{:button (when over?
|
||||
{:text "Start over"
|
||||
:on-click [:reset]})
|
||||
:board
|
||||
{:rows
|
||||
(for [y (range size)]
|
||||
(for [x (range size)]
|
||||
(if-let [player (get tics [y x])]
|
||||
(let [victorious? (highlight? [y x])]
|
||||
(cond-> {:content (player->mark player)}
|
||||
victorious? (assoc :highlight? true)
|
||||
(and over? (not victorious?)) (assoc :dim? true)))
|
||||
(if over?
|
||||
{:dim? true}
|
||||
{:clickable? true
|
||||
:on-click [:tic y x]}))))}}))
|
||||
2
resources/public/cljs/script.cljs
Normal file
2
resources/public/cljs/script.cljs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
(defn my-alert2 []
|
||||
(js/alert "My alert 2!"))
|
||||
85
resources/public/cljs/tictactoe.cljs
Normal file
85
resources/public/cljs/tictactoe.cljs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
(ns tictactoe
|
||||
"Ported from https://github.com/borkdude/tictactoe-cljs"
|
||||
(:require [reagent.core :as r]
|
||||
[reagent.dom :as rdom]))
|
||||
|
||||
(def empty-board [[\- \- \-]
|
||||
[\- \- \-]
|
||||
[\- \- \-]])
|
||||
|
||||
(def state (r/atom {:board empty-board :player \X}))
|
||||
|
||||
(defn get-board-cell
|
||||
([board row col]
|
||||
(get-in board [row col])))
|
||||
|
||||
(defn get-player [app-state]
|
||||
(-> app-state :game-state :player))
|
||||
|
||||
(defn other-player [player]
|
||||
(if (= player \X) \O \X))
|
||||
|
||||
(defn winner-in-rows? [board player]
|
||||
(boolean (some (fn [row] (every? (fn [c] (= c player)) row)) board)))
|
||||
|
||||
(defn transposed-board [board]
|
||||
(vec (apply map vector board)))
|
||||
|
||||
(defn winner-in-cols? [board player]
|
||||
(winner-in-rows? (transposed-board board) player))
|
||||
|
||||
(defn winner-in-diagonals? [board player]
|
||||
(let [diag-coords [[[0 0] [1 1] [2 2]]
|
||||
[[0 2] [1 1] [2 0]]]]
|
||||
(boolean (some (fn [coords]
|
||||
(every? (fn [coord]
|
||||
(= player (apply get-board-cell board coord)))
|
||||
coords))
|
||||
diag-coords))))
|
||||
|
||||
(defn winner?
|
||||
"checks if there is a winner. when called with no args, checks for player X and player O.
|
||||
returns the character for the winning player, nil if there is no winner"
|
||||
([board]
|
||||
(boolean (or (winner? board \X)
|
||||
(winner? board \O))))
|
||||
([board player]
|
||||
(when (or (winner-in-rows? board player)
|
||||
(winner-in-cols? board player)
|
||||
(winner-in-diagonals? board player))
|
||||
player)))
|
||||
|
||||
(defn full-board?
|
||||
[board]
|
||||
(let [all-cells (apply concat board)]
|
||||
(not-any? #(= % \-) all-cells)))
|
||||
|
||||
(defn new-state [old-state row col]
|
||||
(if (and (= (get-board-cell (:board old-state) row col) \-)
|
||||
(not (winner? (:board old-state))))
|
||||
{:board (assoc-in (:board old-state) [row col] (:player old-state))
|
||||
:player (other-player (:player old-state))}
|
||||
old-state))
|
||||
|
||||
(defn tictactoe []
|
||||
[:div
|
||||
(if (winner? (:board @state))
|
||||
(str "The winner is " (other-player (:player @state)))
|
||||
(if (full-board? (:board @state))
|
||||
"It's a draw"
|
||||
(str "Your turn, player " (:player @state))))
|
||||
(let [board (-> @state :board)]
|
||||
[:table
|
||||
[:tbody
|
||||
(map-indexed
|
||||
(fn [i row]
|
||||
^{:key i}
|
||||
[:tr
|
||||
(map-indexed (fn [j elt]
|
||||
^{:key j}
|
||||
[:td {:on-click (fn []
|
||||
(swap! state new-state i j))}elt])
|
||||
row)])
|
||||
board)]])])
|
||||
|
||||
(rdom/render [tictactoe] (.getElementById js/document "app"))
|
||||
Loading…
Add table
Add a link
Reference in a new issue