 (ns clojure-learning.game-of-life)
 (require '[clojure.string :as str])
 
+;http://www.4clojure.com/problem/94
 ;Each cell interacts with its eight neighbours (horizontal, vertical, diagonal)
 ;1) Any live cell with fewer than two live neighbours dies, as if caused by under-population.
 ;2) Any live cell with two or three live neighbours lives on to the next generation.

      (map vec (gen-neighbours-by-row [row col] next-row-func))
    )))
    
(gen-neighbours-for [0 1]) 
;; ((0 0)   ..   (0 2)
;;  (1 0) (1 1)  (1 2))

matrix
(defn transpose-table->positions 
  [[head-row-with-cols & tail-rows] row-counter transposed-table-acc]
    (letfn [(generate-row-with-cols-tuple [row-counter cols-counter]
              (map vec 

                     (structure-cells head-row-with-cols row-counter)
)))))
        
;; test
(def transposed->positions
  (-> matrix
    (transpose-table->positions 0 [])))
transposed->positions
;[... [["0" [4 0]] ["0" [4 1]] ["0" [4 2]] ["0" [4 3]] ["0" [4 4]]]]

;; function inner
(defn transpose-table-w-positions->map [table-cells-w-positions]
  "function that will flatten and normalize the cells-with-positions to a map for 
   faster/easier-to-understand/constant-time lookups"
  (let [flatten-cell->pos-pairs #(partition 3 (flatten %))]
    (reduce 
      (fn [mapped-cells [cell-primitive-state & cell-position]]
        (assoc mapped-cells (vec cell-position) cell-primitive-state))
      {}
      (flatten-cell->pos-pairs table-cells-w-positions))))

;; test
(def transposed->mapped-positions (transpose-table-w-positions->map transposed->positions))
transposed->mapped-positions
;{...[2 1] "#", [4 4] "0", [1 2] "0", ... }

;; function inner
(defn get-alive-neighbours [neighbours table-of-cells-w-positions]
  "function that takes some neighbours and returns the ones for which cells are alive"
  (let [map-of-cells-w-positions (transpose-table-w-positions->map table-of-cells-w-positions)
        alive? #(= "#" %1)]
  (reduce (fn [alive-neighbours-acc neighbour]
            (if (alive? (get map-of-cells-w-positions neighbour))
              (conj alive-neighbours-acc neighbour)
              alive-neighbours-acc))
    [] neighbours)))


;; function outer-highest
(defn one-generation->map-cells
  "function that takes a table-of-cells and maps each cell to the INITIAL SEED given.
   Each cell will have it's own view of the initial-seed hence might be updated simultaneously"
  [table-of-cells-w-positions-view]
  (letfn [(convert-cell [cell] (if (= "#" cell) {:alive true} {:alive false}))
          (identify-cell-state [cell] (if (true? (:alive cell)) :alive :dead))
          
          (cell-should-born? [cell identified-alive-neighbours] 
            (and (false? (:alive cell)) (= 3 (count identified-alive-neighbours))))

          (cell-should-die? [cell identified-alive-neighbours] 
            (and (:alive cell) (or (> (count identified-alive-neighbours) 3) ;; overpopulation
                                   (< (count identified-alive-neighbours) 2)))) ;; underpopulation

          (cell-should-live? [cell identified-alive-neighbours] 
            (and (:alive cell) (or (<= (count identified-alive-neighbours) 3)
                                   (>= (count identified-alive-neighbours) 2))))
          
          (bless-cell [cell alive-neighbours]
            (cond (cell-should-born? cell alive-neighbours)
                  :alive
                  (cell-should-die? cell alive-neighbours)
                  :dead
                  (cell-should-live? cell alive-neighbours)
                  :alive
                  :else (identify-cell-state cell)))]

    (map (fn [row-cells-with-position]
        (map (fn [cell-with-position]
               (let [[cell-primitive-state cell-position] cell-with-position
                     converted-cell (convert-cell cell-primitive-state)
                     alive-neighbours (get-alive-neighbours 
                                                  (gen-neighbours-for cell-position) table-of-cells-w-positions-view)]
                 (bless-cell converted-cell alive-neighbours)
             ))
             row-cells-with-position
        ))
        table-of-cells-w-positions-view
)))

;;test
(def transposed
  (-> matrix
    (transpose-table->positions 0 [])))

;; DONE! game of life!!!!
(one-generation->map-cells transposed)
;((:dead :dead :dead :dead :dead)
; (:dead :dead :alive :dead :dead)
; (:dead :dead :alive :dead :dead)
; (:dead :dead :alive :dead :dead)
; (:dead :dead :dead :dead :dead))

;;;;;;;;;;;;;;;;;;;;;;;
;; another test-case:: => try beating clojure at structuring the code into pipelined-chaining-computations
(->
  ["      "  
   " ##   "
   " ##   "
   "   ## "
   "   ## "
   "      "]
  (replace-ws-with-char "0")
  table->matrix
  (transpose-table->positions 0 [])
  one-generation->map-cells)

;; comparing outputs::
;["      "  
; " ##   "
; " #    "
; "    # "
; "   ## "
; "      "]
;((:dead :dead :dead :dead :dead :dead)
; (:dead :alive :alive :dead :dead :dead)
; (:dead :alive :dead :dead :dead :dead)
; (:dead :dead :dead :dead :alive :dead)
; (:dead :dead :dead :alive :alive :dead)
; (:dead :dead :dead :dead :dead :dead))
;; WHOOOOOOHOOOOOO :)