One of the defining traits of clojure is that its data structures are immutable. That means that board will never change, functions that operate on data structures return modified copies of the data structure. So what you've done in funcs is make a vector of three different boards, the original board with a fuction applied.
Usually what you want is to apply a bunch of functions to some initial value, where each function takes the returned value of the function before, then use the returned value for something. Usually passing it around in function parameters.
;; First of all, there's a function for that
(assoc board :a (inc (get board :a)))
;; The function update takes a map and a key and a function
;; It applies the function to value currently at key,
;; then sets key in the retuned "copy" of the map to be the return value of the function.
;; Equivalent to the above
(update board :a inc)
If you want an updated board with these functions applied, you need to to pass the return value through the functions, because the original board never changes, they all just return updated copies of their input.
(def updated-board
;; The innermost forms are evaluated first.
(update (assoc (assoc board :a 2) :b 4) :a inc))
This can be made more readable by using the -> or "thread first" macro. It takes an initial value and forms which are missing the first argument, then rewrites the code to "thread" the return value of each one as the first argument to the next.
(def updated-board-threaded
(-> board
(assoc :a 2)
(assoc :b 4)
(update :a inc)))
;; You can expand the macro to see for yourself
;; that the two examples are exactly equivalent.
(macroexpand-1 '(-> board
(assoc :a 2)
(assoc :b 4)
(update :a inc)))
;;=> (update (assoc (assoc board :a 2) :b 4) :a inc)
This is the way to "think in clojure", functions usually just take immutable values and return other immutable values.
But sometimes you need something mutable, and clojure delivers this in the form of atoms. An atom can be thought of as a mutable box that contains an immutable value.
It uses the functions swap! and reset! to apply controlled mutation. And the function deref to get the current value.
(def board (atom {:a 10, :b 12}))
;; I'll define a function that takes a board and returns an updated version of it.
(defn do-stuff-with-board [b]
(-> b
(assoc :a 2)
(assoc :b 4)
(update :a inc)))
;; Get the current value of board.
(deref board) ;;=> {:a 10, :b 12}
;; Swap takes an atom and a function and
;; sets the value of the atom to be the return value of the function
(swap! board do-stuff-with-board)
;; Now the mutable board atom contains a new immutable value.
(deref board) ;;=> {:a 3, :b 4}
;; derefing an atom is a very usual operation, so there's syntax sugar for it
;; Equivalent to (deref board)
@board ;;=> {:a 3, :b 4}
reset! sets the value of board to be another value, like = in "normal" languages. It's not usually idiomatic to do this as it kinda says to the reader that the new value of the atom has nothing to do with the old one, but clojure is pragmatic, and sometimes it's what you need.
(reset! board "And now for something completely different.")
;; The value is now a string.
@board ;;=> "And now for something completely different."
As an aside. The data structures are not actually deep copies of each other, there is magic behind the scenes to make it almost as efficient as updating the data structure in place, but from the perspective of the programmer they are equivalent to deep copies in other languages.
board, have a look at clojure atoms. Otherwise board should probably be an initial value and the new board (funcs) should be passed around in function parameters when you need it.